{"action":"created","comment":{"url":"https://api.github.com/repos/softdevteam/hwtracer/pulls/comments/176371272","pull_request_review_id":106059003,"id":176371272,"diff_hunk":"@@ -35,13 +35,317 @@\n // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n // SOFTWARE.\n \n+#define _GNU_SOURCE\n+\n+#include <stdio.h>\n #include <intel-pt.h>\n+#include <pt_cpu.h>\n+#include <stdbool.h>\n+#include <inttypes.h>\n+#include <stdint.h>\n+#include <link.h>\n+#include <errno.h>\n+#include <stdlib.h>\n+#include <unistd.h>\n \n-void\n-perf_pt_start_decode(void *buf) {\n-    // XXX This function body is only here to check for correct libipt linkage\n-    // at this time.\n-    (void) buf;\n+#include \"perf_pt_private.h\"\n+\n+struct load_self_image_args {\n+    struct pt_image *image;\n+    int vdso_fd;\n+    char *vdso_filename;\n+};\n+\n+// Private prototypes.\n+static int drain_events(struct pt_block_decoder *, int);\n+static bool load_self_image(struct load_self_image_args *);\n+static int load_self_image_cb(struct dl_phdr_info *, size_t, void *);\n+\n+// Public prototypes.\n+void *perf_pt_start_decode(void *, uint64_t, int, char *, int *);\n+bool perf_pt_next_block(struct pt_block_decoder *, int *, uint64_t *);\n+void perf_pt_free_decoder(struct pt_block_decoder *);\n+\n+/*\n+ * Start decoding a PT trace.\n+ *\n+ * Accepts a raw buffer `buf` of length `len`.\n+ *\n+ * `vdso_fd` is an open file descriptor for the filename `vdso_filename`. This\n+ * is where the VDSO code will be written. libipt will read this file lazily,\n+ * so it's up to the caller to make sure this file lives long enough for their\n+ * purposes.\n+ *\n+ * `*decoder_status` will be updated to reflect the status of the decoder after\n+ * it has been synchronised.\n+ *\n+ * Returns a pointer to a configured libipt block decoder or NULL on error.\n+ *\n+ * XXX This function assumes that we are decoding a trace for the current CPU\n+ * and for the current process.\n+ */\n+void *\n+perf_pt_start_decode(void *buf, uint64_t len, int vdso_fd, char *vdso_filename,\n+                     int *decoder_status) {\n+    bool failing = false;\n+\n+    // Make a block decoder configuration.\n     struct pt_config config;\n-    pt_blk_alloc_decoder(&config);\n+    memset(&config, 0, sizeof(config));\n+    config.size = sizeof(config);\n+    config.begin = buf;\n+    config.end = buf + len;\n+    config.flags.variant.block.end_on_call = 1;\n+    config.flags.variant.block.end_on_jump = 1;\n+\n+    // Decode for the current CPU.\n+    struct pt_block_decoder *decoder = NULL;\n+    if (pt_cpu_read(&config.cpu) != 0) {\n+        failing = true;\n+        goto clean;\n+    }\n+\n+    // Work around CPU bugs.\n+    if (config.cpu.vendor) {\n+        if (pt_cpu_errata(&config.errata, &config.cpu) < 0) {\n+            failing = true;\n+            goto clean;\n+        }\n+    }\n+\n+    // Instantiate a decoder.\n+    decoder = pt_blk_alloc_decoder(&config);\n+    if (decoder == NULL) {\n+        failing = true;\n+        goto clean;\n+    }\n+\n+    // Sync the decoder.\n+    *decoder_status = pt_blk_sync_forward(decoder);\n+    if (*decoder_status == -pte_eos) {\n+        // There were no blocks in the stream. The user will find out on next\n+        // call to perf_pt_next_block().\n+        goto clean;\n+    } else if (*decoder_status < 0) {\n+        failing = true;\n+        goto clean;\n+    }\n+\n+    // Build and load a memory image from which to recover control flow.\n+    struct pt_image *image = pt_image_alloc(NULL);\n+    if (image == NULL) {\n+        failing = true;\n+        goto clean;\n+    }\n+\n+    struct load_self_image_args load_args = {image, vdso_fd, vdso_filename};\n+    if (!load_self_image(&load_args)) {\n+        failing = true;\n+        goto clean;\n+    }\n+\n+    if (pt_blk_set_image(decoder, image) < 0) {\n+        failing = true;\n+        goto clean;\n+    }\n+\n+clean:\n+    if (failing) {\n+        pt_blk_free_decoder(decoder);\n+        return NULL;\n+    }\n+    return decoder;\n+}\n+\n+/*\n+ * Updates `*addr' with the start address of the next block in the instruction\n+ * stream.\n+ *\n+ * A start address of 0 indicates that the end of the instruction stream has\n+ * been reached.\n+ *\n+ * `*decoder_status` will be updated with the new decoder status after the operation.\n+ *\n+ * Returns true on success or false otherwise. Upon failure, the start address\n+ * is undefined.\n+ */\n+bool\n+perf_pt_next_block(struct pt_block_decoder *decoder, int *decoder_status,\n+                   uint64_t *addr) {\n+    // If there are events pending, drain them first.\n+    *decoder_status = drain_events(decoder, *decoder_status);\n+    if (*decoder_status < 0) {\n+        // Error.\n+        return false;\n+    } else if (*decoder_status & pts_eos) {\n+        // End of stream.\n+        *addr = 0;\n+        return true;\n+    } else if (*decoder_status > 0) {\n+        // An unhandled case.\n+        return false;\n+    }\n+\n+    // Now fetch the block information.\n+    struct pt_block block;\n+    *decoder_status = pt_blk_next(decoder, &block, sizeof(block));\n+    // Other +ve decoder status codes can arise here. We ignore them for now,\n+    // and let them be detected by drain_events() above when we are next\n+    // called.\n+    if (*decoder_status < 0) {\n+        if (*decoder_status == -pte_eos) {\n+            // End of stream is flagged as an error in the case of pt_blk_next().\n+            *addr = 0;\n+            return true;\n+        } else {\n+            // A real error.\n+            return false;\n+        }\n+    }\n+\n+    if (block.truncated == 1) {\n+        // XXX A truncated block. This occurs when a block straddles a section\n+        // boundary. In this case we may need some extra logic, but this should\n+        // be a rare case.\n+        return false;\n+    }\n+\n+    if (block.ninsn == 0) {\n+        // A block with zero instructions. Shouldn't happen.\n+        return false;\n+    }\n+\n+    *addr = block.ip;\n+    return true;\n+}\n+\n+/*\n+ * Given a decoder and the last decoder status, drains events in the PT packet\n+ * stream.\n+ *\n+ * The new decoder status is returned.\n+ */\n+static int\n+drain_events(struct pt_block_decoder *decoder, int decoder_status) {\n+    while(decoder_status & pts_event_pending) {\n+        struct pt_event event;\n+        decoder_status = pt_blk_event(decoder, &event, sizeof(event));\n+        if (decoder_status < 0) {\n+            return decoder_status;\n+        }\n+    }\n+    return decoder_status;\n+}\n+\n+/*\n+ * Loads the libipt image `image` with the code of the current process.\n+ *\n+ * Returns true on success or false otherwise.\n+ */\n+static bool\n+load_self_image(struct load_self_image_args *args)\n+{\n+    bool ret = dl_iterate_phdr(load_self_image_cb, args) == 0;\n+    if (fsync(args->vdso_fd) == -1) {\n+        ret = false;\n+    }\n+    return ret;\n+}\n+\n+/*\n+ * The callback for `load_self_image()`, called once for each program header.\n+ *\n+ * Returns 1 to stop iterating, and in our case to indicate an error. Returns 0\n+ * on success and to continue iterating. See dl_iterate_phdr(3) for information\n+ * on this interface.\n+ */\n+static int\n+load_self_image_cb(struct dl_phdr_info *info, size_t size, void *data)\n+{\n+    ElfW(Phdr) phdr;\n+    ElfW(Half) i;\n+\n+    (void) size;\n+    struct load_self_image_args *args = data;\n+\n+    const char *filename = info->dlpi_name;\n+    bool vdso = false;\n+    if (!*filename) {\n+        // On Linux, an empty name means that it is the executable itself.\n+        filename = program_invocation_name;\n+    } else {\n+        vdso = strcmp(filename, VDSO_NAME) == 0;\n+    }\n+\n+    for (i = 0; i < info->dlpi_phnum; i++) {\n+        phdr = info->dlpi_phdr[i];\n+        // Ensure we only use loadable and executable sections.\n+        if ((phdr.p_type != PT_LOAD) || (!(phdr.p_flags & PF_X))) {\n+            continue;\n+        }\n+\n+        uint64_t vaddr = info->dlpi_addr + phdr.p_vaddr;\n+        uint64_t offset;\n+\n+        // Load the code into the libipt image.\n+        //\n+        // The VDSO is special. It doesn't exist on-disk as a regular library,\n+        // but rather it is a set of pages shared with the kernel.\n+        //\n+        // XXX Since libipt currently requires us to load from a file, we have\n+        // to dump the VDSO to disk and have libipt load it back in.\n+        //\n+        // Discussion on adding libipt support for loading from memory here:\n+        // https://github.com/01org/processor-trace/issues/37\n+        if (vdso) {\n+            int rv = dump_vdso(args->vdso_fd, vaddr, phdr.p_filesz);\n+            if (!rv) {\n+                return 1;\n+            }\n+            filename = args->vdso_filename;\n+            offset = 0;\n+        } else {\n+            offset = phdr.p_offset;\n+        }\n+\n+        // XXX This could be made faster using a libipt instruction cache.\n+        int rv = pt_image_add_file(args->image, filename, offset,\n+                                   phdr.p_filesz, NULL, vaddr);\n+        if (rv < 0) {\n+            return 1;\n+        }\n+    }\n+\n+    return 0;\n+}\n+\n+/*\n+ * Dump the VDSO code into the open file descriptor `fd`, starting at `vaddr`\n+ * and of size `len` into a temp file.\n+ *\n+ * Returns true on success or false otherwise.\n+ */\n+bool\n+dump_vdso(int fd, uint64_t vaddr, size_t len)\n+{\n+    size_t wrote_so_far = 0;","path":"src/backends/perf_pt/decode.c","position":303,"original_position":303,"commit_id":"ffa6ee0a521aa5f201d446a2c4aff4a979b3e3cb","original_commit_id":"ffa6ee0a521aa5f201d446a2c4aff4a979b3e3cb","user":{"login":"vext01","id":604955,"avatar_url":"https://avatars2.githubusercontent.com/u/604955?v=4","gravatar_id":"","url":"https://api.github.com/users/vext01","html_url":"https://github.com/vext01","followers_url":"https://api.github.com/users/vext01/followers","following_url":"https://api.github.com/users/vext01/following{/other_user}","gists_url":"https://api.github.com/users/vext01/gists{/gist_id}","starred_url":"https://api.github.com/users/vext01/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vext01/subscriptions","organizations_url":"https://api.github.com/users/vext01/orgs","repos_url":"https://api.github.com/users/vext01/repos","events_url":"https://api.github.com/users/vext01/events{/privacy}","received_events_url":"https://api.github.com/users/vext01/received_events","type":"User","site_admin":false},"body":"I like it!","created_at":"2018-03-22T10:30:12Z","updated_at":"2018-03-22T10:30:12Z","html_url":"https://github.com/softdevteam/hwtracer/pull/46#discussion_r176371272","pull_request_url":"https://api.github.com/repos/softdevteam/hwtracer/pulls/46","author_association":"OWNER","_links":{"self":{"href":"https://api.github.com/repos/softdevteam/hwtracer/pulls/comments/176371272"},"html":{"href":"https://github.com/softdevteam/hwtracer/pull/46#discussion_r176371272"},"pull_request":{"href":"https://api.github.com/repos/softdevteam/hwtracer/pulls/46"}},"in_reply_to_id":176362193},"pull_request":{"url":"https://api.github.com/repos/softdevteam/hwtracer/pulls/46","id":176514648,"html_url":"https://github.com/softdevteam/hwtracer/pull/46","diff_url":"https://github.com/softdevteam/hwtracer/pull/46.diff","patch_url":"https://github.com/softdevteam/hwtracer/pull/46.patch","issue_url":"https://api.github.com/repos/softdevteam/hwtracer/issues/46","number":46,"state":"open","locked":false,"title":"Block decoding support.","user":{"login":"vext01","id":604955,"avatar_url":"https://avatars2.githubusercontent.com/u/604955?v=4","gravatar_id":"","url":"https://api.github.com/users/vext01","html_url":"https://github.com/vext01","followers_url":"https://api.github.com/users/vext01/followers","following_url":"https://api.github.com/users/vext01/following{/other_user}","gists_url":"https://api.github.com/users/vext01/gists{/gist_id}","starred_url":"https://api.github.com/users/vext01/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vext01/subscriptions","organizations_url":"https://api.github.com/users/vext01/orgs","repos_url":"https://api.github.com/users/vext01/repos","events_url":"https://api.github.com/users/vext01/events{/privacy}","received_events_url":"https://api.github.com/users/vext01/received_events","type":"User","site_admin":false},"body":"This adds support for block decoding into the perf_pt backend.\r\n\r\nIt's a large change and I'm pretty certain you will be able to find some nits! Let the discussion begin!\r\n\r\nAnything `XXX` is either Rust/Cargo bugs/limitations, or something which I think can be tackled later in later PRs.\r\n\r\nFixes #28 ","created_at":"2018-03-21T15:31:04Z","updated_at":"2018-03-22T10:30:12Z","closed_at":null,"merged_at":null,"merge_commit_sha":"614bf0480c32d85cb205853b6fa369d28a879ea5","assignee":{"login":"ltratt","id":20318,"avatar_url":"https://avatars1.githubusercontent.com/u/20318?v=4","gravatar_id":"","url":"https://api.github.com/users/ltratt","html_url":"https://github.com/ltratt","followers_url":"https://api.github.com/users/ltratt/followers","following_url":"https://api.github.com/users/ltratt/following{/other_user}","gists_url":"https://api.github.com/users/ltratt/gists{/gist_id}","starred_url":"https://api.github.com/users/ltratt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ltratt/subscriptions","organizations_url":"https://api.github.com/users/ltratt/orgs","repos_url":"https://api.github.com/users/ltratt/repos","events_url":"https://api.github.com/users/ltratt/events{/privacy}","received_events_url":"https://api.github.com/users/ltratt/received_events","type":"User","site_admin":false},"assignees":[{"login":"ltratt","id":20318,"avatar_url":"https://avatars1.githubusercontent.com/u/20318?v=4","gravatar_id":"","url":"https://api.github.com/users/ltratt","html_url":"https://github.com/ltratt","followers_url":"https://api.github.com/users/ltratt/followers","following_url":"https://api.github.com/users/ltratt/following{/other_user}","gists_url":"https://api.github.com/users/ltratt/gists{/gist_id}","starred_url":"https://api.github.com/users/ltratt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ltratt/subscriptions","organizations_url":"https://api.github.com/users/ltratt/orgs","repos_url":"https://api.github.com/users/ltratt/repos","events_url":"https://api.github.com/users/ltratt/events{/privacy}","received_events_url":"https://api.github.com/users/ltratt/received_events","type":"User","site_admin":false}],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/softdevteam/hwtracer/pulls/46/commits","review_comments_url":"https://api.github.com/repos/softdevteam/hwtracer/pulls/46/comments","review_comment_url":"https://api.github.com/repos/softdevteam/hwtracer/pulls/comments{/number}","comments_url":"https://api.github.com/repos/softdevteam/hwtracer/issues/46/comments","statuses_url":"https://api.github.com/repos/softdevteam/hwtracer/statuses/ffa6ee0a521aa5f201d446a2c4aff4a979b3e3cb","head":{"label":"softdevteam:decode","ref":"decode","sha":"ffa6ee0a521aa5f201d446a2c4aff4a979b3e3cb","user":{"login":"softdevteam","id":10271085,"avatar_url":"https://avatars0.githubusercontent.com/u/10271085?v=4","gravatar_id":"","url":"https://api.github.com/users/softdevteam","html_url":"https://github.com/softdevteam","followers_url":"https://api.github.com/users/softdevteam/followers","following_url":"https://api.github.com/users/softdevteam/following{/other_user}","gists_url":"https://api.github.com/users/softdevteam/gists{/gist_id}","starred_url":"https://api.github.com/users/softdevteam/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/softdevteam/subscriptions","organizations_url":"https://api.github.com/users/softdevteam/orgs","repos_url":"https://api.github.com/users/softdevteam/repos","events_url":"https://api.github.com/users/softdevteam/events{/privacy}","received_events_url":"https://api.github.com/users/softdevteam/received_events","type":"Organization","site_admin":false},"repo":{"id":111116225,"name":"hwtracer","full_name":"softdevteam/hwtracer","owner":{"login":"softdevteam","id":10271085,"avatar_url":"https://avatars0.githubusercontent.com/u/10271085?v=4","gravatar_id":"","url":"https://api.github.com/users/softdevteam","html_url":"https://github.com/softdevteam","followers_url":"https://api.github.com/users/softdevteam/followers","following_url":"https://api.github.com/users/softdevteam/following{/other_user}","gists_url":"https://api.github.com/users/softdevteam/gists{/gist_id}","starred_url":"https://api.github.com/users/softdevteam/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/softdevteam/subscriptions","organizations_url":"https://api.github.com/users/softdevteam/orgs","repos_url":"https://api.github.com/users/softdevteam/repos","events_url":"https://api.github.com/users/softdevteam/events{/privacy}","received_events_url":"https://api.github.com/users/softdevteam/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/softdevteam/hwtracer","description":"Trace a program using CPU tracer technology.","fork":false,"url":"https://api.github.com/repos/softdevteam/hwtracer","forks_url":"https://api.github.com/repos/softdevteam/hwtracer/forks","keys_url":"https://api.github.com/repos/softdevteam/hwtracer/keys{/key_id}","collaborators_url":"https://api.github.com/repos/softdevteam/hwtracer/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/softdevteam/hwtracer/teams","hooks_url":"https://api.github.com/repos/softdevteam/hwtracer/hooks","issue_events_url":"https://api.github.com/repos/softdevteam/hwtracer/issues/events{/number}","events_url":"https://api.github.com/repos/softdevteam/hwtracer/events","assignees_url":"https://api.github.com/repos/softdevteam/hwtracer/assignees{/user}","branches_url":"https://api.github.com/repos/softdevteam/hwtracer/branches{/branch}","tags_url":"https://api.github.com/repos/softdevteam/hwtracer/tags","blobs_url":"https://api.github.com/repos/softdevteam/hwtracer/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/softdevteam/hwtracer/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/softdevteam/hwtracer/git/refs{/sha}","trees_url":"https://api.github.com/repos/softdevteam/hwtracer/git/trees{/sha}","statuses_url":"https://api.github.com/repos/softdevteam/hwtracer/statuses/{sha}","languages_url":"https://api.github.com/repos/softdevteam/hwtracer/languages","stargazers_url":"https://api.github.com/repos/softdevteam/hwtracer/stargazers","contributors_url":"https://api.github.com/repos/softdevteam/hwtracer/contributors","subscribers_url":"https://api.github.com/repos/softdevteam/hwtracer/subscribers","subscription_url":"https://api.github.com/repos/softdevteam/hwtracer/subscription","commits_url":"https://api.github.com/repos/softdevteam/hwtracer/commits{/sha}","git_commits_url":"https://api.github.com/repos/softdevteam/hwtracer/git/commits{/sha}","comments_url":"https://api.github.com/repos/softdevteam/hwtracer/comments{/number}","issue_comment_url":"https://api.github.com/repos/softdevteam/hwtracer/issues/comments{/number}","contents_url":"https://api.github.com/repos/softdevteam/hwtracer/contents/{+path}","compare_url":"https://api.github.com/repos/softdevteam/hwtracer/compare/{base}...{head}","merges_url":"https://api.github.com/repos/softdevteam/hwtracer/merges","archive_url":"https://api.github.com/repos/softdevteam/hwtracer/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/softdevteam/hwtracer/downloads","issues_url":"https://api.github.com/repos/softdevteam/hwtracer/issues{/number}","pulls_url":"https://api.github.com/repos/softdevteam/hwtracer/pulls{/number}","milestones_url":"https://api.github.com/repos/softdevteam/hwtracer/milestones{/number}","notifications_url":"https://api.github.com/repos/softdevteam/hwtracer/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/softdevteam/hwtracer/labels{/name}","releases_url":"https://api.github.com/repos/softdevteam/hwtracer/releases{/id}","deployments_url":"https://api.github.com/repos/softdevteam/hwtracer/deployments","created_at":"2017-11-17T15:03:17Z","updated_at":"2018-03-18T17:47:34Z","pushed_at":"2018-03-21T15:31:05Z","git_url":"git://github.com/softdevteam/hwtracer.git","ssh_url":"git@github.com:softdevteam/hwtracer.git","clone_url":"https://github.com/softdevteam/hwtracer.git","svn_url":"https://github.com/softdevteam/hwtracer","homepage":"","size":112,"stargazers_count":0,"watchers_count":0,"language":"Rust","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":11,"license":null,"forks":0,"open_issues":11,"watchers":0,"default_branch":"master"}},"base":{"label":"softdevteam:master","ref":"master","sha":"a745b0361bce36e8a3a9ad1296af2d4b0e913c53","user":{"login":"softdevteam","id":10271085,"avatar_url":"https://avatars0.githubusercontent.com/u/10271085?v=4","gravatar_id":"","url":"https://api.github.com/users/softdevteam","html_url":"https://github.com/softdevteam","followers_url":"https://api.github.com/users/softdevteam/followers","following_url":"https://api.github.com/users/softdevteam/following{/other_user}","gists_url":"https://api.github.com/users/softdevteam/gists{/gist_id}","starred_url":"https://api.github.com/users/softdevteam/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/softdevteam/subscriptions","organizations_url":"https://api.github.com/users/softdevteam/orgs","repos_url":"https://api.github.com/users/softdevteam/repos","events_url":"https://api.github.com/users/softdevteam/events{/privacy}","received_events_url":"https://api.github.com/users/softdevteam/received_events","type":"Organization","site_admin":false},"repo":{"id":111116225,"name":"hwtracer","full_name":"softdevteam/hwtracer","owner":{"login":"softdevteam","id":10271085,"avatar_url":"https://avatars0.githubusercontent.com/u/10271085?v=4","gravatar_id":"","url":"https://api.github.com/users/softdevteam","html_url":"https://github.com/softdevteam","followers_url":"https://api.github.com/users/softdevteam/followers","following_url":"https://api.github.com/users/softdevteam/following{/other_user}","gists_url":"https://api.github.com/users/softdevteam/gists{/gist_id}","starred_url":"https://api.github.com/users/softdevteam/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/softdevteam/subscriptions","organizations_url":"https://api.github.com/users/softdevteam/orgs","repos_url":"https://api.github.com/users/softdevteam/repos","events_url":"https://api.github.com/users/softdevteam/events{/privacy}","received_events_url":"https://api.github.com/users/softdevteam/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/softdevteam/hwtracer","description":"Trace a program using CPU tracer technology.","fork":false,"url":"https://api.github.com/repos/softdevteam/hwtracer","forks_url":"https://api.github.com/repos/softdevteam/hwtracer/forks","keys_url":"https://api.github.com/repos/softdevteam/hwtracer/keys{/key_id}","collaborators_url":"https://api.github.com/repos/softdevteam/hwtracer/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/softdevteam/hwtracer/teams","hooks_url":"https://api.github.com/repos/softdevteam/hwtracer/hooks","issue_events_url":"https://api.github.com/repos/softdevteam/hwtracer/issues/events{/number}","events_url":"https://api.github.com/repos/softdevteam/hwtracer/events","assignees_url":"https://api.github.com/repos/softdevteam/hwtracer/assignees{/user}","branches_url":"https://api.github.com/repos/softdevteam/hwtracer/branches{/branch}","tags_url":"https://api.github.com/repos/softdevteam/hwtracer/tags","blobs_url":"https://api.github.com/repos/softdevteam/hwtracer/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/softdevteam/hwtracer/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/softdevteam/hwtracer/git/refs{/sha}","trees_url":"https://api.github.com/repos/softdevteam/hwtracer/git/trees{/sha}","statuses_url":"https://api.github.com/repos/softdevteam/hwtracer/statuses/{sha}","languages_url":"https://api.github.com/repos/softdevteam/hwtracer/languages","stargazers_url":"https://api.github.com/repos/softdevteam/hwtracer/stargazers","contributors_url":"https://api.github.com/repos/softdevteam/hwtracer/contributors","subscribers_url":"https://api.github.com/repos/softdevteam/hwtracer/subscribers","subscription_url":"https://api.github.com/repos/softdevteam/hwtracer/subscription","commits_url":"https://api.github.com/repos/softdevteam/hwtracer/commits{/sha}","git_commits_url":"https://api.github.com/repos/softdevteam/hwtracer/git/commits{/sha}","comments_url":"https://api.github.com/repos/softdevteam/hwtracer/comments{/number}","issue_comment_url":"https://api.github.com/repos/softdevteam/hwtracer/issues/comments{/number}","contents_url":"https://api.github.com/repos/softdevteam/hwtracer/contents/{+path}","compare_url":"https://api.github.com/repos/softdevteam/hwtracer/compare/{base}...{head}","merges_url":"https://api.github.com/repos/softdevteam/hwtracer/merges","archive_url":"https://api.github.com/repos/softdevteam/hwtracer/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/softdevteam/hwtracer/downloads","issues_url":"https://api.github.com/repos/softdevteam/hwtracer/issues{/number}","pulls_url":"https://api.github.com/repos/softdevteam/hwtracer/pulls{/number}","milestones_url":"https://api.github.com/repos/softdevteam/hwtracer/milestones{/number}","notifications_url":"https://api.github.com/repos/softdevteam/hwtracer/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/softdevteam/hwtracer/labels{/name}","releases_url":"https://api.github.com/repos/softdevteam/hwtracer/releases{/id}","deployments_url":"https://api.github.com/repos/softdevteam/hwtracer/deployments","created_at":"2017-11-17T15:03:17Z","updated_at":"2018-03-18T17:47:34Z","pushed_at":"2018-03-21T15:31:05Z","git_url":"git://github.com/softdevteam/hwtracer.git","ssh_url":"git@github.com:softdevteam/hwtracer.git","clone_url":"https://github.com/softdevteam/hwtracer.git","svn_url":"https://github.com/softdevteam/hwtracer","homepage":"","size":112,"stargazers_count":0,"watchers_count":0,"language":"Rust","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":11,"license":null,"forks":0,"open_issues":11,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/softdevteam/hwtracer/pulls/46"},"html":{"href":"https://github.com/softdevteam/hwtracer/pull/46"},"issue":{"href":"https://api.github.com/repos/softdevteam/hwtracer/issues/46"},"comments":{"href":"https://api.github.com/repos/softdevteam/hwtracer/issues/46/comments"},"review_comments":{"href":"https://api.github.com/repos/softdevteam/hwtracer/pulls/46/comments"},"review_comment":{"href":"https://api.github.com/repos/softdevteam/hwtracer/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/softdevteam/hwtracer/pulls/46/commits"},"statuses":{"href":"https://api.github.com/repos/softdevteam/hwtracer/statuses/ffa6ee0a521aa5f201d446a2c4aff4a979b3e3cb"}},"author_association":"OWNER"}}
{"actor":{"display_login":"vext01"}}