Enable stack tracing to jump past v8 |
||
Issue descriptionI was debugging something recently, and found myself wishing that base::debug::StackTrace could skip past v8 on non-x86 platforms. I started hacking something together with libunwind. I'm told that using libunwind directly isn't feasible, as libunwind will give up as soon as it finds a stack frame it doesn't understand. Instead, I'm trying to forge an unw_context_t, using the frame pointer / stack pointer / pc from v8::internal::Isolate::Current()->thread_local_top()->js_entry_sp_, etc. This would be highly-platform specific and not very portable (as this stack trace helper would be implicitly tied to v8). It's been pointed out that this might be a generally useful thing to be able to do. A number of other suggestions from that discussion: - register a custom dwarf entry for exception handling - generalize the lookaside stack currently used for JNI: https://codereview.chromium.org/2361353002 - integrate with gdb's jit debugging infrastructure.
,
May 9 2017
Jakob has a patch that scans the stack skipping JS frames. Maybe you could take some inspiration from that?
,
May 9 2017
FWIW dskiba added a stack-scanner to the base::debug::BackTrace not too long ago. Just a thought about that. Pure stack scanning without extra context has some pathological false positives (regardless of v8), e.g.: Imagine that a callstack looks like this: -+> main() |-+> small_function_1() | |-> small_function_2() | |-+> large_function() |--> v8::Execute() where say small_function_X() have frames of 16 bytes each, and large_function has a larger stack frame (say 64 bytes). When you enter large_function, it will push the stack pointer by -64 bytes and then call v8 execute (again, v8 execute is irrelevant here) The problem is that while doing that, it will _not_ clear up its current stack frame (say that the code that would have used the locals happens after v8::Execute() ) so the currently uninitialized stack frame of large_function() will still have the pointer from the previous execution tree. In other words, soon after small_function_2() returns, the stack will look like this: +----------------------------+----------------------------+--------------+ | SF2 locals, retaddr: &SF1 | SF1 locals, retaddr: &main | main locals | +----------------------------+----------------------------+--------------+ Using a stack scanner at this point will find the right return addresses that is [&SF1, &main] The problem is when you are in the middle of that V8::Execute() after large_function(), now your stack will look like: +-------------------------------+---------------------------------------------------------+--------------+ | v8::exec locals, retaddr: &LF | LF locals (still has the stale pointers &SF1, &main) | main locals | +-------------------------------+---------------------------------------------------------+--------------+ So if you stack scan this, the stack trace which will come out as: v8::exec -> large_function -> small_function_1 -> main where small_function_1 is a false positive, result of having stack scanned an uninitialized stack. Perhaps we could inject some markers on the stack via RAII objects, understood by the stack scanner, which clean up themselves on return. What I am thinking about is: say that v8 execute knows that it will break the FP chain. It could use a base::debug::ScopedCallFrameMarker() and inject two markers on the stack to hint the stack scanner in base, of the form: - From this point on, ignore pointers found on the stack, until you see the next marker. - This is the next frame, resume the walk from here. Resuming is easier for FP-based unwinding but might be a bit trickier for the CFI-based one though.
,
May 9 2017
But that RAII object and markers won't help Windows, which doesn't use frame pointers to unwind. I wonder if Jakob's patch handles that?
,
May 9 2017
patch here: https://codereview.chromium.org/1108013003/
,
Oct 3 2017
On Windows the stack unwinding of x64 processes is based on metadata which is normally stored in the binary by the compiler/linker. When code is generated it is expected that RtlAddFunctionTable will be used to dynamically create the metadata. Here are some references: http://factor-language.blogspot.com/2010/04/frame-based-structured-exception.html https://msdn.microsoft.com/en-us/library/ms680588(VS.85).aspx Using RtlAddFunctionTable should make stack walking work within Chromium, in the VS and windbg debuggers, and also in the ETW profiler and other profilers. Disclaimer: I haven't used this technique.
,
Oct 4 2017
There is integration code in Chrome to do this on Windows: https://cs.chromium.org/chromium/src/components/crash/content/app/crashpad_win.cc?q=RtlAddFunctionTable&dr=C&l=228. I know this because I was recently in there, and briefly broke it. |
||
►
Sign in to add a comment |
||
Comment 1 by dcheng@chromium.org
, May 8 2017Labels: OS-All