Merge pull request #4152 from BrzVlad/misc-gc-altstack
[mono.git] / mcs / tools / mono-symbolicate / LocationProvider.cs
index 2fc8018c7b492d35bf26e46193ca5acadfc1d5b7..f03adfdee58e7b86d9292f53eb28e1bcfdf4ee6f 100644 (file)
@@ -2,166 +2,356 @@ using System;
 using System.IO;
 using System.Linq;
 using System.Text;
-using System.Reflection;
-using System.Diagnostics;
 using System.Collections.Generic;
 using Mono.Cecil;
-using Mono.CompilerServices.SymbolWriter;
+using Mono.Cecil.Cil;
+using Mono.Collections.Generic;
 
-namespace Symbolicate
+namespace Mono
 {
-       struct Location {
-               public string FileName;
-               public int Line;
-       }
+       class AssemblyLocationProvider
+       {
+               AssemblyDefinition assembly;
+               Logger logger;
 
-       class LocationProvider {
-               class AssemblyLocationProvider {
-                       Assembly assembly;
-                       MonoSymbolFile symbolFile;
-                       string seqPointDataPath;
+               public AssemblyLocationProvider (string assemblyPath, Logger logger)
+               {
+                       assemblyPath = Path.GetFullPath (assemblyPath);
+                       this.logger = logger;
 
-                       public AssemblyLocationProvider (Assembly assembly, MonoSymbolFile symbolFile, string seqPointDataPath)
-                       {
-                               this.assembly = assembly;
-                               this.symbolFile = symbolFile;
-                               this.seqPointDataPath = seqPointDataPath;
-                       }
+                       if (!File.Exists (assemblyPath))
+                               throw new ArgumentException ("assemblyPath does not exist: "+ assemblyPath);
 
-                       public bool TryGetLocation (string methodStr, string typeFullName, int offset, bool isOffsetIL, uint methodIndex, out Location location)
-                       {
-                               location = default (Location);
-                               if (symbolFile == null)
-                                       return false;
+                       var readerParameters = new ReaderParameters { ReadSymbols = true };
+                       assembly = AssemblyDefinition.ReadAssembly (assemblyPath, readerParameters);
+               }
 
-                               var type = assembly.GetTypes().FirstOrDefault (t => t.FullName == typeFullName);
-                               if (type == null)
-                                       return false;
+               public bool TryResolveLocation (StackFrameData sfData, SeqPointInfo seqPointInfo)
+               {
+                       if (!assembly.MainModule.HasSymbols)
+                               return false;
 
-                               var bindingflags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
-                               var method = type.GetMethods(bindingflags).FirstOrDefault (m => GetMethodFullName (m) == methodStr);
-                               if (method == null)
-                                       return false;
+                       TypeDefinition type = null;
+                       string[] nested;
+                       if (sfData.TypeFullName.IndexOf ('/') >= 0)
+                                nested = sfData.TypeFullName.Split ('/');
+                       else
+                               nested = sfData.TypeFullName.Split ('+');
+
+                       var types = assembly.MainModule.Types;
+                       foreach (var ntype in nested) {
+                               if (type == null) {
+                                       // Use namespace first time.
+                                       type = types.FirstOrDefault (t => t.FullName == ntype);
+                               } else {
+                                       type = types.FirstOrDefault (t => t.Name == ntype);
+                               }
 
-                               int ilOffset = (isOffsetIL)? offset : GetILOffsetFromFile (method.MetadataToken, methodIndex, offset);
-                               if (ilOffset < 0)
+                               if (type == null) {
+                                       logger.LogWarning ("Could not find type: {0}", ntype);
                                        return false;
+                               }
+
+                               types = type.NestedTypes;
+                       }
 
-                               var methodSymbol = symbolFile.Methods [(method.MetadataToken & 0x00ffffff) - 1];
+                       var parensStart = sfData.MethodSignature.IndexOf ('(');
+                       var methodName = sfData.MethodSignature.Substring (0, parensStart).TrimEnd ();
+                       var methodParameters = sfData.MethodSignature.Substring (parensStart);
+                       var methods = type.Methods.Where (m => CompareName (m, methodName) && CompareParameters (m.Parameters, methodParameters)).ToArray ();
+                       if (methods.Length == 0) {
+                               logger.LogWarning ("Could not find method: {0}", methodName);
+                               return false;
+                       }
+                       if (methods.Length > 1) {
+                               logger.LogWarning ("Ambiguous match for method: {0}", sfData.MethodSignature);
+                               return false;
+                       }
+                       var method = methods [0];
 
-                               var lineNumbers = methodSymbol.GetLineNumberTable ().LineNumbers;
-                               var lineNumber = lineNumbers.FirstOrDefault (l => l.Offset >= ilOffset) ?? lineNumbers.Last ();
+                       int ilOffset;
+                       if (sfData.IsILOffset) {
+                               ilOffset = sfData.Offset;
+                       } else {
+                               if (seqPointInfo == null)
+                                       return false;
 
-                               location.FileName = symbolFile.Sources [lineNumber.File-1].FileName;
-                               location.Line = lineNumber.Row;
-                               return true;
+                               ilOffset = seqPointInfo.GetILOffset (method.MetadataToken.ToInt32 (), sfData.MethodIndex, sfData.Offset);
                        }
 
-                       static MethodInfo methodGetIL;
-                       private int GetILOffsetFromFile (int methodToken, uint methodIndex, int nativeOffset)
-                       {
-                               if (string.IsNullOrEmpty (seqPointDataPath))
-                                       return -1;
+                       if (ilOffset < 0)
+                               return false;
 
-                               if (methodGetIL == null)
-                                       methodGetIL = typeof (StackFrame).GetMethod ("GetILOffsetFromFile", BindingFlags.NonPublic | BindingFlags.Static);
+                       if (!method.DebugInformation.HasSequencePoints)
+                               return false;
 
-                               if (methodGetIL == null)
-                                       throw new Exception ("System.Diagnostics.StackFrame.GetILOffsetFromFile could not be found, make sure you have an updated mono installed.");
+                       SequencePoint prev = null;
+                       foreach (var sp in method.DebugInformation.SequencePoints.OrderBy (l => l.Offset)) {
+                               if (sp.Offset >= ilOffset) {
+                                       sfData.SetLocation (sp.Document.Url, sp.StartLine);
+                                       return true;
+                               }
 
-                               return (int) methodGetIL.Invoke (null, new object[] {seqPointDataPath, methodToken, methodIndex, nativeOffset});
+                               prev = sp;
+                       }
+
+                       if (prev != null) {
+                               sfData.SetLocation (prev.Document.Url, prev.StartLine);
+                               return true;
                        }
 
-                       static MethodInfo methodGetMethodFullName;
-                       private string GetMethodFullName (MethodBase m)
-                       {
+                       return false;
+               }
+
+               static bool CompareName (MethodDefinition candidate, string expected)
+               {
+                       if (candidate.Name == expected)
+                               return true;
 
-                               if (methodGetMethodFullName == null)
-                                       methodGetMethodFullName = typeof (StackTrace).GetMethod ("GetFullNameForStackTrace", BindingFlags.NonPublic | BindingFlags.Static);
+                       if (!candidate.HasGenericParameters)
+                               return false;
 
-                               if (methodGetMethodFullName == null)
-                                       throw new Exception ("System.Exception.GetFullNameForStackTrace could not be found, make sure you have an updated mono installed.");
+                       var genStart = expected.IndexOf ('[');
+                       if (genStart < 0)
+                               genStart = expected.IndexOf ('<');
 
-                               StringBuilder sb = new StringBuilder ();
-                               methodGetMethodFullName.Invoke (null, new object[] {sb, m});
+                       if (genStart < 0)
+                               return false;
 
-                               return sb.ToString ();
+                       if (candidate.Name != expected.Substring (0, genStart))
+                               return false;
+
+                       int arity = 1;
+                       for (int pos = genStart; pos < expected.Length; ++pos) {
+                               if (expected [pos] == ',')
+                                       ++arity;
                        }
+
+                       return candidate.GenericParameters.Count == arity;
                }
 
-               Dictionary<string, AssemblyLocationProvider> assemblies;
-               HashSet<string> directories;
+               static string RemoveGenerics (string expected, char open, char close)
+               {
+                       if (expected.IndexOf (open) < 0)
+                               return expected;
+
+                       var sb = new StringBuilder ();
+                       for (int i = 0; i < expected.Length;) {
+                               int start = expected.IndexOf (open, i);
+                               int end = expected.IndexOf (close, i);
+                               if (start < 0 || end < 0) {
+                                       sb.Append (expected, i, expected.Length - i);
+                                       break;
+                               }
 
-               public LocationProvider () {
-                       assemblies = new Dictionary<string, AssemblyLocationProvider> ();
-                       directories = new HashSet<string> ();
+                               bool is_ginst = false;
+                               for (int j = start + 1; j < end; ++j) {
+                                       if (expected [j] != ',')
+                                               is_ginst = true;
+                               }
+
+                               if (is_ginst) //discard the the generic args
+                                       sb.Append (expected, i, start - i);
+                               else //include array arity
+                                       sb.Append (expected, i, end + 1 - i);
+                               i = end + 1;
+
+                       }
+                       return sb.ToString ();
                }
 
-               public void AddAssembly (string assemblyPath)
+               static bool CompareParameters (Collection<ParameterDefinition> candidate, string expected)
                {
-                       assemblyPath = Path.GetFullPath (assemblyPath);
-                       if (assemblies.ContainsKey (assemblyPath))
-                               return;
+                       var builder = new StringBuilder ();
+                       builder.Append ("(");
 
-                       if (!File.Exists (assemblyPath))
-                               throw new ArgumentException ("assemblyPath does not exist: "+ assemblyPath);
+                       for (int i = 0; i < candidate.Count; i++) {
+                               var parameter = candidate [i];
+                               if (i > 0)
+                                       builder.Append (", ");
 
-                       var assembly = Assembly.LoadFrom (assemblyPath);
-                       MonoSymbolFile symbolFile = null;
+                               if (parameter.ParameterType.IsSentinel)
+                                       builder.Append ("...,");
 
-                       var symbolPath = assemblyPath + ".mdb";
-                       if (!File.Exists (symbolPath))
-                               Debug.WriteLine (".mdb file was not found for " + assemblyPath);
-                       else
-                               symbolFile = MonoSymbolFile.ReadSymbolFile (assemblyPath + ".mdb");
-
-                       var seqPointDataPath = assemblyPath + ".msym";
-                       if (!File.Exists (seqPointDataPath))
-                               seqPointDataPath = null;
-
-                       assemblies.Add (assemblyPath, new AssemblyLocationProvider (assembly, symbolFile, seqPointDataPath));
-
-                       directories.Add (Path.GetDirectoryName (assemblyPath));
-
-                       foreach (var assemblyRef in assembly.GetReferencedAssemblies ()) {
-                               string refPath = null;
-                               foreach (var dir in directories) {
-                                       refPath = Path.Combine (dir, assemblyRef.Name);
-                                       if (File.Exists (refPath))
-                                               break;
-                                       refPath = Path.Combine (dir, assemblyRef.Name + ".dll");
-                                       if (File.Exists (refPath))
-                                               break;
-                                       refPath = Path.Combine (dir, assemblyRef.Name + ".exe");
-                                       if (File.Exists (refPath))
-                                               break;
-                                       refPath = null;
-                               }
-                               if (refPath != null)
-                                       AddAssembly (refPath);
+                               var pt = parameter.ParameterType;
+                               FormatElementType (pt, builder);
+
+                               builder.Append (" ");
+                               builder.Append (parameter.Name);
                        }
+
+                       builder.Append (")");
+
+                       if (builder.ToString () == RemoveGenerics (expected, '[', ']'))
+                               return true;
+
+                       //now try the compact runtime format.
+
+                       builder.Clear ();
+
+                       builder.Append ("(");
+
+                       for (int i = 0; i < candidate.Count; i++) {
+                               var parameter = candidate [i];
+                               if (i > 0)
+                                       builder.Append (",");
+
+                               if (parameter.ParameterType.IsSentinel)
+                                       builder.Append ("...,");
+
+                               var pt = parameter.ParameterType;
+
+                               RuntimeFormatElementType (pt, builder);
+                       }
+
+                       builder.Append (")");
+
+                       if (builder.ToString () == RemoveGenerics (expected, '<', '>'))
+                               return true;
+                       return false;
+
                }
 
-               public void AddDirectory (string directory)
+               static void RuntimeFormatElementType (TypeReference tr, StringBuilder builder)
                {
-                       directory = Path.GetFullPath (directory);
-                       if (!Directory.Exists (directory)) {
-                               Console.Error.WriteLine ("Directory " + directory + " does not exist.");
-                               return;
+                       var ts = tr as TypeSpecification;
+                       if (ts != null) {
+                               if (ts.IsByReference) {
+                                       RuntimeFormatElementType (ts.ElementType, builder);
+                                       builder.Append ("&");
+                                       return;
+                               }
+                       }
+
+                       switch (tr.MetadataType) {
+                       case MetadataType.Void:
+                               builder.Append ("void");
+                               break;
+                       case MetadataType.Boolean:
+                               builder.Append ("bool");
+                               break;
+                       case MetadataType.Char:
+                               builder.Append ("char");
+                               break;
+                       case MetadataType.SByte:
+                               builder.Append ("sbyte");
+                               break;
+                       case MetadataType.Byte:
+                               builder.Append ("byte");
+                               break;
+                       case MetadataType.Int16:
+                               builder.Append ("int16");
+                               break;
+                       case MetadataType.UInt16:
+                               builder.Append ("uint16");
+                               break;
+                       case MetadataType.Int32:
+                               builder.Append ("int");
+                               break;
+                       case MetadataType.UInt32:
+                               builder.Append ("uint");
+                               break;
+                       case MetadataType.Int64:
+                               builder.Append ("long");
+                               break;
+                       case MetadataType.UInt64:
+                               builder.Append ("ulong");
+                               break;
+                       case MetadataType.Single:
+                               builder.Append ("single");
+                               break;
+                       case MetadataType.Double:
+                               builder.Append ("double");
+                               break;
+                       case MetadataType.String:
+                               builder.Append ("string");
+                               break;
+                       case MetadataType.Pointer:
+                               builder.Append (((TypeSpecification)tr).ElementType);
+                               builder.Append ("*");
+                               break;
+                       case MetadataType.ValueType:
+                       case MetadataType.Class:
+                       case MetadataType.GenericInstance: {
+                               FormatName (tr, builder, '/');
+                               break;
+                       }
+                       case MetadataType.Var:
+                       case MetadataType.MVar:
+                               builder.Append (tr.Name);
+                               builder.Append ("_REF");
+                               break;
+                       case MetadataType.Array: {
+                               var array = (ArrayType)tr;
+                               RuntimeFormatElementType (array.ElementType, builder);
+                               builder.Append ("[");
+
+                               for (int i = 0; i < array.Rank - 1; ++i)
+                                       builder.Append (",");
+
+                               builder.Append ("]");
+                               break;
                        }
 
-                       directories.Add (directory);
+                       case MetadataType.TypedByReference:
+                               builder.Append ("typedbyref");
+                               break;
+                       case MetadataType.IntPtr:
+                               builder.Append ("intptr");
+                               break;
+                       case MetadataType.UIntPtr:
+                               builder.Append ("uintptr");
+                               break;
+                       case MetadataType.FunctionPointer:
+                               builder.Append ("*()");
+                               break;
+                       case MetadataType.Object:
+                               builder.Append ("object");
+                               break;
+                       default:
+                               builder.Append ("-unknown-");
+                               break;
+                       }
                }
 
-               public bool TryGetLocation (string method, string typeFullName, int offset, bool isOffsetIL, uint methodIndex, out Location location)
+               static void FormatName (TypeReference tr, StringBuilder builder, char sep)
                {
-                       location = default (Location);
-                       foreach (var assembly in assemblies.Values) {
-                               if (assembly.TryGetLocation (method, typeFullName, offset, isOffsetIL, methodIndex, out location))
-                                       return true;
+                       if (tr.IsNested && !(tr.MetadataType == MetadataType.Var || tr.MetadataType == MetadataType.MVar)) {
+                               FormatName (tr.DeclaringType, builder, sep);
+                               builder.Append (sep);
+                       }
+                       if (!string.IsNullOrEmpty (tr.Namespace)) {
+                               builder.Append (tr.Namespace);
+                               builder.Append (".");
                        }
 
-                       return false;
+                       builder.Append (tr.Name);
+               }
+
+               static void FormatElementType (TypeReference tr, StringBuilder builder)
+               {
+                       var ts = tr as TypeSpecification;
+                       if (ts != null) {
+                               if (ts.IsByReference) {
+                                       FormatElementType (ts.ElementType, builder);
+                                       builder.Append ("&");
+                                       return;
+                               }
+
+                               var array = ts as ArrayType;
+                               if (array != null) {
+                                       FormatElementType (ts.ElementType, builder);
+                                       builder.Append ("[");
+
+                                       for (int ii = 0; ii < array.Rank - 1; ++ii) {
+                                               builder.Append (",");
+                                       }
+
+                                       builder.Append ("]");
+                                       return;
+                               }
+                       }
+                       FormatName (tr, builder, '+');
                }
        }
 }