Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: mockingbirdnest/Principia
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 0f5011980dbe
Choose a base ref
...
head repository: mockingbirdnest/Principia
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 3c84cf6a8e29
Choose a head ref
  • 12 commits
  • 4 files changed
  • 2 contributors

Commits on Jan 6, 2019

  1. Copy the full SHA
    c0add94 View commit details
  2. split out DbgHelp

    eggrobin committed Jan 6, 2019
    Copy the full SHA
    ab2dd62 View commit details
  3. more formatting

    eggrobin committed Jan 6, 2019
    Copy the full SHA
    88dcbf0 View commit details
  4. constant

    eggrobin committed Jan 6, 2019
    Copy the full SHA
    4203314 View commit details
  5. flags!

    eggrobin committed Jan 6, 2019
    Copy the full SHA
    727baa9 View commit details
  6. newline handling

    eggrobin committed Jan 6, 2019
    Copy the full SHA
    904fdd8 View commit details
  7. remove unused declaration

    eggrobin committed Jan 6, 2019
    Copy the full SHA
    7b46a7b View commit details

Commits on Jan 7, 2019

  1. symbol start

    eggrobin committed Jan 7, 2019
    Copy the full SHA
    ec85c89 View commit details

Commits on Jan 8, 2019

  1. Copy the full SHA
    7bdf9c4 View commit details
  2. stack

    eggrobin committed Jan 8, 2019
    Copy the full SHA
    af30a59 View commit details

Commits on Jan 13, 2019

  1. 4.6.1 is enough.

    eggrobin committed Jan 13, 2019
    Copy the full SHA
    80f06fa View commit details
  2. Merge pull request #2061 from eggrobin/better-stacktrace-decoding

    Better stacktrace decoding
    pleroy authored Jan 13, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    3c84cf6 View commit details
Showing with 278 additions and 74 deletions.
  1. +3 −3 stacktrace_decoder/App.config
  2. +139 −0 stacktrace_decoder/dbghelp.cs
  3. +133 −70 stacktrace_decoder/stacktrace_decoder.cs
  4. +3 −1 stacktrace_decoder/stacktrace_decoder.csproj
6 changes: 3 additions & 3 deletions stacktrace_decoder/App.config
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
</startup>
</configuration>
</configuration>
139 changes: 139 additions & 0 deletions stacktrace_decoder/dbghelp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;


