cad20ae8e16f6bdb6e073e1a10ef447851605493
[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 using System.Runtime.InteropServices;
11
12 namespace Symbolicate
13 {
14         struct Location {
15                 public string FileName;
16                 public int Line;
17         }
18
19         class LocationProvider {
20                 class AssemblyLocationProvider {
21                         Assembly assembly;
22                         MonoSymbolFile symbolFile;
23                         string seqPointDataPath;
24
25                         public AssemblyLocationProvider (Assembly assembly, MonoSymbolFile symbolFile, string seqPointDataPath)
26                         {
27                                 this.assembly = assembly;
28                                 this.symbolFile = symbolFile;
29                                 this.seqPointDataPath = seqPointDataPath;
30                         }
31
32                         public bool TryGetLocation (string methodStr, string typeFullName, int offset, bool isOffsetIL, uint methodIndex, out Location location)
33                         {
34                                 location = default (Location);
35                                 if (symbolFile == null)
36                                         return false;
37
38                                 var type = assembly.GetTypes().FirstOrDefault (t => t.FullName == typeFullName);
39                                 if (type == null)
40                                         return false;
41
42                                 var bindingflags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
43                                 var method = type.GetMethods(bindingflags).FirstOrDefault (m => GetMethodFullName (m) == methodStr);
44                                 if (method == null)
45                                         return false;
46
47                                 int ilOffset = (isOffsetIL)? offset : GetILOffsetFromFile (method.MetadataToken, methodIndex, offset);
48                                 if (ilOffset < 0)
49                                         return false;
50
51                                 var methodSymbol = symbolFile.Methods [(method.MetadataToken & 0x00ffffff) - 1];
52
53                                 var lineNumbers = methodSymbol.GetLineNumberTable ().LineNumbers;
54                                 var lineNumber = lineNumbers.FirstOrDefault (l => l.Offset >= ilOffset) ?? lineNumbers.Last ();
55
56                                 location.FileName = symbolFile.Sources [lineNumber.File-1].FileName;
57                                 location.Line = lineNumber.Row;
58                                 return true;
59                         }
60
61                         [DllImport("mono-symbolicate-native.dll")]
62                         private static extern bool mono_seq_point_data_get_il_offset (string path, uint method_token, uint method_index, uint native_offset, out uint il_offset);
63
64                         static MethodInfo methodGetIL;
65                         private int GetILOffsetFromFile (int methodToken, uint methodIndex, int nativeOffset)
66                         {
67                                 uint ilOffset;
68                                 mono_seq_point_data_get_il_offset (seqPointDataPath, (uint) methodToken, methodIndex, (uint) nativeOffset, out ilOffset);
69
70                                 return (int) ilOffset;
71                         }
72
73                         private string GetMethodFullName (MethodBase m)
74                         {
75                                 StringBuilder sb = new StringBuilder ();
76
77                                 StackTraceHelper.GetFullNameForStackTrace (sb, m);
78
79                                 return sb.ToString ();
80                         }
81                 }
82
83                 Dictionary<string, AssemblyLocationProvider> assemblies;
84                 HashSet<string> directories;
85
86                 public LocationProvider () {
87                         assemblies = new Dictionary<string, AssemblyLocationProvider> ();
88                         directories = new HashSet<string> ();
89                 }
90
91                 public void AddAssembly (string assemblyPath)
92                 {
93                         assemblyPath = Path.GetFullPath (assemblyPath);
94                         if (assemblies.ContainsKey (assemblyPath))
95                                 return;
96
97                         if (!File.Exists (assemblyPath))
98                                 throw new ArgumentException ("assemblyPath does not exist: "+ assemblyPath);
99
100                         var assembly = Assembly.ReflectionOnlyLoadFrom (assemblyPath);
101                         MonoSymbolFile symbolFile = null;
102
103                         var symbolPath = assemblyPath + ".mdb";
104                         if (!File.Exists (symbolPath))
105                                 Debug.WriteLine (".mdb file was not found for " + assemblyPath);
106                         else
107                                 symbolFile = MonoSymbolFile.ReadSymbolFile (assemblyPath + ".mdb");
108
109                         var seqPointDataPath = assemblyPath + ".msym";
110                         if (!File.Exists (seqPointDataPath))
111                                 seqPointDataPath = null;
112
113                         assemblies.Add (assemblyPath, new AssemblyLocationProvider (assembly, symbolFile, seqPointDataPath));
114
115                         directories.Add (Path.GetDirectoryName (assemblyPath));
116
117                         foreach (var assemblyRef in assembly.GetReferencedAssemblies ()) {
118                                 string refPath = null;
119                                 foreach (var dir in directories) {
120                                         refPath = Path.Combine (dir, assemblyRef.Name);
121                                         if (File.Exists (refPath))
122                                                 break;
123                                         refPath = Path.Combine (dir, assemblyRef.Name + ".dll");
124                                         if (File.Exists (refPath))
125                                                 break;
126                                         refPath = Path.Combine (dir, assemblyRef.Name + ".exe");
127                                         if (File.Exists (refPath))
128                                                 break;
129                                         refPath = null;
130                                 }
131                                 if (refPath != null)
132                                         AddAssembly (refPath);
133                         }
134                 }
135
136                 public void AddDirectory (string directory)
137                 {
138                         directory = Path.GetFullPath (directory);
139                         if (!Directory.Exists (directory)) {
140                                 Console.Error.WriteLine ("Directory " + directory + " does not exist.");
141                                 return;
142                         }
143
144                         directories.Add (directory);
145                 }
146
147                 public bool TryGetLocation (string method, string typeFullName, int offset, bool isOffsetIL, uint methodIndex, out Location location)
148                 {
149                         location = default (Location);
150                         foreach (var assembly in assemblies.Values) {
151                                 if (assembly.TryGetLocation (method, typeFullName, offset, isOffsetIL, methodIndex, out location))
152                                         return true;
153                         }
154
155                         return false;
156                 }
157         }
158 }
159