Skip to content

Commit de4ca35

Browse files
Check if input is VA only w/o any base address info
* Add helper function to detect VA-only input. Fixes #116. * Add the check for VA-only input in the GUI. * Add automated tests for VA-only input detection. * Fix unrelated dormant bug where module base address info was not cleared if empty string input was provided.
1 parent 877d4ca commit de4ca35

File tree

4 files changed

+53
-7
lines changed

4 files changed

+53
-7
lines changed

Engine/StackResolver.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class StackResolver : IDisposable {
2525

2626
private static readonly RegexOptions rgxOptions = RegexOptions.ExplicitCapture | RegexOptions.Compiled;
2727
private static readonly Regex rgxModuleOffsetFrame = new(@"((?<framenum>[0-9a-fA-F]+)\s+)*(?<module>\w+)(\.(dll|exe))*\s*\+\s*(0[xX])*(?<offset>[0-9a-fA-F]+)\s*", rgxOptions);
28-
private static readonly Regex rgxVAOnly = new (@"^\s*0[xX](?<vaddress>[0-9a-fA-F]+)\s*$", rgxOptions);
28+
private static readonly Regex rgxVAOnly = new (@"\s*0[xX](?<vaddress>[0-9a-fA-F]+)\s*", rgxOptions);
2929
private static readonly Regex rgxAlreadySymbolizedFrame = new (@"((?<framenum>\d+)\s+)*(?<module>\w+)(\.(dll|exe))*!(?<symbolizedfunc>.+?)\s*\+\s*(0[xX])*(?<offset>[0-9a-fA-F]+)\s*", rgxOptions);
3030
private static readonly Regex rgxmoduleaddress = new (@"^\s*(?<filepath>.+)(\t+| +)(?<baseaddress>(0x)?[0-9a-fA-F`]+)\s*$", RegexOptions.Multiline);
3131

@@ -72,6 +72,16 @@ public bool IsInputSingleLine(string text, string patternsToTreatAsMultiline) {
7272
return false;
7373
}
7474

75+
public bool IsInputVAOnly(string text) {
76+
text = System.Net.WebUtility.HtmlDecode(text); // decode XML markup if present
77+
if (Regex.Match(text, @"\<frame", RegexOptions.IgnoreCase).Success || rgxAlreadySymbolizedFrame.Matches(text).Count > 0 || rgxModuleOffsetFrame.Matches(text).Count > 0)
78+
return false;
79+
80+
if (rgxVAOnly.Matches(text).Count > 0) return true;
81+
82+
return false;
83+
}
84+
7585
/// Runs through each of the frames in a call stack and looks up symbols for each
7686
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) {
7787
var finalCallstack = new StringBuilder();
@@ -250,7 +260,10 @@ private string ProcessFrameModuleOffset(Dictionary<string, DiaUtil> _diautils, D
250260
/// </summary>
251261
public bool ProcessBaseAddresses(string baseAddressesString) {
252262
bool retVal = true;
253-
if (string.IsNullOrEmpty(baseAddressesString)) return true; // not a true error condition so we are okay
263+
LoadedModules.Clear(); // regardless of user input, we always clear the loaded modules list first
264+
265+
if (string.IsNullOrEmpty(baseAddressesString)) return true;
266+
254267
LoadedModules.Clear();
255268
var mcmodules = rgxmoduleaddress.Matches(baseAddressesString);
256269
if (!mcmodules.Cast<Match>().Any()) return false; // it is likely that we have malformed input, cannot ignore this so return false.

GUI/MainForm.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ private bool ValidateInputs() {
7777
return false;
7878
}
7979

80+
if (this._resolver.IsInputVAOnly(callStackInput.Text) && string.IsNullOrEmpty(this._baseAddressesString) && DialogResult.No == MessageBox.Show(this,
81+
"The input provided seems to be comprised entirely of virtual addresses only, but the required corresponding module information has not been provided. Do you still want to attempt to process this input?",
82+
"Missing module base information", MessageBoxButtons.YesNo, MessageBoxIcon.Warning)) return false;
83+
8084
var res = this._resolver.ProcessBaseAddresses(this._baseAddressesString);
8185
if (!res) {
8286
MessageBox.Show(this, "Cannot interpret the module base address information. Make sure you just have the output of the following query (no column headers, no other columns) copied from SSMS using the Grid Results\r\n\r\nselect name, base_address from sys.dm_os_loaded_modules where name not like '%.rll'",

Tests/Tests.cs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,35 @@ public class Tests {
5050
Assert.IsFalse(csr.IsInputSingleLine("Histogram 0x1\r\n0x1", PatternsToTreatAsMultiline));
5151
}
5252

53+
[TestMethod][TestCategory("Unit")]
54+
public void VAOnlyInputDetection() {
55+
using var csr = new StackResolver();
56+
Assert.IsFalse(csr.IsInputVAOnly("05 sqldk!SOS_Scheduler::UpdateWaitTimeStats+789"));
57+
Assert.IsFalse(csr.IsInputVAOnly(@"\r\n sqldk+0x40609\r\n"));
58+
Assert.IsFalse(csr.IsInputVAOnly("&lt;frame id=\"00\" address=\"0xf00\" pdb=\"ntdll.pdb\" age=\"1\" guid=\"C374E059-5793-9B92-6525-386A66A2D3F5\" module=\"ntdll.dll\" rva=\"0x9F7E4\" /&gt;&lt;" +
59+
"frame id=\"01\" address=\"0xf00\" pdb=\"kernelbase.pdb\" age=\"1\" guid=\"E77E26E7-D1C4-72BB-2C05-DD17624A9E58\" module=\"KERNELBASE.dll\" rva=\"0x38973\" /&gt;&lt;" +
60+
"frame id=\"02\" address=\"0xf00\" pdb=\"SqlDK.pdb\" age=\"2\" guid=\"6a193443-3512-464b-8b8e-d905ad930ee6\" module=\"sqldk.dll\" rva=\"0x40609\" /&gt;"));
61+
Assert.IsTrue(csr.IsInputVAOnly("callstack\r\n0x00007FFEABD0D919\r\n0x00007FFEABC4D45D\r\n0x00007FFEAC0F7EE0"));
62+
Assert.IsFalse(csr.IsInputVAOnly(@"\r\n sqldk+0x40609 sqldk+40609\r\n"));
63+
Assert.IsFalse(csr.IsInputVAOnly(@"\r\n sqldk+0x40609 sqldk+40609\r\nsqldk+0x40609 sqldk+40609"));
64+
Assert.IsTrue(csr.IsInputVAOnly("<HistogramTarget truncated=\"0\" buckets=\"256\"><Slot count=\"5\"><value>0x00007FFEABD0D919\r\n0x00007FFEABC4D45D\r\n0x00007FFEAC0F7EE0\r\n0x00007FFEAC0F80CF\r\n0x00007FFEAC1EE447\r\n0x00007FFEAC1EE6F5</value></Slot></HistogramTarget>"));
65+
Assert.IsTrue(csr.IsInputVAOnly("annotation for histogram #1 0 <HistogramTarget truncated=\"0\" buckets=\"256\"><Slot count=\"5\"><value>0x00007FFEABD0D919 0x00007FFEABC4D45D 0x00007FFEAC0F7EE0 0x00007FFEAC0F80CF 0x00007FFEAC1EE447 0x00007FFEAC1EE6F5 0x00007FFEAC1D48B0 0x00007FFEAC71475A 0x00007FFEA9A708F1 0x00007FFEA9991FB9 0x00007FFEA9993D21 0x00007FFEA99B59F1 0x00007FFEA99B5055 0x00007FFEA99B2B8F 0x00007FFEA9675AD1 0x00007FFEA9671EFB 0x00007FFEAA37D83D 0x00007FFEAA37D241 0x00007FFEAA379F98 0x00007FFEA96719CA 0x00007FFEA9672933 0x00007FFEA9672041 0x00007FFEA967A82B 0x00007FFEA9681542</value></Slot></HistogramTarget>\r\n" +
66+
"annotation for histogram #2 1 <HistogramTarget truncated=\"0\" buckets=\"256\"><Slot count=\"5\"><value>0x00007FFEABD0D919 0x00007FFEABC4D45D 0x00007FFEAC0F7EE0 0x00007FFEAC0F80CF 0x00007FFEAC1EE447 0x00007FFEAC1EE6F5 0x00007FFEAC1D48B0 0x00007FFEAC71475A 0x00007FFEA9A708F1 0x00007FFEA9991FB9 0x00007FFEA9993D21 0x00007FFEA99B59F1 0x00007FFEA99B5055 0x00007FFEA99B2B8F 0x00007FFEA9675AD1 0x00007FFEA9671EFB 0x00007FFEAA37D83D 0x00007FFEAA37D241 0x00007FFEAA379F98 0x00007FFEA96719CA 0x00007FFEA9672933 0x00007FFEA9672041 0x00007FFEA967A82B 0x00007FFEA9681542</value></Slot></HistogramTarget>\r\n"));
67+
Assert.IsFalse(csr.IsInputVAOnly("<HistogramTarget truncated=\"0\" buckets=\"256\"><Slot count=\"5\"><value><![CDATA[<frame id=\"00\" pdb=\"ntdll.pdb\" age=\"1\" guid=\"C374E059-5793-9B92-6525-386A66A2D3F5\" module=\"ntdll.dll\" rva=\"0x9F7E4\" />" +
68+
"<frame id=\"01\" pdb=\"kernelbase.pdb\" age=\"1\" guid=\"E77E26E7-D1C4-72BB-2C05-DD17624A9E58\" module=\"KERNELBASE.dll\" rva=\"0x38973\" />" +
69+
"<frame id=\"02\" pdb=\"SqlDK.pdb\" age=\"2\" guid=\"6a193443-3512-464b-8b8e-d905ad930ee6\" module=\"sqldk.dll\" rva=\"0x40609\" />" +
70+
"]]></value></Slot><Slot count=\"3\"><value><![CDATA[<frame id=\"00\" pdb=\"vcruntime140.amd64.pdb\" age=\"1\" guid=\"AF138C3F-2933-4097-8883-C1071B13375E\" module=\"VCRUNTIME140.dll\" rva=\"0xB8F0\" />" +
71+
"<frame id=\"01\" pdb=\"SqlDK.pdb\" age=\"2\" guid=\"6a193443-3512-464b-8b8e-d905ad930ee6\" module=\"sqldk.dll\" rva=\"0x2249f\" />" +
72+
"]]></value></Slot></HistogramTarget>"));
73+
Assert.IsFalse(csr.IsInputVAOnly("<HistogramTarget truncated=\"0\" buckets=\"256\">\r\n<Slot count=\"5\">\r\n<value>&lt;frame id=\"00\" address=\"0xf00\" pdb=\"ntdll.pdb\" age=\"1\" guid=\"C374E059-5793-9B92-6525-386A66A2D3F5\" module=\"ntdll.dll\" rva=\"0x9F7E4\" /&gt;&lt;" +
74+
"frame id=\"01\" address=\"0xf00\" pdb=\"kernelbase.pdb\" age=\"1\" guid=\"E77E26E7-D1C4-72BB-2C05-DD17624A9E58\" module=\"KERNELBASE.dll\" rva=\"0x38973\" /&gt;&lt;" +
75+
"frame id=\"02\" address=\"0xf00\" pdb=\"SqlDK.pdb\" age=\"2\" guid=\"6a193443-3512-464b-8b8e-d905ad930ee6\" module=\"sqldk.dll\" rva=\"0x40609\" /&gt;" +
76+
"</value>\r\n</Slot>\r\n<Slot count=\"3\">\r\n<value><frame id=\"00\" address=\"0xf00\" pdb=\"vcruntime140.amd64.pdb\" age=\"1\" guid=\"AF138C3F-2933-4097-8883-C1071B13375E\" module=\"VCRUNTIME140.dll\" rva=\"0xB8F0\" /&gt;&lt;" +
77+
"frame id=\"01\" address=\"0xf00\" pdb=\"SqlDK.pdb\" age=\"2\" guid=\"6a193443-3512-464b-8b8e-d905ad930ee6\" module=\"sqldk.dll\" rva=\"0x2249f\" /&gt;" +
78+
"</value>\r\n</Slot>\r\n</HistogramTarget>"));
79+
Assert.IsTrue(csr.IsInputVAOnly("Histogram\r\n0x1\r\n0x1"));
80+
}
81+
5382
/// Validate that "block symbols" in a PDB are resolved correctly.
5483
[TestMethod][TestCategory("Unit")] public async Task BlockResolution() {
5584
using var csr = new StackResolver();
@@ -713,12 +742,12 @@ public async Task E2EHistogramOffsets() {
713742
var pdbPath = @"..\..\..\Tests\TestCases\sqlsyms\13.0.4001.0\x64";
714743
var input = "<HistogramTargetWrongTag truncated=\"0\" buckets=\"256\"><Slot count=\"5\"><value>0x00007FFEABD0D919 0x00007FFEABC4D45D 0x00007FFEAC0F7EE0 0x00007FFEAC0F80CF </value></Slot></HistogramTargetWrongTag>";
715744
var ret = await csr.ResolveCallstacksAsync(await csr.GetListofCallStacksAsync(input, true, cts), pdbPath, false, null, false, false, false, true, false, false, null, cts);
716-
var expected = "<HistogramTargetWrongTag\r\ntruncated=\"0\"\r\nbuckets=\"256\"><Slot\r\ncount=\"5\"><value>0x00007FFEABD0D919\r\nsqldk!SpinlockBase::Sleep+182\r\nsqlmin!Spinlock<143,7,1>::SpinToAcquireWithExponentialBackoff+363\r\nsqlmin!lck_lockInternal+2042\r\n</value></Slot></HistogramTargetWrongTag>";
717-
Assert.AreEqual(expected.Trim(), ret.Trim()); // we just expect the input text back as-is
745+
var expected = "<HistogramTargetWrongTag\r\ntruncated=\"0\"\r\nbuckets=\"256\"><Slot\r\nsqldk!XeSosPkg::spinlock_backoff::Publish+425\r\nsqldk!SpinlockBase::Sleep+182\r\nsqlmin!Spinlock<143,7,1>::SpinToAcquireWithExponentialBackoff+363\r\nsqlmin!lck_lockInternal+2042\r\n</value></Slot></HistogramTargetWrongTag>";
746+
Assert.AreEqual(expected.Trim(), ret.Trim());
718747
input = "<HistogramTarget truncated=\"0\" buckets=\"256\"><Slot count=\"5\"><value>0x00007FFEABD0D919 0x00007FFEABC4D45D 0x00007FFEAC0F7EE0 0x00007FFEAC0F80CF </value></Slot></HistogramTargetWrongTag>";
719748
ret = await csr.ResolveCallstacksAsync(await csr.GetListofCallStacksAsync(input, true, cts), pdbPath, false, null, false, false, false, true, false, false, null, cts);
720-
expected = "<HistogramTarget\r\ntruncated=\"0\"\r\nbuckets=\"256\"><Slot\r\ncount=\"5\"><value>0x00007FFEABD0D919\r\nsqldk!SpinlockBase::Sleep+182\r\nsqlmin!Spinlock<143,7,1>::SpinToAcquireWithExponentialBackoff+363\r\nsqlmin!lck_lockInternal+2042\r\n</value></Slot></HistogramTargetWrongTag>";
721-
Assert.AreEqual(expected.Trim(), ret.Trim()); // we just expect the input text back as-is
749+
expected = "<HistogramTarget\r\ntruncated=\"0\"\r\nbuckets=\"256\"><Slot\r\nsqldk!XeSosPkg::spinlock_backoff::Publish+425\r\nsqldk!SpinlockBase::Sleep+182\r\nsqlmin!Spinlock<143,7,1>::SpinToAcquireWithExponentialBackoff+363\r\nsqlmin!lck_lockInternal+2042\r\n</value></Slot></HistogramTargetWrongTag>";
750+
Assert.AreEqual(expected.Trim(), ret.Trim());
722751
}
723752

724753
/// End-to-end test with stacks being resolved based on symbols from symsrv.

latestrelease.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2025-06-06 00:00
1+
2025-06-14 00:00

0 commit comments

Comments
 (0)