Added Symbolicate tool.
authorMarcos Henrich <marcos.henrich@xamarin.com>
Wed, 19 Nov 2014 19:45:15 +0000 (19:45 +0000)
committerMarcos Henrich <marcos.henrich@xamarin.com>
Wed, 19 Nov 2014 19:45:15 +0000 (19:45 +0000)
Symbolicate tool replaces stack frames "<filename unknown>:0" with correct file name and line.

mcs/tools/symbolicate/LocationProvider.cs [new file with mode: 0644]
mcs/tools/symbolicate/Makefile [new file with mode: 0644]
mcs/tools/symbolicate/symbolicate.cs [new file with mode: 0644]
mcs/tools/symbolicate/symbolicate.exe.sources [new file with mode: 0644]

diff --git a/mcs/tools/symbolicate/LocationProvider.cs b/mcs/tools/symbolicate/LocationProvider.cs
new file mode 100644 (file)
index 0000000..634915c
--- /dev/null
@@ -0,0 +1,144 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Diagnostics;
+using System.Collections.Generic;
+using Mono.Cecil;
+using Mono.CompilerServices.SymbolWriter;
+
+namespace Symbolicate
+{
+       struct Location {
+               public string FileName;
+               public int Line;
+       }
+
+       class LocationProvider {
+               class AssemblyLocationProvider {
+                       AssemblyDefinition assembly;
+                       MonoSymbolFile symbolFile;
+
+                       public AssemblyLocationProvider (AssemblyDefinition assembly, MonoSymbolFile symbolFile)
+                       {
+                               this.assembly = assembly;
+                               this.symbolFile = symbolFile;
+                       }
+
+                       public bool TryGetLocation (string methodFullName, string[] methodParamsTypes, int ilOffset, out Location location)
+                       {
+                               location = default (Location);
+                               if (symbolFile == null)
+                                       return false;
+
+                               var typeNameEnd = methodFullName.LastIndexOf (".");
+                               var typeName = methodFullName.Substring (0, typeNameEnd);
+                               var methodName = methodFullName.Substring (typeNameEnd + 1, methodFullName.Length - typeNameEnd - 1);
+
+                               var type = assembly.MainModule.Types.FirstOrDefault (t => t.FullName == typeName);
+                               if (type == null)
+                                       return false;
+
+                               var method = type.Methods.FirstOrDefault (m => {
+                                       if (m.Name != methodName)
+                                               return false;
+
+                                       if (m.Parameters.Count != methodParamsTypes.Length)
+                                               return false;
+
+                                       for (var i = 0; i < methodParamsTypes.Length; i++) {
+                                               var paramType = m.Parameters[i].ParameterType;
+                                               if (paramType.Name != methodParamsTypes[i])
+                                                       return false;
+                                       }
+
+                                       return true;
+                               });
+
+                               if (method == null)
+                                       return false;
+
+                               var methodSymbol = symbolFile.Methods [method.MetadataToken.RID-1];
+
+                               foreach (var lineNumber in methodSymbol.GetLineNumberTable ().LineNumbers) {
+                                       if (lineNumber.Offset < ilOffset)
+                                               continue;
+
+                                       location.FileName = symbolFile.Sources [lineNumber.File-1].FileName;
+                                       location.Line = lineNumber.Row;
+                                       return true;
+                               }
+
+                               return false;
+                       }
+               }
+
+               Dictionary<string, AssemblyLocationProvider> assemblies;
+               HashSet<string> directories;
+
+               public LocationProvider () {
+                       assemblies = new Dictionary<string, AssemblyLocationProvider> ();
+                       directories = new HashSet<string> ();
+               }
+
+               public void AddAssembly (string assemblyPath)
+               {
+                       assemblyPath = Path.GetFullPath (assemblyPath);
+                       if (assemblies.ContainsKey (assemblyPath))
+                               return;
+
+                       if (!File.Exists (assemblyPath))
+                               throw new ArgumentException ("assemblyPath does not exist: "+ assemblyPath);
+
+                       var assembly = AssemblyDefinition.ReadAssembly (assemblyPath);
+                       MonoSymbolFile symbolFile = null;
+
+                       var symbolPath = assemblyPath + ".mdb";
+                       if (!File.Exists (symbolPath))
+                               Debug.WriteLine (".mdb file was not found for " + assemblyPath);
+                       else
+                               symbolFile = MonoSymbolFile.ReadSymbolFile (assemblyPath + ".mdb");
+
+                       assemblies.Add (assemblyPath, new AssemblyLocationProvider (assembly, symbolFile));
+
+                       directories.Add (Path.GetDirectoryName (assemblyPath));
+
+                       foreach (var assemblyRef in assembly.MainModule.AssemblyReferences) {
+                               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;
+                               }
+                               if (refPath != null)
+                                       AddAssembly (refPath);
+                       }
+               }
+
+               public void AddDirectory (string directory)
+               {
+                       if (Directory.Exists (directory))
+                               throw new ArgumentException ("Directory " + directory + " does not exist.");
+
+                       directories.Add (directory);
+               }
+
+               public bool TryGetLocation (string methodName, string[] methodParams, int ilOffset, out Location location)
+               {
+                       location = default (Location);
+                       foreach (var assembly in assemblies.Values) {
+                               if (assembly.TryGetLocation (methodName, methodParams, ilOffset, out location))
+                                       return true;
+                       }
+
+                       return false;
+               }
+       }
+}
+
diff --git a/mcs/tools/symbolicate/Makefile b/mcs/tools/symbolicate/Makefile
new file mode 100644 (file)
index 0000000..bd6f970
--- /dev/null
@@ -0,0 +1,12 @@
+thisdir = tools/symbolicate
+SUBDIRS =
+include ../../build/rules.make
+
+PROGRAM = symbolicate.exe
+
+LOCAL_MCS_FLAGS = \
+       /r:Mono.Cecil.dll       \
+       /r:Mono.CompilerServices.SymbolWriter.dll \
+       /r:System.Xml
+
+include ../../build/executable.make
diff --git a/mcs/tools/symbolicate/symbolicate.cs b/mcs/tools/symbolicate/symbolicate.cs
new file mode 100644 (file)
index 0000000..5df3b51
--- /dev/null
@@ -0,0 +1,69 @@
+using System;
+using System.IO;
+using System.Globalization;
+using System.Text.RegularExpressions;
+
+namespace Symbolicate
+{
+       public class Program
+       {
+               static Regex regex = new Regex (@"\w*at (?<MethodName>.+) \((?<MethodParams>.*)\) \[0x(?<IL>.+)\] in <filename unknown>:0");
+
+               public static int Main (String[] args)
+               {
+                       if (args.Length < 2) {
+                               Console.Error.WriteLine ("Usage: symbolicate <assembly path> <input file> [lookup directories]");
+                               return 1;
+                       }
+
+                       var assemblyPath = args [0];
+                       var inputFile = args [1];
+
+                       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);
+                           }
+                       }
+
+                       return 0;
+               }
+
+               static string SymbolicateLine (string line, LocationProvider locProvider)
+               {
+                       var match = regex.Match (line);
+                       if (!match.Success)
+                               return line;
+
+                       var methodName = match.Groups ["MethodName"].Value;
+                       var methodParams = ParseParametersTypes (match.Groups ["MethodParams"].Value);
+                       var ilOffset = int.Parse (match.Groups ["IL"].Value, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
+
+                       Location location;
+                       if (!locProvider.TryGetLocation (methodName, methodParams, ilOffset, out location))
+                               return line;
+
+                       return line.Replace ("<filename unknown>:0", string.Format ("{0}:{1}", location.FileName, location.Line));
+               }
+
+               static string[] ParseParametersTypes (string parameters)
+               {
+                       if (string.IsNullOrEmpty (parameters))
+                               return new string [0];
+
+                       var paramsArray = parameters.Split (',');
+                       var paramsTypes = new string [paramsArray.Length];
+                       for (var i = 0; i < paramsArray.Length; i++)
+                               paramsTypes [i] = paramsArray [i].Trim ().Split (new char[]{' '}, 2)[0];
+
+                       return paramsTypes;
+               }
+       }
+}
\ No newline at end of file
diff --git a/mcs/tools/symbolicate/symbolicate.exe.sources b/mcs/tools/symbolicate/symbolicate.exe.sources
new file mode 100644 (file)
index 0000000..968d028
--- /dev/null
@@ -0,0 +1,2 @@
+symbolicate.cs
+LocationProvider.cs
\ No newline at end of file