DBGConsole.cpp revision d6f8b76ab3b2ec0c270c96f9db6e2568fc41b5fe
/* $Id$ */
/** @file
* DBGC - Debugger Console.
*/
/*
* Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/** @page pg_dbgc DBGC - The Debug Console
*
* The debugger console is an early attempt to make some interactive
* debugging facilities for the VirtualBox VMM. It was initially only
* accessible thru a telnet session in debug builds. Later it was hastily built
* into the VBoxDbg module with a very simple Qt wrapper around it.
*
* The current state is that it's by default shipped with all standard
* VirtualBox builds. The GUI component is by default accessible in all
* non-release builds, while release builds require extra data, environment or
* command line options to make it visible.
*
* Now, even if we ship it with all standard builds we would like it to remain
* an optional feature that can be omitted when building VirtualBox. Therefore,
* all external code interfacing DBGC need to be enclosed in
* \#ifdef VBOX_WITH_DEBUGGER blocks. This is mandatory for components that
* register external commands.
*
*
* @section sec_dbgc_op Operation
*
* The console will process commands in a manner similar to the OS/2 and Windows
* kernel debuggers. This means ';' is a command separator and that when
* possible we'll use the same command names as these two uses. As an
* alternative we intent to provide a set of gdb-like commands as well and let
* the user decide which should take precedence.
*
*
* @subsection sec_dbg_op_numbers Numbers
*
* Numbers are hexadecimal unless specified with a prefix indicating
* elsewise. Prefixes:
* - '0x' - hexadecimal.
* - '0n' - decimal
* - '0t' - octal.
* - '0y' - binary.
*
* Some of the prefixes are a bit uncommon, the reason for this that the
* typical binary prefix '0b' can also be a hexadecimal value since no prefix or
* suffix is required for such values. Ditto for '0n' and '0' for decimal and
* octal.
*
* The '`' can be used in the numeric value to separate parts as the user
* wishes. Generally, though the debugger may use it in output as thousand
* separator in decimal numbers and 32-bit separator in hex numbers.
*
* For historical reasons, a 'h' suffix is suffered on hex numbers. Unlike most
* assemblers, a leading 0 before a-f is not required with the 'h' suffix.
*
* The prefix '0i' can be used instead of '0n', as it was the early decimal
* prefix employed by DBGC. It's being deprecated and may be removed later.
*
*
* @subsection sec_dbg_op_strings Strings and Symbols
*
* The debugger will try to guess, convert or promote what the type of an
* argument to a command, function or operator based on the input description of
* the receiver. If the user wants to make it clear to the debugger that
* something is a string, put it inside double quotes. Symbols should use
* single quotes, though we're current still a bit flexible on this point.
*
* If you need to put a quote character inside the quoted text, you escape it by
* repating it once: echo "printf(""hello world"");"
*
*
* @subsection sec_dbg_op_address Addressing modes
*
* - Default is flat. For compatibility '%' also means flat.
* - Segmented addresses are specified selector:offset.
* - Physical addresses are specified using '%%'.
* - The default target for the addressing is the guest context, the '#'
* will override this and set it to the host.
* Note that several operations won't work on host addresses.
*
* The '%', '%%' and '#' prefixes is implemented as unary operators, while ':'
* is a binary operator. Operator precedence takes care of evaluation order.
*
*
* @subsection sec_dbg_op_c_operators C/C++ Operators
*
* Most unary and binary arithmetic, comparison, logical and bitwise C/C++
* operators are supported by the debugger, with the same precedence rules of
* course. There is one notable change made due to the unary '%' and '%%'
* operators, and that is that the modulo (remainder) operator is called 'mod'
* instead of '%'. This saves a lot of trouble separating argument.
*
* There are no assignment operators. Instead some simple global variable space
* is provided thru the 'set' and 'unset' commands and the unary '$' operator.
*
*
* @subsection sec_dbg_op_registers Registers
*
* All registers and their sub-fields exposed by the DBGF API are accessible via
* the '\@' operator. A few CPU register are accessible directly (as symbols)
* without using the '\@' operator. Hypervisor registers are accessible by
* prefixing the register name with a dot ('.').
*
*
* @subsection sec_dbg_op_commands Commands
*
* Commands names are case sensitive. By convention they are lower cased, starts
* with a letter but may contain digits and underscores afterwards. Operators
* are not allowed in the name (not even part of it), as we would risk
* misunderstanding it otherwise.
*
* Commands returns a status code.
*
* The '.' prefix indicates the set of external commands. External commands are
* command registered by VMM components.
*
*
* @subsection sec_dbg_op_functions Functions
*
* Functions are similar to commands, but return a variable and can only be used
* as part of an expression making up the argument of a command, function,
* operator or language statement (if we get around to implement that).
*
*
* @section sec_dbgc_logging Logging
*
* The idea is to be able to pass thru debug and release logs to the console
* if the user so wishes. This feature requires some kind of hook into the
* logger instance and while this was sketched it hasn't yet been implemented
* (dbgcProcessLog and DBGC::fLog).
*
* This feature has not materialized and probably never will.
*
*
* @section sec_dbgc_linking Linking and API
*
* The DBGC code is linked into the VBoxVMM module.
*
* IMachineDebugger may one day be extended with a DBGC interface so we can work
* with DBGC remotely without requiring TCP. Some questions about callbacks
* (for output) and security (you may wish to restrict users from debugging a
* VM) needs to be answered first though.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DBGC
#include "DBGCInternal.h"
#include "DBGPlugIns.h"
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/**
* Resolves a symbol (or tries to do so at least).
*
* @returns 0 on success.
* @returns VBox status on failure.
* @param pDbgc The debug console instance.
* @param pszSymbol The symbol name.
* @param enmType The result type. Specifying DBGCVAR_TYPE_GC_FAR may
* cause failure, avoid it.
* @param pResult Where to store the result.
*/
{
int rc;
/*
* Builtin?
*/
if (pSymDesc)
{
return VERR_DBGC_PARSE_WRITEONLY_SYMBOL;
}
/*
* A typical register? (Guest only)
*/
static const char s_szSixLetterRegisters[] =
"rflags;eflags;"
;
static const char s_szThreeLetterRegisters[] =
"eax;rax;" "r10;" "r8d;r8w;r8b;" "cr0;" "dr0;"
"ebx;rbx;" "r11;" "r9d;r9w;r8b;" "dr1;"
"ecx;rcx;" "r12;" "cr2;" "dr2;"
"edx;rdx;" "r13;" "cr3;" "dr3;"
"edi;rdi;dil;" "r14;" "cr4;" "dr4;"
"esi;rsi;sil;" "r15;" "cr8;"
"ebp;rbp;"
"esp;rsp;" "dr6;"
"rip;eip;" "dr7;"
"efl;"
;
static const char s_szTwoLetterRegisters[] =
"ax;al;ah;" "r8;"
"bx;bl;bh;" "r9;"
"cx;cl;ch;" "cs;"
"dx;dl;dh;" "ds;"
"di;" "es;"
"si;" "fs;"
"bp;" "gs;"
"sp;" "ss;"
"ip;"
;
{
{
if (RT_SUCCESS(rc))
}
}
/*
* Ask PDM.
*/
/** @todo resolve symbols using PDM. */
/*
* Ask the debug info manager.
*/
if (RT_SUCCESS(rc))
{
/*
* Default return is a flat gc address.
*/
switch (enmType)
{
/* nothing to do. */
case DBGCVAR_TYPE_GC_FLAT:
case DBGCVAR_TYPE_ANY:
return VINF_SUCCESS;
/* impossible at the moment. */
case DBGCVAR_TYPE_GC_FAR:
/* simply make it numeric. */
case DBGCVAR_TYPE_NUMBER:
return VINF_SUCCESS;
/* cast it. */
case DBGCVAR_TYPE_GC_PHYS:
case DBGCVAR_TYPE_HC_FLAT:
case DBGCVAR_TYPE_HC_PHYS:
default:
return VERR_INVALID_PARAMETER;
}
}
return VERR_DBGC_PARSE_NOT_IMPLEMENTED;
}
/**
* Process all commands currently in the buffer.
*
* @returns VBox status code. Any error indicates the termination of the console session.
* @param pDbgc Debugger console instance data.
* @param fNoExecute Indicates that no commands should actually be executed.
*/
{
* allows doing function, loops, if, cases, and such. */
int rc = VINF_SUCCESS;
while (pDbgc->cInputLines)
{
/*
* Empty the log buffer if we're hooking the log.
*/
{
if (RT_FAILURE(rc))
break;
}
{
pDbgc->cInputLines = 0;
return 0;
}
/*
* Copy the command to the parse buffer.
*/
char ch;
{
{
pDbgc->cInputLines = 0;
return 0;
}
pszTrg++;
}
*pszTrg = '\0';
/*
* Advance the buffer.
*/
if (ch == '\n')
pDbgc->cInputLines--;
/*
* Parse and execute this command.
*/
if ( rc == VERR_DBGC_QUIT
|| rc == VWRN_DBGC_CMD_PENDING)
break;
}
return rc;
}
/**
* Handle input buffer overflow.
*
* Will read any available input looking for a '\n' to reset the buffer on.
*
* @returns VBox status.
* @param pDbgc Debugger console instance data.
*/
{
/*
* Assert overflow status and reset the input buffer.
*/
if (!pDbgc->fInputOverflow)
{
pDbgc->fInputOverflow = true;
pDbgc->cInputLines = 0;
}
/*
* Eat input till no more or there is a '\n'.
* When finding a '\n' we'll continue normal processing.
*/
{
int rc = pDbgc->pBack->pfnRead(pDbgc->pBack, &pDbgc->achInput[0], sizeof(pDbgc->achInput) - 1, &cbRead);
if (RT_FAILURE(rc))
return rc;
if (psz)
{
pDbgc->fInputOverflow = false;
pDbgc->cInputLines = 0;
break;
}
}
return 0;
}
/**
* Read input and do some preprocessing.
*
* @returns VBox status.
* In addition to the iWrite and achInput, cInputLines is maintained.
* In case of an input overflow the fInputOverflow flag will be set.
* @param pDbgc Debugger console instance data.
*/
{
/*
* We have ready input.
* Read it till we don't have any or we have a full input buffer.
*/
int rc = 0;
do
{
/*
* More available buffer space?
*/
else
if (!cbLeft)
{
/* overflow? */
if (!pDbgc->cInputLines)
break;
}
/*
* Read one char and interpret it.
*/
char achRead[128];
if (RT_FAILURE(rc))
return rc;
while (cbRead-- > 0)
{
switch (ch)
{
/*
* Ignore.
*/
case '\0':
case '\r':
case '\a':
break;
/*
* Backspace.
*/
case '\b':
Log2(("DBGC: backspace\n"));
{
else
}
break;
/*
* Add char to buffer.
*/
case '\t':
case '\n':
case ';':
switch (ch)
{
}
default:
break;
}
}
/* Terminate it to make it easier to read in the debugger. */
return rc;
}
/**
* Reads input, parses it and executes commands on '\n'.
*
* @returns VBox status.
* @param pDbgc Debugger console instance data.
* @param fNoExecute Indicates that no commands should actually be executed.
*/
{
/*
* We know there's input ready, so let's read it first.
*/
if (RT_FAILURE(rc))
return rc;
/*
* Now execute any ready commands.
*/
if (pDbgc->cInputLines)
{
if ( RT_SUCCESS(rc)
if ( RT_SUCCESS(rc)
}
else
/* Received nonsense; just skip it. */
return rc;
}
/**
* Gets the event context identifier string.
* @returns Read only string.
* @param enmCtx The context.
*/
{
switch (enmCtx)
{
case DBGFEVENTCTX_RAW: return "raw";
case DBGFEVENTCTX_REM: return "rem";
case DBGFEVENTCTX_HM: return "hwaccl";
case DBGFEVENTCTX_HYPER: return "hyper";
case DBGFEVENTCTX_OTHER: return "other";
case DBGFEVENTCTX_INVALID: return "!Invalid Event Ctx!";
default:
return "!Unknown Event Ctx!";
}
}
/**
* Processes debugger events.
*
* @returns VBox status.
* @param pDbgc DBGC Instance data.
* @param pEvent Pointer to event data.
*/
{
/*
* Flush log first.
*/
{
if (RT_FAILURE(rc))
return rc;
}
/*
* Process the event.
*/
bool fPrintPrompt = true;
int rc = VINF_SUCCESS;
{
/*
* The first part is events we have initiated with commands.
*/
case DBGFEVENT_HALT_DONE:
{
if (RT_SUCCESS(rc))
break;
}
/*
* The second part is events which can occur at any time.
*/
case DBGFEVENT_FATAL_ERROR:
{
if (RT_SUCCESS(rc))
break;
}
case DBGFEVENT_BREAKPOINT:
{
switch (rc)
{
case VERR_DBGC_BP_NOT_FOUND:
break;
case VINF_DBGC_BP_NO_COMMAND:
break;
case VINF_BUFFER_OVERFLOW:
rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Breakpoint %u! Command too long to execute! (%s)\n",
break;
default:
break;
}
else
break;
}
case DBGFEVENT_STEPPED:
case DBGFEVENT_STEPPED_HYPER:
{
rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Single step! (%s)\n", dbgcGetEventCtx(pEvent->enmCtx));
if (RT_SUCCESS(rc))
break;
}
{
pDbgc->fRegCtxGuest = false;
"\ndbgf event: Hypervisor Assertion! (%s)\n"
"%s"
"%s"
"\n",
if (RT_SUCCESS(rc))
break;
}
case DBGFEVENT_DEV_STOP:
{
"\n"
"dbgf event: DBGFSTOP (%s)\n"
"File: %s\n"
"Line: %d\n"
"Function: %s\n",
"Message: %s\n",
if (RT_SUCCESS(rc))
break;
}
{
break;
}
case DBGFEVENT_POWERING_OFF:
{
fPrintPrompt = false;
break;
}
default:
{
rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Unknown event %d!\n", pEvent->enmType);
break;
}
}
/*
* Prompt, anyone?
*/
{
if (RT_SUCCESS(rc))
}
return rc;
}
/**
* Prints any log lines from the log buffer.
*
* The caller must not call function this unless pDbgc->fLog is set.
*
* @returns VBox status. (output related)
* @param pDbgc Debugger console instance data.
*/
{
/** @todo */
return 0;
}
/** @callback_method_impl{FNRTDBGCFGLOG} */
static DECLCALLBACK(void) dbgcDbgCfgLogCallback(RTDBGCFG hDbgCfg, uint32_t iLevel, const char *pszMsg, void *pvUser)
{
/** @todo Add symbol noise setting. */
}
/**
* Run the debugger console.
*
* @returns VBox status.
* @param pDbgc Pointer to the debugger console instance data.
*/
{
/*
* We're ready for commands now.
*/
/*
* Main Debugger Loop.
*
* This loop will either block on waiting for input or on waiting on
* debug events. If we're forwarding the log we cannot wait for long
* before we must flush the log.
*/
int rc;
for (;;)
{
if (RT_SUCCESS(rc))
{
/*
* Wait for a debug event.
*/
if (RT_SUCCESS(rc))
{
if (RT_FAILURE(rc))
break;
}
else if (rc != VERR_TIMEOUT)
break;
/*
* Check for input.
*/
{
if (RT_FAILURE(rc))
break;
}
}
else if (rc == VERR_SEM_OUT_OF_TURN)
{
/*
* Wait for input. If Logging is enabled we'll only wait very briefly.
*/
{
if (RT_FAILURE(rc))
break;
}
}
else
break;
/*
* Forward log output.
*/
{
if (RT_FAILURE(rc))
break;
}
}
return rc;
}
/**
* Creates a a new instance.
*
* @returns VBox status code.
* @param ppDbgc Where to store the pointer to the instance data.
* @param pBack Pointer to the backend.
* @param fFlags The flags.
*/
{
/*
* Validate input.
*/
/*
* Allocate and initialize.
*/
if (!pDbgc)
return VERR_NO_MEMORY;
//pDbgc->fLog = false;
pDbgc->fRegCtxGuest = true;
//pDbgc->cPagingHierarchyDumps = 0;
//pDbgc->DisasmPos = {0};
//pDbgc->SourcePos = {0};
//pDbgc->DumpPos = {0};
//pDbgc->cbDumpElement = 0;
//pDbgc->cVars = 0;
//pDbgc->paVars = NULL;
//pDbgc->pPlugInHead = NULL;
//pDbgc->pFirstBp = NULL;
//pDbgc->abSearch = {0};
//pDbgc->cbSearch = 0;
//pDbgc->SearchAddr = {0};
//pDbgc->cbSearchRange = 0;
//pDbgc->uInputZero = 0;
//pDbgc->iRead = 0;
//pDbgc->iWrite = 0;
//pDbgc->cInputLines = 0;
//pDbgc->fInputOverflow = false;
//pDbgc->iArg = 0;
//pDbgc->rcOutput = 0;
//pDbgc->rcCmd = 0;
dbgcEvalInit();
return VINF_SUCCESS;
}
/**
* Destroys a DBGC instance created by dbgcCreate.
*
* @param pDbgc Pointer to the debugger console instance data.
*/
{
/* Disable log hook. */
{
}
/* Unload all plug-ins. */
/* Detach from the VM. */
/* finally, free the instance memory. */
}
/**
* Make a console instance.
*
* This will not return until either an 'exit' command is issued or a error code
* indicating connection loss is encountered.
*
* @returns VINF_SUCCESS if console termination caused by the 'exit' command.
* @returns The VBox status code causing the console termination.
*
* @param pUVM The user mode VM handle.
* @param pBack Pointer to the backend structure. This must contain
* a full set of function pointers to service the console.
* @param fFlags Reserved, must be zero.
* @remark A forced termination of the console is easiest done by forcing the
* callbacks to return fatal failures.
*/
{
/*
* Validate input.
*/
if (pUVM)
{
}
/*
* Allocate and initialize instance data
*/
if (RT_FAILURE(rc))
return rc;
/*
* Print welcome message.
*/
"Welcome to the VirtualBox Debugger!\n");
/*
* Attach to the specified VM.
*/
{
if (RT_SUCCESS(rc))
{
"Current VM is %08x, CPU #%u\n" /** @todo get and print the VM name! */
}
else
rc = pDbgc->CmdHlp.pfnVBoxError(&pDbgc->CmdHlp, rc, "When trying to attach to VM %p\n", pDbgc->pVM);
}
/*
* Load plugins.
*/
if (RT_SUCCESS(rc))
{
if (pVM)
if (RT_SUCCESS(rc))
{
/*
* Set debug config log callback.
*/
if ( hDbgCfg != NIL_RTDBGCFG
{
if (RT_FAILURE(rc2))
{
}
}
else
/*
* Run the debugger main loop.
*/
/*
* Remove debug config log callback.
*/
if (hDbgCfg != NIL_RTDBGCFG)
{
}
}
}
else
/*
* Cleanup console debugger session.
*/
}