|
From: Josef W. <Jos...@gm...> - 2004-01-26 14:02:56
|
On Sunday 25 January 2004 16:53, Nicholas Nethercote wrote: > Josef, > > The topic of tracking function entry/exit has come up a few times on the > mailing lists recently. My usual answer is that it's difficult to do > correctly. However, you seem to do it with Calltree. I looked at the > source code a bit, and it looks like you are doing some reasonably > complicated things to get it right, eg. unwinding the stack. How robust > is your approach? Can you briefly explain how it works? A note before describing the mechanism: I need to have a helper call at start of every BB anyway, so I use this helper to do the tracking. This of course has some overhead, and perhaps can be avoided, but it seems to add to the robustness. I have a bug fix here for reentrent entering of a signal handler (2 bug reports). Otherwise I have no bug reports, so I assume that the mechanism to be quite robust. I have a shadow call stack for every thread. For signal handlers of a thread, I first PUSH a separation marker on the shadow stack, and use the stack as normal. The marker is used for unwinding when leaving the signal handler. This is fine as there is no scheduling among signal handlers of one thread. Instrumentation of calltree: * Store at the end of each basic block the jmpkind into a tool-global, static variable. * At the start of every BB, jump to a helper function. The helper function does the following regarding function call tracking: - for a control transfer to another ELF object/ELF section, override jmpkind with a CALL (*1) - for a control transfer to the 1st basic block of a function, override jmpkind with a CALL (*2) - do unwinding if needed (i.e, POPs of the shadow call stack) - if jmpkind is RET and there was no unwinding/POP: - if our call stack is empty, simulate a CALL lasting from beginning (with Valgrind 2.1.x, this is not needed any more, as we run on simulated CPU from first client instruction) - otherwise this is a JMP using a RET instruction (typically used in the runtime linker). Do a POP, setting previous BB address to call site and override jmpkind with a CALL. By this, you get 2 function calls from a calling site. - when jmpkind is a CALL, push new function call from previous BB to current BB on shadow call stack. - Save current BB address to be available for call to handler in next BB. Special care is needed at thread switches and enter/leave of signal handlers, as we need separate shadow call stacks. Known bug: We should check for the need of unwinding when ESP is explicitly written to. I hope this doesn't create too much overhead. Remarks: (*1) Jumps between ELF objects are function calls to a shared library. This is mainly done to catch the JMP from PLT code. (*2) This is what your function tracking skin/tool does. It is needed here mainly to catch tail recursion. In general, for functions doing a "return otherfunction()", GCC produces JMPs with -O2. Additional points: - If I need a name for a function, but there is no debug info, I use the instruction address minus the load offset of the corresponding ELF object (if there is one) to get a relative address for that ELF object. This offset can be used with objdump later in postprocessing tools (e.g. objdump). I would suggest this change even for cachegrind instead of a "???". - I introduced the ability to specify functions to be "skipped". This means that execution of these functions is attributed to the calling function. The default is to skip all functions located in PLT sections. Thus, in effect, costs of PLT functions are attributed to callers, and the call to a shared library function starts directly with code in the other ELF object. - As Vg 2.1.x does pointerchecking, the instrumentation can't write to memory space of Valgrind any longer. Currently, my tool needs "--pointercheck=no" to be able to run. Jeremy and me already agreed on replacing current LD/ST with a CLD/CST (Client Load/Store) with pointer check and keep original LD/ST for tool usage without pointerchecking. Looking at these things, it seems possible to do function tracking at end of a basic block instead of the beginning of the next BB. This way, we can perhaps avoid calls to helpers at every BB. From my point of view, it would be great to integrate optional function tracking into Valgrind core with some hooks. Josef |