microsoft/MIEngine

Support stack frames pagination for a thread

timonag opened this issue · 2 comments

At this moment EnumFrameInfo method reads parameters info for all stack frames of a thread in spite of a client requesting a range of them (e.g. vscode-cpptools by default requests only the first 20 frames).
Finally the HandleStackTraceRequestAsync handler replies to a client with a requested slice of frames. It means that some resources were wasted and it creates a significant delay on large stacks.

Is it possible to pass start and end frame level into the EnumFrameInfo to get necessary info only?

EnumFrameInfo only has to return a IEnumDebugFrameInfo2.

At the moment, EnumFrameInfo grabs all the stack frames and caches it into an AD7FrameInfoEnum.
But the code can be modified to retrieve frames based on IEnumDebugFrameInfo2.Take(...) and ignore frames with IEnumDebugFrameInfo2.Skip(...).

Reference:

int IDebugThread2.EnumFrameInfo(enum_FRAMEINFO_FLAGS dwFieldSpec, uint nRadix, out IEnumDebugFrameInfo2 enumObject)
{
enumObject = null;
try
{
uint radix = _engine.CurrentRadix();
if (radix != _engine.DebuggedProcess.MICommandFactory.Radix)
{
_engine.DebuggedProcess.WorkerThread.RunOperation(async () =>
{
await _engine.UpdateRadixAsync(radix);
});
}
// get the thread's stack frames
System.Collections.Generic.List<ThreadContext> stackFrames = null;
_engine.DebuggedProcess.WorkerThread.RunOperation(async () => stackFrames = await _engine.DebuggedProcess.ThreadCache.StackFrames(_debuggedThread));
int numStackFrames = stackFrames != null ? stackFrames.Count : 0;
FRAMEINFO[] frameInfoArray;
if (numStackFrames == 0)
{
// failed to walk any frames. Return an empty stack.
frameInfoArray = new FRAMEINFO[0];
}
else
{
uint low = stackFrames[0].Level;
uint high = stackFrames[stackFrames.Count - 1].Level;
FilterUnknownFrames(stackFrames);
numStackFrames = stackFrames.Count;
frameInfoArray = new FRAMEINFO[numStackFrames];
List<ArgumentList> parameters = null;
if ((dwFieldSpec & enum_FRAMEINFO_FLAGS.FIF_FUNCNAME_ARGS) != 0 && !_engine.DebuggedProcess.MICommandFactory.SupportsFrameFormatting)
{
_engine.DebuggedProcess.WorkerThread.RunOperation(async () => parameters = await _engine.DebuggedProcess.GetParameterInfoOnly(this, (dwFieldSpec & enum_FRAMEINFO_FLAGS.FIF_FUNCNAME_ARGS_VALUES) != 0,
(dwFieldSpec & enum_FRAMEINFO_FLAGS.FIF_FUNCNAME_ARGS_TYPES) != 0, low, high));
}
for (int i = 0; i < numStackFrames; i++)
{
var p = parameters != null ? parameters.Find((ArgumentList t) => t.Item1 == stackFrames[i].Level) : null;
AD7StackFrame frame = new AD7StackFrame(_engine, this, stackFrames[i]);
frame.SetFrameInfo(dwFieldSpec, out frameInfoArray[i], p != null ? p.Item2 : null);
}
}
enumObject = new AD7FrameInfoEnum(frameInfoArray);
return Constants.S_OK;
}

It's fine how HandleStackTraceRequestAsync slices stack frames according to client's needs.
The problem is about DebuggedProcess.GetParameterInfoOnly called over all frames on the stack which is not cached and might be quite expensive:

MS_MIDebug: 1: (750055) <-1616-stack-list-arguments 0 0 49
MS_MIDebug: 1: (750065) ->1616^done,stack-args=[
frame={level="0",args=[name="x",name="v",name="v1",name="v2",name="v3",name="v4"]},
............................,
frame={level="36",args=[name="x",name="ub",name="v",name="v1",name="v2",name="v3",name="v4"]},
frame={level="37",args=[name="ub"]},
............................,
frame={level="49",args=[]}]
MS_MIDebug: 1: (750285) <-1629-stack-select-frame 1
.......
MS_MIDebug: 1: (759576) <-2184-stack-select-frame 42

If I patch the method:

int IDebugThread201(enum_FRAMEINFO_FLAGS dwFieldSpec, uint nRadix, uint startFrame, uint endFrame, out IEnumDebugFrameInfo2 enumObject) {
......
    uint low = Math.Max(stackFrames[0].Level, startFrame);
    uint high = Math.Min(stackFrames[stackFrames.Count - 1].Level, endFrame);
......
}

Then it works great. First a breakpoint is hit:

MS_MIDebug: 1: (15842) <-1027-stack-list-arguments 0 0 20
.....
MS_MIDebug: 1: (21016) <-1325-stack-select-frame 20

then I click Load All Stack Frames button in the VSCode:

MS_MIDebug: 1: (191412) <-1664-stack-list-arguments 0 20 49
.......
MS_MIDebug: 1: (196139) <-1935-stack-select-frame 42