Skip to content

add analyzer notebook #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 12, 2024
Merged
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
282 changes: 282 additions & 0 deletions src/benchmarks/gc/Utility-Notebooks/analyzer.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Script to extract and aggregate call stack infomation for dumps in given folder."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Set Variables"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"using System.Diagnostics;\n",
"using System.IO;\n",
"using System.Text.Json;\n",
"using System.Text.RegularExpressions;\n",
"\n",
"\n",
"string windbgPath = \"C:\\\\Users\\\\v-bibu\\\\windbg\\\\windbg.exe\";\n",
"string dumpFolder = \"Q:\\\\dumps\";\n",
"string callStackKeyword = \"::gc_heap::\";\n",
"List<string> debugCommandList = new List<string>(){\n",
" \".symfix\",\n",
" \"k\",\n",
" \"q\"\n",
"};"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Analyze class"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"\n",
"public class DumpAnalyzer\n",
"{\n",
" #nullable enable\n",
" private string? windbgPath;\n",
"\n",
" public DumpAnalyzer(string windbgPath) \n",
" {\n",
" if (String.IsNullOrEmpty(windbgPath))\n",
" throw new ArgumentException($\"Path to windbg.exe should be provided.\");\n",
"\n",
" if (!Path.Exists(windbgPath))\n",
" throw new ArgumentException($\"The given windbg path {windbgPath} doesn't exist.\");\n",
"\n",
" this.windbgPath = windbgPath;\n",
" }\n",
"\n",
" public void DebugDumpWithWinDBG(List<string> debugCommandList, string dumpPath, string outputPath)\n",
" {\n",
" var debugCommandSequence = debugCommandList\n",
" .Select(command => $\"{command}\\n\")\n",
" .Aggregate(String.Concat);\n",
"\n",
" try\n",
" {\n",
" Console.WriteLine($\"Start to debug dump: {dumpPath}.\");\n",
" using (Process process = new Process())\n",
" {\n",
" process.StartInfo.FileName = windbgPath;\n",
" process.StartInfo.Arguments = $\"-z {dumpPath} -c \\\"{debugCommandSequence}\\\" -logo {outputPath}\";\n",
" process.StartInfo.UseShellExecute = false;\n",
" process.Start();\n",
"\n",
" process.WaitForExit();\n",
" }\n",
" Console.WriteLine($\"Write debug output to: {outputPath}.\");\n",
" }\n",
" catch (Exception ex)\n",
" {\n",
" Console.WriteLine(ex.Message);\n",
" }\n",
" }\n",
"\n",
" public string? ExtractCallStackFromDebugOutput(string debugOutput)\n",
" {\n",
" string pattern = @\"# Child-SP\\s+RetAddr\\s+Call Site\\s+(.*?)\\d+:\\d+>\\s.?\";\n",
" Match match = Regex.Match(debugOutput, pattern, RegexOptions.Singleline);\n",
" if (match.Success)\n",
" {\n",
" return match.Groups[1].Value.Trim();\n",
" }\n",
" else\n",
" {\n",
" Console.WriteLine($\"can't extract call stack info from debug output.\");\n",
" return null;\n",
" }\n",
" }\n",
"\n",
" public void SaveCallStack(List<string> debugCommandList, string dumpFolder)\n",
" {\n",
" foreach (string dumpPath in Directory.GetFiles(dumpFolder, \"stress*.dmp\"))\n",
" {\n",
" Console.WriteLine($\"====== Debugging {dumpPath} and extracting call stack ======\");\n",
" try\n",
" {\n",
" string outputPath = dumpPath.Replace(\".dmp\", \".txt\");\n",
" DebugDumpWithWinDBG(debugCommandList, dumpPath, outputPath);\n",
"\n",
" string debugOutput = File.ReadAllText(outputPath);\n",
" string? callStackInfo = ExtractCallStackFromDebugOutput(debugOutput);\n",
" if (string.IsNullOrEmpty(callStackInfo))\n",
" continue;\n",
"\n",
" File.WriteAllText(outputPath, callStackInfo);\n",
" }\n",
" catch (Exception ex)\n",
" {\n",
" Console.WriteLine($\"fail to save call stack for {dumpPath}: {ex.Message}\");\n",
" }\n",
" }\n",
" }\n",
"\n",
" public (string?, int?) GetFileNameAndLineNumberOfTopStackFrame(string keyword, string callStack)\n",
" {\n",
" string[] lines = callStack.Split(new[] { '\\r', '\\n' });\n",
" string lineContainKeyword = lines.FirstOrDefault(line => line.Contains(keyword), String.Empty);\n",
"\n",
" string pattern = @\"\\[(.*?)\\]\";\n",
" Match match = Regex.Match(lineContainKeyword, pattern, RegexOptions.Singleline);\n",
"\n",
" if (!match.Success)\n",
" return (null, null);\n",
" \n",
" string fileNameWithLineNumber = match.Groups[1].Value.Trim();\n",
" string[] splitOutput = fileNameWithLineNumber.Split(\"@\");\n",
"\n",
" string? fileName = splitOutput.FirstOrDefault(String.Empty);\n",
" if (String.IsNullOrEmpty(fileName))\n",
" {\n",
" Console.WriteLine($\"Fail to extract filename.\");\n",
" fileName = null;\n",
" }\n",
"\n",
" string? lineNumberstr = splitOutput.LastOrDefault(String.Empty).Trim();\n",
" if (String.IsNullOrEmpty(lineNumberstr))\n",
" {\n",
" Console.WriteLine($\"Fail to extract line number.\");\n",
" return (fileName, null);\n",
" }\n",
"\n",
" bool success = int.TryParse(lineNumberstr, out int lineNumber);\n",
" if (!success)\n",
" {\n",
" Console.WriteLine($\"Fail to parse line number.\");\n",
" return (fileName, null);\n",
" }\n",
"\n",
" return (fileName, lineNumber);\n",
"\n",
" }\n",
"\n",
" public void GetAndSaveKey(string keyword, string debugOutputFolder)\n",
" {\n",
" string[] callStackFileList = Directory.GetFiles(debugOutputFolder, \"stress*.txt\");\n",
" Dictionary<string, List<string>> keyToPath = new();\n",
"\n",
"\n",
" foreach (string callStackFile in callStackFileList)\n",
" {\n",
" Console.WriteLine($\"====== Processing {callStackFile} ======\");\n",
"\n",
" try\n",
" {\n",
" string callStackInfo = File.ReadAllText(callStackFile);\n",
"\n",
" (string? fileName, int? lineNumber) = GetFileNameAndLineNumberOfTopStackFrame(keyword, callStackInfo);\n",
"\n",
" if (fileName == null || lineNumber == null)\n",
" continue;\n",
"\n",
" string srcLine = File.ReadAllLines(fileName)[lineNumber.Value - 1];\n",
"\n",
" if (!keyToPath.TryGetValue(srcLine, out var kvp))\n",
" {\n",
" keyToPath[srcLine] = new List<string>();\n",
" }\n",
"\n",
" keyToPath[srcLine].Add(callStackFile);\n",
" }\n",
"\n",
" catch (Exception ex)\n",
" {\n",
" Console.WriteLine(ex.Message);\n",
" }\n",
" }\n",
"\n",
" string jsonString = JsonSerializer.Serialize(keyToPath, new JsonSerializerOptions\n",
" {\n",
" WriteIndented = true // Makes the JSON output more readable with indentation\n",
" });\n",
"\n",
" // Write JSON string to a file\n",
" string filePath = Path.Combine(debugOutputFolder, \"dictionary.json\");\n",
" File.WriteAllText(filePath, jsonString);\n",
" }\n",
"}\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Run"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"outputs": [],
"source": [
"var analyzer = new DumpAnalyzer(windbgPath);\n",
"analyzer.SaveCallStack(debugCommandList, dumpFolder);\n",
"analyzer.GetAndSaveKey(callStackKeyword, dumpFolder);"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".NET (C#)",
"language": "C#",
"name": ".net-csharp"
},
"polyglot_notebook": {
"kernelInfo": {
"defaultKernelName": "csharp",
"items": [
{
"aliases": [],
"languageName": "csharp",
"name": "csharp"
}
]
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}