2021-09-20 18:20:01 +02:00

464 lines
18 KiB
C#

/*
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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
namespace dnSpy.Contracts.Debugger {
/// <summary>
/// Manages all debug engines. All events are raised on the dispatcher thread.
/// If you need to hook events before debugging starts, you should export an <see cref="IDbgManagerStartListener"/>.
/// It gets called when <see cref="Start(DebugProgramOptions)"/> gets called for the first time.
/// </summary>
public abstract class DbgManager {
/// <summary>
/// Gets the dispatcher. All debugger events are raised on this thread. <see cref="DbgObject.Close(DbgDispatcher)"/>
/// is also called on this thread including disposing of data added by eg. <see cref="DbgObject.GetOrCreateData{T}()"/>.
/// </summary>
public abstract DbgDispatcher Dispatcher { get; }
/// <summary>
/// Raised on the debugger thread when there's a new message, eg. a process was created, a thread has exited, etc.
/// The listeners can pause the debugged program by setting <see cref="DbgMessageEventArgs.Pause"/> to true.
/// </summary>
public abstract event EventHandler<DbgMessageEventArgs>? Message;
/// <summary>
/// Raised on the debugger thread when there's a new message.
/// The listeners can pause the debugged program by setting <see cref="DbgMessageEventArgs.Pause"/> to true.
/// </summary>
public abstract event EventHandler<DbgMessageProcessCreatedEventArgs>? MessageProcessCreated;
/// <summary>
/// Raised on the debugger thread when there's a new message.
/// The listeners can pause the debugged program by setting <see cref="DbgMessageEventArgs.Pause"/> to true.
/// </summary>
public abstract event EventHandler<DbgMessageProcessExitedEventArgs>? MessageProcessExited;
/// <summary>
/// Raised on the debugger thread when there's a new message.
/// The listeners can pause the debugged program by setting <see cref="DbgMessageEventArgs.Pause"/> to true.
/// </summary>
public abstract event EventHandler<DbgMessageRuntimeCreatedEventArgs>? MessageRuntimeCreated;
/// <summary>
/// Raised on the debugger thread when there's a new message.
/// The listeners can pause the debugged program by setting <see cref="DbgMessageEventArgs.Pause"/> to true.
/// </summary>
public abstract event EventHandler<DbgMessageRuntimeExitedEventArgs>? MessageRuntimeExited;
/// <summary>
/// Raised on the debugger thread when there's a new message.
/// The listeners can pause the debugged program by setting <see cref="DbgMessageEventArgs.Pause"/> to true.
/// </summary>
public abstract event EventHandler<DbgMessageAppDomainLoadedEventArgs>? MessageAppDomainLoaded;
/// <summary>
/// Raised on the debugger thread when there's a new message.
/// The listeners can pause the debugged program by setting <see cref="DbgMessageEventArgs.Pause"/> to true.
/// </summary>
public abstract event EventHandler<DbgMessageAppDomainUnloadedEventArgs>? MessageAppDomainUnloaded;
/// <summary>
/// Raised on the debugger thread when there's a new message.
/// The listeners can pause the debugged program by setting <see cref="DbgMessageEventArgs.Pause"/> to true.
/// </summary>
public abstract event EventHandler<DbgMessageModuleLoadedEventArgs>? MessageModuleLoaded;
/// <summary>
/// Raised on the debugger thread when there's a new message.
/// The listeners can pause the debugged program by setting <see cref="DbgMessageEventArgs.Pause"/> to true.
/// </summary>
public abstract event EventHandler<DbgMessageModuleUnloadedEventArgs>? MessageModuleUnloaded;
/// <summary>
/// Raised on the debugger thread when there's a new message.
/// The listeners can pause the debugged program by setting <see cref="DbgMessageEventArgs.Pause"/> to true.
/// </summary>
public abstract event EventHandler<DbgMessageThreadCreatedEventArgs>? MessageThreadCreated;
/// <summary>
/// Raised on the debugger thread when there's a new message.
/// The listeners can pause the debugged program by setting <see cref="DbgMessageEventArgs.Pause"/> to true.
/// </summary>
public abstract event EventHandler<DbgMessageThreadExitedEventArgs>? MessageThreadExited;
/// <summary>
/// Raised on the debugger thread when there's a new message.
/// The listeners can pause the debugged program by setting <see cref="DbgMessageEventArgs.Pause"/> to true.
/// </summary>
public abstract event EventHandler<DbgMessageExceptionThrownEventArgs>? MessageExceptionThrown;
/// <summary>
/// Raised on the debugger thread when there's a new message.
/// The listeners can pause the debugged program by setting <see cref="DbgMessageEventArgs.Pause"/> to true.
/// </summary>
public abstract event EventHandler<DbgMessageEntryPointBreakEventArgs>? MessageEntryPointBreak;
/// <summary>
/// Raised on the debugger thread when there's a new message.
/// The listeners can pause the debugged program by setting <see cref="DbgMessageEventArgs.Pause"/> to true.
/// </summary>
public abstract event EventHandler<DbgMessageProgramMessageEventArgs>? MessageProgramMessage;
/// <summary>
/// Raised on the debugger thread when there's a new message.
/// The listeners can pause the debugged program by setting <see cref="DbgMessageEventArgs.Pause"/> to true.
/// </summary>
public abstract event EventHandler<DbgMessageBoundBreakpointEventArgs>? MessageBoundBreakpoint;
/// <summary>
/// Raised on the debugger thread when there's a new message.
/// The listeners can pause the debugged program by setting <see cref="DbgMessageEventArgs.Pause"/> to true.
/// </summary>
public abstract event EventHandler<DbgMessageProgramBreakEventArgs>? MessageProgramBreak;
/// <summary>
/// Raised on the debugger thread when there's a new message.
/// The listeners can pause the debugged program by setting <see cref="DbgMessageEventArgs.Pause"/> to true.
/// </summary>
public abstract event EventHandler<DbgMessageStepCompleteEventArgs>? MessageStepComplete;
/// <summary>
/// Raised on the debugger thread when there's a new message.
/// The listeners can pause the debugged program by setting <see cref="DbgMessageEventArgs.Pause"/> to true.
/// </summary>
public abstract event EventHandler<DbgMessageSetIPCompleteEventArgs>? MessageSetIPComplete;
/// <summary>
/// Raised on the debugger thread when there's a new message.
/// The listeners can pause the debugged program by setting <see cref="DbgMessageEventArgs.Pause"/> to true.
/// </summary>
public abstract event EventHandler<DbgMessageUserMessageEventArgs>? MessageUserMessage;
/// <summary>
/// Raised on the debugger thread when there's a new message.
/// The listeners can pause the debugged program by setting <see cref="DbgMessageEventArgs.Pause"/> to true.
/// </summary>
public abstract event EventHandler<DbgMessageBreakEventArgs>? MessageBreak;
/// <summary>
/// Raised on the debugger thread when there's a new message.
/// The listeners can pause the debugged program by setting <see cref="DbgMessageEventArgs.Pause"/> to true.
/// </summary>
public abstract event EventHandler<DbgMessageAsyncProgramMessageEventArgs>? MessageAsyncProgramMessage;
/// <summary>
/// Starts debugging. Returns an error string if it failed to create a debug engine, or null on success.
/// See <see cref="IDbgManagerStartListener"/> on how to get called the first time this method gets called.
/// </summary>
/// <param name="options">Options needed to start the program or attach to it</param>
public abstract string? Start(DebugProgramOptions options);
/// <summary>
/// true if <see cref="Restart"/> can be called
/// </summary>
public abstract bool CanRestart { get; }
/// <summary>
/// Restarts the debugged program(s)
/// </summary>
public abstract void Restart();
/// <summary>
/// true if a program is being debugged
/// </summary>
public abstract bool IsDebugging { get; }
/// <summary>
/// Raised when <see cref="IsDebugging"/> is changed
/// </summary>
public abstract event EventHandler? IsDebuggingChanged;
/// <summary>
/// true if all processes are running, false if they're all paused, and null
/// if some are running and some are paused.
/// This property is valid only if <see cref="IsDebugging"/> is true.
/// </summary>
public abstract bool? IsRunning { get; }
/// <summary>
/// Raised when <see cref="IsRunning"/> is changed, see also <see cref="DelayedIsRunningChanged"/>
/// </summary>
public abstract event EventHandler? IsRunningChanged;
/// <summary>
/// Raised when all processes have been running for a little while, eg. 1 second.
/// </summary>
public abstract event EventHandler? DelayedIsRunningChanged;
/// <summary>
/// Gets all debug tags, see <see cref="PredefinedDebugTags"/>
/// </summary>
public abstract string[] DebugTags { get; }
/// <summary>
/// Raised when <see cref="DebugTags"/> is changed
/// </summary>
public abstract event EventHandler<DbgCollectionChangedEventArgs<string>>? DebugTagsChanged;
/// <summary>
/// Raised when a process gets paused due to some event in the process. If more than one process
/// is being debugged, this is normally only raised once, for the first process.
/// </summary>
public abstract event EventHandler<ProcessPausedEventArgs>? ProcessPaused;
/// <summary>
/// Gets all debugged processes. Can be empty even if <see cref="IsDebugging"/> is true
/// if the process hasn't been created yet.
/// </summary>
public abstract DbgProcess[] Processes { get; }
/// <summary>
/// Raised when <see cref="Processes"/> is changed
/// </summary>
public abstract event EventHandler<DbgCollectionChangedEventArgs<DbgProcess>>? ProcessesChanged;
/// <summary>
/// Pauses all debugged processes
/// </summary>
public abstract void BreakAll();
/// <summary>
/// Lets all programs run again. This is the inverse of <see cref="BreakAll"/>
/// </summary>
public abstract void RunAll();
/// <summary>
/// Lets <paramref name="process"/> run again. If <see cref="DebuggerSettings.BreakAllProcesses"/>
/// is true, all other processes will also run.
/// </summary>
/// <param name="process">Process to run</param>
public abstract void Run(DbgProcess process);
/// <summary>
/// Stops debugging. All programs started by the debugger will be terminated. All
/// other programs will be detached, if possible, else terminated.
/// </summary>
public abstract void StopDebuggingAll();
/// <summary>
/// Terminates all debugged programs
/// </summary>
public abstract void TerminateAll();
/// <summary>
/// Detaches all debugged programs, if possible. If it's not possible to detach a
/// program, it will be terminated.
/// </summary>
public abstract void DetachAll();
/// <summary>
/// true if <see cref="DetachAll"/> can be called without terminating any programs
/// </summary>
public abstract bool CanDetachWithoutTerminating { get; }
/// <summary>
/// Gets the current process
/// </summary>
public abstract DbgCurrentObject<DbgProcess> CurrentProcess { get; }
/// <summary>
/// Raised when <see cref="CurrentProcess"/> is changed
/// </summary>
public abstract event EventHandler<DbgCurrentObjectChangedEventArgs<DbgProcess>>? CurrentProcessChanged;
/// <summary>
/// Gets the current runtime
/// </summary>
public abstract DbgCurrentObject<DbgRuntime> CurrentRuntime { get; }
/// <summary>
/// Raised when <see cref="CurrentRuntime"/> is changed
/// </summary>
public abstract event EventHandler<DbgCurrentObjectChangedEventArgs<DbgRuntime>>? CurrentRuntimeChanged;
/// <summary>
/// Gets the current thread
/// </summary>
public abstract DbgCurrentObject<DbgThread> CurrentThread { get; }
/// <summary>
/// Raised when <see cref="CurrentThread"/> is changed
/// </summary>
public abstract event EventHandler<DbgCurrentObjectChangedEventArgs<DbgThread>>? CurrentThreadChanged;
/// <summary>
/// Raised when the module's memory has been updated (eg. decrypted)
/// </summary>
public abstract event EventHandler<ModulesRefreshedEventArgs>? ModulesRefreshed;
/// <summary>
/// Returns true if the runtime can be debugged
/// </summary>
/// <param name="pid">Process id</param>
/// <param name="rid">Runtime id</param>
/// <returns></returns>
public abstract bool CanDebugRuntime(int pid, RuntimeId rid);
/// <summary>
/// Closes <paramref name="obj"/>
/// </summary>
/// <param name="obj">Object to close</param>
public abstract void Close(DbgObject obj);
/// <summary>
/// Closes <paramref name="objs"/>
/// </summary>
/// <param name="objs">Objects to close</param>
public abstract void Close(IEnumerable<DbgObject> objs);
/// <summary>
/// Writes a message that will be shown in the output window
/// </summary>
/// <param name="message">Message</param>
public void WriteMessage(string message) => WriteMessage(PredefinedDbgManagerMessageKinds.Output, message);
/// <summary>
/// Shows an error message and returns immediately
/// </summary>
/// <param name="errorMessage">Error message</param>
public void ShowError(string errorMessage) => WriteMessage(PredefinedDbgManagerMessageKinds.ErrorUser, errorMessage);
/// <summary>
/// Writes a message
/// </summary>
/// <param name="messageKind">Message kind, see <see cref="PredefinedDbgManagerMessageKinds"/></param>
/// <param name="message">Message</param>
public abstract void WriteMessage(string messageKind, string message);
/// <summary>
/// Raised when <see cref="WriteMessage(string)"/> gets called. This event is raised on a random thread.
/// </summary>
public abstract event EventHandler<DbgManagerMessageEventArgs>? DbgManagerMessage;
}
/// <summary>
/// Predefined message kinds, see <see cref="DbgManager.WriteMessage(string, string)"/>
/// </summary>
public static class PredefinedDbgManagerMessageKinds {
/// <summary>
/// Output window
/// </summary>
public const string Output = nameof(Output);
/// <summary>
/// An error message that should be shown to the user
/// </summary>
public const string ErrorUser = nameof(ErrorUser);
/// <summary>
/// Messages by the stepper
/// </summary>
public const string StepFilter = nameof(StepFilter);
}
/// <summary>
/// Message event args
/// </summary>
public readonly struct DbgManagerMessageEventArgs {
/// <summary>
/// Gets the message kind, see <see cref="PredefinedDbgManagerMessageKinds"/>
/// </summary>
public string MessageKind { get; }
/// <summary>
/// Gets the message
/// </summary>
public string Message { get; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="messageKind">Message kind, see <see cref="PredefinedDbgManagerMessageKinds"/></param>
/// <param name="message">Message</param>
public DbgManagerMessageEventArgs(string messageKind, string message) {
MessageKind = messageKind ?? throw new ArgumentNullException(nameof(messageKind));
Message = message ?? throw new ArgumentNullException(nameof(message));
}
}
/// <summary>
/// Contains the current object and the object that caused the debugger to enter break mode
/// </summary>
/// <typeparam name="T">Type of object</typeparam>
public abstract class DbgCurrentObject<T> where T : DbgObject {
/// <summary>
/// Gets the current object or null if none
/// </summary>
public abstract T? Current { get; set; }
/// <summary>
/// Gets the object that caused the debugger to enter break mode
/// </summary>
public abstract T? Break { get; }
}
/// <summary>
/// <see cref="DbgCurrentObject{T}"/> changed event args
/// </summary>
/// <typeparam name="T"></typeparam>
public readonly struct DbgCurrentObjectChangedEventArgs<T> where T : DbgObject {
/// <summary>
/// true if <see cref="DbgCurrentObject{T}.Current"/> changed
/// </summary>
public bool CurrentChanged { get; }
/// <summary>
/// true if <see cref="DbgCurrentObject{T}.Break"/> changed
/// </summary>
public bool BreakChanged { get; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="currentChanged">true if <see cref="DbgCurrentObject{T}.Current"/> changed</param>
/// <param name="breakChanged">true if <see cref="DbgCurrentObject{T}.Break"/> changed</param>
public DbgCurrentObjectChangedEventArgs(bool currentChanged, bool breakChanged) {
CurrentChanged = currentChanged;
BreakChanged = breakChanged;
}
}
/// <summary>
/// Process paused event args
/// </summary>
public readonly struct ProcessPausedEventArgs {
/// <summary>
/// Gets the process
/// </summary>
public DbgProcess Process { get; }
/// <summary>
/// Gets the thread or null if unknown
/// </summary>
public DbgThread? Thread { get; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="process">Process</param>
/// <param name="thread">Thread or null if unknown</param>
public ProcessPausedEventArgs(DbgProcess process, DbgThread? thread) {
Process = process ?? throw new ArgumentNullException(nameof(process));
Thread = thread;
}
}
}