{"id":29675756,"url":"https://github.com/oxidecomputer/usdt","last_synced_at":"2025-07-22T23:38:33.964Z","repository":{"id":38407132,"uuid":"340463006","full_name":"oxidecomputer/usdt","owner":"oxidecomputer","description":"Dust your Rust with USDT probes","archived":false,"fork":false,"pushed_at":"2025-07-07T18:07:23.000Z","size":925,"stargazers_count":106,"open_issues_count":9,"forks_count":12,"subscribers_count":24,"default_branch":"master","last_synced_at":"2025-07-14T00:09:46.617Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-02-19T19:00:49.000Z","updated_at":"2025-07-07T18:07:27.000Z","dependencies_parsed_at":"2024-05-20T22:54:28.964Z","dependency_job_id":"02359e91-d9fe-400d-bf66-fe4669a7295d","html_url":"https://github.com/oxidecomputer/usdt","commit_stats":{"total_commits":164,"total_committers":9,"mean_commits":18.22222222222222,"dds":0.6951219512195121,"last_synced_commit":"ac02c2ceb3d592c1df0bac5435aa03d5e8b8f1f4"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/oxidecomputer/usdt","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Fusdt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Fusdt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Fusdt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Fusdt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oxidecomputer","download_url":"https://codeload.github.com/oxidecomputer/usdt/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Fusdt/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266591232,"owners_count":23953082,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-22T02:00:09.085Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"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:24.763Z","updated_at":"2025-07-22T23:38:33.940Z","avatar_url":"https://github.com/oxidecomputer.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `usdt`\n\nDust your Rust with USDT probes.\n\n## Overview\n\n`usdt` exposes statically-defined [DTrace probes][1] to Rust code. Users write a _provider_\ndefinition, in either the D language or directly in Rust code. The _probes_ of the provider\ncan then be compiled into Rust code that fire the probes. These are visible via the `dtrace`\ncommand-line tool.\n\nThere are three mechanisms for converting the D probe definitions into Rust.\n\n1. A `build.rs` script\n2. A function-like procedural macro, `usdt::dtrace_provider`.\n3. An attribute macro, `usdt::provider`.\n\nThe generated code is the same in all cases, though the third provides a bit more flexibility\nthan the first two. See [below](#serializable-types) for more details, but briefly, the third\nform supports probe arguments of any type that implement [`serde::Seralize`][2]. These different\nversions are shown in the crates `probe-test-{build,macro,attr}` respectively.\n\n\u003e Note: This crate uses inline assembly to work its magic. See the [notes](#notes) for a discussion\nof its use prior to Rust 1.59 and prior to 1.66 on macOS.\n\n## Example\n\nThe `probe-test-build` binary crate in this package implements a complete example, using the\nbuild-time code generation.\n\nThe starting point is a D script, called `\"test.d\"`. It looks like:\n\n```d\nprovider my_provider {\n\tprobe start_work(uint8_t);\n\tprobe stop_work(char*, uint8_t);\n};\n```\n\nThis script defines a single provider, `test`, with two probes, `start` and `stop`,\nwith a different set of arguments. (Integral primitive types, pointers to\nintegral types, and `\u0026str`s are currently supported. Note that `char*` is used\nto indicate Rust-style UTF-8 strings. If you'd like a byte array, use `uint8_t*`\nor `int8_t*`.)\n\nThis provider definition must be converted into Rust code, which can be done in a simple\nbuild script:\n\n\n```rust\nuse usdt::Builder;\n\nfn main() {\n\tBuilder::new(\"test.d\").build().unwrap();\n}\n```\n\nThis generates a file in the directory `OUT_DIR` which contains the generated Rust macros\nthat fire the probes. Unless it is changed, this file is named the same as the provider\ndefinition file, so `test.rs` in this case.\n\nUsing the probes in Rust code looks like the following, which is in `probe-test-build/src/main.rs`.\n\n```rust\n//! An example using the `usdt` crate, generating the probes via a build script.\n\nuse std::thread::sleep;\nuse std::time::Duration;\n\nuse usdt::register_probes;\n\n// Include the Rust implementation generated by the build script.\ninclude!(concat!(env!(\"OUT_DIR\"), \"/test.rs\"));\n\nfn main() {\n    let duration = Duration::from_secs(1);\n    let mut counter: u8 = 0;\n\n    // NOTE: One _must_ call this function in order to actually register the probes with DTrace.\n    // Without this, it won't be possible to list, enable, or see the probes via `dtrace(1)`.\n    register_probes().unwrap();\n\n    loop {\n        // Call the \"start_work\" probe which accepts a u8.\n        my_provider::start_work!(|| (counter));\n\n        // Do some work.\n        sleep(duration);\n\n        // Call the \"stop_work\" probe, which accepts a \u0026str and a u8.\n        my_provider::stop_work!(|| (\"the probe has fired\", counter));\n\n        counter = counter.wrapping_add(1);\n    }\n}\n```\n\n\u003e Note: Prior to 1.59 (and prior to 1.66 on macOS) nightly features are required. See the\n[notes](#notes) for a discussion.\n\nOne can also see that the Rust code is included directly using the `include!` macro. The probe\ndefinitions are converted into Rust macros, in a module named by the provider, and with macro\nnamed by the probe. In our case, the the first probe is converted into a macro\n`my_provider::start_work!`.\n\n\u003e IMPORTANT: It's important to note that the application _must_ call `usdt::register_probes()`\nin order to actually register the probe points with DTrace. Failing to do this will not impact\nthe application's functionality, but it will be impossible to list, enable, or otherwise see the\nprobes with the `dtrace(1)` tool without this.\n\nWe can see that this is hooked up with DTrace by running the example and listing the expected\nprobes by name.\n\n```bash\n$ cargo run\n```\n\nAnd in another terminal, list the matching probes with:\n\n```bash\n$ sudo dtrace -l -n my_provider*:::\n   ID   PROVIDER            MODULE                          FUNCTION NAME\n 2865  test14314  probe-test-build _ZN16probe_test_build4main17h906db832bb52ab01E [probe_test_build::main::h906db832bb52ab01] start_work\n 2866  test14314  probe-test-build _ZN16probe_test_build4main17h906db832bb52ab01E [probe_test_build::main::h906db832bb52ab01] stop_work\n ```\n\n## Probe arguments\n\nOne can see that the probe macros are called with closures, rather than with the probe\narguments directly. This has two purposes.\n\nFirst, it indicates that the probe arguments may not be evaluated. DTrace generates\n\"is-enabled\" probes for defined probe, which is a simple way to check if the probe has\ncurrently been enabled. The arguments are only unpacked if the probe is enabled, and\nso users _must not_ rely on side-effects. The closure helps indicate this.\n\nThe second point of this is efficiency. Again, the arguments are not evaluated if the\nprobe is not enabled. The closure is only evaluated internally _after_ the probe is\nverified to be enabled, which avoid the unnecessary work of argument marshalling if\nthe probe is disabled.\n\n## Procedural macro version\n\nThe procedural macro version of this crate can be seen in the `probe-test-macro` example,\nwhich is nearly identical to the above example. However, there is no build.rs script,\nso in place of the `include!` macro, one finds the procedural macro:\n\n```rust\ndtrace_provider!(\"test.d\");\n```\n\nThis macro generates the same macros as seen above, but does at the time the source\nitself is compiled. This may be easier for some use cases, as there is no build script.\nHowever, procedural macros have downsides. It can be difficult to understand their\ninternals, especially when things fail. Additionally, the macro is run on every compile,\neven if the provider definition is unchanged. This may be negligible for small provider\ndefinitions, but users may see a noticeable increase in compile times when many probes\nare defined.\n\n## Serializable types\n\nAs described above, the three forms of defining a provider a _nearly_ equivalent. The\nonly distinction is in the support of types implementing [`serde::Serialize`][2]. This uses\nDTrace's [JSON functionality][3] -- Any serializable type is serialized to JSON with\n[`serde_json::to_string()`][4], and the string may be unpacked and inspected in DTrace\nscripts with the `json` function. For example, imagine we have the type:\n\n```rust\n#[derive(serde::Serialize)]\npub struct Arg {\n    val: u8,\n    data: Vec\u003cString\u003e,\n}\n```\n\nand a probe definition:\n\n```rust\n#[usdt::provider]\nmod my_provider {\n    use crate::Arg;\n    fn my_probe(_: \u0026Arg) {}\n}\n```\n\nValues of type `Arg` may be used in the generated probe macros. In a DTrace script, one can\nlook at the data in the argument like:\n\n```\ndtrace -n 'my_probe* { printf(\"%s\", json(copyinstr(arg0), \"ok.val\")); }' # prints `Arg::val`.\n```\n\nThe `json` function also supports nested objects and array indexing, so one could also do:\n\n```\ndtrace -n 'my_probe* { printf(\"%s\", json(copyinstr(arg0), \"ok.data[0]\")); }' # prints `Arg::data[0]`.\n```\n\nSee the `probe-test-attr` example for more details and usage.\n\n### Serialization is fallible\n\nNote that in the above examples, the first key of the JSON blob being accessed is `\"ok\"`. This\nis because the `serde_json::to_string` function is fallible, returning a `Result`. This is mapped\ninto JSON in a natural way:\n\n- `Ok(_) =\u003e {\"ok\": _}`\n- `Err(_) =\u003e {\"err\": _}`\n\nIn the error case, the [`Error`][serde-json-error] returned is formatted using its `Display`\nimplementation. This isn't an academic concern. It's quite easy to build types that successfully\ncompile, and yet fail to serialize at runtime, even with types that `#[derive(Serialize)]`. See\n[this issue][serde-runtime-fail] for details.\n\n## A note about registration\n\nNote that the `usdt::register_probes()` function is called at the top of main in the above\nexample. This method is required to actually register the probes with the DTrace kernel\nmodule. This presents a quandary for library developers who wish to instrument their\ncode, as consumers of their library may forget to (or choose not to) call this function.\nThere are potential workarounds to this problem (init-sections, other magic), but each\ncomes with significant tradeoffs. As such the current recommendation is:\n\n\u003e Library developers are encouraged to re-export the `usdt::register_probes` (or a\nfunction calling it), and document to their users that this function should be called to\nguarantee that probes are registered.\n\n## References\n\n[1]: https://illumos.org/books/dtrace/chp-usdt.html#chp-usdt\n[2]: https://docs.rs/serde/1.0.130/serde/trait.Serialize.html\n[3]: https://sysmgr.org/blog/2012/11/29/dtrace_and_json_together_at_last/\n[4]: https://docs.rs/serde_json/1.0.68/serde_json/fn.to_string.html\n[serde-json-error]: https://docs.serde.rs/serde_json/error/struct.Error.html\n[serde-runtime-fail]: https://github.com/serde-rs/serde/issues/1307\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foxidecomputer%2Fusdt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foxidecomputer%2Fusdt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foxidecomputer%2Fusdt/lists"}