Update COPYING.LIB
[mono.git] / mcs / tools / mono-symbolicate / LocationProvider.cs
1 using System;
2 using System.IO;
3 using System.Linq;
4 using System.Text;
5 using System.Reflection;
6 using System.Diagnostics;
7 using System.Collections.Generic;
8 using Mono.Cecil;
9 using Mono.CompilerServices.SymbolWriter;
10
11 namespace Symbolicate
12 {
13         struct Location {
14                 public string FileName;
15                 public int Line;
16         }
17
18         class LocationProvider {
19                 class AssemblyLocationProvider {
20                         Assembly assembly;
21                         MonoSymbolFile symbolFile;
22                         string seqPointDataPath;
23
24                         public AssemblyLocationProvider (Assembly assembly, MonoSymbolFile symbolFile, string seqPointDataPath)
25                         {
26                                 this.assembly = assembly;
27                                 this.symbolFile = symbolFile;
28                                 this.seqPointDataPath = seqPointDataPath;
29                         }
30
31                         public bool TryGetLocation (string methodStr, string typeFullName, int offset, bool isOffsetIL, uint methodIndex, out Location location)
32                         {
33                                 location = default (Location);
34                                 if (symbolFile == null)
35                                         return false;
36
37                                 var type = assembly.GetTypes().FirstOrDefault (t => t.FullName == typeFullName);
38                                 if (type == null)
39                                         return false;
40
41                                 var bindingflags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
42                                 var method = type.GetMethods(bindingflags).FirstOrDefault (m => GetMethodFullName (m) == methodStr);
43                                 if (method == null)
44                                         return false;
45
46                                 int ilOffset = (isOffsetIL)? offset : GetILOffsetFromFile (method.MetadataToken, methodIndex, offset);
47                                 if (ilOffset < 0)
48                                         return false;
49
50                                 var methodSymbol = symbolFile.Methods [(method.MetadataToken & 0x00ffffff) - 1];
51
52                                 var lineNumbers = methodSymbol.GetLineNumberTable ().LineNumbers;
53                                 var lineNumber = lineNumbers.FirstOrDefault (l => l.Offset >= ilOffset) ?? lineNumbers.Last ();
54
55                                 location.FileName = symbolFile.Sources [lineNumber.File-1].FileName;
56                                 location.Line = lineNumber.Row;
57                                 return true;
58                         }
59
60                         static MethodInfo methodGetIL;
61                         private int GetILOffsetFromFile (int methodToken, uint methodIndex, int nativeOffset)
62                         {
63                                 if (string.IsNullOrEmpty (seqPointDataPath))
64                                         return -1;
65
66                                 if (methodGetIL == null)
67                                         methodGetIL = typeof (StackFrame).GetMethod ("GetILOffsetFromFile", BindingFlags.NonPublic | BindingFlags.Static);
68
69                                 if (methodGetIL == null)
70                                         throw new Exception ("System.Diagnostics.StackFrame.GetILOffsetFromFile could not be found, make sure you have an updated mono installed.");
71
72                                 return (int) methodGetIL.Invoke (null, new object[] {seqPointDataPath, methodToken, methodIndex, nativeOffset});
73                         }
74
75                         static MethodInfo methodGetMethodFullName;
76                         private string GetMethodFullName (MethodBase m)
77                         {
78
79                                 if (methodGetMethodFullName == null)
80                                         methodGetMethodFullName = typeof (StackTrace).GetMethod ("GetFullNameForStackTrace", BindingFlags.NonPublic | BindingFlags.Static);
81
82                                 if (methodGetMethodFullName == null)
83                                         throw new Exception ("System.Exception.GetFullNameForStackTrace could not be found, make sure you have an updated mono installed.");
84
85                                 StringBuilder sb = new StringBuilder ();
86                                 methodGetMethodFullName.Invoke (null, new object[] {sb, m});
87
88                                 return sb.ToString ();
89                         }
90                 }
91
92                 Dictionary<string, AssemblyLocationProvider> assemblies;
93                 HashSet<string> directories;
94
95                 public LocationProvider () {
96                         assemblies = new Dictionary<string, AssemblyLocationProvider> ();
97                         directories = new HashSet<string> ();
98                 }
99
100                 public void AddAssembly (string assemblyPath)
101                 {
102                         assemblyPath = Path.GetFullPath (assemblyPath);
103                         if (assemblies.ContainsKey (assemblyPath))
104                                 return;
105
106                         if (!File.Exists (assemblyPath))
107                                 throw new ArgumentException ("assemblyPath does not exist: "+ assemblyPath);
108
109                         var assembly = Assembly.LoadFrom (assemblyPath);
110                         MonoSymbolFile symbolFile = null;
111
112                         var symbolPath = assemblyPath + ".mdb";
113                         if (!File.Exists (symbolPath))
114                                 Debug.WriteLine (".mdb file was not found for " + assemblyPath);
115                         else
116                                 symbolFile = MonoSymbolFile.ReadSymbolFile (assemblyPath + ".mdb");
117
118                         var seqPointDataPath = assemblyPath + ".msym";
119                         if (!File.Exists (seqPointDataPath))
120                                 seqPointDataPath = null;
121
122                         assemblies.Add (assemblyPath, new AssemblyLocationProvider (assembly, symbolFile, seqPointDataPath));
123
124                         directories.Add (Path.GetDirectoryName (assemblyPath));
125
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))
131                                                 break;
132                                         refPath = Path.Combine (dir, assemblyRef.Name + ".dll");
133                                         if (File.Exists (refPath))
134                                                 break;
135                                         refPath = Path.Combine (dir, assemblyRef.Name + ".exe");
136                                         if (File.Exists (refPath))
137                                                 break;
138                                         refPath = null;
139                                 }
140                                 if (refPath != null)
141                                         AddAssembly (refPath);
142                         }
143                 }
144
145                 public void AddDirectory (string directory)
146                 {
147                         directory = Path.GetFullPath (directory);
148                         if (!Directory.Exists (directory)) {
149                                 Console.Error.WriteLine ("Directory " + directory + " does not exist.");
150                                 return;
151                         }
152
153                         directories.Add (directory);
154                 }
155
156                 public bool TryGetLocation (string method, string typeFullName, int offset, bool isOffsetIL, uint methodIndex, out Location location)
157                 {
158                         location = default (Location);
159                         foreach (var assembly in assemblies.Values) {
160                                 if (assembly.TryGetLocation (method, typeFullName, offset, isOffsetIL, methodIndex, out location))
161                                         return true;
162                         }
163
164                         return false;
165                 }
166         }
167 }
168