Skip to content

Commit

Permalink
Handle XML frames with abbreviated output (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
arvindshmicrosoft authored Dec 11, 2021
1 parent 70c8db1 commit b1e098f
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 5 deletions.
31 changes: 28 additions & 3 deletions Engine/ModuleInfoHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,40 @@ public static (Dictionary<string, Symbol>, List<StackWithCount>) ParseModuleInfo
readStatus = reader.Read();
if (readStatus) {
// seems to be XML; process attributes only if all 3 are there
var moduleName = Path.GetFileNameWithoutExtension(reader.GetAttribute("module"));
var moduleNameAttributeVal = reader.GetAttribute("module");
if (string.IsNullOrEmpty(moduleNameAttributeVal)){
moduleNameAttributeVal = reader.GetAttribute("name");
}
var moduleName = Path.GetFileNameWithoutExtension(moduleNameAttributeVal);
var addressAttributeVal = reader.GetAttribute("address");
ulong addressIfPresent = string.IsNullOrEmpty(addressAttributeVal) ? ulong.MinValue : Convert.ToUInt64(addressAttributeVal, 16);
var rvaAttributeVal = reader.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;
}
lock (syms) {
if (!syms.ContainsKey(moduleName)) {
syms.Add(moduleName, new Symbol() { PDBName = reader.GetAttribute("pdb").ToLower(), PDBAge = int.Parse(reader.GetAttribute("age")), PDBGuid = Guid.Parse(reader.GetAttribute("guid")).ToString("N") });
syms.Add(moduleName, new Symbol() { PDBName = reader.GetAttribute("pdb").ToLower(), PDBAge = int.Parse(reader.GetAttribute("age")), PDBGuid = Guid.Parse(reader.GetAttribute("guid")).ToString("N"), CalculatedModuleBaseAddress = calcBaseAddress });
} else {
if (ulong.MinValue == syms[moduleName].CalculatedModuleBaseAddress) {
syms[moduleName].CalculatedModuleBaseAddress = calcBaseAddress;
}
}
}
string rvaAsIsOrDerived = null;
if (ulong.MinValue != rvaIfPresent) {
rvaAsIsOrDerived = rvaAttributeVal;
} else if (ulong.MinValue != addressIfPresent && ulong.MinValue != syms[moduleName].CalculatedModuleBaseAddress) {
rvaAsIsOrDerived = "0x" + (addressIfPresent - syms[moduleName].CalculatedModuleBaseAddress).ToString("X");
}

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

var frameNumHex = string.Format(System.Globalization.CultureInfo.CurrentCulture, "{0:x2}", int.Parse(reader.GetAttribute("id")));
// transform the XML into a simple module+offset notation
outCallstack.AppendFormat($"{frameNumHex} {moduleName}+{reader.GetAttribute("rva")}{Environment.NewLine}");
outCallstack.AppendFormat($"{frameNumHex} {moduleName}+{rvaAsIsOrDerived}{Environment.NewLine}");
continue;
}
}
Expand Down
5 changes: 4 additions & 1 deletion Engine/Symbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ public class Symbol {
[JsonIgnore]
public int PDBAge;

[JsonIgnore]
public ulong CalculatedModuleBaseAddress;

public string DownloadURL;

public bool DownloadVerified;
Expand All @@ -36,4 +39,4 @@ public static bool IsURLValid(Uri url) {
return true;
}
}
}
}
36 changes: 35 additions & 1 deletion Tests/Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ public void RegularSymbolHexOffsetNoOutputOffset() {
[Fact][Trait("Category", "Unit")]
public void RegularSymbolHexOffsetNoOutputOffsetWithFrameNums() {
using (var csr = new StackResolver()) {
var dllPaths = new List<string> { @"..\..\..\Tests\TestCases\TestOrdinal" };
var dllPaths = new List<string>{@"..\..\..\Tests\TestCases\TestOrdinal"};
var ret = csr.ResolveCallstacks("00 sqldk+0x40609", @"..\..\..\Tests\TestCases\TestOrdinal", false, null, false, false, false, false, false, false, false, null);
var expectedSymbol = "00 sqldk!MemoryClerkInternal::AllocatePagesWithFailureMode";
Assert.Equal(expectedSymbol, ret.Trim());
Expand Down Expand Up @@ -481,6 +481,24 @@ public void ExtractModuleInfoXMLFrames() {
Assert.Equal(1, syms["VCRUNTIME140"].PDBAge);
}

/// Tests the parsing and extraction of PDB details from a set of rows each with XML frames. Some of those XML frames do not have sym info or RVA included.
[Fact][Trait("Category", "Unit")]
public void ExtractModuleInfoXMLFramesWithCalcBaseAddress() {
var input = new StackWithCount() {
Callstack = "Frame = <frame id=\"02\" pdb=\"SqlDK.pdb\" age=\"2\" guid=\"6a193443-3512-464b-8b8e-\r\n" +
"d905ad930ee6\" module=\"sqldk.dll\" rva=\"0x40609\" address = \"0x100440609\"/>\r\n" +
"<frame id=\"04\" name=\"sqldk.dll\" address=\"0x10042249f\" />\n", Count = 1 };
var param = new List<StackWithCount> { input };
var result = ModuleInfoHelper.ParseModuleInfoXML(param);

var syms = result.Item1;
Assert.Single(syms);
Assert.Equal("sqldk.pdb", syms["sqldk"].PDBName);
Assert.Equal("6a1934433512464b8b8ed905ad930ee6", syms["sqldk"].PDBGuid, ignoreCase: true);
Assert.Equal(2, syms["sqldk"].PDBAge);
Assert.Equal((ulong)0x100400000, syms["sqldk"].CalculatedModuleBaseAddress);
}

/// Tests the parsing and extraction of PDB details from a set of rows each with commma-separated fields.
[Fact][Trait("Category", "Unit")]
public void ExtractModuleInfoEmptyString() {
Expand Down Expand Up @@ -571,6 +589,22 @@ 01 KERNELBASE!WaitForSingleObjectEx+147
}
}

/// End-to-end test with stacks being resolved based on symbols from symsrv, with a frame that needs "calculated base address" handling
[Fact][Trait("Category", "Unit")]
public void E2ESymSrvXMLFramesWithCalcBaseAddress() {
var input = "Frame = <frame id=\"02\" pdb=\"SqlDK.pdb\" age=\"2\" guid=\"6a193443-3512-464b-8b8e-\r\n" +
"d905ad930ee6\" module=\"sqldk.dll\" rva=\"0x40609\" address = \"0x100440609\"/>\r\n" +
"<frame id=\"03\" name=\"sqldk.dll\" address=\"0x10042249f\" />\n";

using (var csr = new StackResolver()) {
var pdbPath = @"srv*https://msdl.microsoft.com/download/symbols";
var ret = csr.ResolveCallstacks(input, pdbPath, false, null, false, false, true, false, true, false, false, null);
var expected = @"02 sqldk!MemoryClerkInternal::AllocatePagesWithFailureMode+644
03 sqldk!Spinlock<244,2,1>::SpinToAcquireWithExponentialBackoff+349";
Assert.Equal(expected.Trim(), ret.Trim());
}
}

/// End-to-end test with XE histogram target and XML frames.
[Fact][Trait("Category", "Unit")]
public void E2ESymSrvXMLFramesHistogram() {
Expand Down

0 comments on commit b1e098f

Please sign in to comment.