// Builds a lookup table of class+method name. // // It's easier to build it at once by enumerating, once we have the table, we // can use the symbolIds to look up the sources when we need them. private static AssemblyData FetchSymbolData(IDiaSession session) { // This will be a *flat* enumerator of all classes. // // A nested class will not contain a '+' in it's name, just a '.' separating the parent class name from // the child class name. IDiaEnumSymbols diaClasses; session.findChildren( session.globalScope, // Search at the top-level. SymTagEnum.SymTagCompiland, // Just find classes. name: null, // Don't filter by name. compareFlags: 0u, // doesn't matter because name is null. ppResult: out diaClasses); var assemblyData = new AssemblyData(); // Resist the urge to use foreach here. It doesn't work well with these APIs. var classesFetched = 0u; IDiaSymbol diaClass; diaClasses.Next(1u, out diaClass, out classesFetched); while (classesFetched == 1 && diaClass != null) { var classData = new ClassData() { Name = diaClass.name, SymbolId = diaClass.symIndexId, }; assemblyData.Classes.Add(diaClass.name, classData); IDiaEnumSymbols diaMethods; session.findChildren( diaClass, SymTagEnum.SymTagFunction, name: null, // Don't filter by name. compareFlags: 0u, // doesn't matter because name is null. ppResult: out diaMethods); // Resist the urge to use foreach here. It doesn't work well with these APIs. var methodsFetched = 0u; IDiaSymbol diaMethod; diaMethods.Next(1u, out diaMethod, out methodsFetched); while (methodsFetched == 1 && diaMethod != null) { classData.Methods[diaMethod.name] = new MethodData() { Name = diaMethod.name, SymbolId = diaMethod.symIndexId, }; diaMethods.Next(1u, out diaMethod, out methodsFetched); } diaClasses.Next(1u, out diaClass, out classesFetched); } return assemblyData; }
private static void HandleTsvStacktrace(string tsvPath, IDiaSession pdbSession) { var lines = File.ReadAllLines(tsvPath); for (int i = 1; i < lines.Length; i++) { string[] frags = lines[i].Split('\t'); string methodName = ""; string module = ""; if (frags.Length >= 4) { module = frags[1]; string rvaStr = frags[3]; string rva = rvaStr.Replace("0x", ""); if (frags[2] != "null") { methodName = frags[2]; } uint offset = HexToUint(rva); if (pdbSession != null && offset != 0) { string tmpMethodName = DiaHelper.GetMethodName(pdbSession, offset); if (!String.IsNullOrEmpty(tmpMethodName)) { methodName = tmpMethodName; } } } _writer.WriteLine($"at {module}!{methodName}"); } }
private void AddEntry(IDiaSession session, IDiaEnumSymbolsByAddr symbolsByAddr, ulong eip, uint rva) { session.findSymbolByRVA(rva, SymTagEnum.SymTagPublicSymbol, out var symbol); if (symbol == null) { session.findSymbolByRVA(rva, SymTagEnum.SymTagFunction, out symbol); if (symbol == null) { symbol = symbolsByAddr.symbolByRVA(rva); } } string functionName; if (symbol != null) { symbol.get_undecoratedNameEx(0x1000, out var unmangled); if (!string.IsNullOrEmpty(unmangled)) { functionName = unmangled; } else { functionName = !string.IsNullOrEmpty(symbol.name) ? symbol.name : eip.ToString("x"); } } else { functionName = eip.ToString("x"); } this.rvaToFunctionNameMap.Add(rva, functionName); }
/// <summary> /// Initializes a new instance of the <see cref="DiaModule"/> class. /// </summary> /// <param name="name">The module name.</param> /// <param name="nameSpace">The default namespace.</param> /// <param name="dia">The DIA data source.</param> /// <param name="session">The DIA session.</param> private DiaModule(string name, string nameSpace, IDiaDataSource dia, IDiaSession session) { this.session = session; this.dia = dia; Name = name; Namespace = nameSpace; }
private async Task <Type> LoadType(string module, string typename) { // Try to load the type from DIA. IDiaSession session = await this.debuggerEngine.DiaLoader.LoadDiaSession(module); bool foundType; Type type = this.typeCache.GetCachedType(session, module, typename, out foundType); if (foundType) { if (type != null) { return(type); } else { // We previously tried to load the type but were unable to. throw new DebuggerException(String.Format("Unable to load type: {0}!{1}", module, typename)); } } this.DebuggerMessage?.Invoke(this, String.Format("Loading type information for {0}!{1}...", module, typename)); if (session != null) { type = await this.LoadTypeFromDiaSession(session, module, typename, DiaHelpers.NameSearchOptions.nsCaseSensitive); if (type == null) { type = await this.LoadTypeFromDiaSession(session, module, typename, DiaHelpers.NameSearchOptions.nsCaseInsensitive); } if (type != null) { this.typeCache.AddType(type); return(type); } else { // We have a DIA session but couldn't find the type. Assume the type is invalid rather than falling back to the debugger. this.typeCache.AddInvalidType(module, typename); throw new DebuggerException(String.Format("Unable to load type: {0}!{1}", module, typename)); } } this.DebuggerMessage?.Invoke(this, String.Format("WARNING: Unable to load {0}!{1} from PDBs. Falling back to the debugger, which could be slow...", module, typename)); type = await this.debuggerEngine.GetTypeFromDebugger(module, typename); if (type != null) { this.typeCache.AddType(type); return(type); } else { this.typeCache.AddInvalidType(module, typename); throw new DebuggerException(String.Format("Unable to load type: {0}!{1}", module, typename)); } }
// Builds a lookup table of class+method name. // // It's easier to build it at once by enumerating, once we have the table, we // can use the symbolIds to look up the sources when we need them. private static AssemblyData FetchSymbolData(IDiaSession session) { // This will be a *flat* enumerator of all classes. // // A nested class will not contain a '+' in it's name, just a '.' separating the parent class name from // the child class name. IDiaEnumSymbols diaClasses; session.findChildren( session.globalScope, // Search at the top-level. SymTagEnum.SymTagCompiland, // Just find classes. name: null, // Don't filter by name. compareFlags: 0u, // doesn't matter because name is null. ppResult: out diaClasses); var assemblyData = new AssemblyData(); // Resist the urge to use foreach here. It doesn't work well with these APIs. var classesFetched = 0u; IDiaSymbol diaClass; diaClasses.Next(1u, out diaClass, out classesFetched); while (classesFetched == 1 && diaClass != null) { var classData = new ClassData() { Name = diaClass.name, SymbolId = diaClass.symIndexId, }; assemblyData.Classes.Add(diaClass.name, classData); IDiaEnumSymbols diaMethods; session.findChildren( diaClass, SymTagEnum.SymTagFunction, name: null, // Don't filter by name. compareFlags: 0u, // doesn't matter because name is null. ppResult: out diaMethods); // Resist the urge to use foreach here. It doesn't work well with these APIs. var methodsFetched = 0u; IDiaSymbol diaMethod; diaMethods.Next(1u, out diaMethod, out methodsFetched); while (methodsFetched == 1 && diaMethod != null) { classData.Methods[diaMethod.name] = new MethodData() { Name = diaMethod.name, SymbolId = diaMethod.symIndexId, }; diaMethods.Next(1u, out diaMethod, out methodsFetched); } diaClasses.Next(1u, out diaClass, out classesFetched); } return(assemblyData); }
/// <summary> /// Loads symbol provider module from the specified module. /// </summary> /// <param name="module">The module.</param> /// <returns>Interface for symbol provider module</returns> public override ISymbolProviderModule LoadModule(Module module) { // Try to get debugger DIA session IDiaSessionProvider diaSessionProvider = Context.Debugger as IDiaSessionProvider; IDiaSession diaSession = diaSessionProvider?.GetModuleDiaSession(module); if (diaSession != null) { return(new DiaModule(diaSession, module)); } // Try to load PDB file into our own DIA session string pdb = module.SymbolFileName; if (!string.IsNullOrEmpty(pdb) && Path.GetExtension(pdb).ToLower() == ".pdb") { try { return(new DiaModule(pdb, module)); } catch { } } return(null); }
private static void TryGetILOffsetInfo(IDiaSession session, int rva, out int ilOffset) { IDiaEnumLineNumbers lineNumbers; int hr = session.FindILOffsetsByRVA(rva, 1, out lineNumbers); if (hr == S_OK) { int numLineNumbers; hr = lineNumbers.Count(out numLineNumbers); if (hr == S_OK && numLineNumbers > 0) { IDiaLineNumber ln; hr = lineNumbers.Item(0, out ln); if (hr == S_OK) { hr = ln.LineNumber(out ilOffset); if (hr == S_OK) { return; } } } } ilOffset = StackFrame.OFFSET_UNKNOWN; }
internal DiaResolver(string binary, string pathExtension, ILogger logger) { _binary = binary; _logger = logger; string pdb = FindPdbFile(binary, pathExtension); if (pdb == null) { _logger.LogWarning($"Couldn't find the .pdb file of file '{binary}'. You will not get any source locations for your tests."); return; } if (!TryCreateDiaInstance(Dia140) && !TryCreateDiaInstance(Dia120) && !TryCreateDiaInstance(Dia110)) { _logger.LogError("Couldn't find the msdia.dll to parse *.pdb files. You will not get any source locations for your tests."); return; } _logger.DebugInfo($"Parsing pdb file \"{pdb}\""); _fileStream = File.Open(pdb, FileMode.Open, FileAccess.Read, FileShare.Read); _diaDataSource.loadDataFromIStream(new DiaMemoryStream(_fileStream)); dynamic diaSession110Or140; _diaDataSource.openSession(out diaSession110Or140); _diaSession = new DiaSessionAdapter(diaSession110Or140); }
/// <summary> /// Clears the symbol map and creates a new Dia source class. Sets Dia session to null /// </summary> private void Reset() { m_SymbolMap.Clear(); // If the creation of the DiaSourceClass fails, then you likely need // to register the MSDIA1xx.dll on your machine. See readme for more details m_DiaSource = new DiaSourceClass(); m_DiaSession = null; }
public void Dispose() { if (m_session != null) { Marshal.ReleaseComObject(m_session); m_session = null; } }
static PdbSession() { var module = Process.GetCurrentProcess().MainModule; var pdb = DiaSourceFactory.CreateInstance(); pdb.loadDataForExe(module.FileName, null, null); pdb.openSession(out IDiaSession session); Current = session; }
/// <summary> /// Opens the image and reads information about the symbols file, associated /// to this image. /// </summary> /// <param name="imagePath">The full path to executable or DLL.</param> /// <returns>Full path to corresponding symbols file or null if the image does /// not contain symbols file information.</returns> public static string GetSymbolsFileName(string imagePath) { Debug.Assert(!string.IsNullOrEmpty(imagePath), "imagePath"); IDiaDataSource source; try { source = (IDiaDataSource) new DiaSourceClass(); } catch (COMException ex) { EqtTrace.ErrorIf(EqtTrace.IsErrorEnabled, "DIA initialization threw:\n" + ex.ToString()); // Let things go fine if we can find a local file, return // null if we can't. This is needed to support xcopy deployment. string pdbFile = Path.ChangeExtension(imagePath, ".pdb"); if (File.Exists(pdbFile)) { return(pdbFile); } return(null); } IDiaSession session = null; IDiaSymbol global = null; try { // Load the image: source.loadDataForExe(imagePath, null, null); // Extract the main symbol via session: source.openSession(out session); global = session.globalScope; Debug.Assert(global != null, "globalScope Symbol"); return(global.symbolsFileName); } catch (COMException ex) { // If exception is thrown the image does not contain symbols or the symbols found // are not correct. EqtTrace.ErrorIf(EqtTrace.IsErrorEnabled, "DIA thew in retrieving symbols: " + ex.ToString()); return(null); } finally { // Ensure that the resources allocated by DIA get cleaned up. // In particular, after loadDataForExe, the PDB will be locked // until CDiaDataSource::~CDiaDataSource is called. ReleaseComObject(global); ReleaseComObject(session); ReleaseComObject(source); } }
public static void ReleaseSession(IDiaSession session) { IDiaDataSource source; if (sessionMap.TryGetValue(session, out source)) { sessionMap.Remove(session); Marshal.FinalReleaseComObject(session); Marshal.FinalReleaseComObject(source); } }
public async Task <IDiaSession> LoadDiaSession(string module) { module = module.ToLowerInvariant(); if (this.activeSessions.ContainsKey(module)) { return(this.activeSessions[module]); } // Try each of the sources until we're able to get one. foreach (IDiaSessionSource source in this.sources) { int attempts = 1; while (attempts <= 3) { if (attempts > 1) { await source.WaitUntilReady(); } ++attempts; try { IDiaDataSource dataSource = this.CreateDataSource(); if (dataSource == null) { // Without a data source we can't use DIA at all. break; } IDiaSession session = source.LoadSessionForModule(dataSource, module); if (session != null) { this.activeSessions[module] = session; return(session); } break; } catch (DebuggerException) { throw; } catch (DiaSourceNotReadyException) { // Try again. continue; } catch { // Try the next source. break; } } } // The session load failed. this.activeSessions[module] = null; return(null); }
/// <summary> /// Makes reasonable effort to find the IL offset corresponding to the given address within a method. /// Returns StackFrame.OFFSET_UNKNOWN if not available. /// </summary> public static void TryGetILOffsetWithinMethod(IntPtr ip, out int ilOffset) { ilOffset = StackFrame.OFFSET_UNKNOWN; if (!IsDiaStackTraceResolutionDisabled()) { int rva; IDiaSession session = GetDiaSession(ip, out rva); if (session != null) { TryGetILOffsetInfo(session, rva, out ilOffset); } } }
// // Makes reasonable effort to get source info. Returns null sourceFile and 0 lineNumber/columnNumber if it can't. // public static void TryGetSourceLineInfo(IntPtr ip, out string fileName, out int lineNumber, out int columnNumber) { fileName = null; lineNumber = 0; columnNumber = 0; int rva; IDiaSession session = GetDiaSession(ip, out rva); if (session == null) { return; } TryGetSourceLineInfo(session, rva, out fileName, out lineNumber, out columnNumber); }
protected virtual void Dispose(bool disposing) { if (disposing) { // free the state of any contained objects // we don't contain any other objects! } // free my own state if (session != null) { Marshal.ReleaseComObject(session); session = null; } }
// // Makes reasonable effort to construct one useful line of a stack trace. Returns null if it can't. // public static String CreateStackTraceString(IntPtr ip, bool includeFileInfo) { if (IsDiaStackTraceResolutionDisabled()) { return(null); } try { int hr; int rva; IDiaSession session = GetDiaSession(ip, out rva); if (session == null) { return(null); } StringBuilder sb = new StringBuilder(); IDiaSymbol symbol; hr = session.FindSymbolByRVA(rva, SymTagEnum.SymTagFunction, out symbol); if (hr != S_OK) { return(null); } String functionName; hr = symbol.GetName(out functionName); if (hr == S_OK) { sb.Append(functionName.Demanglify()); } else { sb.Append("<Function Name Not Available>"); } sb.Append(CreateParameterListString(session, symbol)); if (includeFileInfo) { sb.Append(CreateSourceInfoString(session, rva)); } return(sb.ToString()); } catch { return(null); } }
// // Makes reasonable effort to get source info. Returns null sourceFile and 0 lineNumber/columnNumber if it can't. // public static void TryGetSourceLineInfo(IntPtr ip, out string fileName, out int lineNumber, out int columnNumber) { fileName = null; lineNumber = 0; columnNumber = 0; if (!IsDiaStackTraceResolutionDisabled()) { int rva; IDiaSession session = GetDiaSession(ip, out rva); if (session != null) { TryGetSourceLineInfo(session, rva, out fileName, out lineNumber, out columnNumber); } } }
// // Generate the " in <filename>:line <line#>" section. // private static String CreateSourceInfoString(IDiaSession session, int rva) { StringBuilder sb = new StringBuilder(); string fileName; int lineNumber, columnNumber; TryGetSourceLineInfo(session, rva, out fileName, out lineNumber, out columnNumber); if (!string.IsNullOrEmpty(fileName)) { sb.Append(" in ").Append(fileName); if (lineNumber >= 0) { sb.Append(":line ").Append(lineNumber); } } return(sb.ToString()); }
private static void HandleCrash(string hash, IDiaSession pdbSession, int crashCount) { if (!String.IsNullOrEmpty(hash)) { var crashDetails = GetCrashDetails(hash); int crashOccurence = 0; foreach (var crashDetail in crashDetails) { crashOccurence++; string cabId = crashDetail.cabId; if (!String.IsNullOrEmpty(cabId)) { var stackTrace = GetCrashStacktrace(cabId); _writer.WriteLine(); _writer.WriteLine(); _writer.WriteLine(); _writer.WriteLine($"Crash #{crashCount}.{crashOccurence}: {crashDetail.failureName}"); _writer.WriteLine(); if (stackTrace != null) { foreach (var stackLine in stackTrace) { string methodName = stackLine.function; if (String.IsNullOrEmpty(methodName) || methodName == "null") { string fullOffset = stackLine.offset; string hexOffset = fullOffset.Replace("0x", ""); uint offset = HexToUint(hexOffset); if (pdbSession != null && offset != 0) { methodName = DiaHelper.GetMethodName(pdbSession, offset); } } _writer.WriteLine($"at {stackLine.image}!{methodName}"); } if (_preventDuplication) { break; } } } } } }
/// <summary> /// Initializes this instance of the <see cref="DiaModule"/> class. /// </summary> /// <param name="diaSession">The DIA session.</param> /// <param name="module">The module.</param> private void Initialize(IDiaSession diaSession, Module module) { Module = module; session = diaSession; globalScope = session.globalScope; typeAllFields = new DictionaryCache <uint, List <Tuple <string, uint, int> > >(GetTypeAllFields); typeFields = new DictionaryCache <uint, List <Tuple <string, uint, int> > >(GetTypeFields); basicTypes = SimpleCache.Create(() => { var types = new Dictionary <string, IDiaSymbol>(); var basicTypes = globalScope.GetChildren(SymTagEnum.BaseType); foreach (var type in basicTypes) { try { string typeString = TypeToString.GetTypeString(type); if (!types.ContainsKey(typeString)) { types.Add(typeString, type); } } catch (Exception) { } } return(types); }); symbolNamesByAddress = new DictionaryCache <uint, Tuple <string, ulong> >((distance) => { IDiaSymbol symbol; int displacement; string name; session.findSymbolByRVAEx(distance, SymTagEnum.Null, out symbol, out displacement); name = symbol.get_undecoratedNameEx(UndecoratedNameOptions.NameOnly | UndecoratedNameOptions.NoEscu); return(Tuple.Create(name, (ulong)displacement)); }); session.loadAddress = module.Address; enumTypeNames = new DictionaryCache <uint, Dictionary <ulong, string> >(GetEnumName); }
private ConstantCache LoadGlobalConstantsFromDiaSession(IDiaSession diaSession) { List <SConstantResult> constants = new List <SConstantResult>(); diaSession.findChildren(diaSession.globalScope, SymTagEnum.SymTagData, null, 0, out IDiaEnumSymbols symbols); foreach (IDiaSymbol symbol in symbols) { SymTagEnum symTag = (SymTagEnum)symbol.symTag; if (symbol.locationType == (uint)DiaHelpers.LocationType.LocIsConstant) { constants.Add(new SConstantResult() { ConstantName = symbol.name, Value = (ulong)symbol.value }); } } return(new ConstantCache(constants)); }
/// <summary> /// Initializes a new instance of the <see cref="Module"/> class. /// </summary> /// <param name="name">The module name.</param> /// <param name="nameSpace">The default namespace.</param> /// <param name="dia">The DIA data source.</param> /// <param name="session">The DIA session.</param> private Module(string name, string nameSpace, IDiaDataSource dia, IDiaSession session) { this.session = session; this.dia = dia; Name = name; Namespace = nameSpace; GlobalScope = GetSymbol(session.globalScope); PublicSymbols = new HashSet<string>(session.globalScope.GetChildren(SymTagEnum.SymTagPublicSymbol).Select((type) => { if (type.code != 0 || type.function != 0 || (LocationType)type.locationType != LocationType.Static) return string.Empty; string undecoratedName; type.get_undecoratedNameEx(0x1000, out undecoratedName); return undecoratedName ?? type.name; })); }
private async Task <ConstantCache> LoadConstants(string module) { if (this.constantCaches.ContainsKey(module)) { return(this.constantCaches[module]); } IDiaSession session = await this.debuggerEngine.DiaLoader.LoadDiaSession(module); if (session != null) { this.constantCaches[module] = this.LoadGlobalConstantsFromDiaSession(session); return(this.constantCaches[module]); } else { return(null); } }
/// <summary> /// Initializes a new instance of the <see cref="Module"/> class. /// </summary> /// <param name="name">The module name.</param> /// <param name="nameSpace">The default namespace.</param> /// <param name="dia">The DIA data source.</param> /// <param name="session">The DIA session.</param> private Module(string name, string nameSpace, IDiaDataSource dia, IDiaSession session) { this.session = session; this.dia = dia; Name = name; Namespace = nameSpace; GlobalScope = GetSymbol(session.globalScope); PublicSymbols = new HashSet <string>(session.globalScope.GetChildren(SymTagEnum.SymTagPublicSymbol).Select((type) => { if (type.code != 0 || type.function != 0 || (LocationType)type.locationType != LocationType.Static) { return(string.Empty); } string undecoratedName; type.get_undecoratedNameEx(0x1000, out undecoratedName); return(undecoratedName ?? type.name); })); }
private Type GetBuiltinType(IDiaSession session, string module, string typename) { string strippedType = typename.Replace("unsigned", "").Replace("signed", "").Trim(); if (BuiltInTypes.ContainsKey(strippedType)) { return(new Type(module, typename, BuiltInTypes[strippedType], false, null, null, null)); } else if (strippedType.EndsWith("*")) { uint pointerSize = this.isPointer64Bit ? 8u : 4u; if (this.modulePointerSizes.ContainsKey(module)) { uint cachedPointerSize = this.modulePointerSizes[module]; if (cachedPointerSize != 0) { pointerSize = cachedPointerSize; } } else if (session != null) { // Try to infer the pointer size from the DIA Session. IDiaEnumSymbols pointerSymbols; session.findChildren(session.globalScope, SymTagEnum.SymTagPointerType, null, 0, out pointerSymbols); foreach (IDiaSymbol symbol in pointerSymbols) { pointerSize = (uint)symbol.length; break; } this.modulePointerSizes[module] = pointerSize; } return(new Type(module, typename, pointerSize, false, null, null, null)); } else { return(null); } }
private static void HandleCrashTxtStacktrace(string crashTxt, IDiaSession pdbSession) { var lines = File.ReadAllLines(crashTxt); for (int i = 0; i < lines.Length; i++) { var currentLine = lines[i]; if (currentLine.Contains("<BaseAddress>")) { string outputLine = ""; string[] splitted = currentLine.Split(new string[] { "<BaseAddress>" }, StringSplitOptions.None); outputLine += splitted[0]; string rva = splitted[1].Replace("+0x", ""); uint offset = HexToUint(rva); if (pdbSession != null && offset != 0) { string tmpMethodName = DiaHelper.GetMethodName(pdbSession, offset); if (!String.IsNullOrEmpty(tmpMethodName)) { outputLine += tmpMethodName; } else { outputLine += "<BaseAddress>" + splitted[1]; } } else { outputLine += "<BaseAddress>" + splitted[1]; } _writer.WriteLine(outputLine); } else { _writer.WriteLine(currentLine); } } }
public Type GetCachedType(IDiaSession session, string module, string typename, out bool foundType) { string key = TypeKey(module, typename); if (this.types.ContainsKey(key)) { foundType = true; return(this.types[key]); } // Is it a built-in type? Type builtinType = this.GetBuiltinType(session, module, typename); if (builtinType != null) { this.types.Add(key, builtinType); foundType = true; return(builtinType); } foundType = false; return(null); }
// // Retrieve the source fileName, line number, and column // private static void TryGetSourceLineInfo(IDiaSession session, int rva, out string fileName, out int lineNumber, out int columnNumber) { fileName = null; lineNumber = 0; columnNumber = 0; IDiaEnumLineNumbers lineNumbers; int hr = session.FindLinesByRVA(rva, 1, out lineNumbers); if (hr == S_OK) { int numLineNumbers; hr = lineNumbers.Count(out numLineNumbers); if (hr == S_OK && numLineNumbers > 0) { IDiaLineNumber ln; hr = lineNumbers.Item(0, out ln); if (hr == S_OK) { IDiaSourceFile sourceFile; hr = ln.SourceFile(out sourceFile); if (hr == S_OK) { hr = sourceFile.FileName(out fileName); if (hr == S_OK) { hr = ln.LineNumber(out lineNumber); if (hr == S_OK) { hr = ln.ColumnNumber(out columnNumber); } } } } } } }
/// <summary> /// Initializes a new instance of the <see cref="DiaModule"/> class. /// </summary> /// <param name="diaSession">The DIA session.</param> /// <param name="module">The module.</param> public DiaModule(IDiaSession diaSession, Module module) { Initialize(diaSession, module); }
// // Create the method parameter list. // private static String CreateParameterListString(IDiaSession session, IDiaSymbol symbol) { StringBuilder sb = new StringBuilder("("); // find the parameters IDiaEnumSymbols dataSymbols; int hr = session.FindChildren(symbol, SymTagEnum.SymTagData, null, NameSearchOptions.nsNone, out dataSymbols); if (hr == S_OK) { int count; hr = dataSymbols.Count(out count); if (hr == S_OK) { for (int i = 0, iParam = 0; i < count; i++) { IDiaSymbol dataSym; hr = dataSymbols.Item(i, out dataSym); if (hr != S_OK) continue; DataKind dataKind; hr = dataSym.GetDataKind(out dataKind); if (hr != S_OK || dataKind != DataKind.DataIsParam) continue; string paramName; hr = dataSym.GetName(out paramName); if (hr != S_OK) { continue; } //this approximates the way C# displays methods by not including these hidden arguments if (paramName == "InstParam" || paramName == "this") { continue; } IDiaSymbol parameterType; hr = dataSym.GetType(out parameterType); if (hr != S_OK) { continue; } if (iParam++ != 0) sb.Append(", "); sb.Append(parameterType.ToTypeString(session)); sb.Append(' '); sb.Append(paramName); } } } sb.Append(')'); return sb.ToString(); }
internal DiaResolver(string binary, string pathExtension, ILogger logger, bool debugMode) { _binary = binary; _logger = logger; _debugMode = debugMode; string pdb = FindPdbFile(binary, pathExtension); if (pdb == null) { _logger.LogWarning($"Couldn't find the .pdb file of file '{binary}'. You will not get any source locations for your tests."); return; } if (!TryCreateDiaInstance(Dia140) && !TryCreateDiaInstance(Dia120) && !TryCreateDiaInstance(Dia110)) { _logger.LogError("Couldn't find the msdia.dll to parse *.pdb files. You will not get any source locations for your tests."); return; } if (_debugMode) _logger.LogInfo($"Parsing pdb file \"{pdb}\""); _fileStream = File.Open(pdb, FileMode.Open, FileAccess.Read, FileShare.Read); _diaDataSource.loadDataFromIStream(new DiaMemoryStream(_fileStream)); dynamic diaSession110Or140; _diaDataSource.openSession(out diaSession110Or140); _diaSession = new DiaSessionAdapter(diaSession110Or140); }
// // Generate the " in <filename>:line <line#>" section. // private static String CreateSourceInfoString(IDiaSession session, int rva) { StringBuilder sb = new StringBuilder(); string fileName; int lineNumber, columnNumber; TryGetSourceLineInfo(session, rva, out fileName, out lineNumber, out columnNumber); if(!string.IsNullOrEmpty(fileName)) { sb.Append(" in ").Append(fileName); if(lineNumber >= 0) { sb.Append(":line ").Append(lineNumber); } } return sb.ToString(); }
private static String ToTypeString(this IDiaSymbol parameterType, IDiaSession session) { bool ignore; return parameterType.ToTypeStringWorker(session, 0, out ignore); }
private static String ToTypeStringWorker(this IDiaSymbol parameterType, IDiaSession session, int recursionLevel, out bool isValueTypeOrByRef) { int hr; isValueTypeOrByRef = false; // Block runaway recursions. if (recursionLevel++ > 10) return "?"; SymTagEnum symTag; hr = parameterType.GetSymTag(out symTag); if (hr != S_OK) return "?"; if (symTag == SymTagEnum.SymTagPointerType) { bool isReference; hr = parameterType.GetReference(out isReference); if (hr != S_OK) return "?"; if (isReference) { // An isReference pointer can mean one of two things: // 1. ELEMENT_TYPE_BYREF // 2. An indication that the UDT that follows is actually a class, not a struct. // isValueTypeOrByRef = true; IDiaSymbol targetType; hr = parameterType.GetType(out targetType); if (hr != S_OK) return "?"; bool targetIsValueTypeOrByRef; String targetTypeString = targetType.ToTypeStringWorker(session, recursionLevel, out targetIsValueTypeOrByRef); if (targetIsValueTypeOrByRef) return targetTypeString + "&"; else return targetTypeString; } else { // A non-isReference pointer means an ELEMENT_TYPE_PTR IDiaSymbol targetType; hr = parameterType.GetType(out targetType); if (hr != S_OK) return "?"; bool ignore; return targetType.ToTypeStringWorker(session, recursionLevel, out ignore) + "*"; } } else if (symTag == SymTagEnum.SymTagArrayType) { // Note: We don't actually hit this case in NUTC-generated PDB's as NUTC emits arrays as if they were UDT's with square brackets in the name. // But just in case NUTC ever changes its PDB emission, we'll print out the most obvious interpretation and hope we're right. IDiaSymbol elementType; hr = parameterType.GetType(out elementType); if (hr != S_OK) return "?"; bool ignore; return elementType.ToTypeStringWorker(session, recursionLevel, out ignore) + "[]"; } else if (symTag == SymTagEnum.SymTagUDT || symTag == SymTagEnum.SymTagEnum) { // Need to figure out whether this is a value type as our recursive caller needs to know whether the "byref pointer" that wrapped this // is a true managed byref or just the "byref pointer" that wraps all non-valuetypes. if (symTag == SymTagEnum.SymTagEnum) { isValueTypeOrByRef = true; } else { IDiaEnumSymbols baseClasses; hr = session.FindChildren(parameterType, SymTagEnum.SymTagBaseClass, null, 0, out baseClasses); if (hr != S_OK) return "?"; int count; hr = baseClasses.Count(out count); if (hr != S_OK) return "?"; for (int i = 0; i < count; i++) { IDiaSymbol baseClass; if (S_OK == baseClasses.Item(i, out baseClass)) { String baseClassName; if (S_OK == baseClass.GetName(out baseClassName)) { if (baseClassName == "System::ValueType") isValueTypeOrByRef = true; } } } } String name; hr = parameterType.GetName(out name); if (hr != S_OK) return "?"; return name.RemoveNamespaces().Demanglify(); } else if (symTag == SymTagEnum.SymTagBaseType) { // Certain "primitive" types are encoded specially. BasicType basicType; hr = parameterType.GetBaseType(out basicType); if (hr != S_OK) return "?"; long length; hr = parameterType.GetLength(out length); if (hr != S_OK) return "?"; return ConvertBasicTypeToTypeString(basicType, length, out isValueTypeOrByRef); } else { return "?"; } }
/// <summary> /// Locate and lazily load debug info for the native app module overlapping given /// virtual address. /// </summary> /// <param name="ip">Instruction pointer address (code address for the lookup)</param> /// <param name="rva">Output VA relative to module base</param> private static IDiaSession GetDiaSession(IntPtr ip, out int rva) { if (ip == IntPtr.Zero) { rva = -1; return null; } IntPtr moduleBase = RuntimeAugments.GetModuleFromPointer(ip); if (moduleBase == IntPtr.Zero) { rva = -1; return null; } rva = (int)(ip.ToInt64() - moduleBase.ToInt64()); if (s_loadedModules == null) { // Lazily create the parallel arrays s_loadedModules and s_perModuleDebugInfo int moduleCount = RuntimeAugments.GetLoadedModules(null); s_loadedModules = new IntPtr[moduleCount]; s_perModuleDebugInfo = new IDiaSession[moduleCount]; // Actually read the module addresses into the array RuntimeAugments.GetLoadedModules(s_loadedModules); } // Locate module index based on base address int moduleIndex = s_loadedModules.Length; do { if (--moduleIndex < 0) { return null; } } while(s_loadedModules[moduleIndex] != moduleBase); IDiaSession diaSession = s_perModuleDebugInfo[moduleIndex]; if (diaSession != null) { return diaSession; } string modulePath = RuntimeAugments.TryGetFullPathToApplicationModule(moduleBase); if (modulePath == null) { return null; } int indexOfLastDot = modulePath.LastIndexOf('.'); if (indexOfLastDot == -1) { return null; } IDiaDataSource diaDataSource = GetDiaDataSource(); if (diaDataSource == null) { return null; } // Look for .pdb next to .exe / dll - if it's not there, bail. String pdbPath = modulePath.Substring(0, indexOfLastDot) + ".pdb"; int hr = diaDataSource.LoadDataFromPdb(pdbPath); if (hr != S_OK) { return null; } hr = diaDataSource.OpenSession(out diaSession); if (hr != S_OK) { return null; } s_perModuleDebugInfo[moduleIndex] = diaSession; return diaSession; }