{"id":29675708,"url":"https://github.com/oxidecomputer/tockilator","last_synced_at":"2026-03-10T15:04:20.400Z","repository":{"id":37179973,"uuid":"237501034","full_name":"oxidecomputer/tockilator","owner":"oxidecomputer","description":"Deducing Tock execution flows from Ibex Verilator traces","archived":false,"fork":false,"pushed_at":"2022-06-17T01:27:12.000Z","size":11760,"stargazers_count":70,"open_issues_count":6,"forks_count":2,"subscribers_count":30,"default_branch":"master","last_synced_at":"2026-02-19T11:15:33.481Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/oxidecomputer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-01-31T19:25:55.000Z","updated_at":"2025-02-10T21:54:49.000Z","dependencies_parsed_at":"2022-09-17T23:22:42.089Z","dependency_job_id":null,"html_url":"https://github.com/oxidecomputer/tockilator","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/oxidecomputer/tockilator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Ftockilator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Ftockilator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Ftockilator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Ftockilator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oxidecomputer","download_url":"https://codeload.github.com/oxidecomputer/tockilator/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Ftockilator/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30338599,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T15:02:55.010Z","status":"ssl_error","status_checked_at":"2026-03-10T15:02:36.911Z","response_time":106,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-07-22T23:38:01.910Z","updated_at":"2026-03-10T15:04:19.431Z","avatar_url":"https://github.com/oxidecomputer.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tockilator\n\n[![Build Status](https://travis-ci.org/oxidecomputer/tockilator.svg?branch=master)](https://travis-ci.org/oxidecomputer/tockilator)\n\n## Overview\n\nTockilator is a Rust program that consumes the output from the tracer module of\nan \u003ca href=\"https://github.com/lowRISC/ibex\"\u003eIbex RISC-V core\u003c/a\u003e running under\n\u003ca href=\"https://www.veripool.org/wiki/verilator\"\u003eVerilator\u003c/a\u003e along with an\nELF file (or ELF files) that correspond to the running software (e.g., boot\nloader, operating system and application) and symbolically interprets the\ninstruction trace to provide a view of execution flow.\n\nTo learn more about the context for Tockilator, see this\n\u003ca href=\"https://oxidizeconf.com/oxidize-1k/\"\u003eOxidize 1K\u003c/a\u003e talk,\n\u003ca href=\"https://www.slideshare.net/bcantrill/tockilator-deducing-tock-execution-flows-from-ibex-verilator-traces\"\u003eTockilator: Deducing Tock execution flows from Ibex Verilator traces\u003c/a\u003e (\u003ca href=\"https://www.youtube.com/watch?v=zPuELAzJyno\u0026t=10534s\"\u003evideo\u003c/a\u003e).\n\nHere is an example of Tockilator output for \n\u003ca href=\"https://github.com/tock/libtock-rs\"\u003elibtock-rs\u003c/a\u003e\nrunning the \u003ca href=\"https://github.com/tock/libtock-rs/blob/master/examples/hello_world.rs\"\u003ehello_world\u003c/a\u003e example:\n\n```\n~/tockilator/example/libtock-rs/hello_world$ ../../../target/release/tockilator -e ./hello_world.elf ./trace_core_00000000.log\n147794         =\u003e SYSCALL MEMOP operand=0 (brk) arg0=0x10003004\n165897         \u003c= SYSCALL MEMOP operand=0 (brk) arg0=0x10003004\n165900         =\u003e SYSCALL MEMOP operand=10 (update-stack-start) arg0=0x10003400\n181174         \u003c= SYSCALL MEMOP operand=10 (update-stack-start) arg0=0x10003400\n181177         =\u003e SYSCALL MEMOP operand=11 (update-heap-start) arg0=0x10003004\n195872         \u003c= SYSCALL MEMOP operand=11 (update-heap-start) arg0=0x10003004\n195879         -\u003e rust_start\n195894            | copy_nonoverlapping\u003cu8\u003e (GOFF 0x7ee8)\n195898            -\u003e memcpy\n195913            \u003c- memcpy\n195921            | write\u003cu8\u003e (GOFF 0x8006)\n195922            | add_usize (GOFF 0x7f69)\n195922              | checked_add (GOFF 0x7f94)\n195922                | overflowing_add (GOFF 0x7fb6)\n195923            | lt (GOFF 0x7fdd)\n195928            | write\u003cu8\u003e (GOFF 0x8006)\n195929            | add_usize (GOFF 0x7f69)\n195929              | checked_add (GOFF 0x7f94)\n195929                | overflowing_add (GOFF 0x7fb6)\n195930            | lt (GOFF 0x7fdd)\n195935            | write\u003cu8\u003e (GOFF 0x8006)\n195936            | add_usize (GOFF 0x7f69)\n195936              | checked_add (GOFF 0x7f94)\n195936                | overflowing_add (GOFF 0x7fb6)\n195937            | lt (GOFF 0x7fdd)\n195942            | write\u003cu8\u003e (GOFF 0x8006)\n195943            | add_usize (GOFF 0x7f69)\n195943              | checked_add (GOFF 0x7f94)\n195943                | overflowing_add (GOFF 0x7fb6)\n195944            | lt (GOFF 0x7fdd)\n195947            | set_brk (GOFF 0x8072)\n195947              | memop (GOFF 0x808b)\n195949                =\u003e SYSCALL MEMOP operand=0 (brk) arg0=0x10003404\n212924                \u003c= SYSCALL MEMOP operand=0 (brk) arg0=0x10003404\n212927           -\u003e main\n212936             -\u003e start\u003ccore::result::Result\u003c(), libtock::result::TockError\u003e\u003e\n212953                | block_on\u003ccore::result::Result\u003c(), libtock::result::TockError\u003e,async_support::executor::GeneratorFuture\u003cgenerator-0\u003e\u003e (GOFF 0x3a4a)\n212953                  | poll\u003casync_support::executor::GeneratorFuture\u003cgenerator-0\u003e\u003e (GOFF 0x3a6c)\n212953                    | poll\u003cgenerator-0\u003e (GOFF 0x3a8f)\n212953                      | {{closure}} (GOFF 0x3ab7)\n212953                        | retrieve_drivers (GOFF 0x3acc)\n212960                        | create_console (GOFF 0x3adc)\n212966                    -\u003e memset\n213489                    \u003c- memset\n213492                | block_on\u003ccore::result::Result\u003c(), libtock::result::TockError\u003e,async_support::executor::GeneratorFuture\u003cgenerator-0\u003e\u003e (GOFF 0x3a4a)\n213492                  | poll\u003casync_support::executor::GeneratorFuture\u003cgenerator-0\u003e\u003e (GOFF 0x3a6c)\n213492                    | poll\u003cgenerator-0\u003e (GOFF 0x3a8f)\n213492                      | {{closure}} (GOFF 0x3ab7)\n213492                        | create_console (GOFF 0x3adc)\n213493                        | write_fmt\u003clibtock::console::Console\u003e (GOFF 0x3afe)\n213515                    -\u003e write\n213559                       | iter\u003ccore::fmt::ArgumentV1\u003e (GOFF 0x2145)\n213559                         | add\u003ccore::fmt::ArgumentV1\u003e (GOFF 0x2156)\n213559                           | offset\u003ccore::fmt::ArgumentV1\u003e (GOFF 0x2167)\n213582                       | zip\u003ccore::slice::Iter\u003ccore::fmt::ArgumentV1\u003e,core::slice::Iter\u003c\u0026str\u003e\u003e (GOFF 0x2311)\n213582                         | new\u003ccore::slice::Iter\u003ccore::fmt::ArgumentV1\u003e,core::slice::Iter\u003c\u0026str\u003e\u003e (GOFF 0x2322)\n213582                           | new\u003ccore::slice::Iter\u003ccore::fmt::ArgumentV1\u003e,core::slice::Iter\u003c\u0026str\u003e\u003e (GOFF 0x2333)\n213582                             | min\u003cusize\u003e (GOFF 0x2343)\n213582                               | min\u003cusize\u003e (GOFF 0x2353)\n213582                                 | min_by\u003cusize,fn(\u0026usize, \u0026usize) -\u003e core::cmp::Ordering\u003e (GOFF 0x2364)\n213588                       | next\u003ccore::slice::Iter\u003ccore::fmt::ArgumentV1\u003e,core::slice::Iter\u003c\u0026str\u003e\u003e (GOFF 0x237a)\n213588                         | next\u003ccore::slice::Iter\u003ccore::fmt::ArgumentV1\u003e,core::slice::Iter\u003c\u0026str\u003e\u003e (GOFF 0x2387)\n213607                      -\u003e write_str\u003clibtock::console::Console\u003e\n213666                         | copy_from_slice\u003cu8\u003e (GOFF 0x592b)\n213666                           | copy_nonoverlapping\u003cu8\u003e (GOFF 0x594d)\n213672                          -\u003e memcpy\n213870                          \u003c- memcpy\n213871                         | copy_from_slice\u003cu8\u003e (GOFF 0x592b)\n213871                           | copy_nonoverlapping\u003cu8\u003e (GOFF 0x594d)\n213872                         | allow (GOFF 0x575d)\n213872                           | allow (GOFF 0x579a)\n213877                             =\u003e SYSCALL ALLOW driver=1 subdriver=1 addr=0x10003370 len=17\n231428                             \u003c= SYSCALL ALLOW driver=1 subdriver=1 addr=0x10003370 len=17\n231428                         | allow (GOFF 0x575d)\n231428                           | allow (GOFF 0x579a)\n231434                         | subscribe\u003clibtock_core::callback::Identity0Consumer,closure-0\u003e (GOFF 0x535c)\n231434                           | subscribe_fn (GOFF 0x5387)\n231434                             | subscribe (GOFF 0x53bb)\n231439                               =\u003e SYSCALL SUBSCRIBE driver=1 subdriver=1 callback=0x20030b40 data=0x100032c8\n247187                               \u003c= SYSCALL SUBSCRIBE driver=1 subdriver=1 callback=0x20030b40 data=0x100032c8\n247187                         | subscribe\u003clibtock_core::callback::Identity0Consumer,closure-0\u003e (GOFF 0x535c)\n247187                           | subscribe_fn (GOFF 0x5387)\n247187                             | subscribe (GOFF 0x53bb)\n247188                         | command (GOFF 0x540f)\n247188                           | command (GOFF 0x5443)\n247193                             =\u003e SYSCALL COMMAND driver=1 subdriver=1 arg0=0x11 arg1=0x0\n263288                             \u003c= SYSCALL COMMAND driver=1 subdriver=1 arg0=0x11 arg1=0x0\n263288                         | command (GOFF 0x540f)\n263288                           | command (GOFF 0x5443)\n263295                         | poll_with_tls_context\u003clibtock::futures::WaitForValue\u003cclosure-0\u003e\u003e (GOFF 0x5559)\n263295                           | poll\u003clibtock::futures::WaitForValue\u003cclosure-0\u003e\u003e (GOFF 0x556e)\n263295                             | poll\u003c(),closure-0\u003e (GOFF 0x559f)\n263304                         | yieldk (GOFF 0x55cb)\n263304                           | yieldk (GOFF 0x55db)\n263305                             =\u003e SYSCALL YIELD\n295020                         | consume\u003cclosure-0\u003e (GOFF 0x5ca9)\n295023                           | {{closure}} (GOFF 0x5ccf)\n295023                             | set\u003cbool\u003e (GOFF 0x5cdf)\n295023                               | replace\u003cbool\u003e (GOFF 0x5cfc)\n295023                                 | replace\u003cbool\u003e (GOFF 0x5d23)\n295023                                   | swap\u003cbool\u003e (GOFF 0x5d41)\n295023                                     | swap_nonoverlapping_one\u003cbool\u003e (GOFF 0x5d59)\n295023                                       | copy_nonoverlapping\u003cbool\u003e (GOFF 0x5d71)\n295030                      \u003c- libtock_core::syscalls::subscribe::c_callback::hf280be3ae7cea60e\n295041                       | poll_with_tls_context\u003clibtock::futures::WaitForValue\u003cclosure-0\u003e\u003e (GOFF 0x5559)\n295041                         | poll\u003clibtock::futures::WaitForValue\u003cclosure-0\u003e\u003e (GOFF 0x556e)\n295041                           | poll\u003c(),closure-0\u003e (GOFF 0x559f)\n295046                       | drop\u003clibtock_core::callback::CallbackSubscription\u003e (GOFF 0x55ed)\n295046                         | real_drop_in_place\u003clibtock_core::callback::CallbackSubscription\u003e (GOFF 0x55fd)\n295046                           | drop (GOFF 0x560e)\n295046                             | subscribe (GOFF 0x561d)\n295051                               =\u003e SYSCALL SUBSCRIBE driver=1 subdriver=1 callback=0x0 data=0x0\n310752                               \u003c= SYSCALL SUBSCRIBE driver=1 subdriver=1 callback=0x0 data=0x0\n310752                  | drop\u003clibtock_core::shared_memory::SharedMemory\u003e (GOFF 0x5664)\n310752                    | real_drop_in_place\u003clibtock_core::shared_memory::SharedMemory\u003e (GOFF 0x5679)\n310752                      | drop (GOFF 0x568f)\n310752                        | allow (GOFF 0x56a3)\n310757                          =\u003e SYSCALL ALLOW driver=1 subdriver=1 addr=0x0 len=0\n328162                          \u003c= SYSCALL ALLOW driver=1 subdriver=1 addr=0x0 len=0\n328162                  | index\u003cu8,core::ops::range::RangeFrom\u003cusize\u003e\u003e (GOFF 0x5854)\n328162                    | index\u003cu8\u003e (GOFF 0x5876)\n328162                      | index\u003cu8\u003e (GOFF 0x5899)\n328162                        | get_unchecked\u003cu8\u003e (GOFF 0x58bc)\n328162                          | add\u003cu8\u003e (GOFF 0x58df)\n328162                            | offset\u003cu8\u003e (GOFF 0x5902)\n328199               \u003c- write_str\n328232             \u003c- write\n328234              | block_on\u003ccore::result::Result\u003c(), libtock::result::TockError\u003e,async_support::executor::GeneratorFuture\u003cgenerator-0\u003e\u003e (GOFF 0x3a4a)\n328234                | poll\u003casync_support::executor::GeneratorFuture\u003cgenerator-0\u003e\u003e (GOFF 0x3a6c)\n328234                  | poll\u003cgenerator-0\u003e (GOFF 0x3a8f)\n328234                    | {{closure}} (GOFF 0x3ab7)\n328234                      | write_fmt\u003clibtock::console::Console\u003e (GOFF 0x3afe)\n328246           \u003c- start\u003ccore::result::Result\u003c(), libtock::result::TockError\u003e\u003e\n328254         \u003c- main\n328255          | yieldk (GOFF 0x80be)\n328255            | yieldk (GOFF 0x80ce)\n328256              =\u003e SYSCALL YIELD\n```\n\nAs an example of going to more depth, Tockilator can also be given particular\ncycle ranges with the `-c` option, and be told to print parameters (such as\nthey can be determined) with the `-p` option -- and if provided multiple\nELF files as input, it can follow code flow across privilege boundaries:\n\n```\n~/tockilator/example/libtock-rs/hello_world$ ../../../target/release/tockilator -e ./hello_world.elf -e ./opentitan.elf -c 213607-214200 -p ./trace_core_00000000.log \n213607                            -\u003e write_str\u003clibtock::console::Console\u003e\n213666                               | copy_from_slice\u003cu8\u003e (GOFF 0x592b)\n213666                                 | copy_nonoverlapping\u003cu8\u003e (GOFF 0x594d)\n213672                                -\u003e memcpy\n213870                                \u003c- memcpy\n213871                               | copy_from_slice\u003cu8\u003e (GOFF 0x592b)\n213871                                 | copy_nonoverlapping\u003cu8\u003e (GOFF 0x594d)\n213872                               | allow (GOFF 0x575d)\n213872                                 | allow (GOFF 0x579a)\n213877                                   =\u003e SYSCALL ALLOW driver=1 subdriver=1 addr=0x10003370 len=17\n213980                               | switch_to_process (GOFF 0x81f4 in object 1)\n213980                                 ( state=0x10000c00 (GOFF 0x820b in object 1)\n214045                               -\u003e from\n214045                                  ( csr_val=0x8 (GOFF 0x6fea0 in object 1)\n214054                                 -\u003e from\n214054                                    ( val=0x8 (GOFF 0x6fe2d in object 1)\n214065                                    | from_reason (GOFF 0x6fe3c in object 1)\n214093                                 \u003c- from\n214101                               \u003c- from\n214102                               | switch_to_process (GOFF 0x81f4 in object 1)\n214102                                 ( state=0x10000d38 (GOFF 0x820b in object 1)\n214127                                 | arguments_to_syscall (GOFF 0x8222 in object 1)\n214127                                   ( r0=0x1 (GOFF 0x8238 in object 1)\n214127                                   ( r1=0x1 (GOFF 0x8241 in object 1)\n214127                                   ( r2=0x10003370 (GOFF 0x824a in object 1)\n214127                                   ( r3=0x11 (GOFF 0x8253 in object 1)\n214142                               | set\u003c*const u8\u003e (GOFF 0x8293 in object 1)\n214142                                 ( self=0x10004884 (GOFF 0x82a4 in object 1)\n214142                                 | replace\u003c*const u8\u003e (GOFF 0x82ad in object 1)\n214142                                   ( self=0x10004884 (GOFF 0x82be in object 1)\n214142                                   | replace\u003c*const u8\u003e (GOFF 0x82c7 in object 1)\n214142                                     ( dest=0x10004884 (GOFF 0x82d8 in object 1)\n214142                                     | swap\u003c*const u8\u003e (GOFF 0x82e6 in object 1)\n214142                                       ( x=0x10004884 (GOFF 0x82f7 in object 1)\n214142                                       | swap_nonoverlapping_one\u003c*const u8\u003e (GOFF 0x8305 in object 1)\n214142                                         ( x=0x10004884 (GOFF 0x8316 in object 1)\n214142                                         | copy_nonoverlapping\u003c*const u8\u003e (GOFF 0x8324 in object 1)\n214142                                           ( count=0x1 (GOFF 0x8343 in object 1)\n214142                                           ( dst=0x10004884 (GOFF 0x833a in object 1)\n214150                              -\u003e memcpy\n```\n\nTo better illustrate symbols coming from different objects, Tockilator will\ncolor them differently.  For example, here is the above output when rendered\nto a terminal:\n\n![Example output](./example/libtock-rs/hello_world/example-output.png)\n\n## Running Tockilator\n\nTo run Tockilator, you will need Verilator trace files, ELF binaries of\nthe software that was running when the trace data was collected, and the\nTockilator binary itself.\nIf you want to experiment without having Verilator configured and/or without\nELF binaries, see some of the fully worked\n\u003ca href=\"./example/libtock-rs\"\u003elibtock-rs examples\u003c/a\u003e.\n\n### Verilator trace files\n\nFor an example of how to generate Verilator output for an Ibex core,\nsee (for example) the documentation for \n\u003ca href=\"https://docs.opentitan.org/doc/ug/getting_started_verilator/\"\u003econfiguring Verilator for OpenTitan\u003c/a\u003e.  \n\n### ELF binaries\n\nFor anything written in Rust (or presumably, C++), it is important that\n\u003ca href=\"https://en.wikipedia.org/wiki/DWARF\"\u003eDWARF information\u003c/a\u003e be included\nin the ELF binary, as Tockilator will use this information to find\n(and display) inlined functions and parameter information.\nWhen building release builds in Rust, this information is not generated by\ndefault; to generate it, add the `debug = true` under the `[profile.release]`\nprofile, \u003ca href=\"https://doc.rust-lang.org/nightly/cargo/reference/profiles.html#debug\"\u003eas described in the Cargo documentation\u003c/a\u003e.\nAdding this information will result in a (much) larger binary, but the additional\ndata is in unloadable sections:  it does not at all affect the size of the\nbinary loaded onto the system.\n\n### Building Tockilator\n\nTockilator itself is a Rust program; it should be built with `cargo`:\n\n    cargo build --release\n\n## Warnings, limitations, bugs, etc.\n\nThe source to Tockilator is a bit of a mess, and because it uses heuristics\nto determine some things, it can get things wrong.  While originally developed\nto understand Tock execution, Tockilator isn't particularly bound to Tock\n(though it does interpret Tock system calls); at the moment, it is much \nmore bound to RISC-V, though an ARM port (and surely resulting refactor)\nwould be easily attainable should one have a source of instruction traces.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foxidecomputer%2Ftockilator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foxidecomputer%2Ftockilator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foxidecomputer%2Ftockilator/lists"}