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