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