Merge pull request #4840 from kumpera/unaligned-access
[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.Collections.Generic;
6 using Mono.Cecil;
7 using Mono.Cecil.Cil;
8 using Mono.Collections.Generic;
9
10 namespace Mono
11 {
12         class AssemblyLocationProvider
13         {
14                 string assemblyFullPath;
15                 Logger logger;
16
17                 public AssemblyLocationProvider (string assemblyPath, Logger logger)
18                 {
19                         assemblyPath = Path.GetFullPath (assemblyPath);
20                         this.logger = logger;
21
22                         if (!File.Exists (assemblyPath))
23                                 throw new ArgumentException ("assemblyPath does not exist: "+ assemblyPath);
24
25                         assemblyFullPath = assemblyPath;
26                 }
27
28                 public bool TryResolveLocation (StackFrameData sfData, SeqPointInfo seqPointInfo)
29                 {
30                         var readerParameters = new ReaderParameters { ReadSymbols = true };
31                         using (var assembly = AssemblyDefinition.ReadAssembly (assemblyFullPath, readerParameters)) {
32
33                                 if (!assembly.MainModule.HasSymbols)
34                                         return false;
35
36                                 TypeDefinition type = null;
37                                 string[] nested;
38                                 if (sfData.TypeFullName.IndexOf ('/') >= 0)
39                                         nested = sfData.TypeFullName.Split ('/');
40                                 else
41                                         nested = sfData.TypeFullName.Split ('+');
42
43                                 var types = assembly.MainModule.Types;
44                                 foreach (var ntype in nested) {
45                                         if (type == null) {
46                                                 // Use namespace first time.
47                                                 type = types.FirstOrDefault (t => t.FullName == ntype);
48                                         } else {
49                                                 type = types.FirstOrDefault (t => t.Name == ntype);
50                                         }
51
52                                         if (type == null) {
53                                                 logger.LogWarning ("Could not find type: {0}", ntype);
54                                                 return false;
55                                         }
56
57                                         types = type.NestedTypes;
58                                 }
59
60                                 var parensStart = sfData.MethodSignature.IndexOf ('(');
61                                 var methodName = sfData.MethodSignature.Substring (0, parensStart).TrimEnd ();
62                                 var methodParameters = sfData.MethodSignature.Substring (parensStart);
63                                 var methods = type.Methods.Where (m => CompareName (m, methodName) && CompareParameters (m.Parameters, methodParameters)).ToArray ();
64                                 if (methods.Length == 0) {
65                                         logger.LogWarning ("Could not find method: {0}", methodName);
66                                         return false;
67                                 }
68                                 if (methods.Length > 1) {
69                                         logger.LogWarning ("Ambiguous match for method: {0}", sfData.MethodSignature);
70                                         return false;
71                                 }
72                                 var method = methods [0];
73
74                                 int ilOffset;
75                                 if (sfData.IsILOffset) {
76                                         ilOffset = sfData.Offset;
77                                 } else {
78                                         if (seqPointInfo == null)
79                                                 return false;
80
81                                         ilOffset = seqPointInfo.GetILOffset (method.MetadataToken.ToInt32 (), sfData.MethodIndex, sfData.Offset);
82                                 }
83
84                                 if (ilOffset < 0)
85                                         return false;
86
87                                 if (!method.DebugInformation.HasSequencePoints)
88                                         return false;
89
90                                 SequencePoint prev = null;
91                                 foreach (var sp in method.DebugInformation.SequencePoints.OrderBy (l => l.Offset)) {
92                                         if (sp.Offset >= ilOffset) {
93                                                 sfData.SetLocation (sp.Document.Url, sp.StartLine);
94                                                 return true;
95                                         }
96
97                                         prev = sp;
98                                 }
99
100                                 if (prev != null) {
101                                         sfData.SetLocation (prev.Document.Url, prev.StartLine);
102                                         return true;
103                                 }
104
105                                 return false;
106                         }
107                 }
108
109                 static bool CompareName (MethodDefinition candidate, string expected)
110                 {
111                         if (candidate.Name == expected)
112                                 return true;
113
114                         if (!candidate.HasGenericParameters)
115                                 return false;
116
117                         var genStart = expected.IndexOf ('[');
118                         if (genStart < 0)
119                                 genStart = expected.IndexOf ('<');
120
121                         if (genStart < 0)
122                                 return false;
123
124                         if (candidate.Name != expected.Substring (0, genStart))
125                                 return false;
126
127                         int arity = 1;
128                         for (int pos = genStart; pos < expected.Length; ++pos) {
129                                 if (expected [pos] == ',')
130                                         ++arity;
131                         }
132
133                         return candidate.GenericParameters.Count == arity;
134                 }
135
136                 static string RemoveGenerics (string expected, char open, char close)
137                 {
138                         if (expected.IndexOf (open) < 0)
139                                 return expected;
140
141                         var sb = new StringBuilder ();
142                         for (int i = 0; i < expected.Length;) {
143                                 int start = expected.IndexOf (open, i);
144                                 int end = expected.IndexOf (close, i);
145                                 if (start < 0 || end < 0) {
146                                         sb.Append (expected, i, expected.Length - i);
147                                         break;
148                                 }
149
150                                 bool is_ginst = false;
151                                 for (int j = start + 1; j < end; ++j) {
152                                         if (expected [j] != ',')
153                                                 is_ginst = true;
154                                 }
155
156                                 if (is_ginst) //discard the the generic args
157                                         sb.Append (expected, i, start - i);
158                                 else //include array arity
159                                         sb.Append (expected, i, end + 1 - i);
160                                 i = end + 1;
161
162                         }
163                         return sb.ToString ();
164                 }
165
166                 static bool CompareParameters (Collection<ParameterDefinition> candidate, string expected)
167                 {
168                         var builder = new StringBuilder ();
169                         builder.Append ("(");
170
171                         for (int i = 0; i < candidate.Count; i++) {
172                                 var parameter = candidate [i];
173                                 if (i > 0)
174                                         builder.Append (", ");
175
176                                 if (parameter.ParameterType.IsSentinel)
177                                         builder.Append ("...,");
178
179                                 var pt = parameter.ParameterType;
180                                 FormatElementType (pt, builder);
181
182                                 builder.Append (" ");
183                                 builder.Append (parameter.Name);
184                         }
185
186                         builder.Append (")");
187
188                         if (builder.ToString () == RemoveGenerics (expected, '[', ']'))
189                                 return true;
190
191                         //now try the compact runtime format.
192
193                         builder.Clear ();
194
195                         builder.Append ("(");
196
197                         for (int i = 0; i < candidate.Count; i++) {
198                                 var parameter = candidate [i];
199                                 if (i > 0)
200                                         builder.Append (",");
201
202                                 if (parameter.ParameterType.IsSentinel)
203                                         builder.Append ("...,");
204
205                                 var pt = parameter.ParameterType;
206
207                                 RuntimeFormatElementType (pt, builder);
208                         }
209
210                         builder.Append (")");
211
212                         if (builder.ToString () == RemoveGenerics (expected, '<', '>'))
213                                 return true;
214                         return false;
215
216                 }
217
218                 static void RuntimeFormatElementType (TypeReference tr, StringBuilder builder)
219                 {
220                         var ts = tr as TypeSpecification;
221                         if (ts != null) {
222                                 if (ts.IsByReference) {
223                                         RuntimeFormatElementType (ts.ElementType, builder);
224                                         builder.Append ("&");
225                                         return;
226                                 }
227                         }
228
229                         switch (tr.MetadataType) {
230                         case MetadataType.Void:
231                                 builder.Append ("void");
232                                 break;
233                         case MetadataType.Boolean:
234                                 builder.Append ("bool");
235                                 break;
236                         case MetadataType.Char:
237                                 builder.Append ("char");
238                                 break;
239                         case MetadataType.SByte:
240                                 builder.Append ("sbyte");
241                                 break;
242                         case MetadataType.Byte:
243                                 builder.Append ("byte");
244                                 break;
245                         case MetadataType.Int16:
246                                 builder.Append ("int16");
247                                 break;
248                         case MetadataType.UInt16:
249                                 builder.Append ("uint16");
250                                 break;
251                         case MetadataType.Int32:
252                                 builder.Append ("int");
253                                 break;
254                         case MetadataType.UInt32:
255                                 builder.Append ("uint");
256                                 break;
257                         case MetadataType.Int64:
258                                 builder.Append ("long");
259                                 break;
260                         case MetadataType.UInt64:
261                                 builder.Append ("ulong");
262                                 break;
263                         case MetadataType.Single:
264                                 builder.Append ("single");
265                                 break;
266                         case MetadataType.Double:
267                                 builder.Append ("double");
268                                 break;
269                         case MetadataType.String:
270                                 builder.Append ("string");
271                                 break;
272                         case MetadataType.Pointer:
273                                 builder.Append (((TypeSpecification)tr).ElementType);
274                                 builder.Append ("*");
275                                 break;
276                         case MetadataType.ValueType:
277                         case MetadataType.Class:
278                         case MetadataType.GenericInstance: {
279                                 FormatName (tr, builder, '/');
280                                 break;
281                         }
282                         case MetadataType.Var:
283                         case MetadataType.MVar:
284                                 builder.Append (tr.Name);
285                                 builder.Append ("_REF");
286                                 break;
287                         case MetadataType.Array: {
288                                 var array = (ArrayType)tr;
289                                 RuntimeFormatElementType (array.ElementType, builder);
290                                 builder.Append ("[");
291
292                                 for (int i = 0; i < array.Rank - 1; ++i)
293                                         builder.Append (",");
294
295                                 builder.Append ("]");
296                                 break;
297                         }
298
299                         case MetadataType.TypedByReference:
300                                 builder.Append ("typedbyref");
301                                 break;
302                         case MetadataType.IntPtr:
303                                 builder.Append ("intptr");
304                                 break;
305                         case MetadataType.UIntPtr:
306                                 builder.Append ("uintptr");
307                                 break;
308                         case MetadataType.FunctionPointer:
309                                 builder.Append ("*()");
310                                 break;
311                         case MetadataType.Object:
312                                 builder.Append ("object");
313                                 break;
314                         default:
315                                 builder.Append ("-unknown-");
316                                 break;
317                         }
318                 }
319
320                 static void FormatName (TypeReference tr, StringBuilder builder, char sep)
321                 {
322                         if (tr.IsNested && !(tr.MetadataType == MetadataType.Var || tr.MetadataType == MetadataType.MVar)) {
323                                 FormatName (tr.DeclaringType, builder, sep);
324                                 builder.Append (sep);
325                         }
326                         if (!string.IsNullOrEmpty (tr.Namespace)) {
327                                 builder.Append (tr.Namespace);
328                                 builder.Append (".");
329                         }
330
331                         builder.Append (tr.Name);
332                 }
333
334                 static void FormatElementType (TypeReference tr, StringBuilder builder)
335                 {
336                         var ts = tr as TypeSpecification;
337                         if (ts != null) {
338                                 if (ts.IsByReference) {
339                                         FormatElementType (ts.ElementType, builder);
340                                         builder.Append ("&");
341                                         return;
342                                 }
343
344                                 var array = ts as ArrayType;
345                                 if (array != null) {
346                                         FormatElementType (ts.ElementType, builder);
347                                         builder.Append ("[");
348
349                                         for (int ii = 0; ii < array.Rank - 1; ++ii) {
350                                                 builder.Append (",");
351                                         }
352
353                                         builder.Append ("]");
354                                         return;
355                                 }
356                         }
357                         FormatName (tr, builder, '+');
358                 }
359         }
360 }
361