Open stej opened 7 years ago
Thanks for reporting this!
Well, the thing is that we don't have a frame index -- the frames are enumerated in that case by using the IXCLRDataStackWalk::Next
enumerator method, and I don't really have any guarantee that these frames will exactly agree with the frames passed to the constructor as a IList<ClrStackFrame>
; although they probably will agree in most cases.
I suppose we could resort to passing the frame index but verifying that the display string matches the method name we get from the IXCLR interface. Otherwise, we could end up with totally mismatched and bogus locals/arguments.
/cc @Pavel-Durov
@Pavel-Durov is somehow responsible for the code we are talking about? Are you open for pull requests? I might try to implement what you suggested in previous comment.
@Pavel-Durov is a contributor who helped with some other features. I implemented this specific part, but I am more than open to pull requests -- if you have the time to tackle this, it would be most appreciated.
Looking at the issue. I made a sample program and made a dump of that program. I ran windbg on that dump and looked at the stack. Windbg shows this:
0:000> !clrstack
OS Thread Id: 0x6fa8 (0)
Child SP IP Call Site
000000104f10e428 00007ffe57856194 [InlinedCallFrame: 000000104f10e428] Microsoft.Win32.Win32Native.ReadFile(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
000000104f10e428 00007ffe388ea5b4 [InlinedCallFrame: 000000104f10e428] Microsoft.Win32.Win32Native.ReadFile(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
000000104f10e3f0 00007ffe388ea5b4 DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
000000104f10e4d0 00007ffe390feb81 System.IO.__ConsoleStream.ReadFileNative(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte[], Int32, Int32, Boolean, Boolean, Int32 ByRef)
000000104f10e560 00007ffe390fea36 System.IO.__ConsoleStream.Read(Byte[], Int32, Int32)
000000104f10e5c0 00007ffe388c7fec System.IO.StreamReader.ReadBuffer()
000000104f10e610 00007ffe388c7d6d System.IO.StreamReader.ReadLine()
000000104f10e680 00007ffe392c931f System.IO.TextReader+SyncTextReader.ReadLine()
000000104f10e6e0 00007ffe39097a77 System.Console.ReadLine()
000000104f10e710 00007ffdda8006be dupstack.Program.Multi() [C:\dev\github\msos-stej\dupStackRepro\dupstack\dupstack\Program.cs @ 26]
000000104f10e7c0 00007ffdda8006de dupstack.Program.Multi() [C:\dev\github\msos-stej\dupStackRepro\dupstack\dupstack\Program.cs @ 30]
000000104f10e870 00007ffdda8006de dupstack.Program.Multi() [C:\dev\github\msos-stej\dupStackRepro\dupstack\dupstack\Program.cs @ 30]
000000104f10e920 00007ffdda8006de dupstack.Program.Multi() [C:\dev\github\msos-stej\dupStackRepro\dupstack\dupstack\Program.cs @ 30]
000000104f10e9d0 00007ffdda8006de dupstack.Program.Multi() [C:\dev\github\msos-stej\dupStackRepro\dupstack\dupstack\Program.cs @ 30]
000000104f10ea80 00007ffdda80050d dupstack.Program.Main(System.String[]) [C:\dev\github\msos-stej\dupStackRepro\dupstack\dupstack\Program.cs @ 15]
000000104f10ed30 00007ffe39df6573 [GCFrame: 000000104f10ed30]
The test program is this: Program.zip Very simple, the vars there are just for testing.
Modified the code so that it takes zerobased stackFrameIndex like this
private void ProcessStackWalk(uint osThreadId)
{
....
int idx = 0;
while (HR.S_OK == stackWalk.Next())
{
ProcessFrame(stackWalk, idx);
idx++;
}
}
......
private void ProcessFrame(IXCLRDataStackWalk stackWalk, int stackFrameIndex)
{
object tmp;
if (HR.Failed(stackWalk.GetFrame(out tmp)))
return;
IXCLRDataFrame frame = (IXCLRDataFrame)tmp;
StringBuilder methodName = new StringBuilder(MaxNameSize);
uint methodNameLen;
if (HR.Failed(frame.GetCodeName(0 /*default flags*/, (uint)methodName.Capacity, out methodNameLen, methodName)))
return;
Console.WriteLine($"by frame: {methodName}");
Console.WriteLine($"by index: {_stackTrace[stackFrameIndex]}");
.......
}
and the result is interesting. (I copied the output to console to paste it here)
When using the frame
(COM object), I get
by frame: DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
by frame: DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
by frame: System.IO.__ConsoleStream.ReadFileNative(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte[], Int32, Int32, Boolean, Boolean, Int32 ByRef)
by frame: System.IO.__ConsoleStream.Read(Byte[], Int32, Int32)
by frame: System.IO.StreamReader.ReadBuffer()
by frame: System.IO.StreamReader.ReadLine()
by frame: System.IO.TextReader+SyncTextReader.ReadLine()
by frame: System.Console.ReadLine()
by frame: dupstack.Program.Multi()
by frame: dupstack.Program.Multi()
by frame: dupstack.Program.Multi()
by frame: dupstack.Program.Multi()
by frame: dupstack.Program.Multi()
by frame: dupstack.Program.Main(System.String[])
There is duplicated DomainNeutralILStubClass.IL_STUB_PInvoke
. Why?
When I use the index approach, I get
by index: [InlinedCallFrame] (Microsoft.Win32.Win32Native.ReadFile)
by index: [InlinedCallFrame] (Microsoft.Win32.Win32Native.ReadFile)
by index: DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
by index: System.IO.__ConsoleStream.ReadFileNative(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte[], Int32, Int32, Boolean, Boolean, Int32 ByRef)
by index: System.IO.__ConsoleStream.Read(Byte[], Int32, Int32)
by index: System.IO.StreamReader.ReadBuffer()
by index: System.IO.StreamReader.ReadLine()
by index: System.IO.TextReader+SyncTextReader.ReadLine()
by index: System.Console.ReadLine()
by index: dupstack.Program.Multi()
by index: dupstack.Program.Multi()
by index: dupstack.Program.Multi()
by index: dupstack.Program.Multi()
by index: dupstack.Program.Multi()
So there are the inline stack frames that are not in the first listing (by frame).
Why there is the difference, is not very clear to me..
I'm running
!ClrStack -a
and I get this error message for some threadsThe callstack I'm analyzing looks like this:
The code crashes on inspeciting the frame
System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
on this linevar matchingFrame = _stackTrace.SingleOrDefault(f => f.DisplayString == frameArgsLocals.MethodName);
(see ClrThreadExtensions, methodProcessFrame
, my line numbers are a little bit different from the ones on github).Possible solutions
SingleOrDefault
withWhere
and ignore cases where count is != 1ProcessFrame
and use this index like thisvar matchingFrame = _stackTrace[index]
- this looks obvious, so there was probably a catch, right?