public static void ExecuteDbgEngCommand(this DataTarget target, string command, CommandExecutionContext context) { IDebugControl6 control = (IDebugControl6)target.DebuggerInterface; int hr = control.ExecuteWide( DEBUG_OUTCTL.THIS_CLIENT, command, DEBUG_EXECUTE.DEFAULT); if (HR.Failed(hr)) { context.WriteErrorLine("Command execution failed with hr = {0:x8}", hr); } }
private string GetModuleName(IXCLRDataModule module) { StringBuilder name = new StringBuilder(MaxNameSize); uint nameLen; if (HR.Failed(module.GetName((uint)name.Capacity, out nameLen, name))) { return(null); } return(name.ToString()); }
private bool FailedUnlessOnCLR2DAC(int hr) { // For CLR v2 DAC, if the return HRESULT is random garbage, ignore the error. // This happens because of a source-level bug in the DAC that returns an // uninitialized stack variable. This was confirmed by a Microsoft engineer. if (_isOnCLRv2) { return(false); } return(HR.Failed(hr)); }
private DataTarget CreateDbgEngDataTargetImpl() { if (String.IsNullOrEmpty(DumpFile)) { throw new InvalidOperationException("DbgEng targets can be created only for dump files at this point."); } var target = DataTarget.LoadCrashDump(DumpFile, CrashDumpReader.DbgEng); target.AppendSymbolPath(SymbolPath); var outputCallbacks = new OutputCallbacks(this); msos_IDebugClient5 client = (msos_IDebugClient5)target.DebuggerInterface; HR.Verify(client.SetOutputCallbacksWide(outputCallbacks)); return(target); }
private void ProcessStackWalk(uint osThreadId) { IXCLRDataProcess ixclrDataProcess = _context.Runtime.DacInterface; object tmp; HR.Verify(ixclrDataProcess.GetTaskByOSThreadID(osThreadId, out tmp)); IXCLRDataTask task = (IXCLRDataTask)tmp; HR.Verify(task.CreateStackWalk(0xf /*all flags*/, out tmp)); IXCLRDataStackWalk stackWalk = (IXCLRDataStackWalk)tmp; while (HR.S_OK == stackWalk.Next()) { ProcessFrame(stackWalk); } }
public void Fill(IDebugAdvanced2 debugAdvanced) { int size = Marshal.SizeOf(typeof(DEBUG_THREAD_BASIC_INFORMATION)); byte[] buffer = new byte[size]; if (HR.Failed(debugAdvanced.GetSystemObjectInformation( DEBUG_SYSOBJINFO.THREAD_BASIC_INFORMATION, 0, EngineThreadId, buffer, buffer.Length, out size))) { return; } var gch = GCHandle.Alloc(buffer, GCHandleType.Pinned); try { var threadBasicInformation = (DEBUG_THREAD_BASIC_INFORMATION) Marshal.PtrToStructure(gch.AddrOfPinnedObject(), typeof(DEBUG_THREAD_BASIC_INFORMATION)); if ((threadBasicInformation.Valid & DEBUG_TBINFO.AFFINITY) != 0) { Affinity = threadBasicInformation.Affinity; } if ((threadBasicInformation.Valid & DEBUG_TBINFO.PRIORITY_CLASS) != 0) { PriorityClass = threadBasicInformation.PriorityClass; } if ((threadBasicInformation.Valid & DEBUG_TBINFO.PRIORITY) != 0) { Priority = threadBasicInformation.Priority; } if ((threadBasicInformation.Valid & DEBUG_TBINFO.TIMES) != 0) { CreateTime = threadBasicInformation.CreateTime; KernelTime = threadBasicInformation.KernelTime; UserTime = threadBasicInformation.UserTime; } } finally { gch.Free(); } }
private ulong GetStacksSize(DataTarget target) { // Find all the TEBs and then sum StackBase - StackLimit for all of them. // This gives us the committed size for each thread, but we don't have the // reserved size (which is the actual address space consumed). Theoretically, // we could get it from enumerating the memory region adjacent to the committed // pages and the guard page that follows. Also, for WoW64 threads, we are only // reporting the x86 stack (the x64 stack doesn't live in the 4GB address space // anyway, so it's not that relevant). IDebugSystemObjects sysObjects = (IDebugSystemObjects)target.DebuggerInterface; uint numThreads; HR.Verify(sysObjects.GetNumberThreads(out numThreads)); ulong totalCommit = 0; for (uint i = 0; i < numThreads; ++i) { HR.Verify(sysObjects.SetCurrentThreadId(i)); ulong tebAddress; HR.Verify(sysObjects.GetCurrentThreadTeb(out tebAddress)); int read; byte[] teb = new byte[IntPtr.Size * 3]; // ExceptionList, StackBase, StackLimit if (target.ReadProcessMemory(tebAddress, teb, teb.Length, out read) && read == teb.Length) { ulong stackBase = AddressFromBytes(teb, IntPtr.Size); ulong stackLimit = AddressFromBytes(teb, IntPtr.Size * 2); totalCommit = stackBase - stackLimit; } } return(totalCommit); }
private DataTarget CreateDbgEngDataTargetImpl() { if (String.IsNullOrEmpty(DumpFile)) { throw new InvalidOperationException("DbgEng targets can be created only for dump files at this point."); } var target = DataTarget.LoadCrashDump(DumpFile, CrashDumpReader.DbgEng); target.SymbolLocator.SymbolPath = SymbolPath; ((IDebugSymbols)target.DebuggerInterface).SetSymbolPath(SymbolPath); if (DisplayDiagnosticInformation) { ((IDebugControl)target.DebuggerInterface).Execute(DEBUG_OUTCTL.NOT_LOGGED, "!sym noisy", DEBUG_EXECUTE.NOT_LOGGED); } ((IDebugControl)target.DebuggerInterface).Execute(DEBUG_OUTCTL.NOT_LOGGED, ".reload", DEBUG_EXECUTE.NOT_LOGGED); var outputCallbacks = new OutputCallbacks(this); IDebugClient5 client = (IDebugClient5)target.DebuggerInterface; HR.Verify(client.SetOutputCallbacksWide(outputCallbacks)); return(target); }
private void FillValue(ArgumentOrLocal argOrLocal, IXCLRDataValue value) { object tmp; ulong size; if (HR.Failed(value.GetSize(out size))) { size = 0; // When the value is unavailable, GetSize fails; consider it 0 } argOrLocal.Size = size; bool probablyReferenceType = false; int getTypeHr = value.GetType(out tmp); if (getTypeHr == HR.S_FALSE) { // For reference types, GetType returns S_FALSE and we need to call GetAssociatedType // to retrieve the type that the reference points to. getTypeHr = value.GetAssociatedType(out tmp); probablyReferenceType = (getTypeHr == HR.S_OK); } if (getTypeHr != HR.S_OK) { return; } IXCLRDataTypeInstance typeInstance = (IXCLRDataTypeInstance)tmp; StringBuilder typeName = new StringBuilder(MaxNameSize); uint typeNameLen; if (FailedUnlessOnCLR2DAC(typeInstance.GetName(0 /*CLRDATA_GETNAME_DEFAULT*/, (uint)typeName.Capacity, out typeNameLen, typeName))) { return; } argOrLocal.StaticTypeName = typeName.ToString(); argOrLocal.ClrType = _context.Heap.GetTypeByName(argOrLocal.StaticTypeName); // If the value is unavailable, we're done here. if (size == 0) { return; } FillLocation(argOrLocal, value); argOrLocal.Value = new byte[size]; uint dataSize; if (HR.Failed(value.GetBytes((uint)argOrLocal.Value.Length, out dataSize, argOrLocal.Value))) { argOrLocal.Value = null; } // If the type is an array type (e.g. System.Byte[]), or a pointer type // (e.g. System.Byte*), or a by-ref type (e.g. System.Byte&), ClrHeap.GetTypeByName // will never return a good value. This is only a problem with variables that // are either ref types and null (and then we can't get the type name from // the object itself), variables that are pointers, and variables that are // by-ref types. Here's the plan: // 1) If the variable is a ref type and is null, we don't care about the // ClrType being correct anyway. We report the type returned by GetName // above, and report the value as null. // 2) If the value is a by-ref type or a pointer type, IXCLRDataValue::GetFlags // can detect it. Then, we keep the ClrType null (because ClrMD doesn't have // a representation for pointer types or by-ref types), but we read the // value anyway by dereferencing the pointer. According to the comments in // xclrdata.idl, IXCLRDataValue::GetAssociatedValue is supposed to return the // pointed-to value, but it doesn't (it only works for references). uint vf; CLRDataValueFlag valueFlags = CLRDataValueFlag.Invalid; if (HR.S_OK == value.GetFlags(out vf)) { valueFlags = (CLRDataValueFlag)vf; } // * Pointers are identified as CLRDATA_VALUE_IS_POINTER. // * By-refs are identified as CLRDATA_VALUE_DEFAULT regardless of referenced type. bool byRefOrPointerType = (valueFlags & CLRDataValueFlag.CLRDATA_VALUE_IS_POINTER) != 0 || (valueFlags == CLRDataValueFlag.CLRDATA_VALUE_DEFAULT /* it is 0 */); if (byRefOrPointerType) { // By-refs to pointers are identified as CLRDATA_VALUE_DEFAULT with target UInt64, // which makes them undistinguishable from 'ref ulong', unfortunately. But if the // type name reported didn't include the &, we know that's what it is. if (argOrLocal.StaticTypeName == "System.UInt64") { argOrLocal.ClrType = null; // We don't really know what the type is argOrLocal.StaticTypeName = "UNKNOWN*&"; } if (argOrLocal.Value != null) { ulong ptrValue = RawBytesToAddress(argOrLocal.Value); ulong potentialReference; if (_context.Runtime.ReadPointer(ptrValue, out potentialReference) && (argOrLocal.ClrType = _context.Heap.GetObjectType(potentialReference)) != null) { // If the type was resolved, then this was the address of a heap object. // In that case, we're done and we have a type. argOrLocal.ObjectAddress = potentialReference; } else { // Otherwise, this address is the address of a value type. We don't know // which type, because IXCLRDataValue::GetAssociatedType doesn't return anything // useful when the value is a pointer or by-ref. But we can try to remove // the * or & from the type name, and then try to figure out what the target // type is. string noRefNoPtrTypeName = argOrLocal.StaticTypeName.TrimEnd('&', '*'); if ((argOrLocal.ClrType = _context.Heap.GetTypeByName(noRefNoPtrTypeName)) != null) { argOrLocal.Location = ptrValue; } } } } if (argOrLocal.ClrType == null) { // If the type is an inner type, IXCLRDataTypeInstance::GetName reports only // the inner part of the type. This isn't enough for ClrHeap.GetTypeByName, // so we have yet another option in that case -- searching by metadata token. TryGetTypeByMetadataToken(argOrLocal, typeInstance); // If we had a pointer or by-ref type and didn't know what it was, we now do, // so we can store its location and have it displayed. if (byRefOrPointerType && argOrLocal.ClrType != null) { argOrLocal.Location = RawBytesToAddress(argOrLocal.Value); } } if (!byRefOrPointerType && (probablyReferenceType || argOrLocal.IsReferenceType)) { argOrLocal.ObjectAddress = RawBytesToAddress(argOrLocal.Value); // The type assigned here can be different from the previous value, // because the static and dynamic type of the argument could differ. // If the object reference is null or invalid, it could also be null -- // so we keep the previous type if it was already available. argOrLocal.ClrType = _context.Heap.GetObjectType(argOrLocal.ObjectAddress) ?? argOrLocal.ClrType; } }
private void ProcessFrame(IXCLRDataStackWalk stackWalk) { 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; } uint numArgs, numLocals; if (HR.Failed(frame.GetNumArguments(out numArgs))) { numArgs = 0; } if (HR.Failed(frame.GetNumLocalVariables(out numLocals))) { numLocals = 0; } FrameArgumentsAndLocals frameArgsLocals = new FrameArgumentsAndLocals() { MethodName = methodName.ToString() }; for (uint argIdx = 0; argIdx < numArgs; ++argIdx) { StringBuilder argName = new StringBuilder(MaxNameSize); uint argNameLen; if (FailedUnlessOnCLR2DAC(frame.GetArgumentByIndex(argIdx, out tmp, (uint)argName.Capacity, out argNameLen, argName)) || tmp == null) { continue; } var arg = new ArgumentOrLocal() { Name = argName.ToString() }; FillValue(arg, (IXCLRDataValue)tmp); frameArgsLocals.Arguments.Add(arg); } for (uint lclIdx = 0; lclIdx < numLocals; ++lclIdx) { // The mscordacwks!ClrDataFrame::GetLocalVariableByIndex implementation never returns // names for local variables. Need to go through metadata to get them. StringBuilder dummy = new StringBuilder(2); uint lclNameLen; if (FailedUnlessOnCLR2DAC(frame.GetLocalVariableByIndex(lclIdx, out tmp, (uint)dummy.Capacity, out lclNameLen, dummy)) || tmp == null) { continue; } string lclName = dummy.ToString(); var matchingFrame = _stackTrace.SingleOrDefault(f => f.DisplayString == frameArgsLocals.MethodName); if (matchingFrame != null) { lclName = GetLocalVariableName(matchingFrame.InstructionPointer, lclIdx); } var lcl = new ArgumentOrLocal() { Name = lclName }; FillValue(lcl, (IXCLRDataValue)tmp); frameArgsLocals.LocalVariables.Add(lcl); } _results.Add(frameArgsLocals); }
public override bool Generate(CommandExecutionContext context) { _title = Path.GetFileName(context.DumpFile); switch (context.TargetType) { case TargetType.DumpFile: DumpType = "Full memory dump with heap"; break; case TargetType.DumpFileNoHeap: DumpType = "Mini dump with no heap"; break; default: DumpType = "Unsupported dump file type"; break; } var target = context.NativeDbgEngTarget; IDebugSystemObjects2 sysObjects = (IDebugSystemObjects2)target.DebuggerInterface; IDebugControl2 control = (IDebugControl2)target.DebuggerInterface; uint dummy; StringBuilder exeName = new StringBuilder(2048); if (HR.Succeeded(sysObjects.GetCurrentProcessExecutableName(exeName, exeName.Capacity, out dummy))) { ExecutableName = exeName.ToString(); } uint uptime; if (HR.Succeeded(sysObjects.GetCurrentProcessUpTime(out uptime))) { ProcessUpTimeInSeconds = uptime; } if (HR.Succeeded(control.GetCurrentSystemUpTime(out uptime))) { SystemUpTimeInSeconds = uptime; } uint time; if (HR.Succeeded(control.GetCurrentTimeDate(out time))) { SessionTime = DateTimeOffset.FromUnixTimeSeconds(time); } uint num; if (HR.Succeeded(control.GetNumberProcessors(out num))) { NumberOfProcessors = num; } uint platformId, major, minor, servicePackNumber; StringBuilder servicePack = new StringBuilder(1048); StringBuilder build = new StringBuilder(1048); if (HR.Succeeded(control.GetSystemVersion(out platformId, out major, out minor, servicePack, servicePack.Capacity, out dummy, out servicePackNumber, build, build.Capacity, out dummy))) { WindowsBuildNumber = minor; WindowsServicePack = servicePack.ToString(); WindowsServicePackNumber = servicePackNumber; WindowsBuild = build.ToString(); } ClrVersions.AddRange(context.Runtime.DataTarget.ClrVersions.Select(v => v.Version.ToString())); if (context.Runtime.DataTarget.ClrVersions.Any(v => v.Version.Minor == 2)) { Recommendations.Add(new CLRV2Detected()); } return(true); }
public static LastEventInformation GetLastEventInformation(this DataTarget target) { var control = (IDebugControl)target.DebuggerInterface; DEBUG_EVENT eventType; uint procId, threadId; StringBuilder description = new StringBuilder(2048); uint unused; uint descriptionSize; if (HR.Failed(control.GetLastEventInformation( out eventType, out procId, out threadId, IntPtr.Zero, 0, out unused, description, description.Capacity, out descriptionSize))) { return(null); } var osThreadIds = target.GetOSThreadIds(); var eventInformation = new LastEventInformation { OSThreadId = (int)osThreadIds[threadId], EventType = eventType, EventDescription = description.ToString() }; IDebugAdvanced2 debugAdvanced = (IDebugAdvanced2)target.DebuggerInterface; int outSize; byte[] buffer = new byte[Marshal.SizeOf(typeof(EXCEPTION_RECORD64))]; int hr = debugAdvanced.Request(DEBUG_REQUEST.TARGET_EXCEPTION_RECORD, null, 0, buffer, buffer.Length, out outSize); if (HR.Succeeded(hr)) { GCHandle gch = GCHandle.Alloc(buffer, GCHandleType.Pinned); try { eventInformation.ExceptionRecord = (EXCEPTION_RECORD64)Marshal.PtrToStructure(gch.AddrOfPinnedObject(), typeof(EXCEPTION_RECORD64)); } finally { gch.Free(); } } buffer = new byte[Marshal.SizeOf(typeof(uint))]; hr = debugAdvanced.Request(DEBUG_REQUEST.TARGET_EXCEPTION_THREAD, null, 0, buffer, buffer.Length, out outSize); if (HR.Succeeded(hr)) { // If there is a stored exception event with a thread id, use that instead // of what GetLastEventInformation returns, because it might be different. eventInformation.OSThreadId = (int)BitConverter.ToUInt32(buffer, 0); } buffer = new byte[Marshal.SizeOf(typeof(CONTEXT))]; hr = debugAdvanced.Request(DEBUG_REQUEST.TARGET_EXCEPTION_CONTEXT, null, 0, buffer, buffer.Length, out outSize); if (HR.Succeeded(hr)) { var gch = GCHandle.Alloc(buffer, GCHandleType.Pinned); try { eventInformation.ExceptionContext = (CONTEXT)Marshal.PtrToStructure( gch.AddrOfPinnedObject(), typeof(CONTEXT)); } finally { gch.Free(); } } return(eventInformation); }