Tracing in Rust with uprobes


(posted on users first, but this may be better place)

I’m interested in making rust able to instrument my apps via usdt on Linux. There is project to do this [1], I’ve got working demo [2] utilising it and that looks extremely useful. Currently it works as a macro, and has some annoying limitations:

  • It doesn’t understand argument types (I believe macros can’t do that).
  • It uses asm! to insert probes, and that doesn’t work on stable.

I’d like to push this forward and I’d like to get some input of how I should proceed:

  1. Are there any plans to stabilize asm, so that I could get it working on stable (I have no intention of using nightly in production)?
  2. I believe argument types can be retrieved if it was compiler plugin. Am I right?
  3. Would there be an interest in making it a language feature? I believe it would be extremely useful, relatively easy to achieve, and once done, not require much maintenance.

[1] libprobe [2] using libprobe [3] Relevant rust rfc issue


As the author of probe!, of course I’d love to see it get further use! :smile:

FWIW, I did initially submit this to the rust repo in PR14031. Perhaps if we’d gone that route, it could have transitioned into a real compiler feature with hooks whereever is needed to get proper type info. But alas, it was decided to let it bake as an external repo, and admittedly I haven’t really tried to promote it, so I expect it hasn’t gotten much use. And as you say, requiring nightly really restricts who would be willing to use it.

One way you could get stable probes today is to write the actual probe hooks in a C shim, then call those from your Rust code. That’s a lot more manual effort, and changes the minimal overhead from a nop to arg-setup;call;nop;return, which is sad, but perhaps acceptable in practice.

(side note: your links all have a bogus “1” at the end – probably copied the click-count from your post on users.)


One way you could get stable probes today is to write the actual probe hooks in a C shim, then call those from your Rust code.

I am not sure how exactly would that work.

I’ll play with compiler plugins first, and try to get them into usable shape.Having that, it would be easier to demonstrate how useful this is, gain some users and get rust developers onboard. Ultimately I think it would be nice to have it at language level, including listing probes in generated documentation and perhaps have some other tooling around it that makes discovering and using probes easier.


Just something like this:

#include <sys/sdt.h>

void probe_foo_bar(void) {
    STAP_PROBE(foo, bar);

void probe_foo_with_args(int i, char* p) {
    STAP_PROBE2(foo, with_args, i, p);

Compile that with the gcc crate in your script, and then make FFI calls from Rust. Hmm, maybe this could be done somewhat automatically with a probe-codegen sort of crate?

But it would still be nicer to have direct integration, to get it down to an inline nop.


That will hopefully not be necessary.

I’ve managed to obtain type information from mir by writing a compiler plugin. Here is the code:

It will print information about types of arguments passed to macro. It doesn’t generate asm yet, but I should be able to adapt your code.

One thing I’ve learned about plugins is that unstable rust features actually are present in stable release, and plugins may choose to use them. Whether its a good idea to do so I don’t know, but it is an option to consider.


Great! Let me know if you have any questions about how to format the assembly.

Yes, they’re still there because the standard library and rustc itself both use those features. In some cases it’s to dogfood new features before they’re stabilized, and others are things that may never be stabilized, like #[feature(rustc_private)].

Given that plugins are required to use features to be a plugin at all, using other features isn’t a big deal.

Using it on stable is more controversial. The official answer is that you should use a nightly compiler to use unstable features, but there’s an environment you can set on stable compilers, intended only for building rustc, libstd, etc. It’s sort of a no-promises you-keep-the-broken-pieces situation either way, but you might at least get some informal help if you have issues with nightly.

I guess the more general view is that when using features, you’ll probably be expected to keep up with nightly rust. Trying to straddle that and whatever state is in the stable release may prove challenging, as there’s inherently no promise of compatibility.


I’ve got the assembly now, so there is nothing stopping me from implementing whatever logic I want. What logic I want I do not know yet though :slight_smile: Where can I find documentation defining probe argument format? Googling doesn’t lead me to anything useful. Does it have to be 8 bytes, just perhaps described differently?

Yeah, that’s a problem.


Here’s documentation that includes the argument format:


Thanks. The table for 8/16/32/64 signed/unsigned values is trivial to handle, but this part is not:

“This may be extended with other values” “any operand accepted by gas may appear here, and in particular this means that gas integer expressions may appear.”

I know nothing about assembler and gas. Could I indicate that the argument is a string (c-style or rust)?


Note that this page is descriptive for probe consumers, not prescriptive for probe producers. So it’s describing what sdt.h currently does, and that “may be extended” line is just leaving the door open for future possibilities, like integer vector registers. I expect it will always stay at the level of things that the assember understands.

Generally we expect to see register and memory[+offset] operands, but this note is pointing out that you may see integer literals too, and the complication that powerpc presents with that.

No, assembler doesn’t really know anything about strings. Sometimes CPUs have instructions meant for dealing with strings, like the x86 REP prefix, but operands are still just singular values, like pointers, integers, and floating point.

Concepts like strings can be represented in debuginfo, but sdt.h is explicitly meant to operate at a lower level than that, so it can be used in production even on fully-stripped binaries.

So it’s left to the probe author to document elsewhere what the probe arguments actually mean, and the semantics of how to interpret them. You could pass a C char* string as a simple pointer argument, and a “fat” pointer like Rust &str would need two probe arguments, pointer and length. You could probably destructure that &str automatically in your plugin, but it might be confusing for using a probe with multiple arguments if the arg indexes don’t quite line up to what was written.


Thanks for explanation. It’s working now for numeric types, I’ll add pointers soon.