namespace principia {
namespace tools {

internal static class DbgHelp {
internal const UInt32 SYMOPT_LOAD_LINES = 0x00000010;
internal const Int32 MAX_SYM_NAME = 2000;

[StructLayout(LayoutKind.Sequential,
Size = 88 + 2 * (MAX_SYM_NAME - 1),
CharSet = CharSet.Unicode)]
internal class SYMBOL_INFOW {
public Int32 SizeOfStruct = 88;
public Int32 TypeIndex;
public Int64 Reserved0;
public Int64 Reserved1;
public Int32 Index;
public Int32 Size;
public Int64 ModBase;
public Int32 Flags;
public Int64 Value;
public Int64 Address;
public Int32 Register;
public Int32 Scope;
public Int32 Tag;
public Int32 NameLen;
public Int32 MaxNameLen = MAX_SYM_NAME;
// The entire name goes here, but in order to access it we would need
// a fixed-size buffer, which would require making this an unsafe struct
// instead of a class. We don't need the name at this point.
public char Name0;
}

[StructLayout(LayoutKind.Sequential)]
internal class IMAGEHLP_LINEW64 {
public Int32 SizeOfStruct = Marshal.SizeOf<IMAGEHLP_LINEW64>();
public IntPtr Key;
public Int32 LineNumber;
private IntPtr FileName_;
public Int64 Address;

public string FileName {
get {
StringBuilder result = new StringBuilder();
for (int i = 0;; i += 2) {
char code_unit = (char)Marshal.ReadInt16(FileName_, i);
if (code_unit == 0) {
return result.ToString();
}
result.Append(code_unit);
}
}
}
}

[DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SymInitializeW(
IntPtr hProcess,
[MarshalAs(UnmanagedType.LPWStr)] string UserSearchPath,
[MarshalAs(UnmanagedType.Bool)] bool fInvadeProcess);

[DllImport("dbghelp.dll", CharSet = CharSet.Unicode)]
internal static extern Int32 SymAddrIncludeInlineTrace(
IntPtr hProcess,
Int64 Address);

[DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SymQueryInlineTrace(
IntPtr hProcess,
Int64 StartAddress,
Int32 StartContext,
Int64 StartRetAddress,
Int64 CurAddress,
out Int32 CurContext,
out Int32 CurFrameIndex);

[DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SymGetLineFromInlineContextW(
IntPtr hProcess,
Int64 dwAddr,
Int32 InlineContext,
Int64 qwModuleBaseAddress,
out Int32 pdwDisplacement,
[MarshalAs(UnmanagedType.LPStruct)] IMAGEHLP_LINEW64 Line);


[DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SymGetLineFromAddrW64(
IntPtr hProcess,
Int64 dwAddr,
out Int32 pdwDisplacement,
[MarshalAs(UnmanagedType.LPStruct)] IMAGEHLP_LINEW64 Line);

[DllImport("dbghelp.dll", CharSet = CharSet.Unicode)]
internal static extern UInt32 SymSetOptions(
UInt32 SymOptions);

[DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern Int64 SymLoadModuleExW(
IntPtr hProcess,
IntPtr hFile,
[MarshalAs(UnmanagedType.LPWStr)] string ImageName,
[MarshalAs(UnmanagedType.LPWStr)] string ModuleName,
Int64 BaseOfDll,
Int32 DllSize,
IntPtr Data,
UInt32 Flags);

[DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SymFromAddrW(
IntPtr hProcess,
Int64 Address,
out Int64 Displacement,
[MarshalAs(UnmanagedType.LPStruct)] SYMBOL_INFOW Symbol);

[DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SymFromInlineContextW(
IntPtr hProcess,
Int64 Address,
Int32 InlineContext,
out Int64 Displacement,
[MarshalAs(UnmanagedType.LPStruct)] SYMBOL_INFOW Symbol);
}

} // namespace tools
} // namespace principia
203 changes: 133 additions & 70 deletions stacktrace_decoder/stacktrace_decoder.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using static principia.tools.DbgHelp;

namespace principia {
namespace tools {

class StackTraceDecoder {
const string dbh =
@"\Program Files (x86)\Windows Kits\10\Debuggers\x64\dbh.exe";

// Returns the base address for the given DLL.
private static Int64 GetBaseAddress(bool unity_crash,
string unity_regex,
@@ -32,67 +29,84 @@ private static Int64 GetBaseAddress(bool unity_crash,
return Convert.ToInt64(base_address_string, 16);
}

// Returns the output of running DBH for the given address.
private static string DecodeUsingPdbFile(Int64 address,
Int64 base_address,
string pdb_file) {
Int64 dbh_base_address = 0x1000000;
string rebased_address =
Convert.ToString(address - base_address + dbh_base_address, 16);
var p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = dbh;
p.StartInfo.Arguments =
'"' + pdb_file + "\" laddr \"" + rebased_address + '"';
p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
return output;
// If the IMAGEHLP_LINEW64 represents a line of Principia code, returns a
// GitHub link. Otherwise, returns null.
// If snippets is true, emits a raw link without Markdown formatting;
// within the Principia repository, this will be turned into a snippet by
// GitHub: https://help.github.com/articles/creating-a-permanent-link-to-a-code-snippet/.
private static string ParseLine(IntPtr handle, IMAGEHLP_LINEW64 line,
SYMBOL_INFOW symbol,
string commit, bool snippets) {
var file_regex = new Regex(@".*\\principia\\([a-z_]+)\\(\S+)");
Match file_match = file_regex.Match(line.FileName);
if (!file_match.Success) {
return null;
}
string file = $"{file_match.Groups[1]}/{file_match.Groups[2]}";
int line_number = line.LineNumber;
int? start_line_number = line.LineNumber;

SymGetLineFromAddrW64(
handle, symbol.Address, out Int32 displacement, line);
Match symbol_file_match = file_regex.Match(line.FileName);
if (symbol_file_match.Success &&
$@"{symbol_file_match.Groups[1]}/{
symbol_file_match.Groups[2]}" == file &&
line.LineNumber < line_number) {
start_line_number = line.LineNumber;
}

string url = $@"https://github.com/mockingbirdnest/Principia/blob/{
commit}/{file}#{(start_line_number.HasValue ? $"L{start_line_number}-"
: "")}L{line_number}";
// Snippets should not be separated by new lines, as they are on their own
// line anyway, so that a new line spaces them more than necessary. In
// order to keep the Markdown readable, hide a new line in a comment.
// `file:line` links need still to be separated by new lines.
return snippets ? $"<!---\n--> {url} "
: $"\n[`{file}:{line_number}`]({url})";
}

// Parses the output of DBH and writes the result to the console. Returns
// true iff the parsing succeeded.
private static bool ParseDbhOutput(Regex file_regex,
Regex line_regex,
string commit,
string output) {
Match file_match = file_regex.Match(output);
if (file_match.Success) {
string file = file_match.Groups[1].ToString() + '/' +
file_match.Groups[2].ToString();
string line = line_regex.Match(output).Groups[1].ToString();
string url = "https://github.com/mockingbirdnest/Principia/blob/" +
commit + '/' + file + "#L" + line;
Console.WriteLine("[`" + file + ":" + line + "`](" + url + ")");
return true;
} else {
return false;
private static void Win32Check(bool success,
[CallerMemberName] string member = "",
[CallerFilePath] string file = "",
[CallerLineNumber] int line = 0) {
if (!success) {
Console.WriteLine($"Error {Marshal.GetLastWin32Error()}");
Console.WriteLine($"{file}:{line} ({member})");
Environment.Exit(1);
}
}

private static string Comment(string comment) {
// Put the new line in the comment in order to avoid introducing
// new lines in the Markdown.
return $"<!---\n {comment} -->";
}

private static void Main(string[] args) {
bool unity_crash;
bool unity_crash = false;
Func<string, string> comment = Comment;
bool snippets = true;
string commit = null;
if (args.Length == 3) {
unity_crash = false;
} else if (args.Length == 4) {
var match = Regex.Match(args[3],
"--unity-crash-at-commit=([0-9a-f]{40})");
if (match.Success) {
for (int i = 2; i < args.Length; ++i) {
string flag = args[i];
var match = Regex.Match(flag, "--unity-crash-at-commit=([0-9a-f]{40})");
if (!unity_crash && match.Success) {
unity_crash = true;
commit = match.Groups[1].ToString();
} else if (snippets && flag == "--no-snippet") {
snippets = false;
} else if (comment == Comment && flag == "--no-comment") {
comment = (_) => "";
} else {
PrintUsage();
return;
}
} else {
PrintUsage();
return;
}

string info_file_uri = args[0];
string principia_pdb_file = args[1];
string physics_pdb_file = args[2];
string principia_directory = args[1];
var web_client = new WebClient();
var stream = new StreamReader(web_client.OpenRead(info_file_uri),
Encoding.UTF8);
@@ -119,10 +133,10 @@ private static void Main(string[] args) {
@"\(([0-9A-F]+)\)",
"ksp_physics_lib\\.cpp",
stream);
Console.WriteLine("<!--- Using Principia base address " +
Convert.ToString(principia_base_address, 16) + " -->");
Console.WriteLine("<!--- Using Physics base address " +
Convert.ToString(physics_base_address, 16) + " -->");
Console.Write(
comment($"Using Principia base address {principia_base_address:X}"));
Console.Write(
comment($"Using Physics base address {physics_base_address:X}"));
var stack_regex = new Regex(
unity_crash ? @"\(0x([0-9A-F]+)\) .*"
: @"@\s+[0-9A-F]+\s+.* \[0x([0-9A-F]+)(\+[0-9]+)?\]");
@@ -138,31 +152,80 @@ private static void Main(string[] args) {
do {
stack_match = stack_regex.Match(stream.ReadLine());
} while (!stack_match.Success);
var file_regex = new Regex(
@"file\s+:\s+.*\\principia\\([a-z_]+)\\(\S+)");
var line_regex = new Regex(@"line\s+:\s+([0-9]+)");
IntPtr handle = new IntPtr(1729);
SymSetOptions(SYMOPT_LOAD_LINES);
Win32Check(SymInitializeW(handle, null, fInvadeProcess: false));
Win32Check(
SymLoadModuleExW(handle,
IntPtr.Zero,
Path.Combine(principia_directory, "principia.dll"),
null,
principia_base_address,
0,
IntPtr.Zero,
0) != 0);
Win32Check(
SymLoadModuleExW(handle,
IntPtr.Zero,
Path.Combine(principia_directory, "physics.dll"),
null,
physics_base_address,
0,
IntPtr.Zero,
0) != 0);

var trace = new Stack<string>();
for (;
stack_match.Success;
stack_match = stack_regex.Match(stream.ReadLine())) {
Int64 address = Convert.ToInt64(stack_match.Groups[1].ToString(), 16);
string principia_output = DecodeUsingPdbFile(address,
principia_base_address,
principia_pdb_file);
if (!ParseDbhOutput(file_regex, line_regex, commit, principia_output)) {
string physics_output =
DecodeUsingPdbFile(address, physics_base_address, physics_pdb_file);
if (!ParseDbhOutput(file_regex, line_regex, commit, physics_output)) {
Console.WriteLine("<!--- Nothing for " + stack_match.Groups[0] +
" -->");
IMAGEHLP_LINEW64 line = new IMAGEHLP_LINEW64();
SYMBOL_INFOW symbol = new SYMBOL_INFOW();
Int32 inline_trace = SymAddrIncludeInlineTrace(handle, address);
if (inline_trace != 0) {
Win32Check(SymQueryInlineTrace(handle,
address,
0,
address,
address,
out Int32 current_context,
out Int32 current_frame_index));
for (int i = 0; i < inline_trace; ++i) {
Win32Check(SymGetLineFromInlineContextW(
handle, address, current_context + i, 0, out Int32 dsp, line));
Win32Check(SymFromInlineContextW(handle,
address,
current_context + i,
out Int64 displacement64,
symbol));
trace.Push(ParseLine(handle, line, symbol, commit, snippets) ??
comment("Inline frame not in Principia code"));
}
}
if (SymGetLineFromAddrW64(handle,
address,
out Int32 displacement,
line)) {
Win32Check(
SymFromAddrW(handle, address, out Int64 displacement64, symbol));
trace.Push(ParseLine(handle, line, symbol, commit, snippets) ??
comment($"Not in Principia code: {stack_match.Groups[0]}"));
} else if (Marshal.GetLastWin32Error() == 126) {
trace.Push(comment($"Not in loaded modules: {stack_match.Groups[0]}"));
} else {
Win32Check(false);
}
}
while (trace.Count > 0) {
Console.Write(trace.Pop());
}
}

private static void PrintUsage() {
Console.WriteLine("Usage: stacktrace_decoder " +
"<info_file_uri> <principia_pdb_file> " +
"<physics_pdb_file> [--unity-crash-at-commit=<sha1>]");
"<info_file_uri> <principia_directory> " +
"[--unity-crash-at-commit=<sha1>] " +
"[--no-comment] [--no-snippet]");
}
}

4 changes: 3 additions & 1 deletion stacktrace_decoder/stacktrace_decoder.csproj
Original file line number Diff line number Diff line change
@@ -9,8 +9,9 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>stacktrace_decoder</RootNamespace>
<AssemblyName>stacktrace_decoder</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@@ -41,6 +42,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="dbghelp.cs" />
<Compile Include="stacktrace_decoder.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>