Imported Upstream version 4.6.0.125

Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2016-08-03 10:59:49 +00:00
parent a569aebcfd
commit e79aa3c0ed
17047 changed files with 3137615 additions and 392334 deletions

View File

@@ -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);
}
}
}

View 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));
}
}
}

View File

@@ -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

View 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;
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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
}
}
}
}
}

View File

@@ -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

View File

@@ -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;
}
}
}
}