Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions Engine/DLLOrdinalHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,10 @@ internal void Initialize() {
}

/// This function loads DLLs from a specified path, so that we can then build the DLL export's ordinal / address map
internal string[] LoadDllsIfApplicable(string[] callstackFrames, bool recurse, List<string> dllPaths) {
internal List<string> LoadDllsIfApplicable(List<string> callstackFrames, bool recurse, List<string> dllPaths) {
if (dllPaths == null) return callstackFrames;

var processedFrames = new string[callstackFrames.Length];
for (var idx = 0; idx < callstackFrames.Length; idx++) {
var callstack = callstackFrames[idx];
var processedFrames = new List<string>(callstackFrames.Count);
foreach (var callstack in callstackFrames) {
// first we seek out distinct module names in this call stack
// note that such frames will only be seen in the call stack when trace flag 3656 is enabled, but there were no PDBs in the BINN folder
// sample frames are given below
Expand Down Expand Up @@ -47,7 +45,7 @@ internal string[] LoadDllsIfApplicable(string[] callstackFrames, bool recurse, L

// finally do a pattern based replace the replace method calls a delegate (ReplaceOrdinalWithRealOffset) which figures
// out the start address of the ordinal and then computes the actual offset
processedFrames[idx] = fullpattern.Replace(callstack, ReplaceOrdinalWithRealOffset);
processedFrames.Add(fullpattern.Replace(callstack, ReplaceOrdinalWithRealOffset));
}
return processedFrames;
}
Expand Down
27 changes: 14 additions & 13 deletions Engine/ModuleInfoHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public async static Task<Dictionary<string, Symbol>> ParseModuleInfoAsync(List<S
bool anyTaskFailed = false;
await Task.Run(() => Parallel.ForEach(listOfCallStacks.Where(c => c.Callstack.Contains(",")).Select(c => c.CallstackFrames), lines => {
if (cts.IsCancellationRequested) return;
Contract.Requires(lines.Length > 0);
Contract.Requires(lines.Count > 0);
foreach (var line in lines) {
if (cts.IsCancellationRequested) return;
string moduleName = null, pdbName = null;
Expand Down Expand Up @@ -75,26 +75,27 @@ await Task.Run(() => Parallel.ForEach(listOfCallStacks, currItem => {
// next, replace any pre-post stuff from the XML frame lines
currItem.Callstack = Regex.Replace(currItem.Callstack, @"(?<prefix>.*?)(?<retain>\<frame.+\/\>)(?<suffix>.*?)", "${retain}");
if (cts.IsCancellationRequested) return;

foreach (var line in currItem.Callstack.Split('\n')) {
using var reader = new StringReader(currItem.Callstack);
string line;
while ((line = reader.ReadLine()) != null) {
if (cts.IsCancellationRequested) return;
if (!string.IsNullOrWhiteSpace(line) && line.StartsWith("<frame")) { // only attempt further formal XML parsing if a simple text check works
try {
using var sreader = new StringReader(line);
using var reader = XmlReader.Create(sreader, new XmlReaderSettings() { XmlResolver = null });
if (reader.Read()) {
using var xmlReader = XmlReader.Create(sreader, new XmlReaderSettings() { XmlResolver = null });
if (xmlReader.Read()) {
// seems to be XML; start reading attributes
var moduleNameAttributeVal = reader.GetAttribute("module");
if (string.IsNullOrEmpty(moduleNameAttributeVal)) moduleNameAttributeVal = reader.GetAttribute("name");
var moduleNameAttributeVal = xmlReader.GetAttribute("module");
if (string.IsNullOrEmpty(moduleNameAttributeVal)) moduleNameAttributeVal = xmlReader.GetAttribute("name");
var moduleName = Path.GetFileNameWithoutExtension(moduleNameAttributeVal);
var addressAttributeVal = reader.GetAttribute("address");
var addressAttributeVal = xmlReader.GetAttribute("address");
ulong addressIfPresent = string.IsNullOrEmpty(addressAttributeVal) ? ulong.MinValue : Convert.ToUInt64(addressAttributeVal, 16);
var rvaAttributeVal = reader.GetAttribute("rva");
var rvaAttributeVal = xmlReader.GetAttribute("rva");
ulong rvaIfPresent = string.IsNullOrEmpty(rvaAttributeVal) ? ulong.MinValue : Convert.ToUInt64(rvaAttributeVal, 16);
ulong calcBaseAddress = ulong.MinValue;
if (rvaIfPresent != ulong.MinValue && addressIfPresent != ulong.MinValue) calcBaseAddress = addressIfPresent - rvaIfPresent;
var pdbGuid = reader.GetAttribute("guid");
var pdbAge = reader.GetAttribute("age");
var pdbGuid = xmlReader.GetAttribute("guid");
var pdbAge = xmlReader.GetAttribute("age");
string uniqueModuleName;
// Create a map of the last mapped module names to handle future cases when the frame is "truncated" and the above PDB details are not available
if (pdbGuid != null && pdbAge != null) {
Expand All @@ -112,7 +113,7 @@ await Task.Run(() => Parallel.ForEach(listOfCallStacks, currItem => {
lock (syms) {
if (syms.TryGetValue(uniqueModuleName, out var existingEntry)) {
if (ulong.MinValue == existingEntry.CalculatedModuleBaseAddress) existingEntry.CalculatedModuleBaseAddress = calcBaseAddress;
} else syms.Add(uniqueModuleName, new Symbol() { PDBName = reader.GetAttribute("pdb").ToLower(), ModuleName = moduleName, PDBAge = int.Parse(pdbAge), PDBGuid = Guid.Parse(pdbGuid).ToString("N").ToUpper(), CalculatedModuleBaseAddress = calcBaseAddress });
} else syms.Add(uniqueModuleName, new Symbol() { PDBName = xmlReader.GetAttribute("pdb").ToLower(), ModuleName = moduleName, PDBAge = int.Parse(pdbAge), PDBGuid = Guid.Parse(pdbGuid).ToString("N").ToUpper(), CalculatedModuleBaseAddress = calcBaseAddress });
}
string rvaAsIsOrDerived = null;
if (ulong.MinValue != rvaIfPresent) rvaAsIsOrDerived = rvaAttributeVal;
Expand All @@ -121,7 +122,7 @@ await Task.Run(() => Parallel.ForEach(listOfCallStacks, currItem => {

if (string.IsNullOrEmpty(rvaAsIsOrDerived)) { throw new NullReferenceException(); }

var frameNumHex = string.Format(System.Globalization.CultureInfo.CurrentCulture, "{0:x2}", int.Parse(reader.GetAttribute("id")));
var frameNumHex = string.Format(System.Globalization.CultureInfo.CurrentCulture, "{0:x2}", int.Parse(xmlReader.GetAttribute("id")));
// transform the XML into a simple module+offset notation
outCallstack.AppendFormat($"{frameNumHex} {uniqueModuleName}+{rvaAsIsOrDerived}{Environment.NewLine}");
continue;
Expand Down
11 changes: 9 additions & 2 deletions Engine/StackDetails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,21 @@ public string Callstack {
set { this._callStack = value; }
}

public string[] CallstackFrames {
public List<string> CallstackFrames {
get {
// sometimes we see call stacks which are arranged horizontally (this typically is seen when copy-pasting directly
// from the SSMS XEvent window (copying the callstack field without opening it in its own viewer)
// in that case, space is a valid delimiter, and we need to support that as an option
var delims = this._framesOnSingleLine ? new char[] { '\t', '\n' } : new char[] { '\n' };
if (!this._relookupSource && this._framesOnSingleLine) delims = delims.Append(' ').ToArray();
return this._callStack.Replace("\r", string.Empty).Split(delims);
var result = new List<string>();
using (var reader = new StringReader(this._callStack.Replace("\r", string.Empty))) {
string line;
while ((line = reader.ReadLine()) != null) {
result.AddRange(line.Split(delims, StringSplitOptions.RemoveEmptyEntries));
}
}
return result;
}
}
public string Resolvedstack {
Expand Down
22 changes: 11 additions & 11 deletions Engine/StackResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,22 @@ public async Task<Tuple<int, string>> ExtractFromXELAsync(string[] xelFiles, boo
}

/// Convert virtual-address only type frames to their module+offset format
private string[] PreProcessVAs(string[] callStackLines, CancellationTokenSource cts) {
string[] retval = new string[callStackLines.Length];
int frameNum = 0;
private List<string> PreProcessVAs(List<string> callStackLines, CancellationTokenSource cts) {
var retval = new List<string>(callStackLines.Count);
foreach (var currentFrame in callStackLines) {
if (cts.IsCancellationRequested) return callStackLines;
// let's see if this is an VA-only address
// Check if the frame is a VA-only address
var matchVA = rgxVAOnly.Match(currentFrame);
if (matchVA.Success) {
ulong virtAddress = Convert.ToUInt64(matchVA.Groups["vaddress"].Value, 16);
retval[frameNum] = TryObtainModuleOffset(virtAddress, out string moduleName, out uint offset)
? string.Format(CultureInfo.CurrentCulture, "{0}+0x{1:X}", moduleName, offset)
: currentFrame.Trim();
if (TryObtainModuleOffset(virtAddress, out string moduleName, out uint offset)) {
retval.Add(string.Format(CultureInfo.CurrentCulture, "{0}+0x{1:X}", moduleName, offset));
} else {
retval.Add(currentFrame.Trim());
}
} else {
retval.Add(currentFrame.Trim());
}
else retval[frameNum] = currentFrame.Trim();

frameNum++;
}
return retval;
}
Expand Down Expand Up @@ -83,7 +83,7 @@ public bool IsInputVAOnly(string text) {
}

/// Runs through each of the frames in a call stack and looks up symbols for each
private string ResolveSymbols(Dictionary<string, DiaUtil> _diautils, Dictionary<string, string> moduleNamesMap, string[] callStackLines, string userSuppliedSymPath, string symSrvSymPath, bool searchPDBsRecursively, bool cachePDB, bool includeSourceInfo, bool relookupSource, bool includeOffsets, bool showInlineFrames, List<string> modulesToIgnore, CancellationTokenSource cts) {
private string ResolveSymbols(Dictionary<string, DiaUtil> _diautils, Dictionary<string, string> moduleNamesMap, List<string> callStackLines, string userSuppliedSymPath, string symSrvSymPath, bool searchPDBsRecursively, bool cachePDB, bool includeSourceInfo, bool relookupSource, bool includeOffsets, bool showInlineFrames, List<string> modulesToIgnore, CancellationTokenSource cts) {
var finalCallstack = new StringBuilder();
int runningFrameNum = int.MinValue;
foreach (var iterFrame in callStackLines) {
Expand Down
Loading