SpirvShaderDebugger: Improve stepping for inlined functions Makes stepping around inlined functions less jumpy. Fixes: b/170650010 Change-Id: Ie7b003722e3dee523a39bedf4dc42f1c0b05a041 Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/49228 Kokoro-Result: kokoro <noreply+kokoro@google.com> Reviewed-by: Alexis Hétu <sugoi@google.com> Tested-by: Ben Clayton <bclayton@google.com>
diff --git a/src/Pipeline/SpirvShaderDebugger.cpp b/src/Pipeline/SpirvShaderDebugger.cpp index 45c01ba..2280fa0 100644 --- a/src/Pipeline/SpirvShaderDebugger.cpp +++ b/src/Pipeline/SpirvShaderDebugger.cpp
@@ -2200,7 +2200,7 @@ auto shrink = [&](size_t maxLen) { while(stack.size() > maxLen) { - thread->exit(); + thread->exit(true); stack.pop_back(); } }; @@ -2216,10 +2216,10 @@ { if(stack[i] != newStack[i]) { - bool isTopMostFrame = i == (newStack.size() - 1); + bool wasTopMostFrame = i == (stack.size() - 1); auto oldFunction = debug::find<debug::Function>(stack[i].block); auto newFunction = debug::find<debug::Function>(newStack[i].block); - if(isTopMostFrame && oldFunction == newFunction) + if(wasTopMostFrame && oldFunction == newFunction) { // Deviation is just a movement in the top most frame's // function. @@ -2227,8 +2227,20 @@ // be treated as a step out and step in, breaking stepping // commands. Instead, just update the frame variables for // the new scope. - stack[i].block = newStack[i].block; + stack[i] = newStack[i]; thread->update(true, [&](vk::dbg::Frame &frame) { + // Update the frame location if we're entering a + // function. This allows the debugger to pause at the + // line (which may not have any instructions or OpLines) + // of a inlined function call. This is less jarring + // than magically appearing in another function before + // you've reached the line of the call site. + // See b/170650010 for more context. + if(stack.size() < newStack.size()) + { + auto function = debug::find<debug::Function>(stack[i].block); + frame.location = vk::dbg::Location{ function->source->dbgFile, (int)stack[i].line }; + } updateFrameLocals(state, frame, stack[i].block); }); } @@ -2240,17 +2252,34 @@ } } - // Now rebuild the parts of stack frames that are new + // Now rebuild the parts of stack frames that are new. + // + // This is done in two stages: + // (1) thread->enter() is called to construct the new stack frame with + // the opening scope line. The frames locals and hovers are built + // and assigned. + // (2) thread->update() is called to adjust the frame's location to + // entry.line. This may be different to the function entry in the + // case of multiple nested inline functions. If its the same, then + // this is a no-op. + // + // This two-stage approach allows the debugger to step through chains of + // inlined function calls without having a jarring jump from the outer + // function to the first statement within the function. + // See b/170650010 for more context. for(size_t i = stack.size(); i < newStack.size(); i++) { auto entry = newStack[i]; stack.emplace_back(entry); auto function = debug::find<debug::Function>(entry.block); thread->enter(entry.block->source->dbgFile, function->name, [&](vk::dbg::Frame &frame) { - frame.location = vk::dbg::Location{ function->source->dbgFile, (int)entry.line }; + frame.location = vk::dbg::Location{ function->source->dbgFile, (int)function->line }; frame.hovers->variables->extend(std::make_shared<HoversFromLocals>(frame.locals->variables)); updateFrameLocals(state, frame, entry.block); }); + thread->update(true, [&](vk::dbg::Frame &frame) { + frame.location.line = (int)entry.line; + }); } }
diff --git a/src/Vulkan/Debug/Thread.cpp b/src/Vulkan/Debug/Thread.cpp index 2f4dcc0..dd9e22d 100644 --- a/src/Vulkan/Debug/Thread.cpp +++ b/src/Vulkan/Debug/Thread.cpp
@@ -113,10 +113,14 @@ } } -void Thread::exit() +void Thread::exit(bool isStep /* = false */) { marl::lock lock(mutex); frames.pop_back(); + if(isStep) + { + onLocationUpdate(lock); + } } void Thread::update(bool isStep, const UpdateFrame &f)
diff --git a/src/Vulkan/Debug/Thread.hpp b/src/Vulkan/Debug/Thread.hpp index d9ba32d..1730146 100644 --- a/src/Vulkan/Debug/Thread.hpp +++ b/src/Vulkan/Debug/Thread.hpp
@@ -130,7 +130,10 @@ void enter(const std::shared_ptr<File> &file, const std::string &function, const UpdateFrame &f = nullptr); // exit() pops the thread's stack frame. - void exit(); + // If isStep is true, then this will be considered a line-steppable event, + // and the debugger may pause at the function call at the new top most stack + // frame. + void exit(bool isStep = false); // frame() returns a copy of the thread's top most stack frame. Frame frame() const;