<div dir="ltr"><div>Francis,</div><div><br></div><div>Thanks for your detailed, helpful reply. I was especially excited to learn about the progress on `--userspace-probe`, but unfortunately I *do* need to extract the function arguments. It would seem, then, that interposition through LD_PRELOAD is really the only option. The decision to port to LTTng or not would just come down to how easy it was to generate all the required boilerplate.<br></div><div><br></div><div>Here's a little more about my application:</div><div><ol><li>The whitelist is composed of mangled names that the user can extract from the target object with the standard `nm` workflow.</li><li>Each mangled name in the whitelist gets demangled and parsed into `prefix`, `callable_name`, `argument_types`, etc.</li><li>The list of `argument_types` is compared to the "extra annotation" I alluded to. This extra annotation is just a one-to-one map between argument type names and the names of structs that corresponding variables should be wrapped with before passing them to the logger. These structs just provide custom ostream operators for types that don't log nicely otherwise.<br></li><li>Boilerplate that does the typical `log(...); auto return_val = dlsym(...); log(...); return return_val;` gets generated.<br></li><li>`log(...)` is a thin interface to <a href="https://github.com/gabime/spdlog">spdlog</a> that handles `__attribute__`-based setup and teardown of a logger.</li></ol><div>So at the end of the day, the shim developer provides:</div><div><ul><li>The whitelist of mangled names</li><li>Implementations of struct "wrappers" that provide custom ostream operators</li><li>A map between type names and wrapper names<br></li></ul></div><div>The machinery here seems fairly general-purpose, but I don't presume to be an expert. My implementation is somewhat error-prone, and my main hope in reaching out to the mailing list was that LTTng already had some of these steps better-implemented. Step #2 is particularly problematic due to ambiguities in the mangling grammar, and will need support going forward to generalize well. </div><div><br></div><div>I would be happy to contribute some or all of my implementation if it's something that the LTTng community would be interested in supporting and extending.</div><div><br></div><div>Cheers!</div><div>~br</div></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Jan 22, 2019 at 12:16 PM Francis Deslauriers <<a href="mailto:francis.deslauriers@efficios.com">francis.deslauriers@efficios.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hi Brian,<br>
If I understand correctly, you want developers to provide a whitelist<br>
of functions that they want to instrument in an arbitrary shared<br>
library, and then have a system that instruments those functions<br>
without having to recompile the library with LTTng-UST tracepoints.<br>
Can you give us an example of annotation that the shim developer would<br>
provide?<br>
Do you need to support the extraction of function arguments? If you<br>
don't, here are two straightforward and tested approaches to solve<br>
your problem:<br>
<br>
First, overriding target symbols using LD_PRELOAD (like you suggested above):<br>
1. Create boilerplate script #1 that generates tracepoints definitions<br>
[1] based on function name,<br>
2. Create boilerplate script #2 that generates a function calling the<br>
right tracepoint and the original target function [2],<br>
3. For each target symbol "bar", using script #1, create a tracepoint<br>
definition "libfoo:bar_tp", and compile them into a tracepoints.o.<br>
object file,<br>
4. For each target symbol "bar", using script #2, create a function<br>
"bar" that calls the bar_tp tracepoint (e.g. tracepoint(libfoo,<br>
bar_tp) ) and then calls the original bar function in libfoo.so (using<br>
dlsym(3)), and compile those callsites into a callsites.o object file,<br>
5. Create a shared library libshim.so using tracepoints.o and<br>
callsites.o object files and link it to lttng-ust lib and the dynamic<br>
loader lib using :"-llttng-ust -ldl",<br>
Those steps results in the creation of a libshim.so file that contains<br>
the "bar" overriding function (containing the tracepoint callsite)<br>
that will be called instead of "bar" function in the libfoo.so library<br>
when LD_PRELOADing it (e.g. LD_PRELOAD=libshim.so ./fooapp). Here is<br>
the Makefile I used to test this [3] to give you a sense of the<br>
pipeline.<br>
<br>
Second, using the newly added --userspace-probe instrumentation:<br>
This option allows you to instrument any ELF symbol without<br>
recompiling or restarting the target process.<br>
For example, to instrument the "bar" function in the /tmp/libfoo.so<br>
file you could run to following command:<br>
    > lttng enable-event -k --userspace-probe="elf:/tmp/libfoo.so:bar"<br>
$event_name<br>
You could write a script that takes the list of target functions and<br>
adds userspace-probes on each of them.<br>
This approach has the advantage that you can also instrument functions<br>
in the binary and not only functions in shared libraries.<br>
This instrumentation type uses the kernel tracer and thus requires<br>
using the lttng-modules.<br>
<br>
If you do need to save the function arguments, I believe it's possible<br>
using the first approach, by tweaking the two boilerplate scripts<br>
depending on what are the annotations you want to provide.<br>
Supporting argument extraction with userspace-probes is part of our<br>
long term goal and would be a really powerful feature but will require<br>
a significant engineering effort to accomplish.<br>
<br>
[1] : <a href="https://lttng.org/docs/v2.10/#doc-tpp-header" rel="noreferrer" target="_blank">https://lttng.org/docs/v2.10/#doc-tpp-header</a><br>
[2]: <a href="https://lttng.org/docs/v2.10/#doc-probing-the-application-source-code" rel="noreferrer" target="_blank">https://lttng.org/docs/v2.10/#doc-probing-the-application-source-code</a><br>
[3]: <a href="http://paste.ubuntu.com/p/5fBSgRqXsB/" rel="noreferrer" target="_blank">http://paste.ubuntu.com/p/5fBSgRqXsB/</a><br>
<br>
I hope this make sense. Please don't hesitate if you want more details.<br>
<br>
Thank you,<br>
Francis<br>
<br>
Le ven. 18 janv. 2019, à 16 h 04, Brian Rossa <<a href="mailto:br@f0cal.com" target="_blank">br@f0cal.com</a>> a écrit :<br>
><br>
> Hello,<br>
><br>
> I have a custom tracing solution that I'm interested in porting to LTTng. It works as follows:<br>
><br>
> Input is a whitelist of mangled names taken from a target library, libfoo.so.<br>
> For each name, generate a logging shim (example) for the corresponding call.<br>
> Compile all the logging shims into a shared library, libshim.so.<br>
> Trace any application using libfoo by running it as LD_PRELOAD=libshim.so ./fooapp<br>
><br>
> There are two nice things about this approach that I would like to preserve:<br>
><br>
> The shim developer only needs to provide the whitelist and a bit of extra annotation. From there, the shim can be be authored using a boilerplate generator.<br>
> The app developer doesn't have to do anything other than pass the LD_PRELOAD flag.<br>
><br>
> The downside is that the only tracepoints are those corresponding to the whitelist, but I'm fine with that.<br>
><br>
> Can this kind of "hands-free" developer experience be supported by LTTng?<br>
><br>
> Thanks!<br>
> ~br<br>
> _______________________________________________<br>
> lttng-dev mailing list<br>
> <a href="mailto:lttng-dev@lists.lttng.org" target="_blank">lttng-dev@lists.lttng.org</a><br>
> <a href="https://lists.lttng.org/cgi-bin/mailman/listinfo/lttng-dev" rel="noreferrer" target="_blank">https://lists.lttng.org/cgi-bin/mailman/listinfo/lttng-dev</a><br>
<br>
<br>
<br>
--<br>
Francis Deslauriers<br>
Computer Engineer<br>
EfficiOS inc.<br>
</blockquote></div>