/* Copyright (C) 2014-2019 de4dot@gmail.com This file is part of dnSpy dnSpy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. dnSpy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with dnSpy. If not, see . */ using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using dnSpy.Contracts.Debugger; using dnSpy.Contracts.Debugger.CallStack; using dnSpy.Contracts.Debugger.Code; using dnSpy.Contracts.Debugger.DotNet.Code; using dnSpy.Contracts.Debugger.Engine.CallStack; using dnSpy.Debugger.DotNet.Metadata; using dnSpy.Debugger.DotNet.Mono.Impl; using Mono.Debugger.Soft; using MDS = Mono.Debugger.Soft; namespace dnSpy.Debugger.DotNet.Mono.CallStack { sealed class ILDbgEngineStackFrame : DbgEngineStackFrame { public override DbgCodeLocation? Location { get; } public override DbgModule? Module { get; } public override DbgStackFrameFlags Flags => DbgStackFrameFlags.None; public override uint FunctionOffset { get; } public override uint FunctionToken { get; } internal MDS.StackFrame MonoFrame { get { if (engine.MethodInvokeCounter != methodInvokeCounter) UpdateFrame(); return __monoFrame_DONT_USE; } } readonly ThreadMirror frameThread; MDS.StackFrame __monoFrame_DONT_USE; int frameIndex; int methodInvokeCounter; void UpdateFrame() { var frames = frameThread.GetFrames(); // PERF: It's not likely that frames were removed, so always start searching from the frame index for (int i = frameIndex; i < frames.Length; i++) { var currentFrame = frames[i]; if (IsSameAsOurFrame(currentFrame)) { __monoFrame_DONT_USE = currentFrame; frameIndex = i; methodInvokeCounter = engine.MethodInvokeCounter; return; } } Debug.Fail("Failed to find the frame"); } bool IsSameAsOurFrame(MDS.StackFrame otherFrame) { if (otherFrame is null) return false; // None of the properties below can throw, all values are cached in the StackFrame instance if (__monoFrame_DONT_USE.Method != otherFrame.Method) return false; if (__monoFrame_DONT_USE.ILOffset != otherFrame.ILOffset) return false; if (__monoFrame_DONT_USE.IsDebuggerInvoke != otherFrame.IsDebuggerInvoke) return false; if (__monoFrame_DONT_USE.IsNativeTransition != otherFrame.IsNativeTransition) return false; // Looks like it could be the correct frame. There's no address so we don't know for sure, // but since the properties above matched AND the frame index is probably the same too, // it's very likely the same frame. return true; } readonly DbgEngineImpl engine; public ILDbgEngineStackFrame(DbgEngineImpl engine, DbgModule module, ThreadMirror frameThread, MDS.StackFrame monoFrame, int frameIndex, Lazy dbgDotNetCodeLocationFactory) { this.engine = engine ?? throw new ArgumentNullException(nameof(engine)); this.frameThread = frameThread; __monoFrame_DONT_USE = monoFrame ?? throw new ArgumentNullException(nameof(monoFrame)); this.frameIndex = frameIndex; methodInvokeCounter = engine.MethodInvokeCounter; Module = module ?? throw new ArgumentNullException(nameof(module)); FunctionToken = (uint)monoFrame.Method.MetadataToken; // Native transitions have no IL offset so -1 is used by mono, but we should use 0 instead uint ilOffset = (uint)monoFrame.ILOffset; FunctionOffset = ilOffset == uint.MaxValue ? 0 : ilOffset; var moduleId = DbgEngineImpl.TryGetModuleId(module) ?? default; var ilOffsetMapping = ilOffset == uint.MaxValue || monoFrame.IsNativeTransition ? DbgILOffsetMapping.Unknown : frameIndex == 0 ? DbgILOffsetMapping.Exact : DbgILOffsetMapping.Approximate; Location = dbgDotNetCodeLocationFactory.Value.Create(moduleId, FunctionToken, FunctionOffset, ilOffsetMapping); } sealed class ILFrameState { public readonly ILDbgEngineStackFrame ILFrame; public ILFrameState(ILDbgEngineStackFrame ilFrame) => ILFrame = ilFrame; } public override void OnFrameCreated(DbgStackFrame frame) => frame.GetOrCreateData(() => new ILFrameState(this)); internal static bool TryGetEngineStackFrame(DbgStackFrame frame, [NotNullWhen(true)] out ILDbgEngineStackFrame? ilFrame) { if (frame.TryGetData(out var data)) { ilFrame = data.ILFrame; return true; } ilFrame = null; return false; } internal void GetFrameMethodInfo(out DmdModule module, out int methodMetadataToken, out IList genericTypeArguments, out IList genericMethodArguments) { engine.VerifyMonoDebugThread(); methodMetadataToken = MonoFrame.Method.MetadataToken; module = Module!.GetReflectionModule() ?? throw new InvalidOperationException(); TypeMirror[] typeGenArgs; TypeMirror[] methGenArgs; if (MonoFrame.VirtualMachine.Version.AtLeast(2, 15)) { typeGenArgs = MonoFrame.Method.DeclaringType.GetGenericArguments(); methGenArgs = MonoFrame.Method.GetGenericArguments(); } else { typeGenArgs = Array.Empty(); methGenArgs = Array.Empty(); } var reflectionAppDomain = module.AppDomain; genericTypeArguments = Convert(reflectionAppDomain, typeGenArgs); genericMethodArguments = Convert(reflectionAppDomain, methGenArgs); } IList Convert(DmdAppDomain reflectionAppDomain, TypeMirror[] typeArgs) { if (typeArgs.Length == 0) return Array.Empty(); var types = new DmdType[typeArgs.Length]; for (int i = 0; i < types.Length; i++) types[i] = engine.GetReflectionType(reflectionAppDomain, typeArgs[i], null); return types; } internal DmdModule GetReflectionModule() => Module!.GetReflectionModule() ?? throw new InvalidOperationException(); protected override void CloseCore(DbgDispatcher dispatcher) { } } }