// // SoftDebuggerBacktrace.cs // // Authors: Lluis Sanchez Gual // Jeffrey Stedfast // // Copyright (c) 2009 Novell, Inc (http://www.novell.com) // Copyright (c) 2012 Xamarin Inc. (http://www.xamarin.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; using System.Linq; using System.Text; using System.Collections.Generic; using Mono.Debugging.Client; using Mono.Debugging.Backend; using MDB = Mono.Debugger.Soft; using DC = Mono.Debugging.Client; using Mono.Debugging.Evaluation; namespace Mono.Debugging.Soft { internal class SoftDebuggerStackFrame : Mono.Debugging.Client.StackFrame { public Mono.Debugger.Soft.StackFrame StackFrame { get; private set; } public SoftDebuggerStackFrame (Mono.Debugger.Soft.StackFrame frame, string addressSpace, SourceLocation location, string language, bool isExternalCode, bool hasDebugInfo, bool isDebuggerHidden, string fullModuleName, string fullTypeName) : base (frame.ILOffset, addressSpace, location, language, isExternalCode, hasDebugInfo, isDebuggerHidden, fullModuleName, fullTypeName) { StackFrame = frame; } } public class SoftDebuggerBacktrace: BaseBacktrace { readonly SoftDebuggerSession session; readonly MDB.ThreadMirror thread; readonly int stackVersion; MDB.StackFrame[] frames; public SoftDebuggerBacktrace (SoftDebuggerSession session, MDB.ThreadMirror thread): base (session.Adaptor) { this.session = session; this.thread = thread; stackVersion = session.StackVersion; if (thread != null) this.frames = thread.GetFrames (); else this.frames = new MDB.StackFrame[0]; } void ValidateStack () { if (stackVersion != session.StackVersion && thread != null) frames = thread.GetFrames (); } public override DC.StackFrame[] GetStackFrames (int firstIndex, int lastIndex) { ValidateStack (); if (lastIndex < 0) lastIndex = frames.Length - 1; List list = new List (); for (int n = firstIndex; n <= lastIndex && n < frames.Length; n++) list.Add (CreateStackFrame (frames[n], n)); return list.ToArray (); } public override int FrameCount { get { ValidateStack (); return frames.Length; } } DC.StackFrame CreateStackFrame (MDB.StackFrame frame, int frameIndex) { MDB.MethodMirror method = frame.Method; MDB.TypeMirror type = method.DeclaringType; string fileName = frame.FileName; string typeFullName = null; string typeFQN = null; string methodName; if (fileName != null) fileName = SoftDebuggerSession.NormalizePath (fileName); if (method.VirtualMachine.Version.AtLeast (2, 12) && method.IsGenericMethod) { StringBuilder name = new StringBuilder (method.Name); name.Append ('<'); if (method.VirtualMachine.Version.AtLeast (2, 15)) { bool first = true; foreach (var argumentType in method.GetGenericArguments ()) { if (!first) name.Append (", "); name.Append (session.Adaptor.GetDisplayTypeName (argumentType.FullName)); first = false; } } name.Append ('>'); methodName = name.ToString (); } else { methodName = method.Name; } if (string.IsNullOrEmpty (methodName)) methodName = "[Function Without Name]"; // Compiler generated anonymous/lambda methods bool special_method = false; if (methodName [0] == '<' && methodName.Contains (">m__")) { int nidx = methodName.IndexOf (">m__", StringComparison.Ordinal) + 2; methodName = "AnonymousMethod" + methodName.Substring (nidx, method.Name.Length - nidx); special_method = true; } if (type != null) { string typeDisplayName = session.Adaptor.GetDisplayTypeName (type.FullName); if (SoftDebuggerAdaptor.IsGeneratedType (type)) { // The user-friendly method name is embedded in the generated type name var mn = SoftDebuggerAdaptor.GetNameFromGeneratedType (type); // Strip off the generated type name int dot = typeDisplayName.LastIndexOf ('.'); var tname = typeDisplayName.Substring (0, dot); // Keep any type arguments int targs = typeDisplayName.LastIndexOf ('<'); if (targs > dot + 1) mn += typeDisplayName.Substring (targs, typeDisplayName.Length - targs); typeDisplayName = tname; if (special_method) typeDisplayName += "." + mn; else methodName = mn; } methodName = typeDisplayName + "." + methodName; typeFQN = type.Module.FullyQualifiedName; typeFullName = type.FullName; } bool external = false; bool hidden = false; if (session.VirtualMachine.Version.AtLeast (2, 21)) { var ctx = GetEvaluationContext (frameIndex, session.EvaluationOptions); var hiddenAttr = session.Adaptor.GetType (ctx, "System.Diagnostics.DebuggerHiddenAttribute") as MDB.TypeMirror; if (hiddenAttr != null) hidden = method.GetCustomAttributes (hiddenAttr, true).Any (); } if (hidden) { external = true; } else { external = session.IsExternalCode (frame); if (!external && session.Options.ProjectAssembliesOnly && session.VirtualMachine.Version.AtLeast (2, 21)) { var ctx = GetEvaluationContext (frameIndex, session.EvaluationOptions); var nonUserCodeAttr = session.Adaptor.GetType (ctx, "System.Diagnostics.DebuggerNonUserCodeAttribute") as MDB.TypeMirror; if (nonUserCodeAttr != null) external = method.GetCustomAttributes (nonUserCodeAttr, true).Any (); } } var sourceLink = session.GetSourceLink (frame.Method, frame.FileName); var location = new DC.SourceLocation (methodName, fileName, frame.LineNumber, frame.ColumnNumber, frame.Location.EndLineNumber, frame.Location.EndColumnNumber, frame.Location.SourceFileHash, sourceLink); string addressSpace = string.Empty; bool hasDebugInfo = false; string language; if (frame.Method != null) { if (frame.IsNativeTransition) { language = "Transition"; } else { addressSpace = method.FullName; language = "Managed"; hasDebugInfo = true; } } else { language = "Native"; } return new SoftDebuggerStackFrame (frame, addressSpace, location, language, external, hasDebugInfo, hidden, typeFQN, typeFullName); } public override EvaluationContext GetEvaluationContext (int frameIndex, EvaluationOptions options) { ValidateStack (); if (frameIndex >= frames.Length) return null; var frame = frames[frameIndex]; return new SoftEvaluationContext (session, frame, options); } public override AssemblyLine[] Disassemble (int frameIndex, int firstLine, int count) { return session.Disassemble (frames [frameIndex], firstLine, count); } } }