5 using System.Reflection;
6 using System.Diagnostics;
7 using System.Collections.Generic;
9 using Mono.CompilerServices.SymbolWriter;
14 public string FileName;
18 class LocationProvider {
19 class AssemblyLocationProvider {
21 MonoSymbolFile symbolFile;
22 string seqPointDataPath;
24 public AssemblyLocationProvider (Assembly assembly, MonoSymbolFile symbolFile, string seqPointDataPath)
26 this.assembly = assembly;
27 this.symbolFile = symbolFile;
28 this.seqPointDataPath = seqPointDataPath;
31 public bool TryGetLocation (string methodStr, string typeFullName, int offset, bool isOffsetIL, uint methodIndex, out Location location)
33 location = default (Location);
34 if (symbolFile == null)
37 var type = assembly.GetTypes().FirstOrDefault (t => t.FullName == typeFullName);
41 var bindingflags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
42 var method = type.GetMethods(bindingflags).FirstOrDefault (m => GetMethodFullName (m) == methodStr);
46 int ilOffset = (isOffsetIL)? offset : GetILOffsetFromFile (method.MetadataToken, methodIndex, offset);
50 var methodSymbol = symbolFile.Methods [(method.MetadataToken & 0x00ffffff) - 1];
52 var lineNumbers = methodSymbol.GetLineNumberTable ().LineNumbers;
53 var lineNumber = lineNumbers.FirstOrDefault (l => l.Offset >= ilOffset) ?? lineNumbers.Last ();
55 location.FileName = symbolFile.Sources [lineNumber.File-1].FileName;
56 location.Line = lineNumber.Row;
60 static MethodInfo methodGetIL;
61 private int GetILOffsetFromFile (int methodToken, uint methodIndex, int nativeOffset)
63 if (string.IsNullOrEmpty (seqPointDataPath))
66 if (methodGetIL == null)
67 methodGetIL = typeof (StackFrame).GetMethod ("GetILOffsetFromFile", BindingFlags.NonPublic | BindingFlags.Static);
69 if (methodGetIL == null)
70 throw new Exception ("System.Diagnostics.StackFrame.GetILOffsetFromFile could not be found, make sure you have an updated mono installed.");
72 return (int) methodGetIL.Invoke (null, new object[] {seqPointDataPath, methodToken, methodIndex, nativeOffset});
75 static MethodInfo methodGetMethodFullName;
76 private string GetMethodFullName (MethodBase m)
79 if (methodGetMethodFullName == null)
80 methodGetMethodFullName = typeof (StackTrace).GetMethod ("GetFullNameForStackTrace", BindingFlags.NonPublic | BindingFlags.Static);
82 if (methodGetMethodFullName == null)
83 throw new Exception ("System.Exception.GetFullNameForStackTrace could not be found, make sure you have an updated mono installed.");
85 StringBuilder sb = new StringBuilder ();
86 methodGetMethodFullName.Invoke (null, new object[] {sb, m});
88 return sb.ToString ();
92 Dictionary<string, AssemblyLocationProvider> assemblies;
93 HashSet<string> directories;
95 public LocationProvider () {
96 assemblies = new Dictionary<string, AssemblyLocationProvider> ();
97 directories = new HashSet<string> ();
100 public void AddAssembly (string assemblyPath)
102 assemblyPath = Path.GetFullPath (assemblyPath);
103 if (assemblies.ContainsKey (assemblyPath))
106 if (!File.Exists (assemblyPath))
107 throw new ArgumentException ("assemblyPath does not exist: "+ assemblyPath);
109 var assembly = Assembly.LoadFrom (assemblyPath);
110 MonoSymbolFile symbolFile = null;
112 var symbolPath = assemblyPath + ".mdb";
113 if (!File.Exists (symbolPath))
114 Debug.WriteLine (".mdb file was not found for " + assemblyPath);
116 symbolFile = MonoSymbolFile.ReadSymbolFile (assemblyPath + ".mdb");
118 var seqPointDataPath = assemblyPath + ".msym";
119 if (!File.Exists (seqPointDataPath))
120 seqPointDataPath = null;
122 assemblies.Add (assemblyPath, new AssemblyLocationProvider (assembly, symbolFile, seqPointDataPath));
124 directories.Add (Path.GetDirectoryName (assemblyPath));
126 foreach (var assemblyRef in assembly.GetReferencedAssemblies ()) {
127 string refPath = null;
128 foreach (var dir in directories) {
129 refPath = Path.Combine (dir, assemblyRef.Name);
130 if (File.Exists (refPath))
132 refPath = Path.Combine (dir, assemblyRef.Name + ".dll");
133 if (File.Exists (refPath))
135 refPath = Path.Combine (dir, assemblyRef.Name + ".exe");
136 if (File.Exists (refPath))
141 AddAssembly (refPath);
145 public void AddDirectory (string directory)
147 directory = Path.GetFullPath (directory);
148 if (!Directory.Exists (directory)) {
149 Console.Error.WriteLine ("Directory " + directory + " does not exist.");
153 directories.Add (directory);
156 public bool TryGetLocation (string method, string typeFullName, int offset, bool isOffsetIL, uint methodIndex, out Location location)
158 location = default (Location);
159 foreach (var assembly in assemblies.Values) {
160 if (assembly.TryGetLocation (method, typeFullName, offset, isOffsetIL, methodIndex, out location))