On 9/1/2010 8:54 AM, raju_...@hotmail.com wrote:
In one multithreaded application we want to trace a thread specific variable and monitor how it is changing the value. Is it possible using dtrace?
I'm not aware of any particular support for reading TLS variables easily (or any other variable in userland, for that matter). However, that needn't stop anyone ;)

You could attach with a debugger and use it to print the TLS address, then pass that to a .d script as a parameter, but that would fix it to a particular thread instance of a particular version of your binary.

You could also compute the address manually (see below***) which may let you trace any thread's version of the TLS var for that particular version of the binary. However, this only works in normal (non-PIC) code. PIC code forces the compiler to call a function to look up the TLS address (since it's dynamically loaded), so dtrace would be out of luck.

Combining the previous two, you could attach with a debugger, ask it for any thread's address of the TLS var, then subtract off that thread's value of %tls-reg to get OFFSET. Then pass the offset to dtrace and recompute %tls-reg+OFFSET when needed. This would avoid mucking around in assembler and would work even for PIC code as long as you don't unload/reload the module which contains your variable of interest. You're still stuck if the binary changes, though:

=== using dbx on sparcv9 for TLS of type int ===
export TLS_OFFSET=$(echo 'print &tls_var_of_interest - $g7' | dbx -p pid-of-interest | tail -n2 | awk '{print $3}') dtrace -n 'the:probe:of:interest { this->tls_val = *(int*) copyin($1+uregs[REG_G7], sizeof(int)) }' $TLS_OFFSET
======

The most flexible way to tell dtrace about an address of interest requires source code and a recompile. Create a hook function which accepts a void* as its only parameter and does nothing. Have every thread call that hook at startup with &tls_var_of_interest as the argument. Then, in the .d script, trace the hook's entry and store the address in self->tls_addr or some such. From then on, you can just copyin() that address whenever you want to see its contents. Just make sure dtrace is running before the thread of interest starts and don't let the compiler inline the hook function. This works for any variable, not just TLS (heap, stack, global).

=== c code ===
void give_address_to_dtrace(void* addr) { /* do nothing */ }
static __thread int tls_var = 10;
void* worker_thread_run(void* arg) {
    give_address_to_dtrace(&tls_var);
    ....
}
======

=== d script ===
pid$target::give_address_to_dtrace:entry { self->tls_addr = args[0] }
the:probe:of:interest { this->tls_value = *(int*) copyin(self->tls_addr, sizeof(int)) }
======

*** The manual way... thread-local storage is implemented by making some register (%g7 on sparcv9) always point to the running thread's internal data structure, which (among other things) stores TLS variables. The ABI specification for your architecture will have all the hairy details (disassembling toy programs also works pretty well). In a normal (not PIC) app the address of a TLS variable is simply %tls-reg + OFFSET, where OFFSET is some number assigned by the compiler. You should be able to disassemble the app and figure out that OFFSET. On x86-64-gcc this is fairly simple (2-3 instructions usually), but sparcv9-SunCC takes an impressively convoluted approach to computing it...

Regards,
Ryan

_______________________________________________
dtrace-discuss mailing list
dtrace-discuss@opensolaris.org

Reply via email to