2263 lines
74 KiB
C++
2263 lines
74 KiB
C++
RegisterVariable(String, BinaryUnderDebug, "build/te.exe");
|
|
RegisterVariable(String, RemedyBGPath, "remedybg.exe");
|
|
|
|
#if OS_WINDOWS
|
|
|
|
#define RDBG_MAX_SERVERNAME_LEN 64
|
|
|
|
typedef uint8_t rdbg_Bool;
|
|
|
|
// A rolling 32-bit integer is used for any command that takes or returns a UID.
|
|
// These UIDs are never persisted and as such, can change between runs of
|
|
// RemedyBG. Zero will never be a valid id.
|
|
typedef uint32_t rdbg_Id;
|
|
|
|
// A string consists of a length followed by an UTF-8 encoded character array of
|
|
// 'length' bytes. Strings are never nul-terminated.
|
|
#pragma warning(push)
|
|
#pragma warning(disable: 4200)
|
|
#pragma pack(push, 1)
|
|
struct rdbg_String
|
|
{
|
|
uint16_t len;
|
|
uint8_t data[0];
|
|
};
|
|
#pragma pack(pop)
|
|
#pragma warning(pop)
|
|
|
|
enum rdbg_CommandResult
|
|
{
|
|
RDBG_COMMAND_RESULT_UNKNOWN = 0,
|
|
|
|
RDBG_COMMAND_RESULT_OK = 1,
|
|
|
|
// Generic failure
|
|
RDBG_COMMAND_RESULT_FAIL = 2,
|
|
|
|
// Result if the command is aborted due to a specified behavior and
|
|
// condition including RDBG_IF_DEBUGGING_TARGET_ABORT_COMMAND or
|
|
// RDBG_IF_SESSION_IS_MODIFIED_ABORT_COMMAND. The result can also be returned
|
|
// if an unnamed session is saved, prompts for a filename, and the user
|
|
// cancels this operation.
|
|
RDBG_COMMAND_RESULT_ABORTED = 3,
|
|
|
|
// Result if the given command buffer given is less than 2 bytes or if the
|
|
// command is not one of the enumerated commands in rdbg_Command.
|
|
RDBG_COMMAND_RESULT_INVALID_COMMAND = 4,
|
|
|
|
// Result if the response generated is too large to fit in the buffer.
|
|
RDBG_COMMAND_RESULT_BUFFER_TOO_SMALL = 5,
|
|
|
|
// Result if an opening a file (i.e., a session, text file).
|
|
RDBG_COMMAND_RESULT_FAILED_OPENING_FILE = 6,
|
|
|
|
// Result if saving a session fails.
|
|
RDBG_COMMAND_RESULT_FAILED_SAVING_SESSION = 7,
|
|
|
|
// Result if the given ID is invalid.
|
|
RDBG_COMMAND_RESULT_INVALID_ID = 8,
|
|
|
|
// Result if a command expects the target to be in a particular state (not
|
|
// debugging, debugging and suspended, or debugging and executing) and is
|
|
// not.
|
|
RDBG_COMMAND_RESULT_INVALID_TARGET_STATE = 9,
|
|
|
|
// Result if an active configuration does not exist
|
|
RDBG_COMMAND_RESULT_FAILED_NO_ACTIVE_CONFIG = 10,
|
|
|
|
// Result if the command does not apply to given breakpoint's kind
|
|
RDBG_COMMAND_RESULT_INVALID_BREAKPOINT_KIND = 11,
|
|
};
|
|
|
|
// Commands that take an rdbg_DebuggingTargetBehavior can specify what should
|
|
// happen in the case the target is being debugged.
|
|
enum rdbg_DebuggingTargetBehavior
|
|
{
|
|
RDBG_IF_DEBUGGING_TARGET_STOP_DEBUGGING = 1,
|
|
RDBG_IF_DEBUGGING_TARGET_ABORT_COMMAND = 2
|
|
};
|
|
|
|
// Commands that take an rdbg_ModifiedSessionBehavior can specify what should
|
|
// happen when there is an open, modified session.
|
|
enum rdbg_ModifiedSessionBehavior
|
|
{
|
|
RDBG_IF_SESSION_IS_MODIFIED_SAVE_AND_CONTINUE = 1,
|
|
RDBG_IF_SESSION_IS_MODIFIED_CONTINUE_WITHOUT_SAVING = 2,
|
|
RDBG_IF_SESSION_IS_MODIFIED_ABORT_COMMAND = 3,
|
|
};
|
|
|
|
enum rdbg_TargetState
|
|
{
|
|
RDBG_TARGET_STATE_NONE = 1,
|
|
RDBG_TARGET_STATE_SUSPENDED = 2,
|
|
RDBG_TARGET_STATE_EXECUTING = 3,
|
|
};
|
|
|
|
enum rdbg_BreakpointKind
|
|
{
|
|
RDBG_BREAKPOINT_KIND_FUNCTION_NAME = 1,
|
|
RDBG_BREAKPOINT_KIND_FILENAME_LINE = 2,
|
|
RDBG_BREAKPOINT_KIND_ADDRESS = 3,
|
|
RDBG_BREAKPOINT_KIND_PROCESSOR = 4,
|
|
};
|
|
|
|
enum rdbg_ProcessorBreakpointAccessKind
|
|
{
|
|
RDBG_PROCESSOR_BREAKPOINT_ACCESS_KIND_WRITE = 1,
|
|
RDBG_PROCESSOR_BREAKPOINT_ACCESS_KIND_READ_WRITE = 2,
|
|
RDBG_PROCESSOR_BREAKPOINT_ACCESS_KIND_EXECUTE = 3,
|
|
};
|
|
|
|
enum rdbg_Command
|
|
{
|
|
// Bring the RemedyBG window to the foreground and activate it. No additional
|
|
// arguments follow the command. Returns RDBG_COMMAND_RESULT_OK or
|
|
// RDBG_COMMAND_RESULT_FAIL.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_BRING_DEBUGGER_TO_FOREGROUND = 50,
|
|
|
|
// Set the size and position of the RemedyBG window.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [x :: int32_t]
|
|
// [y :: int32_t]
|
|
// [width :: int32_t]
|
|
// [height :: int32_t]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_SET_WINDOW_POS = 51,
|
|
|
|
// Get the size and position of the RemedyBG window.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
// [x :: int32_t]
|
|
// [y :: int32_t]
|
|
// [width :: int32_t]
|
|
// [height :: int32_t]
|
|
// [is_maximized: rdbg_Bool]
|
|
RDBG_COMMAND_GET_WINDOW_POS = 52,
|
|
|
|
// Set whether to automatically bring the debugger to the foreground whenever
|
|
// the target is suspended (breakpoint hit, exception, single-step complete,
|
|
// etc.). Defaults to true if not set.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [bring_to_foreground_on_suspended :: rdbg_Bool (uint8_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_SET_BRING_TO_FOREGROUND_ON_SUSPENDED = 53,
|
|
|
|
// Exit the RemedyBG application.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [dtb :: rdbg_DebuggingTargetBehavior (uint8_t)]
|
|
// [msb :: rdbg_ModifiedSessionBehavior (uint8_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_EXIT_DEBUGGER = 75,
|
|
|
|
// Session
|
|
//
|
|
|
|
// Returns whether the current session is modified, or "dirty".
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
// [modified :: rdbg_Bool (uint8_t)]
|
|
RDBG_COMMAND_GET_IS_SESSION_MODIFIED = 100,
|
|
|
|
// Returns the current session's filename. If the filename has not been set
|
|
// for the session then the result will be
|
|
// RDBG_COMMAND_RESULT_UNNAMED_SESSION and the length of |filename| will be
|
|
// zero.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
// [filename :: rdbg_String]
|
|
RDBG_COMMAND_GET_SESSION_FILENAME = 101,
|
|
|
|
// Creates a new session. All configurations are cleared and reset.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [dtb :: rdbg_DebuggingTargetBehavior (uint8_t)]
|
|
// [msb :: rdbg_ModifiedSessionBehavior (uint8_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_NEW_SESSION = 102,
|
|
|
|
// Open a session with the given filename.
|
|
//
|
|
// [command :: rdbg_Command (uint16_t)]
|
|
// [dtb :: rdbg_DebuggingTargetBehavior (uint8_t)]
|
|
// [msb :: rdbg_ModifiedSessionBehavior (uint8_t)]
|
|
// [filename :: rdbg_String]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_OPEN_SESSION = 103,
|
|
|
|
// Save session with its current filename. If the filename is has not been
|
|
// specified for the session the user will be prompted. To save with a
|
|
// filename see RDBG_COMMAND_SAVE_AS_SESSION, instead.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_SAVE_SESSION = 104,
|
|
|
|
// Save session with a given filename.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [filename :: rdbg_String]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_SAVE_AS_SESSION = 105,
|
|
|
|
// Retrieve a list of configurations for the current session.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
// [num_configs :: uint16_t]
|
|
// .FOR(num_configs) {
|
|
// [uid :: rdbg_Id (uint32_t)]
|
|
// [command :: rdbg_String]
|
|
// [command_args :: rdbg_String]
|
|
// [working_dir :: rdbg_String]
|
|
// [environment_vars :: rdbg_String]
|
|
// [inherit_environment_vars_from_parent :: rdbg_Bool]
|
|
// [break_at_nominal_entry_point :: rdbg_Bool]
|
|
// [name :: rdbg_String]
|
|
// }
|
|
RDBG_COMMAND_GET_SESSION_CONFIGS = 106,
|
|
|
|
// Add a new session configuration to the current session. All string
|
|
// parameters accept zero length strings. Multiple environment variables
|
|
// should be newline, '\n', separated. Returns the a unique ID for the
|
|
// configuration.
|
|
//
|
|
// Note that 'name' is currently optional.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)
|
|
// [command :: rdbg_String]
|
|
// [command_args :: rdbg_String]
|
|
// [working_dir :: rdbg_String]
|
|
// [environment_vars :: rdbg_String]
|
|
// [inherit_environment_vars_from_parent :: rdbg_Bool]
|
|
// [break_at_nominal_entry_point :: rdbg_Bool]
|
|
// [name :: rdbg_String]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
// [uid :: rdbg_Id]
|
|
RDBG_COMMAND_ADD_SESSION_CONFIG = 107,
|
|
|
|
// Sets the active configuration for a session by configuration ID. If the
|
|
// ID is not valid for the current session
|
|
// RDBG_COMMAND_RESULT_INVALID_ID is returned.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)
|
|
// [id :: rdbg_Id]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_SET_ACTIVE_SESSION_CONFIG = 108,
|
|
|
|
// Deletes a session configuration by ID. If the ID is not valid for the
|
|
// current session RDBG_COMMAND_REMOVE_SESSION_CONFIG is returned.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)
|
|
// [id :: rdbg_Id]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_DELETE_SESSION_CONFIG = 109,
|
|
|
|
// Deletes all session configurations in the current session.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_DELETE_ALL_SESSION_CONFIGS = 110,
|
|
|
|
// Source Files
|
|
//
|
|
|
|
// Opens the given file, if not already opened, and navigates to the
|
|
// specified line number. The line number is optional and can be elided from
|
|
// the command buffer. Returns result along with an ID for the file.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)
|
|
// [filename :: rdbg_String]
|
|
// [line_num :: uint32_t]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
// [id :: rdbg_Id]
|
|
RDBG_COMMAND_GOTO_FILE_AT_LINE = 200,
|
|
|
|
// Close the file with the given ID.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [id :: rdbg_Id]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_CLOSE_FILE = 201,
|
|
|
|
// Close all open files
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_CLOSE_ALL_FILES = 202,
|
|
|
|
// Returns the current file. If no file is open, returns a zero ID,
|
|
// zero-length filename, and zero line number.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
// [id :: rdbg_Id]
|
|
// [filename :: rdbg_String]
|
|
// [line_num :: uint32_t]
|
|
RDBG_COMMAND_GET_CURRENT_FILE = 203,
|
|
|
|
// Retrieve a list of open files.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
// [num_files :: uint16_t]
|
|
// .FOR(num_files) {
|
|
// [id :: rdbg_Id]
|
|
// [filename :: rdbg_String]
|
|
// [line_num :: uint32_t]
|
|
// }
|
|
RDBG_COMMAND_GET_OPEN_FILES = 204,
|
|
|
|
//
|
|
// Debugger Control
|
|
|
|
// Returns the target state for the current session.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
// [staste :: rdbg_TargetState (uint16_t)]
|
|
RDBG_COMMAND_GET_TARGET_STATE = 300,
|
|
|
|
// If the target is stopped, i.e., not currently being debugged, then start
|
|
// debugging the active configuration. Setting break_at_entry to true will
|
|
// stop execution at the at entry point specified in the configuration:
|
|
// either the nominal entry point, such as "main" or "WinMain" or the entry
|
|
// point function as described in the PE header. If the target is already
|
|
// being debugged, this will return RDBG_COMMAND_RESULT_INVALID_TARGET_STATE.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [break_at_entry_point :: rdbg_Bool (uint8_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_START_DEBUGGING = 301,
|
|
|
|
// Stop debugging the target. If the target is not executing this will return
|
|
// RDBG_COMMAND_RESULT_INVALID_TARGET_STATE.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_STOP_DEBUGGING = 302,
|
|
|
|
// Restart debugging if the target is being debugging (either suspended or
|
|
// executing) and the target was not attached to a process. Otherwise,
|
|
// returns RDBG_COMMAND_RESULT_INVALID_TARGET_STATE.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_RESTART_DEBUGGING = 303,
|
|
|
|
// Attach to a process by the given process-id. The value of
|
|
// |continue_execution| indicates whether the process should resume execution
|
|
// after attached. The debugger target behavior specifies what should happen
|
|
// in the case when the target is being debugged (suspended or executing).
|
|
// Can return: RDBG_COMMAND_RESULT_OK, RDBG_COMMAND_RESULT_FAIL, or
|
|
// RDBG_COMMAND_RESULT_ABORT.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [process_id :: uint32_t]
|
|
// [continue_execution :: rdbg_Bool]
|
|
// [dtb :: rdbg_DebuggingTargetBehavior (uint8_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_ATTACH_TO_PROCESS_BY_PID = 304,
|
|
|
|
// Attach to a process by the given name. The first process found, in the
|
|
// case there are more than one with the same name, is used. The value of
|
|
// |continue_execution| indicates whether the process should resume execution
|
|
// after attached. The debugger target behavior specifies what should happen
|
|
// in the case when the target is being debugged (suspended or executing).
|
|
// Can return: RDBG_COMMAND_RESULT_OK, RDBG_COMMAND_RESULT_FAIL, or
|
|
// RDBG_COMMAND_RESULT_ABORT.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [process_name :: rdbg_String]
|
|
// [continue_execution :: rdbg_Bool]
|
|
// [dtb :: rdbg_DebuggingTargetBehavior (uint8_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_ATTACH_TO_PROCESS_BY_NAME = 305,
|
|
|
|
// Detach from a target that is being debugged. Can return
|
|
// RDBG_COMMAND_RESULT_OK or RDBG_COMMAND_RESULT_INVALID_TARGET_STATE.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_DETACH_FROM_PROCESS = 306,
|
|
|
|
// With the target suspended, step into by line. If a function call occurs,
|
|
// this command will enter the function.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_STEP_INTO_BY_LINE = 307,
|
|
|
|
// With the target suspended, step into by instruction. If a function call
|
|
// occurs, this command will enter the function. Can return
|
|
// RDBG_COMMAND_RESULT_OK or RDBG_COMMAND_RESULT_INVALID_TARGET_STATE.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_STEP_INTO_BY_INSTRUCTION = 308,
|
|
|
|
// With the target suspended, step into by line. If a function call occurs,
|
|
// this command step over that function and not enter it. Can return
|
|
// return RDBG_COMMAND_RESULT_OK or RDBG_COMMAND_RESULT_INVALID_TARGET_STATE.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_STEP_OVER_BY_LINE = 309,
|
|
|
|
// With the target suspended, step into by instruction. If a function call
|
|
// occurs, this command will step over that function and not enter it. Can
|
|
// return RDBG_COMMAND_RESULT_OK or RDBG_COMMAND_RESULT_INVALID_TARGET_STATE.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_STEP_OVER_BY_INSTRUCTION = 310,
|
|
|
|
// With the target suspended, continue running to the call site of the
|
|
// current function, i.e., step out.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_STEP_OUT = 311,
|
|
|
|
// With the target suspended, continue execution. Can return
|
|
// RDBG_COMMAND_RESULT_OK or RDBG_COMMAND_RESULT_INVALID_TARGET_STATE.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_CONTINUE_EXECUTION = 312,
|
|
|
|
// When the target is not being debugged or is suspended, run to the given
|
|
// filename and line number.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [filename :: rdbg_String]
|
|
// [line_num :: uint32_t]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_RUN_TO_FILE_AT_LINE = 313,
|
|
|
|
// Halt the execution of a target that is in the executing state. Can return
|
|
// RDBG_COMMAND_RESULT_OK or RDBG_COMMAND_RESULT_INVALID_TARGET_STATE.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_BREAK_EXECUTION = 314,
|
|
|
|
//
|
|
// Breakpoints
|
|
|
|
// Return the current list of breakpoints. These are the user requested
|
|
// breakpoints. Resolved breakpoint locations, if any, for a requested
|
|
// breakpoint can be obtained using RDBG_COMMAND_GET_BREAKPOINT_LOCATIONS.
|
|
//
|
|
// * Presently, module name is not used and will always be a zero length
|
|
// string.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
// [num_bps :: uint16_t]
|
|
// .FOR(num_bps) {
|
|
// [uid :: rdbg_Id]
|
|
// [enabled :: rdbg_Bool]
|
|
// [module_name :: rdbg_String]
|
|
// [condition_expr :: rdbg_String]
|
|
// [kind :: rdbg_BreakpointKind (uint8_t)]
|
|
// .SWITCH(kind) {
|
|
// .CASE(BreakpointKind_FunctionName):
|
|
// [function_name :: rdbg_String]
|
|
// [overload_id :: uint32_t]
|
|
// .CASE(BreakpointKind_FilenameLine):
|
|
// [filename :: rdbg_String]
|
|
// [line_num :: uint32_t]
|
|
// .CASE(BreakpointKind_Address):
|
|
// [address :: uint64_t]
|
|
// .CASE(BreakpointKind_Processor):
|
|
// [addr_expression :: rdbg_String]
|
|
// [num_bytes :: uint8_t]
|
|
// [access_kind :: rdbg_ProcessorBreakpointAccessKind (uint8_t)]
|
|
// }
|
|
// }
|
|
RDBG_COMMAND_GET_BREAKPOINTS = 600,
|
|
|
|
// Return the list of resolved locations for a particular breakpoint. If the
|
|
// ID is not valid for the current session RDBG_COMMAND_RESULT_INVALID_ID is
|
|
// returned.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [bp_id :: rdbg_Id]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
// [num_locs :: uint16_t]
|
|
// .FOR(num_locs) {
|
|
// [address :: uint64_t]
|
|
// [module_name :: rdbg_String]
|
|
// [filename :: rdbg_String]
|
|
// [actual_line_num :: uint32_t]
|
|
// }
|
|
RDBG_COMMAND_GET_BREAKPOINT_LOCATIONS = 601,
|
|
|
|
// Return a list of function overloads for the given function name. If the
|
|
// target is being debugged (suspended or executing) then returns a list of
|
|
// function overloads for the given function name, otherwise
|
|
// RDBG_COMMAND_RESULT_INVALID_TARGET_STATE is returned. Note that,
|
|
// presently, all modules are searched for the given function.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [function_name :: rdbg_String]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
// [num_overloads :: uint8_t]
|
|
// .FOR(num_overloads) {
|
|
// [overload_id :: rdbg_Id]
|
|
// [signature :: rdbg_String]
|
|
// }
|
|
RDBG_COMMAND_GET_FUNCTION_OVERLOADS = 602,
|
|
|
|
// Request a breakpoint at the given function name and overload. Pass an
|
|
// overload ID of zero to add requested breakpoints for all functions with
|
|
// the given name.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [function_name :: rdbg_String]
|
|
// [overload_id :: rdbg_Id]
|
|
// [condition_expr :: rdbg_String]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
// [bp_id :: rdbg_Id]
|
|
RDBG_COMMAND_ADD_BREAKPOINT_AT_FUNCTION = 603,
|
|
|
|
// Request a breakpoint at the given source file and line number.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [filename :: rdbg_String]
|
|
// [line_num :: uint32_t]
|
|
// [condition_expr :: rdbg_String]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
// [bp_id :: rdbg_Id]
|
|
RDBG_COMMAND_ADD_BREAKPOINT_AT_FILENAME_LINE = 604,
|
|
|
|
// Request a breakpoint at the given address.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [address :: uint64_t]
|
|
// [condition_expr :: rdbg_String]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
// [bp_id :: rdbg_Id]
|
|
RDBG_COMMAND_ADD_BREAKPOINT_AT_ADDRESS = 605,
|
|
|
|
// Add a processor (hardware) breakpoint.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [addr_expression :: rdbg_String]
|
|
// [num_bytes :: uint8_t]
|
|
// [access_kind :: rdbg_ProcessorBreakpointAccessKind (uint8_t)]
|
|
// [condition_expr :: rdbg_String]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
// [bp_id :: rdbg_Id]
|
|
RDBG_COMMAND_ADD_PROCESSOR_BREAKPOINT = 606,
|
|
|
|
// Sets the conditional expression for the given breakpoint. Can pass in a
|
|
// zero-length string for none.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [bp_id :: rdbg_Id]
|
|
// [condition_expr :: rdbg_String]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_SET_BREAKPOINT_CONDITION = 607,
|
|
|
|
// Given an existing breakpoint of type RDBG_BREAKPOINT_KIND_FILENAME_LINE,
|
|
// update its line number to the given one-based value.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [bp_id :: rdbg_Id]
|
|
// [line_num :: uint32_t]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_UPDATE_BREAKPOINT_LINE = 608,
|
|
|
|
// Enable or disable an existing breakpoint.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [bp_id :: rdbg_Id]
|
|
// [enable :: rdbg_Bool]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_ENABLE_BREAKPOINT = 609,
|
|
|
|
// Delete an existing breakpoint.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [bp_id :: rdbg_Id]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_DELETE_BREAKPOINT = 610,
|
|
|
|
// Delete all existing breakpoints.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_DELETE_ALL_BREAKPOINTS = 611,
|
|
|
|
// Return information about a specific user requested breakpoint.
|
|
//
|
|
// * Presently, module name is not used and will always be a zero length
|
|
// string.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [bp_id :: rdbg_Id]
|
|
// =>
|
|
// [uid :: rdbg_Id]
|
|
// [enabled :: rdbg_Bool]
|
|
// [module_name :: rdbg_String]
|
|
// [condition_expr :: rdbg_String]
|
|
// [kind :: rdbg_BreakpointKind (uint8_t)]
|
|
// .SWITCH(kind) {
|
|
// .CASE(BreakpointKind_FunctionName):
|
|
// [function_name :: rdbg_String]
|
|
// [overload_id :: uint32_t]
|
|
// .CASE(BreakpointKind_FilenameLine):
|
|
// [filename :: rdbg_String]
|
|
// [line_num :: uint32_t]
|
|
// .CASE(BreakpointKind_Address):
|
|
// [address :: uint64_t]
|
|
// .CASE(BreakpointKind_Processor):
|
|
// [addr_expression :: rdbg_String]
|
|
// [num_bytes :: uint8_t]
|
|
// [access_kind :: rdbg_ProcessorBreakpointAccessKind (uint8_t)]
|
|
// }
|
|
RDBG_COMMAND_GET_BREAKPOINT = 612,
|
|
|
|
//
|
|
// Watch Window Expressions
|
|
|
|
// Return a list of watch expressions for the given, one-based watch window,
|
|
// presently ranging in [1,8].
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [window_num :: uint8_t]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
// [num_watches :: uint16_t]
|
|
// .FOR(num_watches) {
|
|
// [uid :: rdbg_Id]
|
|
// [expr :: rdbg_String]
|
|
// [comment :: rdbg_String]
|
|
// }
|
|
RDBG_COMMAND_GET_WATCHES = 700,
|
|
|
|
// Add a watch expresion to the given, one-based watch window. Presently,
|
|
// only single line comments are supported. Spaces will replace any newlines
|
|
// found in a comment.
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [window_num :: uint8_t]
|
|
// [expr :: rdbg_String]
|
|
// [comment :: rdbg_String]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
// [uid :: rdbg_Id]
|
|
RDBG_COMMAND_ADD_WATCH = 701,
|
|
|
|
// Updates the expression for a given watch
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [uid :: rdbg_Id]
|
|
// [expr :: rdbg_String]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_UPDATE_WATCH_EXPRESSION = 702,
|
|
|
|
// Updates the comment for a given watch
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [uid :: rdbg_Id]
|
|
// [comment :: rdbg_String]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_UPDATE_WATCH_COMMENT = 703,
|
|
|
|
// Delete the given watch
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [uid :: rdbg_Id]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_DELETE_WATCH = 704,
|
|
|
|
// Delete all watches in the given watch window
|
|
//
|
|
// [cmd :: rdbg_Command (uint16_t)]
|
|
// [window_num :: uint8_t]
|
|
// ->
|
|
// [result :: rdbg_CommandResult (uint16_t)]
|
|
RDBG_COMMAND_DELETE_ALL_WATCHES = 705,
|
|
};
|
|
|
|
enum rdbg_SourceLocChangedReason
|
|
{
|
|
RDBG_SOURCE_LOC_CHANGED_REASON_UNSPECIFIED = 0,
|
|
|
|
// An open-file from the command-line updated the source location
|
|
RDBG_SOURCE_LOC_CHANGED_REASON_BY_COMMAND_LINE = 1,
|
|
|
|
// A RDBG_COMMAND_GOTO_FILE_AT_LINE from a named-pipes driver updated the source location
|
|
RDBG_SOURCE_LOC_CHANGED_REASON_BY_DRIVER = 2,
|
|
|
|
// A selection of a breakpoint in breakpoints pane updated the source location
|
|
RDBG_SOURCE_LOC_CHANGED_REASON_BREAKPOINT_SELECTED = 3,
|
|
|
|
// The current stack frame was changed in the callstack pane and updated the source location
|
|
RDBG_SOURCE_LOC_CHANGED_REASON_CURRENT_FRAME_CHANGED = 4,
|
|
|
|
// The active thread was changed in the threads pane and updated the source location
|
|
RDBG_SOURCE_LOC_CHANGED_REASON_ACTIVE_THREAD_CHANGED = 5,
|
|
|
|
//
|
|
// The process was suspended and updated the source location
|
|
//
|
|
RDBG_SOURCE_LOC_CHANGED_REASON_BREAKPOINT_HIT = 6,
|
|
RDBG_SOURCE_LOC_CHANGED_REASON_EXCEPTION_HIT = 7,
|
|
RDBG_SOURCE_LOC_CHANGED_REASON_STEP_OVER = 8,
|
|
RDBG_SOURCE_LOC_CHANGED_REASON_STEP_IN = 9,
|
|
RDBG_SOURCE_LOC_CHANGED_REASON_STEP_OUT = 10,
|
|
RDBG_SOURCE_LOC_CHANGED_REASON_NON_USER_BREAKPOINT = 11,
|
|
RDBG_SOURCE_LOC_CHANGED_REASON_DEBUG_BREAK = 12,
|
|
};
|
|
|
|
enum rdbg_DebugEventKind
|
|
{
|
|
// A target being debugged has exited.
|
|
//
|
|
// [kind :: rdbg_DebugEventKind (uint16_t)]
|
|
// [exit_code :: uint32_t]
|
|
RDBG_DEBUG_EVENT_KIND_EXIT_PROCESS = 100,
|
|
|
|
// The target for the active configuration is now being debugged.
|
|
//
|
|
// [kind :: rdbg_DebugEventKind (uint16_t)]
|
|
// [process_id :: uint32_t]
|
|
RDBG_DEBUG_EVENT_KIND_TARGET_STARTED = 101,
|
|
|
|
// The debugger has attached to a target process.
|
|
//
|
|
// [kind :: rdbg_DebugEventKind (uint16_t)]
|
|
// [process_id :: uint32_t]
|
|
RDBG_DEBUG_EVENT_KIND_TARGET_ATTACHED = 102,
|
|
|
|
// The debugger has detached from a target process.
|
|
//
|
|
// [kind :: rdbg_DebugEventKind (uint16_t)]
|
|
// [process_id :: uint32_t]
|
|
RDBG_DEBUG_EVENT_KIND_TARGET_DETACHED = 103,
|
|
|
|
// The debugger has transitioned from suspended to executing.
|
|
//
|
|
// [kind :: rdbg_DebugEventKind (uint16_t)]
|
|
// [process_id :: uint32_t]
|
|
RDBG_DEBUG_EVENT_KIND_TARGET_CONTINUED = 104,
|
|
|
|
// The source location changed due to an event in the debugger.
|
|
//
|
|
// [kind :: rdbg_DebugEventKind (uint16_t)]
|
|
// [filename :: rdbg_String]
|
|
// [line_num :: uint32_t]
|
|
// [reason :: rdbg_SourceLocChangedReason (uint16_t) ]
|
|
RDBG_DEBUG_EVENT_KIND_SOURCE_LOCATION_CHANGED = 200,
|
|
|
|
// An external text editor was requested to be launched for the current file
|
|
// and line number. The event is sent even if an external text editor is not
|
|
// configured and, if configured, even if the external text editor failed to
|
|
// launch for any reason.
|
|
//
|
|
// [kind :: rdbg_DebugEventKind (uint16_t)]
|
|
// [filename :: rdbg_String]
|
|
// [line_num :: uint32_t]
|
|
RDBG_DEBUG_EVENT_KIND_EXTERNAL_TEXT_EDITOR_LAUNCHED = 201,
|
|
|
|
// A user breakpoint was hit
|
|
//
|
|
// [kind :: rdbg_DebugEventKind (uint16_t)]
|
|
// [bp_id :: rdbg_Id]
|
|
RDBG_DEBUG_EVENT_KIND_BREAKPOINT_HIT = 600,
|
|
|
|
// The breakpoint with the given ID has been resolved (has a valid location).
|
|
// This can happen if the breakpoint was set in module that became loaded,
|
|
// for instance.
|
|
//
|
|
// [kind :: rdbg_DebugEventKind (uint16_t)]
|
|
// [bp_id :: rdbg_Id]
|
|
RDBG_DEBUG_EVENT_KIND_BREAKPOINT_RESOLVED = 601,
|
|
|
|
// A new user breakpoint was added.
|
|
//
|
|
// [kind :: rdbg_DebugEventKind (uint16_t)]
|
|
// [bp_id :: rdbg_Id]
|
|
RDBG_DEBUG_EVENT_KIND_BREAKPOINT_ADDED = 602,
|
|
|
|
// A user breakpoint was modified.
|
|
//
|
|
// [kind :: rdbg_DebugEventKind (uint16_t)]
|
|
// [bp_id :: rdbg_Id]
|
|
RDBG_DEBUG_EVENT_KIND_BREAKPOINT_MODIFIED = 603,
|
|
|
|
// A user breakpoint was removed.
|
|
//
|
|
// [kind :: rdbg_DebugEventKind (uint16_t)]
|
|
// [bp_id :: rdbg_Id]
|
|
RDBG_DEBUG_EVENT_KIND_BREAKPOINT_REMOVED = 604,
|
|
|
|
// An OutputDebugString was received by the debugger. The given string will
|
|
// be UTF-8 encoded.
|
|
//
|
|
// [kind :: rdbg_DebugEventKind (uint16_t)]
|
|
// [str :: rdbg_String]
|
|
RDBG_DEBUG_EVENT_KIND_OUTPUT_DEBUG_STRING = 800,
|
|
};
|
|
|
|
// Sample usage of driving an instance of RemedyBG using named pipes.
|
|
//
|
|
// To use: first, start an named instance of RemedyBG using the "--servername"
|
|
// switch.
|
|
//
|
|
// remedybg.exe --servername {name}
|
|
//
|
|
// After RemedyBG is started, this sample can be run to control and make queries
|
|
// to the running instance.
|
|
//
|
|
// driver {name}
|
|
//
|
|
// The sample code demonstrates the usage of both these pipes and is written
|
|
// using non-overlapped IO for simplicity. Depending on your application, using
|
|
// non-blocking IO may be preferable.
|
|
|
|
|
|
#define COMMAND_BUF_SIZE 8192
|
|
#define REPLY_BUF_SIZE 8192
|
|
#define ERROR_MSG_LEN 512
|
|
|
|
enum PipeType
|
|
{
|
|
DebugControlPipe,
|
|
DebugEventsPipe
|
|
};
|
|
|
|
struct rdb_Buffer
|
|
{
|
|
uint8_t* data;
|
|
uint8_t* curr;
|
|
uint32_t capacity;
|
|
|
|
bool err; // true if overflow (read or write) on the buffer
|
|
};
|
|
|
|
static void ReinitBuffer(struct rdb_Buffer* buf)
|
|
{
|
|
buf->curr = buf->data;
|
|
buf->err = false;
|
|
}
|
|
|
|
struct ClientContext
|
|
{
|
|
HANDLE command_pipe_handle;
|
|
|
|
struct rdb_Buffer cmd;
|
|
struct rdb_Buffer reply;
|
|
|
|
// Stateful behavior so we don't have to pass these to every command that
|
|
// needs them.
|
|
enum rdbg_DebuggingTargetBehavior dbg_target_behavior;
|
|
enum rdbg_ModifiedSessionBehavior mod_session_behavior;
|
|
|
|
char last_error[ERROR_MSG_LEN];
|
|
};
|
|
|
|
static void WriteError(int err_msg_len, char* err_msg, char* format, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, format);
|
|
|
|
int n = vsnprintf(err_msg, err_msg_len, format, args);
|
|
if (n > 0)
|
|
{
|
|
FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL, GetLastError(), 0, err_msg + n,
|
|
err_msg_len - n, 0);
|
|
}
|
|
|
|
va_end(args);
|
|
}
|
|
|
|
static bool ContextHadError(struct ClientContext* ctx)
|
|
{
|
|
return ctx->last_error[0] != 0 || ctx->cmd.err || ctx->reply.err;
|
|
}
|
|
|
|
//
|
|
// Utilities for working with a ClientContext buffers
|
|
//
|
|
#define SetErrIfOverwrite(buf, sz) \
|
|
(buf)->err = (buf)->err || ((buf)->curr > ((buf)->data + (buf)->capacity) - sz)
|
|
|
|
#define PushBuffer(buf, ty, val) \
|
|
SetErrIfOverwrite(buf, sizeof(ty)); \
|
|
if (!(buf)->err) { *(ty*)((buf)->curr) = (val); (buf)->curr += sizeof(ty); }
|
|
|
|
#define PushCommand(buf, cmd) PushBuffer(buf, uint16_t, (uint16_t)(cmd))
|
|
#define PushDebuggingTargetBehavior(buf, dtb) \
|
|
PushBuffer(buf, uint8_t, (uint8_t)dtb)
|
|
#define PushModifiedSessionBehavior(buf, msb) \
|
|
PushBuffer(buf, uint8_t, (uint8_t)msb)
|
|
#define PushBool(buf, val) PushBuffer(buf, uint8_t, (uint8_t)(val))
|
|
#define PushId(buf, val) PushBuffer(buf, rdbg_Id, (rdbg_Id)(val))
|
|
#define PushU8(buf, val) PushBuffer(buf, uint8_t, (uint8_t)(val))
|
|
#define PushU32(buf, val) PushBuffer(buf, uint32_t, (uint32_t)(val))
|
|
#define PushS32(buf, val) PushBuffer(buf, int32_t, (int32_t)(val))
|
|
#define PushU64(buf, val) PushBuffer(buf, uint64_t, (uint64_t)(val))
|
|
#define PushProcessorBreakpointAccessKind(buf, akind) \
|
|
PushBuffer(buf, uint8_t, (uint8_t)akind)
|
|
|
|
static void PushStringZ(struct rdb_Buffer* b, char* str)
|
|
{
|
|
uint16_t len = str ? (uint16_t)strlen(str) : 0;
|
|
PushBuffer(b, uint16_t, len);
|
|
SetErrIfOverwrite(b, len);
|
|
if (!b->err && len > 0)
|
|
{
|
|
memcpy(b->curr, str, len);
|
|
b->curr += len;
|
|
}
|
|
}
|
|
|
|
#define PopBuffer(buf, ty) ( \
|
|
(buf)->err = (buf)->err || \
|
|
((buf)->curr > ((buf)->data + (buf)->capacity) - sizeof(ty)), \
|
|
(buf)->curr += ((buf)->err == 0 ? sizeof(ty) : 0), \
|
|
(buf)->err == 0 ? *(ty*)((buf)->curr - sizeof(ty)) : (ty)0 )
|
|
|
|
#define PopBool(buf) (rdbg_Bool)PopBuffer(buf, uint8_t)
|
|
#define PopU8(buf) PopBuffer(buf, uint8_t)
|
|
#define PopU16(buf) PopBuffer(buf, uint16_t)
|
|
#define PopU32(buf) PopBuffer(buf, uint32_t)
|
|
#define PopS32(buf) PopBuffer(buf, int32_t)
|
|
#define PopU64(buf) PopBuffer(buf, uint64_t)
|
|
#define PopId(buf) (rdbg_Id)PopU32(buf)
|
|
#define PopCommandResult(buf) (enum rdbg_CommandResult)PopBuffer(buf, uint16_t)
|
|
#define PopTargetState(buf) (enum rdbg_TargetState)PopBuffer(buf, uint16_t)
|
|
#define PopBreakpointKind(buf) (enum rdbg_BreakpointKind)PopBuffer(buf, uint8_t)
|
|
#define PopProcessorBreakpointAccessKind(buf) \
|
|
(enum rdbg_ProcessorBreakpointAccessKind)PopBuffer(buf, uint8_t)
|
|
#define PopDebugEventKind(buf) (enum rdbg_DebugEventKind)PopBuffer(buf, uint16_t)
|
|
|
|
static void PopString(struct rdb_Buffer* buf, struct rdbg_String** str)
|
|
{
|
|
uint16_t len = PopBuffer(buf, uint16_t);
|
|
buf->err = buf->err || buf->curr > buf->data + buf->capacity + len;
|
|
if (!buf->err)
|
|
{
|
|
*str = (struct rdbg_String*)(buf->curr - sizeof(uint16_t));
|
|
buf->curr += len;
|
|
}
|
|
else
|
|
{
|
|
*str = 0;
|
|
}
|
|
}
|
|
|
|
#define PIPE_NAME_PREFIX "\\\\.\\pipe\\"
|
|
#define PIPE_NAME_PREFIX_LEN 9
|
|
|
|
bool InitConnection(char* server_name, enum PipeType pipe_type,
|
|
int last_error_len, char* last_error, HANDLE* ret_pipe_handle)
|
|
{
|
|
bool result = false;
|
|
|
|
unsigned len = (unsigned)strlen(server_name);
|
|
if (len <= RDBG_MAX_SERVERNAME_LEN)
|
|
{
|
|
char pipe_name[256] = PIPE_NAME_PREFIX;
|
|
strcat(pipe_name, server_name);
|
|
if (pipe_type == DebugEventsPipe)
|
|
{
|
|
strcat(pipe_name, "-events");
|
|
}
|
|
|
|
DWORD flags = pipe_type == DebugControlPipe ?
|
|
GENERIC_READ | GENERIC_WRITE :
|
|
GENERIC_READ | FILE_WRITE_ATTRIBUTES;
|
|
|
|
HANDLE pipe_handle = CreateFile(pipe_name, flags, 0, NULL, OPEN_EXISTING,
|
|
0, NULL);
|
|
if (pipe_handle != INVALID_HANDLE_VALUE)
|
|
{
|
|
DWORD new_mode = PIPE_READMODE_MESSAGE;
|
|
if (SetNamedPipeHandleState(pipe_handle, &new_mode, NULL, NULL))
|
|
{
|
|
*ret_pipe_handle = pipe_handle;
|
|
result = true;
|
|
}
|
|
else
|
|
{
|
|
WriteError(last_error_len, last_error,
|
|
"SetNamedPipeHandleState failed: ");
|
|
CloseHandle(pipe_handle);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WriteError(last_error_len, last_error, "CreateFile failed: ");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WriteError(last_error_len, last_error, "Server name too long.");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void CloseConnection(struct ClientContext* ctx)
|
|
{
|
|
CloseHandle(ctx->command_pipe_handle);
|
|
}
|
|
|
|
static void TransactCommand(struct ClientContext* ctx)
|
|
{
|
|
DWORD bytes_read = 0;
|
|
BOOL res = 0;
|
|
struct rdb_Buffer* reply = &ctx->reply;
|
|
|
|
ReinitBuffer(reply);
|
|
|
|
if (!ContextHadError(ctx))
|
|
{
|
|
uint8_t* write_ptr = reply->data;
|
|
|
|
res = TransactNamedPipe(ctx->command_pipe_handle, ctx->cmd.data,
|
|
(uint32_t)(ctx->cmd.curr - ctx->cmd.data), write_ptr,
|
|
REPLY_BUF_SIZE, &bytes_read, NULL);
|
|
write_ptr += bytes_read;
|
|
|
|
while (!res && GetLastError() == ERROR_MORE_DATA)
|
|
{
|
|
int bytes_left = REPLY_BUF_SIZE - (int)(write_ptr - reply->data);
|
|
if (bytes_left > 0)
|
|
{
|
|
res = ReadFile(ctx->command_pipe_handle, write_ptr, bytes_left,
|
|
&bytes_read, NULL);
|
|
write_ptr += bytes_read;
|
|
}
|
|
else
|
|
break; // reply buffer full
|
|
}
|
|
if (res)
|
|
{
|
|
reply->capacity = (uint32_t)(write_ptr - reply->data);
|
|
}
|
|
else
|
|
{
|
|
WriteError(sizeof(ctx->last_error), ctx->last_error,
|
|
"TransactCommand failed: ");
|
|
}
|
|
}
|
|
}
|
|
|
|
#define BeginCommand(ctx, c) \
|
|
ReinitBuffer(&((ctx)->cmd)); \
|
|
PushCommand(&((ctx)->cmd), c)
|
|
|
|
#define SendCommand(ctx, c, res) \
|
|
BeginCommand(ctx, c); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply))
|
|
|
|
#define BeginCommandWithFlags(ctx, c) \
|
|
BeginCommand(ctx, c); \
|
|
PushDebuggingTargetBehavior(&((ctx)->cmd), (ctx)->dbg_target_behavior); \
|
|
PushModifiedSessionBehavior(&((ctx)->cmd), (ctx)->mod_session_behavior)
|
|
|
|
#define SendCommandWithFlags(ctx, c, res) \
|
|
BeginCommandWithFlags(ctx, c); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply))
|
|
|
|
#define BringDebuggerToForeground(ctx, res) \
|
|
SendCommand(ctx, RDBG_COMMAND_BRING_DEBUGGER_TO_FOREGROUND, res)
|
|
|
|
#define SetDebuggerWindowPos(ctx, x, y, cx, cy, res) \
|
|
BeginCommand(ctx, RDBG_COMMAND_SET_WINDOW_POS); \
|
|
PushS32(&(ctx)->cmd, x); \
|
|
PushS32(&(ctx)->cmd, y); \
|
|
PushS32(&(ctx)->cmd, cx); \
|
|
PushS32(&(ctx)->cmd, cy); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply))
|
|
|
|
#define GetDebuggerWindowPos(ctx, res, x, y, cx, cy, is_maximized) \
|
|
SendCommand(ctx, RDBG_COMMAND_GET_WINDOW_POS, res); \
|
|
*(x) = PopId(&((ctx)->reply)); \
|
|
*(y) = PopId(&((ctx)->reply)); \
|
|
*(cx) = PopId(&((ctx)->reply)); \
|
|
*(cy) = PopId(&((ctx)->reply)); \
|
|
*(is_maximized) = PopBool(&((ctx)->reply)) != 0
|
|
|
|
#define SetBringToForegroundOnSuspended(ctx, btfos, res) \
|
|
BeginCommand(ctx, RDBG_COMMAND_SET_BRING_TO_FOREGROUND_ON_SUSPENDED); \
|
|
PushBool(&(ctx)->cmd, btfos); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply))
|
|
|
|
#define ExitDebugger(ctx, res) \
|
|
SendCommandWithFlags(ctx, RDBG_COMMAND_EXIT_DEBUGGER, res)
|
|
|
|
#define GetIsSessionModified(ctx, res, is_modified) \
|
|
SendCommand(ctx, RDBG_COMMAND_GET_IS_SESSION_MODIFIED, res); \
|
|
*(is_modified) = PopBool(&((ctx)->reply)) != 0
|
|
|
|
#define GetSessionFilename(ctx, res, filename) \
|
|
SendCommand(ctx, RDBG_COMMAND_GET_SESSION_FILENAME, res); \
|
|
PopString(&((ctx)->reply), filename)
|
|
|
|
#define NewSession(ctx, res) \
|
|
SendCommandWithFlags(ctx, RDBG_COMMAND_NEW_SESSION, res)
|
|
|
|
#define SendCommandWithString(ctx, c, str, res) \
|
|
BeginCommand(ctx, c); \
|
|
PushStringZ(&(ctx)->cmd, str); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply))
|
|
|
|
#define OpenSession(ctx, filename, res) \
|
|
SendCommandWithString(ctx, RDBG_COMMAND_OPEN_SESSION, filename, res)
|
|
|
|
#define SaveSession(ctx, res) \
|
|
SendCommandWithFlags(ctx, RDBG_COMMAND_SAVE_SESSION, res)
|
|
|
|
#define SaveAsSession(ctx, filename, res) \
|
|
SendCommandWithString(ctx, RDBG_COMMAND_SAVE_AS_SESSION, filename, res)
|
|
|
|
#define GetSessionConfigs(ctx, res, cfg_it) \
|
|
SendCommand(ctx, RDBG_COMMAND_GET_SESSION_CONFIGS, res); \
|
|
BufIterator_Init(cfg_it, PopBuffer(&((ctx)->reply), uint16_t), \
|
|
((ctx)->reply))
|
|
|
|
#define AddSessionConfig(ctx, command, args, wdir, env, inh, brk, res, id) \
|
|
BeginCommand(ctx, RDBG_COMMAND_ADD_SESSION_CONFIG); \
|
|
PushStringZ(&(ctx)->cmd, command); \
|
|
PushStringZ(&(ctx)->cmd, args); \
|
|
PushStringZ(&(ctx)->cmd, wdir); \
|
|
PushStringZ(&(ctx)->cmd, env); \
|
|
PushBool(&(ctx)->cmd, inh); \
|
|
PushBool(&(ctx)->cmd, brk); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply)); \
|
|
*(id) = PopId(&((ctx)->reply))
|
|
|
|
#define SendCommandWithId(ctx, c, id, res) \
|
|
BeginCommand(ctx, c); \
|
|
PushId(&(ctx)->cmd, id); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply))
|
|
|
|
#define SetActiveSessionConfig(ctx, id, res) \
|
|
SendCommandWithId(ctx, RDBG_COMMAND_SET_ACTIVE_SESSION_CONFIG, id, res)
|
|
|
|
#define DeleteSessionConfig(ctx, id, res) \
|
|
SendCommandWithId(ctx, RDBG_COMMAND_DELETE_SESSION_CONFIG, id, res)
|
|
|
|
#define DeleteAllSessionConfigs(ctx, res) \
|
|
SendCommand(ctx, RDBG_COMMAND_DELETE_ALL_SESSION_CONFIGS, res)
|
|
|
|
#define GoToFileAtLine(ctx, filename, line, res, id) \
|
|
BeginCommand(ctx, RDBG_COMMAND_GOTO_FILE_AT_LINE); \
|
|
PushStringZ(&(ctx)->cmd, filename); \
|
|
PushBuffer(&(ctx)->cmd, uint32_t, line); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply)); \
|
|
*(id) = PopId(&((ctx)->reply))
|
|
|
|
#define CloseFileById(ctx, id, res) \
|
|
SendCommandWithId(ctx, RDBG_COMMAND_CLOSE_FILE, id, res)
|
|
|
|
#define CloseAllFiles(ctx, res) \
|
|
SendCommand(ctx, RDBG_COMMAND_CLOSE_ALL_FILES, res)
|
|
|
|
#define GetCurrentFile(ctx, res, file_id, filename, line_num) \
|
|
SendCommand(ctx, RDBG_COMMAND_GET_CURRENT_FILE, res); \
|
|
*(file_id) = PopId(&(ctx)->reply); \
|
|
PopString(&(ctx)->reply, filename); \
|
|
*(line_num) = PopBuffer(&(ctx)->reply, uint32_t)
|
|
|
|
#define GetOpenFiles(ctx, res, file_it) \
|
|
SendCommand(ctx, RDBG_COMMAND_GET_OPEN_FILES, res); \
|
|
BufIterator_Init(file_it, PopBuffer(&((ctx)->reply), uint16_t), ((ctx)->reply))
|
|
|
|
#define StartDebugging(ctx, break_at_entry_point, res) \
|
|
BeginCommand(ctx, RDBG_COMMAND_START_DEBUGGING); \
|
|
PushBool(&(ctx)->cmd, break_at_entry_point); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply))
|
|
|
|
#define StopDebugging(ctx, res) \
|
|
SendCommand(ctx, RDBG_COMMAND_STOP_DEBUGGING, res)
|
|
|
|
#define RestartDebugging(ctx, res) \
|
|
SendCommand(ctx, RDBG_COMMAND_RESTART_DEBUGGING, res)
|
|
|
|
#define AttachToProcessById(ctx, pid, cnt, res) \
|
|
BeginCommand(ctx, RDBG_COMMAND_ATTACH_TO_PROCESS_BY_PID); \
|
|
PushBuffer(&(ctx)->cmd, uint32_t, (uint32_t)(pid)); \
|
|
PushBool(&(ctx)->cmd, cnt); \
|
|
PushDebuggingTargetBehavior(&((ctx)->cmd), (ctx)->dbg_target_behavior); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply))
|
|
|
|
#define AttachToProcessByName(ctx, name, cnt, res) \
|
|
BeginCommand(ctx, RDBG_COMMAND_ATTACH_TO_PROCESS_BY_NAME); \
|
|
PushStringZ(&(ctx)->cmd, name); \
|
|
PushBool(&(ctx)->cmd, cnt); \
|
|
PushDebuggingTargetBehavior(&((ctx)->cmd), (ctx)->dbg_target_behavior); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply))
|
|
|
|
#define DetachFromProcess(ctx, res) \
|
|
SendCommand(ctx, RDBG_COMMAND_DETACH_FROM_PROCESS, res)
|
|
|
|
#define StepIntoByLine(ctx, res) \
|
|
SendCommand(ctx, RDBG_COMMAND_STEP_INTO_BY_LINE, res)
|
|
|
|
#define StepIntoByInstruction(ctx, res) \
|
|
SendCommand(ctx, RDBG_COMMAND_STEP_INTO_BY_INSTRUCTION, res)
|
|
|
|
#define StepOverByLine(ctx, res) \
|
|
SendCommand(ctx, RDBG_COMMAND_STEP_OVER_BY_LINE, res)
|
|
|
|
#define StepOverByInstruction(ctx, res) \
|
|
SendCommand(ctx, RDBG_COMMAND_STEP_OVER_BY_INSTRUCTION, res)
|
|
|
|
#define StepOut(ctx, res) \
|
|
SendCommand(ctx, RDBG_COMMAND_STEP_OUT, res)
|
|
|
|
#define ContinueExecution(ctx, res) \
|
|
SendCommand(ctx, RDBG_COMMAND_CONTINUE_EXECUTION, res)
|
|
|
|
#define RunToFileAtLine(ctx, filename, line, res) \
|
|
BeginCommand(ctx, RDBG_COMMAND_RUN_TO_FILE_AT_LINE); \
|
|
PushStringZ(&(ctx)->cmd, filename); \
|
|
PushBuffer(&(ctx)->cmd, uint32_t, line); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply))
|
|
|
|
#define BreakExecution(ctx, res) \
|
|
SendCommand(ctx, RDBG_COMMAND_BREAK_EXECUTION, res)
|
|
|
|
#define GetTargetState(ctx, res, state) \
|
|
SendCommand(ctx, RDBG_COMMAND_GET_TARGET_STATE, res); \
|
|
*(state) = PopTargetState(&((ctx)->reply))
|
|
|
|
#define GetAllBreakpoints(ctx, res, bp_it) \
|
|
SendCommand(ctx, RDBG_COMMAND_GET_BREAKPOINTS, res); \
|
|
BufIterator_Init(bp_it, PopBuffer(&((ctx)->reply), uint16_t), ((ctx)->reply))
|
|
|
|
#define GetBreakpoint(ctx, id, res, bp) \
|
|
SendCommandWithId(ctx, RDBG_COMMAND_GET_BREAKPOINT, id, res); \
|
|
PopBreakpoint(&((ctx)->reply), bp)
|
|
|
|
#define GetBreakpointLocations(ctx, id, res, num_locs) \
|
|
SendCommandWithId(ctx, RDBG_COMMAND_GET_BREAKPOINT_LOCATIONS, id, res); \
|
|
*(num_locs) = PopU16(&((ctx)->reply))
|
|
|
|
#define GetFunctionOverloads(ctx, res, fn_name, fno_it) \
|
|
BeginCommand(ctx, RDBG_COMMAND_GET_FUNCTION_OVERLOADS); \
|
|
PushStringZ(&(ctx)->cmd, fn_name); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply)); \
|
|
BufIterator_Init(fno_it, PopU8(&((ctx)->reply)), ((ctx)->reply))
|
|
|
|
#define AddBreakpointAtFn(ctx, fn_name, oid, cond, res, bp_id) \
|
|
BeginCommand(ctx, RDBG_COMMAND_ADD_BREAKPOINT_AT_FUNCTION); \
|
|
PushStringZ(&(ctx)->cmd, fn_name); \
|
|
PushId(&(ctx)->cmd, oid) \
|
|
PushStringZ(&(ctx)->cmd, cond); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply)); \
|
|
*(bp_id) = PopId(&((ctx)->reply))
|
|
|
|
#define AddBreakpointAtFilenameLine(ctx, file, line, cond, res, bp_id) \
|
|
BeginCommand(ctx, RDBG_COMMAND_ADD_BREAKPOINT_AT_FILENAME_LINE); \
|
|
PushStringZ(&(ctx)->cmd, file); \
|
|
PushU32(&(ctx)->cmd, line) \
|
|
PushStringZ(&(ctx)->cmd, cond); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply)); \
|
|
*(bp_id) = PopId(&((ctx)->reply))
|
|
|
|
#define AddBreakpointAtAddress(ctx, addr, cond, res, bp_id) \
|
|
BeginCommand(ctx, RDBG_COMMAND_ADD_BREAKPOINT_AT_ADDRESS); \
|
|
PushU64(&(ctx)->cmd, addr) \
|
|
PushStringZ(&(ctx)->cmd, cond); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply)); \
|
|
*(bp_id) = PopId(&((ctx)->reply))
|
|
|
|
#define AddProcessorBreakpoint(ctx, addr_expr, nbytes, akind, cond, res, bp_id) \
|
|
BeginCommand(ctx, RDBG_COMMAND_ADD_PROCESSOR_BREAKPOINT); \
|
|
PushStringZ(&(ctx)->cmd, addr_expr); \
|
|
PushU8(&(ctx)->cmd, nbytes); \
|
|
PushProcessorBreakpointAccessKind(&(ctx)->cmd, akind); \
|
|
PushStringZ(&(ctx)->cmd, cond); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply)); \
|
|
*(bp_id) = PopId(&((ctx)->reply))
|
|
|
|
#define SetBreakpointCondition(ctx, bp_id, cond, res) \
|
|
BeginCommand(ctx, RDBG_COMMAND_SET_BREAKPOINT_CONDITION); \
|
|
PushId(&(ctx)->cmd, bp_id) \
|
|
PushStringZ(&(ctx)->cmd, cond); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply))
|
|
|
|
#define UpdateBreakpointLine(ctx, bp_id, line, res) \
|
|
BeginCommand(ctx, RDBG_COMMAND_UPDATE_BREAKPOINT_LINE); \
|
|
PushId(&(ctx)->cmd, bp_id); \
|
|
PushU32(&(ctx)->cmd, line); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply))
|
|
|
|
#define EnableBreakpoint(ctx, bp_id, enable, res) \
|
|
BeginCommand(ctx, RDBG_COMMAND_ENABLE_BREAKPOINT); \
|
|
PushId(&(ctx)->cmd, bp_id); \
|
|
PushBool(&(ctx)->cmd, enable); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply))
|
|
|
|
#define DeleteBreakpoint(ctx, bp_id, res) \
|
|
BeginCommand(ctx, RDBG_COMMAND_DELETE_BREAKPOINT); \
|
|
PushId(&(ctx)->cmd, bp_id); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply))
|
|
|
|
#define DeleteAllBreakpoints(ctx, res) \
|
|
SendCommand(ctx, RDBG_COMMAND_DELETE_ALL_BREAKPOINTS, res)
|
|
|
|
#define GetWatches(ctx, window_num, res, it) \
|
|
BeginCommand(ctx, RDBG_COMMAND_GET_WATCHES); \
|
|
PushU8(&(ctx)->cmd, window_num); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply)); \
|
|
BufIterator_Init(it, PopU16(&((ctx)->reply)), ((ctx)->reply))
|
|
|
|
#define AddWatch(ctx, window_num, expr, comment, res, id) \
|
|
BeginCommand(ctx, RDBG_COMMAND_ADD_WATCH); \
|
|
PushU8(&(ctx)->cmd, window_num); \
|
|
PushStringZ(&(ctx)->cmd, expr); \
|
|
PushStringZ(&(ctx)->cmd, comment); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply)); \
|
|
*id = PopId(&((ctx)->reply))
|
|
|
|
#define UpdateWatchExpression(ctx, id, expr, res) \
|
|
BeginCommand(ctx, RDBG_COMMAND_UPDATE_WATCH_EXPRESSION); \
|
|
PushId(&(ctx)->cmd, id); \
|
|
PushStringZ(&(ctx)->cmd, expr); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply))
|
|
|
|
#define UpdateWatchComment(ctx, id, comment, res) \
|
|
BeginCommand(ctx, RDBG_COMMAND_UPDATE_WATCH_COMMENT); \
|
|
PushId(&(ctx)->cmd, id); \
|
|
PushStringZ(&(ctx)->cmd, comment); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply))
|
|
|
|
#define DeleteWatch(ctx, id, res) \
|
|
SendCommandWithId(ctx, RDBG_COMMAND_DELETE_WATCH, id, res)
|
|
|
|
#define DeleteAllWatches(ctx, window_num, res) \
|
|
BeginCommand(ctx, RDBG_COMMAND_DELETE_ALL_WATCHES); \
|
|
PushU8(&(ctx)->cmd, window_num); \
|
|
TransactCommand((ctx)); \
|
|
*(res) = PopCommandResult(&((ctx)->reply))
|
|
|
|
struct BufIterator
|
|
{
|
|
int n;
|
|
int cur_idx;
|
|
struct rdb_Buffer buf;
|
|
};
|
|
|
|
static void BufIterator_Init(struct BufIterator* it, int n,
|
|
struct rdb_Buffer b)
|
|
{
|
|
it->n = n;
|
|
it->cur_idx = -1;
|
|
|
|
// Make a copy of reply buffer so we can make additional calls within loop.
|
|
static uint8_t it_buf[REPLY_BUF_SIZE];
|
|
it->buf.data = it_buf;
|
|
it->buf.curr = it_buf + (b.curr - b.data);
|
|
it->buf.capacity = b.capacity;
|
|
it->buf.err = b.err;
|
|
if (!b.err)
|
|
{
|
|
memcpy(it->buf.data, b.data, b.capacity);
|
|
}
|
|
}
|
|
|
|
struct SessionConfig
|
|
{
|
|
rdbg_Id id;
|
|
struct rdbg_String* command;
|
|
struct rdbg_String* command_args;
|
|
struct rdbg_String* working_dir;
|
|
struct rdbg_String* environment_vars;
|
|
rdbg_Bool inherit_environment_vars_from_parent;
|
|
rdbg_Bool break_at_nominal_entry_point;
|
|
};
|
|
|
|
static bool SessionConfigIt_Next(struct BufIterator* it,
|
|
struct SessionConfig* cfg)
|
|
{
|
|
bool result = false;
|
|
if (++it->cur_idx < it->n)
|
|
{
|
|
struct rdb_Buffer* b = &it->buf;
|
|
|
|
cfg->id = PopId(b);
|
|
PopString(b, &cfg->command);
|
|
PopString(b, &cfg->command_args);
|
|
PopString(b, &cfg->working_dir);
|
|
PopString(b, &cfg->environment_vars);
|
|
cfg->inherit_environment_vars_from_parent = PopBool(b);
|
|
cfg->break_at_nominal_entry_point = PopBool(b);
|
|
|
|
result = true;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
struct File
|
|
{
|
|
rdbg_Id id;
|
|
struct rdbg_String* filename;
|
|
uint32_t line_num;
|
|
};
|
|
|
|
static bool FileIt_Next(struct BufIterator* it, struct File* file)
|
|
{
|
|
bool result = false;
|
|
if (++it->cur_idx < it->n)
|
|
{
|
|
struct rdb_Buffer* b = &it->buf;
|
|
|
|
file->id = PopId(b);
|
|
PopString(b, &file->filename);
|
|
file->line_num = PopBuffer(b, uint32_t);
|
|
|
|
result = true;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
struct Breakpoint
|
|
{
|
|
rdbg_Id uid;
|
|
rdbg_Bool enabled;
|
|
struct rdbg_String* module_name;
|
|
struct rdbg_String* condition_expr;
|
|
enum rdbg_BreakpointKind kind;
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
struct rdbg_String* function_name;
|
|
rdbg_Id overload_id;
|
|
};
|
|
struct
|
|
{
|
|
struct rdbg_String* filename;
|
|
uint32_t line_num;
|
|
};
|
|
uint64_t address;
|
|
struct
|
|
{
|
|
struct rdbg_String* expression;
|
|
uint8_t num_bytes;
|
|
enum rdbg_InternalProcessorBreakpointType access_kind;
|
|
};
|
|
};
|
|
};
|
|
|
|
struct BreakpointLocation
|
|
{
|
|
uint64_t address;
|
|
struct rdbg_String* module_name;
|
|
struct rdbg_String* filename;
|
|
uint32_t actual_line_num;
|
|
};
|
|
|
|
static char* BreakpointKindToString(enum rdbg_BreakpointKind kind)
|
|
{
|
|
char* result = "";
|
|
switch (kind)
|
|
{
|
|
case RDBG_BREAKPOINT_KIND_FUNCTION_NAME:
|
|
result = "RDBG_BREAKPOINT_KIND_FUNCTION_NAME";
|
|
break;
|
|
case RDBG_BREAKPOINT_KIND_FILENAME_LINE:
|
|
result = "RDBG_BREAKPOINT_KIND_FILENAME_LINE";
|
|
break;
|
|
case RDBG_BREAKPOINT_KIND_ADDRESS:
|
|
result = "RDBG_BREAKPOINT_KIND_ADDRESS";
|
|
break;
|
|
case RDBG_BREAKPOINT_KIND_PROCESSOR:
|
|
result = "RDBG_BREAKPOINT_KIND_PROCESSOR";
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
char *ProcessorBreakpointAccessKindToString(
|
|
enum rdbg_ProcessorBreakpointAccessKind kind)
|
|
{
|
|
char* result = "";
|
|
switch (kind)
|
|
{
|
|
case RDBG_PROCESSOR_BREAKPOINT_ACCESS_KIND_WRITE:
|
|
result = "RDBG_PROCESSOR_BREAKPOINT_ACCESS_KIND_WRITE";
|
|
break;
|
|
case RDBG_PROCESSOR_BREAKPOINT_ACCESS_KIND_READ_WRITE:
|
|
result = "RDBG_PROCESSOR_BREAKPOINT_ACCESS_KIND_READ_WRITE";
|
|
break;
|
|
case RDBG_PROCESSOR_BREAKPOINT_ACCESS_KIND_EXECUTE:
|
|
result = "RDBG_PROCESSOR_BREAKPOINT_ACCESS_KIND_EXECUTE";
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static void PopBreakpoint(struct rdb_Buffer* b, struct Breakpoint* bp)
|
|
{
|
|
bp->uid = PopId(b);
|
|
bp->enabled = PopBool(b);
|
|
PopString(b, &bp->module_name);
|
|
PopString(b, &bp->condition_expr);
|
|
bp->kind = PopBreakpointKind(b);
|
|
switch (bp->kind)
|
|
{
|
|
case RDBG_BREAKPOINT_KIND_FUNCTION_NAME:
|
|
PopString(b, &bp->function_name);
|
|
bp->overload_id = PopId(b);
|
|
break;
|
|
case RDBG_BREAKPOINT_KIND_FILENAME_LINE:
|
|
PopString(b, &bp->filename);
|
|
bp->line_num = PopU32(b);
|
|
break;
|
|
case RDBG_BREAKPOINT_KIND_ADDRESS:
|
|
bp->address = PopU64(b);
|
|
break;
|
|
case RDBG_BREAKPOINT_KIND_PROCESSOR:
|
|
PopString(b, &bp->expression);
|
|
bp->num_bytes = PopU8(b);
|
|
bp->access_kind = (enum rdbg_InternalProcessorBreakpointType)PopProcessorBreakpointAccessKind(b);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static bool BreakpointIt_Next(struct BufIterator* it, struct Breakpoint* bp)
|
|
{
|
|
bool result = false;
|
|
if (++it->cur_idx < it->n)
|
|
{
|
|
PopBreakpoint(&it->buf, bp);
|
|
result = true;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
struct FunctionOverload
|
|
{
|
|
rdbg_Id id;
|
|
struct rdbg_String* fn_sig;
|
|
};
|
|
|
|
static bool FunctionOverloadIt_Next(struct BufIterator* it,
|
|
struct FunctionOverload* overload)
|
|
{
|
|
bool result = false;
|
|
if (++it->cur_idx < it->n)
|
|
{
|
|
struct rdb_Buffer* b = &it->buf;
|
|
|
|
overload->id = PopId(b);
|
|
PopString(b, &overload->fn_sig);
|
|
result = true;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
struct Watch
|
|
{
|
|
rdbg_Id uid;
|
|
struct rdbg_String* expression;
|
|
struct rdbg_String* comment;
|
|
};
|
|
|
|
static bool WatchExpressionIt_Next(struct BufIterator* it,
|
|
struct Watch* watch)
|
|
{
|
|
bool result = false;
|
|
if (++it->cur_idx < it->n)
|
|
{
|
|
struct rdb_Buffer* b = &it->buf;
|
|
|
|
watch->uid = PopId(b);
|
|
PopString(b, &watch->expression);
|
|
PopString(b, &watch->comment);
|
|
|
|
result = true;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static void MaybePrintField(struct rdbg_String* str, char* field_name)
|
|
{
|
|
if (str && str->len)
|
|
{
|
|
fprintf(stderr, "\t%s: %.*s\n", field_name, str->len,
|
|
(char*)str->data);
|
|
}
|
|
}
|
|
|
|
static char* SourceLocChangedReasonToString(enum rdbg_SourceLocChangedReason reason)
|
|
{
|
|
char* result = "";
|
|
switch (reason)
|
|
{
|
|
case RDBG_SOURCE_LOC_CHANGED_REASON_UNSPECIFIED:
|
|
result = "RDBG_SOURCE_LOC_CHANGED_REASON_UNSPECIFIED";
|
|
break;
|
|
case RDBG_SOURCE_LOC_CHANGED_REASON_BY_COMMAND_LINE:
|
|
result = "RDBG_SOURCE_LOC_CHANGED_REASON_BY_COMMAND_LINE";
|
|
break;
|
|
case RDBG_SOURCE_LOC_CHANGED_REASON_BY_DRIVER:
|
|
result = "RDBG_SOURCE_LOCATION_CHANGED_REASON_BY_DRIVER";
|
|
break;
|
|
case RDBG_SOURCE_LOC_CHANGED_REASON_BREAKPOINT_SELECTED:
|
|
result = "RDBG_SOURCE_LOC_CHANGED_REASON_BREAKPOINT_SELECTED";
|
|
break;
|
|
case RDBG_SOURCE_LOC_CHANGED_REASON_CURRENT_FRAME_CHANGED:
|
|
result = "RDBG_SOURCE_LOC_CHANGED_REASON_CURRENT_FRAME_CHANGED";
|
|
break;
|
|
case RDBG_SOURCE_LOC_CHANGED_REASON_ACTIVE_THREAD_CHANGED:
|
|
result = "RDBG_SOURCE_LOC_CHANGED_REASON_ACTIVE_THREAD_CHANGED";
|
|
break;
|
|
case RDBG_SOURCE_LOC_CHANGED_REASON_BREAKPOINT_HIT:
|
|
result = "RDBG_SOURCE_LOC_CHANGED_REASON_BREAKPOINT_HIT";
|
|
break;
|
|
case RDBG_SOURCE_LOC_CHANGED_REASON_EXCEPTION_HIT:
|
|
result = "RDBG_SOURCE_LOC_CHANGED_REASON_EXCEPTION_HIT";
|
|
break;
|
|
case RDBG_SOURCE_LOC_CHANGED_REASON_STEP_OVER:
|
|
result = "RDBG_SOURCE_LOC_CHANGED_REASON_STEP_OVER";
|
|
break;
|
|
case RDBG_SOURCE_LOC_CHANGED_REASON_STEP_IN:
|
|
result = "RDBG_SOURCE_LOC_CHANGED_REASON_STEP_IN";
|
|
break;
|
|
case RDBG_SOURCE_LOC_CHANGED_REASON_STEP_OUT:
|
|
result = "RDBG_SOURCE_LOC_CHANGED_REASON_STEP_OUT";
|
|
break;
|
|
case RDBG_SOURCE_LOC_CHANGED_REASON_NON_USER_BREAKPOINT:
|
|
result = "RDBG_SOURCE_LOC_CHANGED_REASON_NON_USER_BREAKPOINT";
|
|
break;
|
|
case RDBG_SOURCE_LOC_CHANGED_REASON_DEBUG_BREAK:
|
|
result = "RDBG_SOURCE_LOC_CHANGED_REASON_DEBUG_BREAK";
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static void WriteDebugEvent(struct rdb_Buffer* eb)
|
|
{
|
|
struct rdbg_String* str;
|
|
|
|
enum rdbg_DebugEventKind kind = PopDebugEventKind(eb);
|
|
switch (kind)
|
|
{
|
|
case RDBG_DEBUG_EVENT_KIND_EXIT_PROCESS:
|
|
fprintf(stderr, "RDBG_DEBUG_EVENT_KIND_EXIT_PROCESS\n");
|
|
fprintf(stderr, "\texit_code: %u\n", PopU32(eb));
|
|
break;
|
|
case RDBG_DEBUG_EVENT_KIND_TARGET_STARTED:
|
|
fprintf(stderr, "RDBG_DEBUG_EVENT_KIND_TARGET_STARTED\n");
|
|
fprintf(stderr, "\tprocess_id: %u\n", PopU32(eb));
|
|
break;
|
|
case RDBG_DEBUG_EVENT_KIND_TARGET_ATTACHED:
|
|
fprintf(stderr, "RDBG_DEBUG_EVENT_KIND_TARGET_ATTACHED\n");
|
|
fprintf(stderr, "\tprocess_id: %u\n", PopU32(eb));
|
|
break;
|
|
case RDBG_DEBUG_EVENT_KIND_TARGET_DETACHED:
|
|
fprintf(stderr, "RDBG_DEBUG_EVENT_KIND_TARGET_DETACHED\n");
|
|
fprintf(stderr, "\tprocess_id: %u\n", PopU32(eb));
|
|
break;
|
|
case RDBG_DEBUG_EVENT_KIND_TARGET_CONTINUED:
|
|
fprintf(stderr, "RDBG_DEBUG_EVENT_KIND_TARGET_CONTINUED\n");
|
|
break;
|
|
case RDBG_DEBUG_EVENT_KIND_SOURCE_LOCATION_CHANGED:
|
|
fprintf(stderr, "RDBG_DEBUG_EVENT_KIND_SOURCE_LOCATION_CHANGED\n");
|
|
PopString(eb, &str);
|
|
if (str && str->len)
|
|
{
|
|
fprintf(stderr, "\tfilename: %.*s\n", str->len, (char*)str->data);
|
|
}
|
|
fprintf(stderr, "\tline num: %u\n", PopU32(eb));
|
|
fprintf(stderr, "\treason: %s\n", SourceLocChangedReasonToString((rdbg_SourceLocChangedReason)PopU16(eb)));
|
|
break;
|
|
case RDBG_DEBUG_EVENT_KIND_EXTERNAL_TEXT_EDITOR_LAUNCHED:
|
|
fprintf(stderr, "RDBG_DEBUG_EVENT_KIND_EXTERNAL_TEXT_EDITOR_LAUNCHED\n");
|
|
PopString(eb, &str);
|
|
if (str && str->len)
|
|
{
|
|
fprintf(stderr, "\tfilename: %.*s\n", str->len, (char*)str->data);
|
|
}
|
|
fprintf(stderr, "\tline num: %u\n", PopU32(eb));
|
|
break;
|
|
case RDBG_DEBUG_EVENT_KIND_BREAKPOINT_HIT:
|
|
fprintf(stderr, "RDBG_DEBUG_EVENT_KIND_BREAKPOINT_HIT\n");
|
|
fprintf(stderr, "\tuid: %u\n", PopId(eb));
|
|
break;
|
|
case RDBG_DEBUG_EVENT_KIND_BREAKPOINT_RESOLVED:
|
|
fprintf(stderr, "RDBG_DEBUG_EVENT_KIND_BREAKPOINT_RESOLVED\n");
|
|
fprintf(stderr, "\tuid: %u\n", PopId(eb));
|
|
break;
|
|
case RDBG_DEBUG_EVENT_KIND_BREAKPOINT_ADDED:
|
|
fprintf(stderr, "RDBG_DEBUG_EVENT_KIND_BREAKPOINT_ADDED\n");
|
|
fprintf(stderr, "\tuid: %u\n", PopId(eb));
|
|
break;
|
|
case RDBG_DEBUG_EVENT_KIND_BREAKPOINT_MODIFIED:
|
|
fprintf(stderr, "RDBG_DEBUG_EVENT_KIND_BREAKPOINT_MODIFIED\n");
|
|
fprintf(stderr, "\tuid: %u\n", PopId(eb));
|
|
break;
|
|
case RDBG_DEBUG_EVENT_KIND_BREAKPOINT_REMOVED:
|
|
fprintf(stderr, "RDBG_DEBUG_EVENT_KIND_BREAKPOINT_REMOVED\n");
|
|
fprintf(stderr, "\tuid: %u\n", PopId(eb));
|
|
break;
|
|
case RDBG_DEBUG_EVENT_KIND_OUTPUT_DEBUG_STRING:
|
|
PopString(eb, &str);
|
|
|
|
fprintf(stderr, "RDBG_DEBUG_EVENT_KIND_OUTPUT_DEBUG_STRING\n");
|
|
if (str && str->len)
|
|
{
|
|
fprintf(stderr, "\tstr: %.*s\n", str->len, (char*)str->data);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr, "warning: unknown debug event kind received\n");
|
|
}
|
|
}
|
|
|
|
void DebugControlSample(char* server_name)
|
|
{
|
|
static uint8_t command_buf[COMMAND_BUF_SIZE];
|
|
static uint8_t reply_buf[REPLY_BUF_SIZE];
|
|
struct ClientContext ctx = {};
|
|
ctx.cmd.data = command_buf;
|
|
ctx.cmd.capacity = sizeof(command_buf);
|
|
ctx.reply.data = reply_buf;
|
|
ctx.reply.capacity = sizeof(reply_buf);
|
|
ctx.dbg_target_behavior = RDBG_IF_DEBUGGING_TARGET_STOP_DEBUGGING;
|
|
ctx.mod_session_behavior = RDBG_IF_SESSION_IS_MODIFIED_CONTINUE_WITHOUT_SAVING;
|
|
ctx.last_error[0] = 0;
|
|
|
|
if (InitConnection(server_name, DebugControlPipe, sizeof(ctx.last_error),
|
|
ctx.last_error, &ctx.command_pipe_handle))
|
|
{
|
|
/* Sample calls to each debug control command (commented out).
|
|
*
|
|
enum rdbg_CommandResult res;
|
|
StartDebugging(&ctx, false, &res);
|
|
BringDebuggerToForeground(&ctx, &res);
|
|
SetDebuggerWindowPos(&ctx, 20, 20, 100, 100, &res);
|
|
|
|
SetBringToForegroundOnSuspended(&ctx, false, &res);
|
|
int x, y, width, height;
|
|
bool is_maximized;
|
|
GetDebuggerWindowPos(&ctx, &res, &x, &y, &width, &height, &is_maximized);
|
|
printf("Window pos: (%d, %d) %d x %d; is maximized: %s\n",
|
|
x, y, width, height, is_maximized ? "true" : "false");
|
|
bool is_modified;
|
|
GetIsSessionModified(&ctx, &res, &is_modified);
|
|
struct rdbg_String* filename;
|
|
GetSessionFilename(&ctx, &res, &filename);
|
|
if (filename && filename->len)
|
|
{
|
|
printf("Session filename: %.*s\n", filename->len, filename->data);
|
|
}
|
|
NewSession(&ctx, &res);
|
|
OpenSession(&ctx, "c:\\path\\to\\session.rdbg", &res);
|
|
SaveSession(&ctx, &res);
|
|
SaveAsSession(&ctx, "c:\\path\\to\\session.rdbg", &res);
|
|
struct BufIterator cfg_it;
|
|
struct SessionConfig cfg;
|
|
GetSessionConfigs(&ctx, &res, &cfg_it);
|
|
while (SessionConfigIt_Next(&cfg_it, &cfg))
|
|
{
|
|
fprintf(stderr, "Config #%d\n", cfg_it.cur_idx);
|
|
fprintf(stderr, "\tuid: %hu\n", cfg.id);
|
|
if (cfg.command && cfg.command->len)
|
|
{
|
|
fprintf(stderr, "\tcommand: %.*s\n", cfg.command->len,
|
|
(char*)cfg.command->data);
|
|
}
|
|
if (cfg.command_args && cfg.command_args->len)
|
|
{
|
|
fprintf(stderr, "\tcommand_args: %.*s\n", cfg.command_args->len,
|
|
(char*)cfg.command_args->data);
|
|
}
|
|
if (cfg.working_dir && cfg.working_dir->len)
|
|
{
|
|
fprintf(stderr, "\tworking_dir: %.*s\n", cfg.working_dir->len,
|
|
(char*)cfg.working_dir->data);
|
|
}
|
|
if (cfg.environment_vars && cfg.environment_vars->len)
|
|
{
|
|
fprintf(stderr, "\tenvironment_vars:\n%.*s\n",
|
|
cfg.environment_vars->len,
|
|
(char*)cfg.environment_vars->data);
|
|
}
|
|
fprintf(stderr, "\tinherit_environment_vars_from_parent: %s\n",
|
|
cfg.inherit_environment_vars_from_parent ? "true" : "false");
|
|
fprintf(stderr, "\tbreak_at_nominal_entry_point: %s\n",
|
|
cfg.break_at_nominal_entry_point ? "true" : "false");
|
|
}
|
|
rdbg_Id cfg_id;
|
|
AddSessionConfig(&ctx,
|
|
"C:\\windows\\system32\\whoami.exe", "/USER", 0,
|
|
"A=1\nBB=2\nCCC=3", true, true, &res, &cfg_id);
|
|
fprintf(stderr, "Added session conf (ID: %u).\n", cfg_id);
|
|
SetActiveSessionConfig(&ctx, cfg_id, &res);
|
|
DeleteSessionConfig(&ctx, cfg.id, &res);
|
|
DeleteAllSessionConfigs(&ctx, &res);
|
|
rdbg_Id cur_file_id;
|
|
struct rdbg_String* cur_filename = 0;
|
|
uint32_t line_num;
|
|
GetCurrentFile(&ctx, &res, &cur_file_id, &cur_filename, &line_num);
|
|
if (cur_filename && cur_filename->len)
|
|
{
|
|
fprintf(stderr, "Topmost file: (%u) %.*s Ln %u\n",
|
|
cur_file_id, cur_filename->len, (char*)cur_filename->data,
|
|
line_num);
|
|
}
|
|
rdbg_Id file_id;
|
|
GoToFileAtLine(&ctx, "C:\\full\\path\\to\\README.txt", 121, &res, &file_id);
|
|
CloseFileById(&ctx, file_id, &res);
|
|
CloseAllFiles(&ctx, &res);
|
|
struct BufIterator file_it;
|
|
struct File file;
|
|
GetOpenFiles(&ctx, &res, &file_it);
|
|
while (FileIt_Next(&file_it, &file))
|
|
{
|
|
fprintf(stderr, "File #%d\n", file_it.cur_idx);
|
|
fprintf(stderr, "\tId: %u\n", file.id);
|
|
if (file.filename && file.filename->len)
|
|
{
|
|
fprintf(stderr, "\tfilename: %.*s\n", file.filename->len,
|
|
(char*)file.filename->data);
|
|
}
|
|
fprintf(stderr, "\tLn: %u\n", file.line_num);
|
|
}
|
|
StopDebugging(&ctx, &res);
|
|
RestartDebugging(&ctx, &res);
|
|
AttachToProcessById(&ctx, 14368, true, &res);
|
|
NewSession(&ctx, &res);
|
|
AttachToProcessByName(&ctx, "Calculator.exe", true, &res);
|
|
DetachFromProcess(&ctx, &res);
|
|
StepIntoByLine(&ctx, &res);
|
|
StepIntoByInstruction(&ctx, &res);
|
|
StepOverByLine(&ctx, &res);
|
|
StepOverByInstruction(&ctx, &res);
|
|
StepOut(&ctx, &res);
|
|
ContinueExecution(&ctx, &res);
|
|
RunToFileAtLine(&ctx, "C:\\full\\path\\to\\test.cpp", 13, &res);
|
|
ContinueExecution(&ctx, &res);
|
|
BreakExecution(&ctx, &res);
|
|
enum rdbg_TargetState state;
|
|
GetTargetState(&ctx, &res, &state);
|
|
fprintf(stderr, "target state: %hu\n", state);
|
|
struct BufIterator bp_it;
|
|
struct Breakpoint bp;
|
|
GetAllBreakpoints(&ctx, &res, &bp_it);
|
|
int idx = 0;
|
|
while (BreakpointIt_Next(&bp_it, &bp))
|
|
{
|
|
fprintf(stderr, "Breakpoint # %d\n", ++idx);
|
|
fprintf(stderr, "\tUID: %u\n", bp.uid);
|
|
fprintf(stderr, "\tEnabled: %s\n", bp.enabled ? "true" : "false");
|
|
MaybePrintField(bp.module_name, "Module");
|
|
MaybePrintField(bp.condition_expr, "Condition");
|
|
fprintf(stderr, "\tKind: %s\n", BreakpointKindToString(bp.kind));
|
|
switch (bp.kind)
|
|
{
|
|
case RDBG_BREAKPOINT_KIND_FUNCTION_NAME:
|
|
MaybePrintField(bp.function_name, "Function");
|
|
fprintf(stderr, "\tOverload: %u\n", bp.overload_id);
|
|
break;
|
|
case RDBG_BREAKPOINT_KIND_FILENAME_LINE:
|
|
MaybePrintField(bp.filename, "Filename");
|
|
fprintf(stderr, "\tLine: %u\n", bp.line_num);
|
|
break;
|
|
case RDBG_BREAKPOINT_KIND_ADDRESS:
|
|
fprintf(stderr, "\tAddress: 0x%llx\n", bp.address);
|
|
break;
|
|
case RDBG_BREAKPOINT_KIND_PROCESSOR:
|
|
MaybePrintField(bp.expression, "Expression");
|
|
fprintf(stderr, "\tBytes: %hhu\n", bp.num_bytes);
|
|
fprintf(stderr, "\tAccess kind: %s\n",
|
|
ProcessorBreakpointAccessKindToString(bp.access_kind));
|
|
break;
|
|
}
|
|
// Test for call to get information on a single user breakpoint
|
|
struct Breakpoint _bp;
|
|
GetBreakpoint(&ctx, bp.uid, &res, &_bp);
|
|
// See if the breakpoint has been resolved. Not using an iterator
|
|
// here, at the moment, because we can only get back one or zero.
|
|
uint16_t num_locs;
|
|
GetBreakpointLocations(&ctx, bp.uid, &res, &num_locs);
|
|
if (num_locs == 1)
|
|
{
|
|
struct BreakpointLocation loc;
|
|
loc.address = PopU64(&ctx.reply);
|
|
PopString(&ctx.reply, &loc.module_name);
|
|
PopString(&ctx.reply, &loc.filename);
|
|
loc.actual_line_num = PopU32(&ctx.reply);
|
|
fprintf(stderr, "\t---------\n");
|
|
fprintf(stderr, "\tResolved address: 0x%llx\n", loc.address);
|
|
MaybePrintField(loc.module_name, "Module");
|
|
MaybePrintField(loc.module_name, "Filename");
|
|
fprintf(stderr, "\tActual line number: %u\n",
|
|
loc.actual_line_num);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "\tUnresolved\n");
|
|
}
|
|
}
|
|
struct BufIterator fn_overload_it;
|
|
struct FunctionOverload overload;
|
|
GetFunctionOverloads(&ctx, &res, "SomeFunction", &fn_overload_it);
|
|
while (FunctionOverloadIt_Next(&fn_overload_it, &overload))
|
|
{
|
|
fprintf(stderr, "Overload %u; sig: ", overload.id);
|
|
if (overload.fn_sig && overload.fn_sig->len)
|
|
{
|
|
fprintf(stderr, "%.*s\n", overload.fn_sig->len,
|
|
(char*)overload.fn_sig->data);
|
|
}
|
|
}
|
|
rdbg_Id bp_id;
|
|
AddBreakpointAtFn(&ctx, "SomeFunction", 0, "", &res, &bp_id);
|
|
AddBreakpointAtFilenameLine(&ctx, "C:\\path\\to\\fn_overloads.cpp", 21,
|
|
"", &res, &bp_id);
|
|
AddBreakpointAtAddress(&ctx, 0x7FF7F592B703, "", &res, &bp_id);
|
|
AddProcessorBreakpoint(&ctx, "&var", 4,
|
|
RDBG_PROCESSOR_BREAKPOINT_ACCESS_KIND_WRITE, "", &res, &bp_id);
|
|
SetBreakpointCondition(&ctx, bp_id, "$rax == 0", &res);
|
|
UpdateBreakpointLine(&ctx, bp_id, 22, &res);
|
|
EnableBreakpoint(&ctx, bp_id, false, &res);
|
|
EnableBreakpoint(&ctx, bp_id, true, &res);
|
|
DeleteBreakpoint(&ctx, bp_id, &res);
|
|
DeleteAllBreakpoints(&ctx, &res);
|
|
rdbg_Id watch_id;
|
|
AddWatch(&ctx, 1, "0xf0c / 0xa", "testing\nblah", &res, &watch_id);
|
|
struct BufIterator watch_it;
|
|
GetWatches(&ctx, 1, &res, &watch_it);
|
|
struct Watch watch;
|
|
while (WatchExpressionIt_Next(&watch_it, &watch))
|
|
{
|
|
fprintf(stderr, "Watch %hu\n", watch.uid);
|
|
if (watch.expression && watch.expression->len)
|
|
{
|
|
fprintf(stderr, "\tWatch expression: '%.*s'\n",
|
|
watch.expression->len,
|
|
(char*)watch.expression->data);
|
|
}
|
|
if (watch.comment && watch.comment->len)
|
|
{
|
|
fprintf(stderr, "\tWatch comment: '%.*s'\n",
|
|
watch.comment->len,
|
|
(char*)watch.comment->data);
|
|
}
|
|
}
|
|
UpdateWatchExpression(&ctx, 37, "expr * expr", &res);
|
|
UpdateWatchComment(&ctx, 37, "something left after", &res);
|
|
DeleteWatch(&ctx, 42, &res);
|
|
DeleteAllWatches(&ctx, 1, &res);
|
|
ExitDebugger(&ctx, &res);
|
|
*/
|
|
|
|
if (ContextHadError(&ctx))
|
|
{
|
|
fprintf(stderr, "[ERROR] %s (cmd-err:%s)(reply-err:%s)\n",
|
|
ctx.last_error,
|
|
ctx.cmd.err ? "true" : "false",
|
|
ctx.reply.err ? "true" : "false");
|
|
}
|
|
CloseConnection(&ctx);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, ctx.last_error);
|
|
}
|
|
}
|
|
|
|
void DebugEventsSample(char* server_name)
|
|
{
|
|
HANDLE pipe_handle;
|
|
char last_error[ERROR_MSG_LEN];
|
|
last_error[0] = 0;
|
|
|
|
if (InitConnection(server_name, DebugEventsPipe, sizeof(last_error),
|
|
last_error, &pipe_handle))
|
|
{
|
|
// blocking read for testing events
|
|
while (1)
|
|
{
|
|
static uint8_t dbg_evt_buf[REPLY_BUF_SIZE];
|
|
struct rdb_Buffer dbg_evt = {};
|
|
dbg_evt.data = dbg_evt_buf;
|
|
dbg_evt.capacity = sizeof(dbg_evt_buf);
|
|
|
|
DWORD bytes_read = 0;
|
|
if (ReadFile(pipe_handle, dbg_evt.data, REPLY_BUF_SIZE, &bytes_read,
|
|
NULL))
|
|
{
|
|
dbg_evt.curr = dbg_evt.data;
|
|
WriteDebugEvent(&dbg_evt);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "ReadFile FAIL: err=%u\n", GetLastError());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, last_error);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
uint8_t command_buf[COMMAND_BUF_SIZE];
|
|
uint8_t reply_buf[REPLY_BUF_SIZE];
|
|
ClientContext RDBG_Ctx;
|
|
|
|
|
|
bool RDBG_InitConnection() {
|
|
enum rdbg_CommandResult res;
|
|
if (RDBG_Ctx.command_pipe_handle != NULL) {
|
|
enum rdbg_TargetState state;
|
|
GetTargetState(&RDBG_Ctx, &res, &state);
|
|
if (!ContextHadError(&RDBG_Ctx)) {
|
|
return true;
|
|
}
|
|
}
|
|
Scratch scratch;
|
|
String session_name = Format(scratch, "te%llu", GetTimeNanos());
|
|
String remedy_string = Format(scratch, "%S --servername %S", RemedyBGPath, session_name);
|
|
Exec(NullViewID, true, remedy_string, GetMainDir());
|
|
MemoryZero(&RDBG_Ctx, sizeof(RDBG_Ctx));
|
|
RDBG_Ctx.cmd.data = command_buf;
|
|
RDBG_Ctx.cmd.capacity = sizeof(command_buf);
|
|
RDBG_Ctx.reply.data = reply_buf;
|
|
RDBG_Ctx.reply.capacity = sizeof(reply_buf);
|
|
RDBG_Ctx.dbg_target_behavior = RDBG_IF_DEBUGGING_TARGET_STOP_DEBUGGING;
|
|
RDBG_Ctx.mod_session_behavior = RDBG_IF_SESSION_IS_MODIFIED_CONTINUE_WITHOUT_SAVING;
|
|
RDBG_Ctx.last_error[0] = 0;
|
|
|
|
bool result = false;
|
|
for (int i = 0; i < 64; i += 1) {
|
|
Sleep(10);
|
|
result = InitConnection(session_name.data, DebugControlPipe, sizeof(RDBG_Ctx.last_error), RDBG_Ctx.last_error, &RDBG_Ctx.command_pipe_handle);
|
|
if (result) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (result == false) {
|
|
ReportErrorf("Remedy error: %s", RDBG_Ctx.last_error);
|
|
RDBG_Ctx = {};
|
|
return false;
|
|
}
|
|
MemoryZero(RDBG_Ctx.last_error, sizeof(RDBG_Ctx.last_error));
|
|
|
|
rdbg_Id cfg_id;
|
|
char *exe = Format(scratch, "%S/%S", WorkDir, BinaryUnderDebug).data;
|
|
char *args = NULL;
|
|
char *work_dir = WorkDir.data;
|
|
char *env = NULL;
|
|
AddSessionConfig(&RDBG_Ctx, exe, args, work_dir, env, true, true, &res, &cfg_id);
|
|
if (ContextHadError(&RDBG_Ctx)) {
|
|
ReportErrorf("Remedy error: %s", RDBG_Ctx.last_error);
|
|
MemoryZero(RDBG_Ctx.last_error, sizeof(RDBG_Ctx.last_error));
|
|
return true;
|
|
}
|
|
|
|
SetActiveSessionConfig(&RDBG_Ctx, cfg_id, &res);
|
|
if (ContextHadError(&RDBG_Ctx)) {
|
|
ReportErrorf("Remedy error: %s", RDBG_Ctx.last_error);
|
|
MemoryZero(RDBG_Ctx.last_error, sizeof(RDBG_Ctx.last_error));
|
|
return true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CMD_StartDebugging(HookParam param) {
|
|
bool conn = RDBG_InitConnection();
|
|
if (!conn) {
|
|
return;
|
|
}
|
|
|
|
enum rdbg_CommandResult res;
|
|
enum rdbg_TargetState state;
|
|
GetTargetState(&RDBG_Ctx, &res, &state);
|
|
if (ContextHadError(&RDBG_Ctx)) {
|
|
ReportErrorf("Remedy error: %s", RDBG_Ctx.last_error);
|
|
MemoryZero(RDBG_Ctx.last_error, sizeof(RDBG_Ctx.last_error));
|
|
return;
|
|
}
|
|
|
|
if (state == RDBG_TARGET_STATE_NONE) {
|
|
StartDebugging(&RDBG_Ctx, false, &res);
|
|
if (ContextHadError(&RDBG_Ctx)) {
|
|
ReportErrorf("Remedy error: %s", RDBG_Ctx.last_error);
|
|
MemoryZero(RDBG_Ctx.last_error, sizeof(RDBG_Ctx.last_error));
|
|
return;
|
|
}
|
|
} else if (state == RDBG_TARGET_STATE_SUSPENDED) {
|
|
ContinueExecution(&RDBG_Ctx, &res);
|
|
if (ContextHadError(&RDBG_Ctx)) {
|
|
ReportErrorf("Remedy error: %s", RDBG_Ctx.last_error);
|
|
MemoryZero(RDBG_Ctx.last_error, sizeof(RDBG_Ctx.last_error));
|
|
return;
|
|
}
|
|
}
|
|
BringDebuggerToForeground(&RDBG_Ctx, &res);
|
|
if (ContextHadError(&RDBG_Ctx)) {
|
|
ReportErrorf("Remedy error: %s", RDBG_Ctx.last_error);
|
|
MemoryZero(RDBG_Ctx.last_error, sizeof(RDBG_Ctx.last_error));
|
|
return;
|
|
}
|
|
} RegisterCommand(CMD_StartDebugging, "f5", "Start debugging, if debugger not active it starts it, uses BinaryUnderDebug");
|
|
|
|
void CMD_RunToLineInDebugger(HookParam param) {
|
|
bool conn = RDBG_InitConnection();
|
|
if (!conn) {
|
|
return;
|
|
}
|
|
|
|
BSet prim = GetBSet(PrimaryWindowID);
|
|
enum rdbg_CommandResult res;
|
|
Int line = PosToLine(prim.buffer, GetFront(prim.view->carets[0]));
|
|
RunToFileAtLine(&RDBG_Ctx, prim.buffer->name.data, (uint32_t)line + 1, &res);
|
|
if (res != RDBG_COMMAND_RESULT_OK) {
|
|
ReportErrorf("Remedy error: %s", RDBG_Ctx.last_error);
|
|
MemoryZero(RDBG_Ctx.last_error, sizeof(RDBG_Ctx.last_error));
|
|
return;
|
|
}
|
|
} RegisterCommand(CMD_RunToLineInDebugger, "ctrl-f10", "Instruct debugger to execute until line and column under caret");
|
|
|
|
void CMD_StopDebugging(HookParam param) {
|
|
bool conn = RDBG_InitConnection();
|
|
if (!conn) {
|
|
return;
|
|
}
|
|
|
|
enum rdbg_CommandResult res;
|
|
StopDebugging(&RDBG_Ctx, &res);
|
|
if (res != RDBG_COMMAND_RESULT_OK) {
|
|
ReportErrorf("Remedy error: %s", RDBG_Ctx.last_error);
|
|
MemoryZero(RDBG_Ctx.last_error, sizeof(RDBG_Ctx.last_error));
|
|
return;
|
|
}
|
|
} RegisterCommand(CMD_StopDebugging, "shift-f5", "Stop debugging");
|
|
|
|
void CMD_AddBreakpoint(HookParam param) {
|
|
if (!RDBG_InitConnection()) {
|
|
return;
|
|
}
|
|
|
|
BSet prim = GetBSet(PrimaryWindowID);
|
|
Int line = PosToLine(prim.buffer, GetFront(prim.view->carets[0]));
|
|
enum rdbg_CommandResult res;
|
|
rdbg_Id bp_id;
|
|
AddBreakpointAtFilenameLine(&RDBG_Ctx, prim.buffer->name.data, (uint32_t)line + 1, "", &res, &bp_id);
|
|
if (res != RDBG_COMMAND_RESULT_OK) {
|
|
ReportErrorf("Remedy error: %s", RDBG_Ctx.last_error);
|
|
MemoryZero(RDBG_Ctx.last_error, sizeof(RDBG_Ctx.last_error));
|
|
return;
|
|
}
|
|
} RegisterCommand(CMD_AddBreakpoint, "f9", "Add a breakpoint at filename + line");
|
|
|
|
void HOOK_QuitDebugger(HookParam param) {
|
|
if (RDBG_Ctx.command_pipe_handle == NULL) {
|
|
return;
|
|
}
|
|
|
|
enum rdbg_CommandResult res;
|
|
ExitDebugger(&RDBG_Ctx, &res);
|
|
CloseConnection(&RDBG_Ctx);
|
|
} RegisterHook(HOOK_QuitDebugger, HookKind_AppQuit, "", "exit the connected debugger");
|
|
|
|
|
|
#endif |