/* 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.Diagnostics; using dnSpy.Contracts.Debugger; using dnSpy.Contracts.Debugger.DotNet.Code; using dnSpy.Contracts.Debugger.Engine.CallStack; using dnSpy.Debugger.DotNet.Mono.Impl; using dnSpy.Debugger.DotNet.Mono.Properties; using Mono.Debugger.Soft; using MDS = Mono.Debugger.Soft; namespace dnSpy.Debugger.DotNet.Mono.CallStack { sealed class DbgEngineStackWalkerImpl : DbgEngineStackWalker { readonly Lazy dbgDotNetCodeLocationFactory; readonly DbgEngineImpl engine; readonly ThreadMirror monoThread; readonly DbgThread thread; readonly uint continueCounter; MDS.StackFrame[]? frames; int frameIndex; public DbgEngineStackWalkerImpl(Lazy dbgDotNetCodeLocationFactory, DbgEngineImpl engine, ThreadMirror monoThread, DbgThread thread) { this.dbgDotNetCodeLocationFactory = dbgDotNetCodeLocationFactory ?? throw new ArgumentNullException(nameof(dbgDotNetCodeLocationFactory)); this.engine = engine ?? throw new ArgumentNullException(nameof(engine)); this.monoThread = monoThread ?? throw new ArgumentNullException(nameof(monoThread)); this.thread = thread ?? throw new ArgumentNullException(nameof(thread)); continueCounter = engine.ContinueCounter; } public override DbgEngineStackFrame[] GetNextStackFrames(int maxFrames) { if (engine.DebuggerThread.CheckAccess()) return GetNextStackFrames_MonoDebug(maxFrames); return GetNextStackFrames2(maxFrames); DbgEngineStackFrame[] GetNextStackFrames2(int maxFrames2) => engine.DebuggerThread.Invoke(() => GetNextStackFrames_MonoDebug(maxFrames2)); } DbgEngineStackFrame[] GetNextStackFrames_MonoDebug(int maxFrames) { engine.DebuggerThread.VerifyAccess(); if (!engine.IsPaused || thread.IsClosed || continueCounter != engine.ContinueCounter) return Array.Empty(); try { if (frames is null) frames = monoThread.GetFrames(); var list = engine.stackFrameData.DbgEngineStackFrameList; try { while (list.Count < maxFrames && frameIndex < frames.Length) { list.Add(CreateEngineStackFrame(frames[frameIndex], frameIndex)); frameIndex++; } return list.Count == 0 ? Array.Empty() : list.ToArray(); } catch { engine.DbgRuntime.Process.DbgManager.Close(list.ToArray()); throw; } finally { list.Clear(); } } catch (VMDisconnectedException) { return Array.Empty(); } catch { return Array.Empty(); } } DbgEngineStackFrame CreateEngineStackFrame(MDS.StackFrame monoFrame, int frameIndex) { engine.DebuggerThread.VerifyAccess(); var method = monoFrame.Method; if (method is null) { if (monoFrame.IsDebuggerInvoke) return engine.ObjectFactory.CreateSpecialStackFrame(dnSpy_Debugger_DotNet_Mono_Resources.StackFrame_FunctionEvaluation); if (monoFrame.IsNativeTransition) return engine.ObjectFactory.CreateSpecialStackFrame(dnSpy_Debugger_DotNet_Mono_Resources.StackFrame_NativeTransition); Debug.Fail("Unknown frame without a method"); return CreateErrorStackFrame(); } else { var module = engine.TryGetModule(method.DeclaringType.Module); if (module is not null) return new ILDbgEngineStackFrame(engine, module, monoThread, monoFrame, frameIndex, dbgDotNetCodeLocationFactory); Debug.Fail("Creating an error stack frame"); return CreateErrorStackFrame(); } } DbgEngineStackFrame CreateErrorStackFrame() => engine.ObjectFactory.CreateSpecialStackFrame("???"); protected override void CloseCore(DbgDispatcher dispatcher) { } } }