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