You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@@ -2,167 +2,170 @@ using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using Mono.Cecil;
|
||||
using Mono.CompilerServices.SymbolWriter;
|
||||
using Mono.Cecil.Cil;
|
||||
using Mono.Collections.Generic;
|
||||
|
||||
namespace Symbolicate
|
||||
namespace Mono
|
||||
{
|
||||
struct Location {
|
||||
public string FileName;
|
||||
public int Line;
|
||||
}
|
||||
class AssemblyLocationProvider
|
||||
{
|
||||
AssemblyDefinition assembly;
|
||||
Logger logger;
|
||||
|
||||
class LocationProvider {
|
||||
class AssemblyLocationProvider {
|
||||
Assembly assembly;
|
||||
MonoSymbolFile symbolFile;
|
||||
string seqPointDataPath;
|
||||
|
||||
public AssemblyLocationProvider (Assembly assembly, MonoSymbolFile symbolFile, string seqPointDataPath)
|
||||
{
|
||||
this.assembly = assembly;
|
||||
this.symbolFile = symbolFile;
|
||||
this.seqPointDataPath = seqPointDataPath;
|
||||
}
|
||||
|
||||
public bool TryGetLocation (string methodStr, string typeFullName, int offset, bool isOffsetIL, uint methodIndex, out Location location)
|
||||
{
|
||||
location = default (Location);
|
||||
if (symbolFile == null)
|
||||
return false;
|
||||
|
||||
var type = assembly.GetTypes().FirstOrDefault (t => t.FullName == typeFullName);
|
||||
if (type == null)
|
||||
return false;
|
||||
|
||||
var bindingflags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
|
||||
var method = type.GetMethods(bindingflags).FirstOrDefault (m => GetMethodFullName (m) == methodStr);
|
||||
if (method == null)
|
||||
return false;
|
||||
|
||||
int ilOffset = (isOffsetIL)? offset : GetILOffsetFromFile (method.MetadataToken, methodIndex, offset);
|
||||
if (ilOffset < 0)
|
||||
return false;
|
||||
|
||||
var methodSymbol = symbolFile.Methods [(method.MetadataToken & 0x00ffffff) - 1];
|
||||
|
||||
var lineNumbers = methodSymbol.GetLineNumberTable ().LineNumbers;
|
||||
var lineNumber = lineNumbers.FirstOrDefault (l => l.Offset >= ilOffset) ?? lineNumbers.Last ();
|
||||
|
||||
location.FileName = symbolFile.Sources [lineNumber.File-1].FileName;
|
||||
location.Line = lineNumber.Row;
|
||||
return true;
|
||||
}
|
||||
|
||||
static MethodInfo methodGetIL;
|
||||
private int GetILOffsetFromFile (int methodToken, uint methodIndex, int nativeOffset)
|
||||
{
|
||||
if (string.IsNullOrEmpty (seqPointDataPath))
|
||||
return -1;
|
||||
|
||||
if (methodGetIL == null)
|
||||
methodGetIL = typeof (StackFrame).GetMethod ("GetILOffsetFromFile", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
|
||||
if (methodGetIL == null)
|
||||
throw new Exception ("System.Diagnostics.StackFrame.GetILOffsetFromFile could not be found, make sure you have an updated mono installed.");
|
||||
|
||||
return (int) methodGetIL.Invoke (null, new object[] {seqPointDataPath, methodToken, methodIndex, nativeOffset});
|
||||
}
|
||||
|
||||
static MethodInfo methodGetMethodFullName;
|
||||
private string GetMethodFullName (MethodBase m)
|
||||
{
|
||||
|
||||
if (methodGetMethodFullName == null)
|
||||
methodGetMethodFullName = typeof (StackTrace).GetMethod ("GetFullNameForStackTrace", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
|
||||
if (methodGetMethodFullName == null)
|
||||
throw new Exception ("System.Exception.GetFullNameForStackTrace could not be found, make sure you have an updated mono installed.");
|
||||
|
||||
StringBuilder sb = new StringBuilder ();
|
||||
methodGetMethodFullName.Invoke (null, new object[] {sb, m});
|
||||
|
||||
return sb.ToString ();
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<string, AssemblyLocationProvider> assemblies;
|
||||
HashSet<string> directories;
|
||||
|
||||
public LocationProvider () {
|
||||
assemblies = new Dictionary<string, AssemblyLocationProvider> ();
|
||||
directories = new HashSet<string> ();
|
||||
}
|
||||
|
||||
public void AddAssembly (string assemblyPath)
|
||||
public AssemblyLocationProvider (string assemblyPath, Logger logger)
|
||||
{
|
||||
assemblyPath = Path.GetFullPath (assemblyPath);
|
||||
if (assemblies.ContainsKey (assemblyPath))
|
||||
return;
|
||||
this.logger = logger;
|
||||
|
||||
if (!File.Exists (assemblyPath))
|
||||
throw new ArgumentException ("assemblyPath does not exist: "+ assemblyPath);
|
||||
|
||||
var assembly = Assembly.LoadFrom (assemblyPath);
|
||||
MonoSymbolFile symbolFile = null;
|
||||
var readerParameters = new ReaderParameters { ReadSymbols = true };
|
||||
assembly = AssemblyDefinition.ReadAssembly (assemblyPath, readerParameters);
|
||||
}
|
||||
|
||||
var symbolPath = assemblyPath + ".mdb";
|
||||
if (!File.Exists (symbolPath))
|
||||
Debug.WriteLine (".mdb file was not found for " + assemblyPath);
|
||||
else
|
||||
symbolFile = MonoSymbolFile.ReadSymbolFile (assemblyPath + ".mdb");
|
||||
public bool TryResolveLocation (StackFrameData sfData, SeqPointInfo seqPointInfo)
|
||||
{
|
||||
if (!assembly.MainModule.HasSymbols)
|
||||
return false;
|
||||
|
||||
var seqPointDataPath = assemblyPath + ".msym";
|
||||
if (!File.Exists (seqPointDataPath))
|
||||
seqPointDataPath = null;
|
||||
|
||||
assemblies.Add (assemblyPath, new AssemblyLocationProvider (assembly, symbolFile, seqPointDataPath));
|
||||
|
||||
directories.Add (Path.GetDirectoryName (assemblyPath));
|
||||
|
||||
foreach (var assemblyRef in assembly.GetReferencedAssemblies ()) {
|
||||
string refPath = null;
|
||||
foreach (var dir in directories) {
|
||||
refPath = Path.Combine (dir, assemblyRef.Name);
|
||||
if (File.Exists (refPath))
|
||||
break;
|
||||
refPath = Path.Combine (dir, assemblyRef.Name + ".dll");
|
||||
if (File.Exists (refPath))
|
||||
break;
|
||||
refPath = Path.Combine (dir, assemblyRef.Name + ".exe");
|
||||
if (File.Exists (refPath))
|
||||
break;
|
||||
refPath = null;
|
||||
TypeDefinition type = null;
|
||||
var nested = sfData.TypeFullName.Split ('+');
|
||||
var types = assembly.MainModule.Types;
|
||||
foreach (var ntype in nested) {
|
||||
if (type == null) {
|
||||
// Use namespace first time.
|
||||
type = types.FirstOrDefault (t => t.FullName == ntype);
|
||||
} else {
|
||||
type = types.FirstOrDefault (t => t.Name == ntype);
|
||||
}
|
||||
if (refPath != null)
|
||||
AddAssembly (refPath);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddDirectory (string directory)
|
||||
{
|
||||
directory = Path.GetFullPath (directory);
|
||||
if (!Directory.Exists (directory)) {
|
||||
Console.Error.WriteLine ("Directory " + directory + " does not exist.");
|
||||
return;
|
||||
if (type == null) {
|
||||
logger.LogWarning ("Could not find type: {0}", ntype);
|
||||
return false;
|
||||
}
|
||||
|
||||
types = type.NestedTypes;
|
||||
}
|
||||
|
||||
directories.Add (directory);
|
||||
}
|
||||
var parensStart = sfData.MethodSignature.IndexOf ('(');
|
||||
var methodName = sfData.MethodSignature.Substring (0, parensStart).TrimEnd ();
|
||||
var methodParameters = sfData.MethodSignature.Substring (parensStart);
|
||||
var method = type.Methods.FirstOrDefault (m => CompareName (m, methodName) && CompareParameters (m.Parameters, methodParameters));
|
||||
if (method == null) {
|
||||
logger.LogWarning ("Could not find method: {0}", methodName);
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetLocation (string method, string typeFullName, int offset, bool isOffsetIL, uint methodIndex, out Location location)
|
||||
{
|
||||
location = default (Location);
|
||||
foreach (var assembly in assemblies.Values) {
|
||||
if (assembly.TryGetLocation (method, typeFullName, offset, isOffsetIL, methodIndex, out location))
|
||||
int ilOffset;
|
||||
if (sfData.IsILOffset) {
|
||||
ilOffset = sfData.Offset;
|
||||
} else {
|
||||
if (seqPointInfo == null)
|
||||
return false;
|
||||
|
||||
ilOffset = seqPointInfo.GetILOffset (method.MetadataToken.ToInt32 (), sfData.MethodIndex, sfData.Offset);
|
||||
}
|
||||
|
||||
if (ilOffset < 0)
|
||||
return false;
|
||||
|
||||
SequencePoint sp = null;
|
||||
foreach (var instr in method.Body.Instructions) {
|
||||
if (instr.SequencePoint != null)
|
||||
sp = instr.SequencePoint;
|
||||
|
||||
if (instr.Offset >= ilOffset) {
|
||||
sfData.SetLocation (sp.Document.Url, sp.StartLine);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool CompareName (MethodDefinition candidate, string expected)
|
||||
{
|
||||
if (candidate.Name == expected)
|
||||
return true;
|
||||
|
||||
if (!candidate.HasGenericParameters)
|
||||
return false;
|
||||
|
||||
var genStart = expected.IndexOf ('[');
|
||||
if (genStart < 0)
|
||||
return false;
|
||||
|
||||
if (candidate.Name != expected.Substring (0, genStart))
|
||||
return false;
|
||||
|
||||
int arity = 1;
|
||||
for (int pos = genStart; pos < expected.Length; ++pos) {
|
||||
if (expected [pos] == ',')
|
||||
++arity;
|
||||
}
|
||||
|
||||
return candidate.GenericParameters.Count == arity;
|
||||
}
|
||||
|
||||
static bool CompareParameters (Collection<ParameterDefinition> candidate, string expected)
|
||||
{
|
||||
var builder = new StringBuilder ();
|
||||
builder.Append ("(");
|
||||
|
||||
for (int i = 0; i < candidate.Count; i++) {
|
||||
var parameter = candidate [i];
|
||||
if (i > 0)
|
||||
builder.Append (", ");
|
||||
|
||||
if (parameter.ParameterType.IsSentinel)
|
||||
builder.Append ("...,");
|
||||
|
||||
var pt = parameter.ParameterType;
|
||||
if (!string.IsNullOrEmpty (pt.Namespace)) {
|
||||
builder.Append (pt.Namespace);
|
||||
builder.Append (".");
|
||||
}
|
||||
|
||||
FormatElementType (pt, builder);
|
||||
|
||||
builder.Append (" ");
|
||||
builder.Append (parameter.Name);
|
||||
}
|
||||
|
||||
builder.Append (")");
|
||||
|
||||
return builder.ToString () == expected;
|
||||
}
|
||||
|
||||
static void FormatElementType (TypeReference tr, StringBuilder builder)
|
||||
{
|
||||
var ts = tr as TypeSpecification;
|
||||
if (ts != null) {
|
||||
if (ts.IsByReference) {
|
||||
FormatElementType (ts.ElementType, builder);
|
||||
builder.Append ("&");
|
||||
return;
|
||||
}
|
||||
|
||||
var array = ts as ArrayType;
|
||||
if (array != null) {
|
||||
FormatElementType (ts.ElementType, builder);
|
||||
builder.Append ("[");
|
||||
|
||||
for (int ii = 0; ii < array.Rank - 1; ++ii) {
|
||||
builder.Append (",");
|
||||
}
|
||||
|
||||
builder.Append ("]");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
builder.Append (tr.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
48
mcs/tools/mono-symbolicate/Logger.cs
Normal file
48
mcs/tools/mono-symbolicate/Logger.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
|
||||
namespace Mono
|
||||
{
|
||||
public class Logger
|
||||
{
|
||||
public enum Level
|
||||
{
|
||||
Debug = 1,
|
||||
Warning = 2,
|
||||
Error = 3,
|
||||
None = 4,
|
||||
}
|
||||
|
||||
Level level;
|
||||
Action<string> logAction;
|
||||
|
||||
public Logger (Level level, Action<string> logAction)
|
||||
{
|
||||
this.level = level;
|
||||
this.logAction = logAction;
|
||||
}
|
||||
|
||||
public void LogDebug (string str, params string[] vals)
|
||||
{
|
||||
Log (Level.Debug, "Debug: " + str, vals);
|
||||
}
|
||||
|
||||
public void LogWarning (string str, params string[] vals)
|
||||
{
|
||||
Log (Level.Warning, "Warning: " + str, vals);
|
||||
}
|
||||
|
||||
public void LogError (string str, params string[] vals)
|
||||
{
|
||||
Log (Level.Error, "Error: " + str, vals);
|
||||
}
|
||||
|
||||
private void Log (Level msgLevel, string str, params string[] vals)
|
||||
{
|
||||
if ((int) level > (int) msgLevel)
|
||||
return;
|
||||
|
||||
logAction (string.Format (str, vals));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ include ../../build/rules.make
|
||||
PROGRAM = mono-symbolicate.exe
|
||||
|
||||
LOCAL_MCS_FLAGS = \
|
||||
/r:Mono.Cecil.dll \
|
||||
/r:Mono.CompilerServices.SymbolWriter.dll \
|
||||
/r:System.Xml
|
||||
/D:NO_AUTHENTICODE
|
||||
|
||||
LIB_REFS = Mono.Cecil Mono.Cecil.Mdb System.Xml System.Core System
|
||||
|
||||
include ../../build/executable.make
|
||||
|
||||
@@ -15,44 +15,66 @@ LIB_PATH = $(topdir)/class/lib/$(PROFILE)
|
||||
|
||||
MONO = MONO_PATH="$(LIB_PATH)$(PLATFORM_PATH_SEPARATOR)$$MONO_PATH" $(RUNTIME) -O=-inline
|
||||
|
||||
OUT_DIR = Test/out
|
||||
MSYM_DIR = $(OUT_DIR)/msymdir
|
||||
TEST_CS = Test/StackTraceDumper.cs
|
||||
TEST_EXE = $(OUT_DIR)/StackTraceDumper.exe
|
||||
RELEASE_FILE = $(OUT_DIR)/release.out
|
||||
SYMBOLICATE_FILE = $(OUT_DIR)/symbolicate.out
|
||||
STACKTRACE_FILE = $(OUT_DIR)/stacktrace.out
|
||||
SYMBOLICATE_RAW_FILE = $(OUT_DIR)/symbolicate_raw.out
|
||||
SYMBOLICATE_RESULT_FILE = $(OUT_DIR)/symbolicate.result
|
||||
SYMBOLICATE_EXPECTED_FILE = Test/symbolicate.expected
|
||||
|
||||
CHECK_DIFF = @\
|
||||
MONO_DEBUG=gen-compact-seq-points $(MONO) $(TEST_EXE) > $(RELEASE_FILE); \
|
||||
$(MONO) $(LIB_PATH)/$(PROGRAM) $(TEST_EXE) $(RELEASE_FILE) | sed "s/).*Test\//) in /" > $(SYMBOLICATE_FILE); \
|
||||
DIFF=$$(diff $(SYMBOLICATE_FILE) $(SYMBOLICATE_EXPECTED_FILE)); \
|
||||
$(MONO) $(TEST_EXE) > $(STACKTRACE_FILE); \
|
||||
$(MONO) $(LIB_PATH)/$(PROGRAM) $(MSYM_DIR) $(STACKTRACE_FILE) > $(SYMBOLICATE_RAW_FILE); \
|
||||
sed "s/) .* in .*\/mcs\//) in mcs\//" $(SYMBOLICATE_RAW_FILE) | sed '/\[MVID\]/d' | sed '/\[AOTID\]/d' > $(SYMBOLICATE_RESULT_FILE); \
|
||||
DIFF=$$(diff $(SYMBOLICATE_RESULT_FILE) $(SYMBOLICATE_EXPECTED_FILE)); \
|
||||
if [ ! -z "$$DIFF" ]; then \
|
||||
echo "Symbolicate tests failed."; \
|
||||
echo "If $(SYMBOLICATE_FILE) is correct copy it to $(SYMBOLICATE_EXPECTED_FILE)."; \
|
||||
echo "If $(SYMBOLICATE_RESULT_FILE) is correct copy it to $(SYMBOLICATE_EXPECTED_FILE)."; \
|
||||
echo "Otherwise runtime sequence points need to be fixed."; \
|
||||
echo "$$DIFF"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
BUILD_TEST_EXE = @\
|
||||
PREPARE_OUTDIR = @\
|
||||
rm -rf $(OUT_DIR); \
|
||||
mkdir -p $(OUT_DIR); \
|
||||
$(MCS) -debug $(TEST_CS) -out:$(TEST_EXE)
|
||||
mkdir -p $(MSYM_DIR);
|
||||
|
||||
COMPILE = \
|
||||
$(CSCOMPILE) $(TEST_CS) -out:$(TEST_EXE); \
|
||||
$(MONO) $(LIB_PATH)/$(PROGRAM) store-symbols $(MSYM_DIR) $(OUT_DIR); \
|
||||
$(MONO) $(LIB_PATH)/$(PROGRAM) store-symbols $(MSYM_DIR) $(LIB_PATH);
|
||||
|
||||
check: test-local
|
||||
|
||||
AOT_SUPPORTED = $(shell $(MONO) --aot 2>&1 | grep -q "AOT compilation is not supported" && echo 0 || echo 1)
|
||||
|
||||
test-local: all
|
||||
$(BUILD_TEST_EXE)
|
||||
@echo "Checking $(PROGRAM) without AOT"
|
||||
test-local: test-without-aot test-with-aot test-with-aot-msym
|
||||
|
||||
test-without-aot: OUT_DIR = Test/without_aot
|
||||
test-without-aot: all
|
||||
@echo "Checking $(TEST_EXE) without AOT in $(OUT_DIR)"
|
||||
$(PREPARE_OUTDIR)
|
||||
$(COMPILE)
|
||||
$(CHECK_DIFF)
|
||||
|
||||
test-with-aot: OUT_DIR = Test/with_aot
|
||||
test-with-aot: all
|
||||
ifeq ($(AOT_SUPPORTED), 1)
|
||||
@echo "Checking $(PROGRAM) with AOT"
|
||||
@MONO_DEBUG=gen-compact-seq-points $(MONO) --aot $(TEST_EXE) > /dev/null
|
||||
$(CHECK_DIFF)
|
||||
@echo "Checking $(PROGRAM) with AOT (using .msym)"
|
||||
$(BUILD_TEST_EXE)
|
||||
@MONO_DEBUG=gen-compact-seq-points $(MONO) --aot=gen-seq-points-file $(TEST_EXE) > /dev/null
|
||||
@echo "Checking $(TEST_EXE) with AOT in $(OUT_DIR)"
|
||||
$(PREPARE_OUTDIR)
|
||||
$(COMPILE)
|
||||
@$(MONO) --aot $(TEST_EXE) > /dev/null
|
||||
$(CHECK_DIFF)
|
||||
endif
|
||||
|
||||
test-with-aot-msym: OUT_DIR = Test/with_aot_msym
|
||||
test-with-aot-msym: all
|
||||
ifeq ($(AOT_SUPPORTED), 1)
|
||||
@echo "Checking $(TEST_EXE) with AOT (using .msym) in $(OUT_DIR)"
|
||||
$(PREPARE_OUTDIR)
|
||||
$(COMPILE)
|
||||
@$(MONO) --aot=msym-dir=$(MSYM_DIR) $(TEST_EXE) > /dev/null
|
||||
$(CHECK_DIFF)
|
||||
endif
|
||||
|
||||
156
mcs/tools/mono-symbolicate/SeqPointInfo.cs
Normal file
156
mcs/tools/mono-symbolicate/SeqPointInfo.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Mono
|
||||
{
|
||||
static class BinaryReaderExtensions
|
||||
{
|
||||
public static int ReadVariableInt (this BinaryReader reader)
|
||||
{
|
||||
int val = 0;
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var b = reader.ReadByte ();
|
||||
val |= (b & 0x7f) << (7 * i);
|
||||
if ((b & 0x80) == 0)
|
||||
return val;
|
||||
}
|
||||
|
||||
throw new Exception ("Invalid variable int");
|
||||
}
|
||||
|
||||
public static int ReadVariableZigZagInt (this BinaryReader reader)
|
||||
{
|
||||
int enc = ReadVariableInt (reader);
|
||||
int val = enc >> 1;
|
||||
return ((enc & 1) == 0)? val : -val;
|
||||
}
|
||||
}
|
||||
|
||||
class SeqPointInfo
|
||||
{
|
||||
class MethodData
|
||||
{
|
||||
List<SeqPoint> seqPoints;
|
||||
|
||||
public static MethodData Read (BinaryReader reader)
|
||||
{
|
||||
var hasDebugData = reader.ReadVariableInt () != 0;
|
||||
var dataSize = reader.ReadVariableInt ();
|
||||
var dataEnd = reader.BaseStream.Position + dataSize;
|
||||
|
||||
var seqPoints = new List<SeqPoint> ();
|
||||
SeqPoint prev = null;
|
||||
while (reader.BaseStream.Position < dataEnd) {
|
||||
var seqPoint = SeqPoint.Read (reader, prev, hasDebugData);
|
||||
seqPoints.Add (seqPoint);
|
||||
prev = seqPoint;
|
||||
}
|
||||
|
||||
if (reader.BaseStream.Position != dataEnd)
|
||||
throw new Exception ("Read more seq point than expected.");
|
||||
|
||||
return new MethodData () { seqPoints = seqPoints };
|
||||
}
|
||||
|
||||
public bool TryGetILOffset (int nativeOffset, out int ilOffset)
|
||||
{
|
||||
ilOffset = 0;
|
||||
SeqPoint prev = null;
|
||||
foreach (var seqPoint in seqPoints) {
|
||||
if (seqPoint.NativeOffset > nativeOffset)
|
||||
break;
|
||||
prev = seqPoint;
|
||||
}
|
||||
|
||||
if (prev == null)
|
||||
return false;
|
||||
|
||||
ilOffset = prev.ILOffset;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class SeqPoint
|
||||
{
|
||||
public readonly int ILOffset;
|
||||
public readonly int NativeOffset;
|
||||
|
||||
public SeqPoint (int ilOffset, int nativeOffset)
|
||||
{
|
||||
ILOffset = ilOffset;
|
||||
NativeOffset = nativeOffset;
|
||||
}
|
||||
|
||||
public static SeqPoint Read (BinaryReader reader, SeqPoint prev, bool hasDebug)
|
||||
{
|
||||
var ilOffset = reader.ReadVariableZigZagInt ();
|
||||
var nativeOffset = reader.ReadVariableZigZagInt ();
|
||||
|
||||
// Respect delta encoding
|
||||
if (prev != null) {
|
||||
ilOffset += prev.ILOffset;
|
||||
nativeOffset += prev.NativeOffset;
|
||||
}
|
||||
|
||||
//Read everything to ensure the buffer position is at the end of the seq point data.
|
||||
if (hasDebug) {
|
||||
reader.ReadVariableInt (); // flags
|
||||
|
||||
var next_length = reader.ReadVariableInt ();
|
||||
for (var i = 0; i < next_length; ++i)
|
||||
reader.ReadVariableInt ();
|
||||
}
|
||||
|
||||
return new SeqPoint (ilOffset, nativeOffset);
|
||||
}
|
||||
};
|
||||
|
||||
Dictionary<Tuple<int,int>, MethodData> dataByIds;
|
||||
Dictionary<int, MethodData> dataByTokens;
|
||||
|
||||
public static SeqPointInfo Read (string path)
|
||||
{
|
||||
using (var reader = new BinaryReader (File.Open (path, FileMode.Open)))
|
||||
{
|
||||
var dataByIds = new Dictionary<Tuple<int,int>, MethodData> ();
|
||||
var dataByTokens = new Dictionary<int, MethodData> ();
|
||||
|
||||
var methodCount = reader.ReadVariableInt ();
|
||||
|
||||
for (var i = 0; i < methodCount; ++i) {
|
||||
var methodToken = reader.ReadVariableInt ();
|
||||
var methodIndex = reader.ReadVariableInt ();
|
||||
var methodId = new Tuple<int, int> (methodToken, methodIndex);
|
||||
|
||||
var methodData = MethodData.Read (reader);
|
||||
|
||||
dataByIds.Add (methodId, methodData);
|
||||
if (!dataByTokens.ContainsKey (methodToken))
|
||||
dataByTokens.Add (methodToken, methodData);
|
||||
}
|
||||
|
||||
return new SeqPointInfo { dataByIds = dataByIds, dataByTokens = dataByTokens };
|
||||
}
|
||||
}
|
||||
|
||||
public int GetILOffset (int methodToken, uint methodIndex, int nativeOffset)
|
||||
{
|
||||
MethodData methodData;
|
||||
if (methodIndex == 0xffffff) {
|
||||
if (!dataByTokens.TryGetValue (methodToken, out methodData))
|
||||
throw new Exception (string.Format ("Could not find data for method token {0:X}", methodToken));
|
||||
} else {
|
||||
var methodId = new Tuple<int, int> (methodToken, (int)methodIndex);
|
||||
if (!dataByIds.TryGetValue (methodId, out methodData))
|
||||
throw new Exception (string.Format ("Could not find data for method token {0:X} with index {1:X}", methodToken, methodIndex));
|
||||
}
|
||||
|
||||
int ilOffset;
|
||||
if (!methodData.TryGetILOffset (nativeOffset, out ilOffset))
|
||||
throw new Exception ("Could not retrieve IL offset");
|
||||
|
||||
return ilOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
116
mcs/tools/mono-symbolicate/StackFrameData.cs
Normal file
116
mcs/tools/mono-symbolicate/StackFrameData.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Mono
|
||||
{
|
||||
class StackFrameData
|
||||
{
|
||||
static Regex regex = new Regex (@"\w*at (?<Method>.+) *(\[0x(?<IL>.+)\]|<0x.+ \+ 0x(?<NativeOffset>.+)>( (?<MethodIndex>\d+)|)) in <filename unknown>:0");
|
||||
|
||||
public readonly string TypeFullName;
|
||||
public readonly string MethodSignature;
|
||||
public readonly int Offset;
|
||||
public readonly bool IsILOffset;
|
||||
public readonly uint MethodIndex;
|
||||
public readonly string Line;
|
||||
|
||||
public readonly bool IsValid;
|
||||
|
||||
public string File { get; private set; }
|
||||
public int LineNumber { get; private set; }
|
||||
|
||||
private StackFrameData (string line, string typeFullName, string methodSig, int offset, bool isILOffset, uint methodIndex)
|
||||
{
|
||||
LineNumber = -1;
|
||||
|
||||
Line = line;
|
||||
TypeFullName = typeFullName;
|
||||
MethodSignature = methodSig;
|
||||
Offset = offset;
|
||||
IsILOffset = isILOffset;
|
||||
MethodIndex = methodIndex;
|
||||
|
||||
IsValid = true;
|
||||
}
|
||||
|
||||
private StackFrameData (string line)
|
||||
{
|
||||
LineNumber = -1;
|
||||
|
||||
Line = line;
|
||||
}
|
||||
|
||||
public static bool TryParse (string line, out StackFrameData stackFrame)
|
||||
{
|
||||
stackFrame = null;
|
||||
|
||||
var match = regex.Match (line);
|
||||
if (!match.Success) {
|
||||
if (line.Trim ().StartsWith ("at ", StringComparison.InvariantCulture)) {
|
||||
stackFrame = new StackFrameData (line);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
string typeFullName, methodSignature;
|
||||
var methodStr = match.Groups ["Method"].Value.Trim ();
|
||||
if (!ExtractSignatures (methodStr, out typeFullName, out methodSignature))
|
||||
return false;
|
||||
|
||||
var isILOffset = !string.IsNullOrEmpty (match.Groups ["IL"].Value);
|
||||
var offsetVarName = (isILOffset)? "IL" : "NativeOffset";
|
||||
var offset = int.Parse (match.Groups [offsetVarName].Value, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
||||
|
||||
uint methodIndex = 0xffffff;
|
||||
if (!string.IsNullOrEmpty (match.Groups ["MethodIndex"].Value))
|
||||
methodIndex = uint.Parse (match.Groups ["MethodIndex"].Value, CultureInfo.InvariantCulture);
|
||||
|
||||
stackFrame = new StackFrameData (line, typeFullName, methodSignature, offset, isILOffset, methodIndex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ExtractSignatures (string str, out string typeFullName, out string methodSignature)
|
||||
{
|
||||
var methodNameEnd = str.IndexOf ('(');
|
||||
if (methodNameEnd == -1) {
|
||||
typeFullName = methodSignature = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var typeNameEnd = str.LastIndexOf ('.', methodNameEnd);
|
||||
if (typeNameEnd == -1) {
|
||||
typeFullName = methodSignature = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Adjustment for Type..ctor ()
|
||||
if (typeNameEnd > 0 && str [typeNameEnd - 1] == '.') {
|
||||
--typeNameEnd;
|
||||
}
|
||||
|
||||
typeFullName = str.Substring (0, typeNameEnd);
|
||||
// Remove generic parameters
|
||||
typeFullName = Regex.Replace (typeFullName, @"\[[^\[\]]*\]", "");
|
||||
|
||||
methodSignature = str.Substring (typeNameEnd + 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void SetLocation (string file, int lineNumber)
|
||||
{
|
||||
File = file;
|
||||
LineNumber = lineNumber;
|
||||
}
|
||||
|
||||
public override string ToString () {
|
||||
if (Line.Contains ("<filename unknown>:0") && LineNumber != -1)
|
||||
return Line.Replace ("<filename unknown>:0", string.Format ("{0}:{1}", File, LineNumber));
|
||||
|
||||
return Line;
|
||||
}
|
||||
}
|
||||
}
|
||||
36
mcs/tools/mono-symbolicate/StackTraceMetadata.cs
Normal file
36
mcs/tools/mono-symbolicate/StackTraceMetadata.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Mono
|
||||
{
|
||||
class StackTraceMetadata
|
||||
{
|
||||
static Regex regex = new Regex (@"\[(?<Id>.+)\] (?<Value>.+)");
|
||||
|
||||
public readonly string Id;
|
||||
public readonly string Value;
|
||||
public readonly string Line;
|
||||
|
||||
private StackTraceMetadata (string line, string id, string val)
|
||||
{
|
||||
Line = line;
|
||||
Id = id;
|
||||
Value = val;
|
||||
}
|
||||
|
||||
public static bool TryParse (string line, out StackTraceMetadata metadata)
|
||||
{
|
||||
metadata = null;
|
||||
|
||||
var match = regex.Match (line);
|
||||
if (!match.Success)
|
||||
return false;
|
||||
|
||||
string id = match.Groups ["Id"].Value;
|
||||
string val = match.Groups ["Value"].Value;
|
||||
|
||||
metadata = new StackTraceMetadata (line, id, val);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
131
mcs/tools/mono-symbolicate/SymbolManager.cs
Normal file
131
mcs/tools/mono-symbolicate/SymbolManager.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using Mono.Collections.Generic;
|
||||
|
||||
namespace Mono
|
||||
{
|
||||
public class SymbolManager
|
||||
{
|
||||
string msymDir;
|
||||
Logger logger;
|
||||
|
||||
public SymbolManager (string msymDir, Logger logger) {
|
||||
this.msymDir = msymDir;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
internal bool TryResolveLocation (StackFrameData sfData, string mvid, string aotid)
|
||||
{
|
||||
if (mvid == null)
|
||||
return false;
|
||||
|
||||
var assemblyLocProvider = GetOrCreateAssemblyLocationProvider (mvid);
|
||||
if (assemblyLocProvider == null)
|
||||
return false;
|
||||
|
||||
SeqPointInfo seqPointInfo = null;
|
||||
if (!sfData.IsILOffset && aotid != null)
|
||||
seqPointInfo = GetOrCreateSeqPointInfo (aotid);
|
||||
|
||||
return assemblyLocProvider.TryResolveLocation (sfData, seqPointInfo);
|
||||
}
|
||||
|
||||
Dictionary<string, AssemblyLocationProvider> assemblies = new Dictionary<string, AssemblyLocationProvider> ();
|
||||
|
||||
private AssemblyLocationProvider GetOrCreateAssemblyLocationProvider (string mvid)
|
||||
{
|
||||
if (assemblies.ContainsKey (mvid))
|
||||
return assemblies[mvid];
|
||||
|
||||
var mvidDir = Path.Combine (msymDir, mvid);
|
||||
if (!Directory.Exists (mvidDir)) {
|
||||
logger.LogWarning ("MVID directory does not exist: {0}", mvidDir);
|
||||
return null;
|
||||
}
|
||||
|
||||
string assemblyPath = null;
|
||||
var exeFiles = Directory.GetFiles (mvidDir, "*.exe");
|
||||
var dllFiles = Directory.GetFiles (mvidDir, "*.dll");
|
||||
|
||||
if (exeFiles.Length + dllFiles.Length != 1) {
|
||||
logger.LogError ("MVID directory should include one assembly: {0}", mvidDir);
|
||||
return null;
|
||||
}
|
||||
|
||||
assemblyPath = (exeFiles.Length > 0)? exeFiles[0] : dllFiles[0];
|
||||
|
||||
var locProvider = new AssemblyLocationProvider (assemblyPath, logger);
|
||||
|
||||
assemblies.Add (mvid, locProvider);
|
||||
|
||||
return locProvider;
|
||||
}
|
||||
|
||||
Dictionary<string, SeqPointInfo> seqPointInfos = new Dictionary<string, SeqPointInfo> ();
|
||||
|
||||
private SeqPointInfo GetOrCreateSeqPointInfo (string aotid)
|
||||
{
|
||||
if (seqPointInfos.ContainsKey (aotid))
|
||||
return seqPointInfos[aotid];
|
||||
|
||||
var aotidDir = Path.Combine (msymDir, aotid);
|
||||
if (!Directory.Exists (aotidDir)) {
|
||||
logger.LogError ("AOTID directory does not exist: {0}", aotidDir);
|
||||
return null;
|
||||
}
|
||||
|
||||
string msymFile = null;
|
||||
var msymFiles = Directory.GetFiles(aotidDir, "*.msym");
|
||||
msymFile = msymFiles[0];
|
||||
|
||||
var seqPointInfo = SeqPointInfo.Read (msymFile);
|
||||
|
||||
seqPointInfos.Add (aotid, seqPointInfo);
|
||||
|
||||
return seqPointInfo;
|
||||
}
|
||||
|
||||
public void StoreSymbols (params string[] lookupDirs)
|
||||
{
|
||||
foreach (var dir in lookupDirs) {
|
||||
var exeFiles = Directory.GetFiles (dir, "*.exe");
|
||||
var dllFiles = Directory.GetFiles (dir, "*.dll");
|
||||
var assemblies = exeFiles.Concat (dllFiles);
|
||||
foreach (var assemblyPath in assemblies) {
|
||||
var mdbPath = assemblyPath + ".mdb";
|
||||
if (!File.Exists (mdbPath)) {
|
||||
logger.LogWarning ("Directory {0} contains {1} but no mdb {2}.", dir, Path.GetFileName (assemblyPath), Path.GetFileName (mdbPath));
|
||||
// assemblies without mdb files are useless
|
||||
continue;
|
||||
}
|
||||
|
||||
var assembly = AssemblyDefinition.ReadAssembly (assemblyPath);
|
||||
|
||||
var mvid = assembly.MainModule.Mvid.ToString ("N");
|
||||
var mvidDir = Path.Combine (msymDir, mvid);
|
||||
|
||||
if (Directory.Exists (mvidDir)) {
|
||||
try {
|
||||
Directory.Delete (mvidDir, true);
|
||||
} catch (DirectoryNotFoundException e) {}
|
||||
}
|
||||
|
||||
Directory.CreateDirectory (mvidDir);
|
||||
|
||||
var mvidAssemblyPath = Path.Combine (mvidDir, Path.GetFileName (assemblyPath));
|
||||
File.Copy (assemblyPath, mvidAssemblyPath);
|
||||
|
||||
var mvidMdbPath = Path.Combine (mvidDir, Path.GetFileName (mdbPath));
|
||||
File.Copy (mdbPath, mvidMdbPath);
|
||||
|
||||
// TODO create MVID dir for non main modules with links to main module MVID
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,8 @@
|
||||
symbolicate.cs
|
||||
LocationProvider.cs
|
||||
LocationProvider.cs
|
||||
SeqPointInfo.cs
|
||||
StackFrameData.cs
|
||||
StackTraceMetadata.cs
|
||||
SymbolManager.cs
|
||||
Logger.cs
|
||||
../../class/Mono.Options/Mono.Options/Options.cs
|
||||
|
||||
@@ -1,89 +1,181 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
using Mono.Options;
|
||||
|
||||
namespace Symbolicate
|
||||
namespace Mono
|
||||
{
|
||||
public class Program
|
||||
public class Symbolicate
|
||||
{
|
||||
static Regex regex = new Regex (@"\w*at (?<Method>.+) *(\[0x(?<IL>.+)\]|<0x.+ \+ 0x(?<NativeOffset>.+)>( (?<MethodIndex>\d+)|)) in <filename unknown>:0");
|
||||
class Command {
|
||||
public readonly int MinArgCount;
|
||||
public readonly int MaxArgCount;
|
||||
public readonly Action<List<string>> Action;
|
||||
|
||||
public Command (Action<List<string>> action, int minArgCount = 0, int maxArgCount = int.MaxValue)
|
||||
{
|
||||
Action = action;
|
||||
MinArgCount = minArgCount;
|
||||
MaxArgCount = maxArgCount;
|
||||
}
|
||||
}
|
||||
|
||||
static Logger logger;
|
||||
|
||||
public static int Main (String[] args)
|
||||
{
|
||||
if (args.Length < 2) {
|
||||
Console.Error.WriteLine ("Usage: symbolicate <assembly path> <input file> [lookup directories]");
|
||||
var showHelp = false;
|
||||
List<string> extra = null;
|
||||
|
||||
Command cmd = null;
|
||||
|
||||
var logLevel = Logger.Level.Warning;
|
||||
|
||||
var options = new OptionSet {
|
||||
{ "h|help", "Show this help", v => showHelp = true },
|
||||
{ "q", "Quiet, warnings are not displayed", v => logLevel = Logger.Level.Error },
|
||||
{ "v", "Verbose, log debug messages", v => logLevel = Logger.Level.Debug },
|
||||
};
|
||||
|
||||
try {
|
||||
extra = options.Parse (args);
|
||||
} catch (OptionException e) {
|
||||
Console.WriteLine ("Option error: {0}", e.Message);
|
||||
showHelp = true;
|
||||
}
|
||||
|
||||
if (extra.Count > 0 && extra[0] == "store-symbols")
|
||||
cmd = new Command (StoreSymbolsAction, 2);
|
||||
|
||||
if (cmd != null) {
|
||||
extra.RemoveAt (0);
|
||||
} else {
|
||||
cmd = new Command (SymbolicateAction, 2, 2);
|
||||
}
|
||||
|
||||
if (showHelp || extra == null || extra.Count < cmd.MinArgCount || extra.Count > cmd.MaxArgCount) {
|
||||
Console.Error.WriteLine ("Usage: symbolicate [options] <msym dir> <input file>");
|
||||
Console.Error.WriteLine (" symbolicate [options] store-symbols <msym dir> [<dir>]+");
|
||||
Console.WriteLine ();
|
||||
Console.WriteLine ("Available options:");
|
||||
options.WriteOptionDescriptions (Console.Out);
|
||||
return 1;
|
||||
}
|
||||
|
||||
var assemblyPath = args [0];
|
||||
var inputFile = args [1];
|
||||
logger = new Logger (logLevel, msg => Console.Error.WriteLine (msg));
|
||||
|
||||
var locProvider = new LocationProvider ();
|
||||
|
||||
for (var i = 2; i < args.Length; i++)
|
||||
locProvider.AddDirectory (args [i]);
|
||||
|
||||
locProvider.AddAssembly (assemblyPath);
|
||||
|
||||
using (StreamReader r = new StreamReader (inputFile)) {
|
||||
for (var line = r.ReadLine (); line != null; line = r.ReadLine ()) {
|
||||
line = SymbolicateLine (line, locProvider);
|
||||
Console.WriteLine (line);
|
||||
}
|
||||
}
|
||||
cmd.Action (extra);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static string SymbolicateLine (string line, LocationProvider locProvider)
|
||||
private static void SymbolicateAction (List<string> args)
|
||||
{
|
||||
var match = regex.Match (line);
|
||||
if (!match.Success)
|
||||
return line;
|
||||
var msymDir = args [0];
|
||||
var inputFile = args [1];
|
||||
|
||||
string typeFullName;
|
||||
var methodStr = match.Groups ["Method"].Value.Trim ();
|
||||
if (!TryParseMethodType (methodStr, out typeFullName))
|
||||
return line;
|
||||
var symbolManager = new SymbolManager (msymDir, logger);
|
||||
|
||||
var isOffsetIL = !string.IsNullOrEmpty (match.Groups ["IL"].Value);
|
||||
var offsetVarName = (isOffsetIL)? "IL" : "NativeOffset";
|
||||
var offset = int.Parse (match.Groups [offsetVarName].Value, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
||||
|
||||
uint methodIndex = 0xffffff;
|
||||
if (!string.IsNullOrEmpty (match.Groups ["MethodIndex"].Value))
|
||||
methodIndex = uint.Parse (match.Groups ["MethodIndex"].Value, CultureInfo.InvariantCulture);
|
||||
|
||||
Location location;
|
||||
if (!locProvider.TryGetLocation (methodStr, typeFullName, offset, isOffsetIL, methodIndex, out location))
|
||||
return line;
|
||||
|
||||
return line.Replace ("<filename unknown>:0", string.Format ("{0}:{1}", location.FileName, location.Line));
|
||||
using (StreamReader r = new StreamReader (inputFile)) {
|
||||
var sb = Process (r, symbolManager);
|
||||
Console.Write (sb.ToString ());
|
||||
}
|
||||
}
|
||||
|
||||
static bool TryParseMethodType (string str, out string typeFullName)
|
||||
private static void StoreSymbolsAction (List<string> args)
|
||||
{
|
||||
typeFullName = null;
|
||||
var msymDir = args[0];
|
||||
var lookupDirs = args.Skip (1).ToArray ();
|
||||
|
||||
var methodNameEnd = str.IndexOf ("(");
|
||||
if (methodNameEnd == -1)
|
||||
return false;
|
||||
var symbolManager = new SymbolManager (msymDir, logger);
|
||||
|
||||
// Remove parameters
|
||||
str = str.Substring (0, methodNameEnd);
|
||||
symbolManager.StoreSymbols (lookupDirs);
|
||||
}
|
||||
|
||||
// Remove generic parameters
|
||||
str = Regex.Replace (str, @"\[[^\[\]]*\]", "");
|
||||
public static StringBuilder Process (StreamReader reader, SymbolManager symbolManager)
|
||||
{
|
||||
List<StackFrameData> stackFrames = new List<StackFrameData>();
|
||||
List<StackTraceMetadata> metadata = new List<StackTraceMetadata>();
|
||||
StringBuilder sb = new StringBuilder ();
|
||||
bool linesEnded = false;
|
||||
|
||||
var typeNameEnd = str.LastIndexOf (".");
|
||||
if (methodNameEnd == -1 || typeNameEnd == -1)
|
||||
return false;
|
||||
for (var line = reader.ReadLine (); line != null; line = reader.ReadLine ()) {
|
||||
StackFrameData sfData;
|
||||
if (!linesEnded && StackFrameData.TryParse (line, out sfData)) {
|
||||
stackFrames.Add (sfData);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove method name
|
||||
typeFullName = str.Substring (0, typeNameEnd);
|
||||
if (stackFrames.Count > 0) {
|
||||
linesEnded = true;
|
||||
|
||||
return true;
|
||||
StackTraceMetadata stMetadata;
|
||||
if (StackTraceMetadata.TryParse (line, out stMetadata)) {
|
||||
metadata.Add (stMetadata);
|
||||
continue;
|
||||
}
|
||||
|
||||
DumpStackTrace (symbolManager, sb, stackFrames, metadata);
|
||||
|
||||
// Clear lists for next stack trace
|
||||
stackFrames.Clear ();
|
||||
metadata.Clear ();
|
||||
}
|
||||
|
||||
linesEnded = false;
|
||||
|
||||
// Append last line
|
||||
sb.AppendLine (line);
|
||||
}
|
||||
|
||||
if (stackFrames.Count > 0)
|
||||
DumpStackTrace (symbolManager, sb, stackFrames, metadata);
|
||||
|
||||
return sb;
|
||||
}
|
||||
|
||||
private static void DumpStackTrace (SymbolManager symbolManager, StringBuilder sb, List<StackFrameData> stackFrames, List<StackTraceMetadata> metadata)
|
||||
{
|
||||
string aotid = null;
|
||||
var aotidMetadata = metadata.FirstOrDefault ( m => m.Id == "AOTID" );
|
||||
if (aotidMetadata != null)
|
||||
aotid = aotidMetadata.Value;
|
||||
|
||||
var linesMvid = ProcessLinesMVID (metadata);
|
||||
var lineNumber = -1;
|
||||
foreach (var sfData in stackFrames) {
|
||||
string mvid = null;
|
||||
lineNumber++;
|
||||
if (!sfData.IsValid)
|
||||
continue;
|
||||
if (linesMvid.ContainsKey (lineNumber))
|
||||
mvid = linesMvid [lineNumber];
|
||||
|
||||
symbolManager.TryResolveLocation (sfData, mvid, aotid);
|
||||
|
||||
sb.AppendLine (sfData.ToString ());
|
||||
}
|
||||
|
||||
foreach (var m in metadata)
|
||||
sb.AppendLine (m.Line);
|
||||
}
|
||||
|
||||
private static Dictionary<int, string> ProcessLinesMVID (List<StackTraceMetadata> metadata)
|
||||
{
|
||||
var linesMvid = new Dictionary<int, string> ();
|
||||
var mvidData = metadata.Where ( m => m.Id == "MVID" ).Select ( m => m.Value );
|
||||
foreach (var m in mvidData) {
|
||||
var s1 = m.Split (new char[] {' '}, 2);
|
||||
var mvid = s1 [0];
|
||||
var lines = s1 [1].Split (',');
|
||||
foreach (var line in lines)
|
||||
linesMvid.Add (int.Parse (line), mvid);
|
||||
}
|
||||
|
||||
return linesMvid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user