DBGConsole.cpp revision 5699db7faa791297ad0e44ce408ed2c16c5079c4
/** $Id$ */
/** @file
* DBGC - Debugger Console.
*/
/*
* Copyright (C) 2006-2007 innotek GmbH
*
* 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 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 on debug builds. Later it was hastily
* built into the VBoxDbg module with a very simple Qt wrapper around it.
*
* The debugger is optional and presently not built into release builds
* of VirtualBox. It is therefore necessary to enclose code related to it
* in \#ifdef VBOX_WITH_DEBUGGER blocks. This is mandatory for components
* that register extenral commands.
*
*
* @section sec_dbgc_op Operation (intentions)
*
* 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.
*
*
* @subsection sec_dbg_op_numbers Numbers
*
* Numbers are hexadecimal unless specified with a prefix indicating
* elsewise. Prefixes:
* - '0x' - hexadecimal.
* - '0i' - 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 '0d' and
* '0' for decimal and octal.
*
*
* @subsection sec_dbg_op_address Addressing modes
*
* - Default is flat. For compatability '%' 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_evalution Evaluation
*
* Most unary and binary C operators are supported, check the help text for
* details. However, some of these are not yet implemented because this is
* tiresome and annoying work. So, if something is missing and you need it
* you implement it or complain to bird. (Ditto for missing functions.)
*
* Simple variable support is provided thru the 'set' and 'unset' commands and
* the unary '$' operator.
*
* The unary '@' operator will indicate function calls. Commands and functions
* are the same thing, except that functions has a return type.
*
*
* @subsection sec_dbg_op_registers Registers
*
* Registers are addressed using their name. Some registers which have several fields
* (like gdtr) will have separate names indicating the different fields. The default
* register set is the guest one. To access the hypervisor register one have to
* prefix the register names with '.'.
*
* The registers are implemented as built-in symbols. For making gdb guys more at
* home it is possible to access them with the '$' operator, i.e. as a variable.
*
*
* @subsection sec_dbg_op_commands Commands and Functions
*
* Commands and functions are the same thing, except that functions may return a
* can detect whether they are invoked as a command or function by checking whether
* there is a return variable or not.
*
* with a letter. Operator characters are not permitted in the names of course.
* Space is allowed, but must be flagged so the parser can check for multiple
* spaces and tabs. (This feature is for 'dump xyz' and for emulating the
* gdb 'info abc'.)
*
* The '.' prefix indicates the set of external commands. External commands are
* command registered by VMM components.
*
*
* @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).
*
*
*
* @section sec_dbgc_linking Linking and API
*
* The DBGC code is linked into the VBoxVMM module. (At present it is also
* linked into VBoxDbg, but this is obviously very wrong.)
*
* A COM object will be created for the DBGC so it can be operated remotely
* without using TCP. VBoxDbg is the intended audience for this usage. 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 <stdlib.h>
#include <stdio.h>
#include "DBGCInternal.h"
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** Bitmap where set bits indicates the characters the may start an operator name. */
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/**
* Initalizes g_bmOperatorChars.
*/
static void dbgcInitOpCharBitMap(void)
{
}
/**
* Checks whether the character may be the start of an operator.
*
* @param ch The character.
*/
{
}
/**
* 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.
* @param pResult Where to store the result.
*/
{
/*
* Builtin?
*/
if (pSymDesc)
{
return VERR_PARSE_WRITEONLY_SYMBOL;
}
/*
* Ask PDM.
*/
/** @todo resolve symbols using PDM. */
/*
* Ask the debug info manager.
*/
if (VBOX_SUCCESS(rc))
{
/*
* Default return is a flat gc address.
*/
switch (enmType)
{
/* nothing to do. */
case DBGCVAR_TYPE_GC_FLAT:
case DBGCVAR_TYPE_GC_FAR:
case DBGCVAR_TYPE_ANY:
return VINF_SUCCESS;
/* simply make it numeric. */
case DBGCVAR_TYPE_NUMBER:
return VINF_SUCCESS;
/* cast it. */
case DBGCVAR_TYPE_GC_PHYS:
case DBGCVAR_TYPE_HC_FAR:
case DBGCVAR_TYPE_HC_FLAT:
case DBGCVAR_TYPE_HC_PHYS:
default:
return VERR_INVALID_PARAMETER;
}
}
return VERR_PARSE_NOT_IMPLEMENTED;
}
{
/*
* Removing any quoting and escapings.
*/
{
return VERR_PARSE_UNBALANCED_QUOTE;
cchExpr--;
pszExpr++;
/** @todo string unescaping. */
}
/*
* Make the argument.
*/
return 0;
}
{
/*
* Convert to number.
*/
char ch;
{
unsigned u = ch - '0';
if (u < 10 && u < uBase)
else
return VERR_PARSE_INVALID_NUMBER;
/* check for overflow - ARG!!! How to detect overflow correctly!?!?!? */
return VERR_PARSE_NUMBER_TOO_BIG;
/* next */
pszExpr++;
}
/*
* Initialize the argument.
*/
return 0;
}
/**
* Match variable and variable descriptor, promoting the variable if necessary.
*
* @returns VBox status code.
* @param pDbgc Debug console instanace.
* @param pVar Variable.
* @param pVarDesc Variable descriptor.
*/
{
/*
* (If match or promoted to match, return, else break.)
*/
switch (pVarDesc->enmCategory)
{
/*
* Anything goes
*/
case DBGCVAR_CAT_ANY:
return VINF_SUCCESS;
/*
* Pointer with and without range.
* We can try resolve strings and symbols as symbols and
* promote numbers to flat GC pointers.
*/
return VERR_PARSE_NO_RANGE_ALLOWED;
/* fallthru */
case DBGCVAR_CAT_POINTER:
{
case DBGCVAR_TYPE_GC_FLAT:
case DBGCVAR_TYPE_GC_FAR:
case DBGCVAR_TYPE_GC_PHYS:
case DBGCVAR_TYPE_HC_FLAT:
case DBGCVAR_TYPE_HC_FAR:
case DBGCVAR_TYPE_HC_PHYS:
return VINF_SUCCESS;
case DBGCVAR_TYPE_SYMBOL:
case DBGCVAR_TYPE_STRING:
{
if (VBOX_SUCCESS(rc))
{
/* deal with range */
{
}
return rc;
}
break;
}
case DBGCVAR_TYPE_NUMBER:
{
return VINF_SUCCESS;
}
default:
break;
}
break;
/*
* GC pointer with and without range.
* We can try resolve strings and symbols as symbols and
* promote numbers to flat GC pointers.
*/
return VERR_PARSE_NO_RANGE_ALLOWED;
/* fallthru */
case DBGCVAR_CAT_GC_POINTER:
{
case DBGCVAR_TYPE_GC_FLAT:
case DBGCVAR_TYPE_GC_FAR:
case DBGCVAR_TYPE_GC_PHYS:
return VINF_SUCCESS;
case DBGCVAR_TYPE_HC_FLAT:
case DBGCVAR_TYPE_HC_FAR:
case DBGCVAR_TYPE_HC_PHYS:
return VERR_PARSE_CONVERSION_FAILED;
case DBGCVAR_TYPE_SYMBOL:
case DBGCVAR_TYPE_STRING:
{
if (VBOX_SUCCESS(rc))
{
/* deal with range */
{
}
return rc;
}
break;
}
case DBGCVAR_TYPE_NUMBER:
{
return VINF_SUCCESS;
}
default:
break;
}
break;
/*
* Number with or without a range.
* Numbers can be resolved from symbols, but we cannot demote a pointer
* to a number.
*/
return VERR_PARSE_NO_RANGE_ALLOWED;
/* fallthru */
case DBGCVAR_CAT_NUMBER:
{
case DBGCVAR_TYPE_NUMBER:
return VINF_SUCCESS;
case DBGCVAR_TYPE_SYMBOL:
case DBGCVAR_TYPE_STRING:
{
if (VBOX_SUCCESS(rc))
{
return rc;
}
break;
}
default:
break;
}
break;
/*
* Strings can easily be made from symbols (and of course strings).
* We could consider reformatting the addresses and numbers into strings later...
*/
case DBGCVAR_CAT_STRING:
{
case DBGCVAR_TYPE_SYMBOL:
/* fallthru */
case DBGCVAR_TYPE_STRING:
return VINF_SUCCESS;
default:
break;
}
break;
/*
* Symol is pretty much the same thing as a string (at least until we actually implement it).
*/
case DBGCVAR_CAT_SYMBOL:
{
case DBGCVAR_TYPE_STRING:
/* fallthru */
case DBGCVAR_TYPE_SYMBOL:
return VINF_SUCCESS;
default:
break;
}
break;
/*
* Anything else is illegal.
*/
default:
break;
}
return VERR_PARSE_NO_ARGUMENT_MATCH;
}
/**
* Matches a set of variables with a description set.
*
* This is typically used for routine arguments before a call. The effects in
* addition to the validation, is that some variables might be propagated to
* other types in order to match the description. The following transformations
* are supported:
* - String reinterpreted as a symbol and resolved to a number or pointer.
* - Number to a pointer.
* - Pointer to a number.
* @returns 0 on success with paVars.
* @returns VBox error code for match errors.
*/
{
/*
* Just do basic min / max checks first.
*/
return VERR_PARSE_TOO_FEW_ARGUMENTS;
return VERR_PARSE_TOO_MANY_ARGUMENTS;
/*
* Match the descriptors and actual variables.
*/
unsigned cCurDesc = 0;
unsigned iVar = 0;
unsigned iVarDesc = 0;
{
/* walk the descriptors */
return VERR_PARSE_TOO_MANY_ARGUMENTS;
{
iVarDesc++;
return VERR_PARSE_TOO_MANY_ARGUMENTS;
cCurDesc = 0;
}
/*
* Skip thru optional arguments until we find something which matches
* or can easily be promoted to what the descriptor want.
*/
for (;;)
{
if (VBOX_SUCCESS(rc))
{
cCurDesc++;
break;
}
/* can we advance? */
cCurDesc = 0;
}
/* next var */
iVar++;
}
/*
* Check that the rest of the descriptors are optional.
*/
{
return VERR_PARSE_TOO_FEW_ARGUMENTS;
cCurDesc = 0;
/* next */
iVarDesc++;
}
return 0;
}
/**
* Evaluates one argument with respect to unary operators.
*
* @returns 0 on success. pResult contains the result.
* @returns VBox error code on parse or other evaluation error.
*
* @param pDbgc Debugger console instance data.
* @param pszExpr The expression string.
* @param pResult Where to store the result of the expression evaluation.
*/
{
/*
* The state of the expression is now such that it will start by zero or more
* unary operators and being followed by an expression of some kind.
* The expression is either plain or in parenthesis.
*
* Being in a lazy, recursive mode today, the parsing is done as simple as possible. :-)
* ASSUME: unary operators are all of equal precedence.
*/
int rc = 0;
if (pOp)
{
/* binary operators means syntax error. */
return VERR_PARSE_UNEXPECTED_OPERATOR;
/*
* If the next expression (the one following the unary operator) is in a
* parenthesis a full eval is needed. If not the unary eval will suffice.
*/
/* calc and strip next expr. */
pszExpr2++;
if (!*pszExpr2)
else
{
if (*pszExpr2 == '(')
else
if (VBOX_SUCCESS(rc))
}
}
else
{
/*
* Didn't find any operators, so it we have to check if this can be an
* function call before assuming numeric or string expression.
*
* (ASSUMPTIONS:)
* A function name only contains alphanumerical chars and it can not start
* with a numerical character.
* Immediately following the name is a parenthesis which must over
* the remaining part of the expression.
*/
{
pszFunEnd++;
if (*pszFunEnd != '(')
}
if (pszFunEnd)
{
/*
* Ok, it's a function call.
*/
if (fExternal)
if (!pFun)
return VERR_PARSE_FUNCTION_NOT_FOUND;
if (!pFun->pResultDesc)
return VERR_PARSE_NOT_A_FUNCTION;
/*
* Parse the expression in parenthesis.
*/
/** @todo implement multiple arguments. */
if (!rc)
{
rc = dbgcEvalSubMatchVars(pDbgc, pFun->cArgsMin, pFun->cArgsMax, pFun->paArgDescs, pFun->cArgDescs, &Arg, 1);
if (!rc)
}
}
else
{
/*
* Didn't find any operators, so it must be a plain expression.
* This might be numeric or a string expression.
*/
/// @todo 0b doesn't work as a binary prefix, we confuse it with 0bf8:0123 and stuff.
//else if (ch == '0' && (ch2 == 'b' || ch2 == 'b'))
// rc = dbgcEvalSubNum(pszExpr + 2, 2, pResult);
else
{
/*
* Hexadecimal number or a string?
*/
psz++;
if (!*psz)
{
*psz = '\0';
}
else
}
}
}
return rc;
}
/**
* Evaluates one argument.
*
* @returns 0 on success. pResult contains the result.
* @returns VBox error code on parse or other evaluation error.
*
* @param pDbgc Debugger console instance data.
* @param pszExpr The expression string.
* @param pResult Where to store the result of the expression evaluation.
*/
{
/*
* First we need to remove blanks in both ends.
* ASSUMES: There is no quoting unless the entire expression is a string.
*/
/* stripping. */
if (!*pszExpr)
return VERR_PARSE_EMPTY_ARGUMENT;
/* it there is any kind of quoting in the expression, it's string meat. */
/*
* Check if there are any parenthesis which needs removing.
*/
{
do
{
unsigned cPar = 1;
char ch;
{
if (ch == '(')
cPar++;
else if (ch == ')')
{
if (cPar <= 0)
cPar--;
break;
}
/* next */
psz++;
}
if (ch)
break;
/* remove the parenthesis. */
pszExpr++;
cchExpr -= 2;
/* strip blanks. */
if (!*pszExpr)
return VERR_PARSE_EMPTY_ARGUMENT;
}
/* tabs to spaces. */
*psz = ' ';
/*
* Now, we need to look for the binary operator with the lowest precedence.
*
* If there are no operators we're left with a simple expression which we
* evaluate with respect to unary operators
*/
char *pszOpSplit = NULL;
unsigned cBinaryOps = 0;
unsigned cPar = 0;
char ch;
char chPrev = ' ';
bool fBinary = false;
{
//Log2(("ch=%c cPar=%d fBinary=%d\n", ch, cPar, fBinary));
/*
* Parenthesis.
*/
if (ch == '(')
{
cPar++;
fBinary = false;
}
else if (ch == ')')
{
if (cPar <= 0)
cPar--;
fBinary = true;
}
/*
* Potential operator.
*/
{
: NULL;
if (pOp)
{
/* If not the right kind of operator we've got a syntax error. */
return VERR_PARSE_UNEXPECTED_OPERATOR;
/*
* Update the parse state and skip the operator.
*/
if (!pOpSplit)
{
pszOpSplit = psz;
}
else if (fBinary)
{
cBinaryOps++;
{
pszOpSplit = psz;
}
}
fBinary = false;
}
else
fBinary = true;
}
/* next */
psz++;
} /* parse loop. */
/*
* Either we found an operator to divide the expression by
* or we didn't find any. In the first case it's divide and
* conquer. In the latter it's a single expression which
* needs dealing with its unary operators if any.
*/
int rc;
if ( cBinaryOps
{
/* process 1st sub expression. */
*pszOpSplit = '\0';
if (VBOX_SUCCESS(rc))
{
/* process 2nd sub expression. */
if (VBOX_SUCCESS(rc))
/* apply the operator. */
}
}
else if (cBinaryOps)
{
/* process sub expression. */
if (VBOX_SUCCESS(rc))
/* apply the operator. */
}
else
/* plain expression or using unary operators perhaps with paratheses. */
return rc;
}
/**
* Parses the arguments of one command.
*
* @returns 0 on success.
* @returns VBox error code on parse error with *pcArg containing the argument causing trouble.
* @param pDbgc Debugger console instance data.
* @param pCmd Pointer to the command descriptor.
* @param pszArg Pointer to the arguments to parse.
* @param paArgs Where to store the parsed arguments.
* @param cArgs Size of the paArgs array.
* @param pcArgs Where to store the number of arguments.
* In the event of an error this is used to store the index of the offending argument.
*/
static int dbgcProcessArguments(PDBGC pDbgc, PCDBGCCMD pCmd, char *pszArgs, PDBGCVAR paArgs, unsigned cArgs, unsigned *pcArgs)
{
/*
* Check if we have any argument and if the command takes any.
*/
*pcArgs = 0;
/* strip leading blanks. */
pszArgs++;
if (!*pszArgs)
{
return 0;
return VERR_PARSE_TOO_FEW_ARGUMENTS;
}
/** @todo fixme - foo() doesn't work. */
return VERR_PARSE_TOO_MANY_ARGUMENTS;
/*
* This is a hack, it's "temporary" and should go away "when" the parser is
* modified to match arguments while parsing.
*/
&& cArgs >= 1)
{
*pcArgs = 1;
}
/*
* The parse loop.
*/
*pcArgs = 0;
do
{
/*
* Can we have another argument?
*/
return VERR_PARSE_TOO_MANY_ARGUMENTS;
return VERR_PARSE_ARGUMENT_OVERFLOW;
/*
* Find the end of the argument.
*/
int cPar = 0;
char chQuote = '\0';
char ch;
bool fBinary = false;
for (;;)
{
/*
* Check for the end.
*/
{
if (chQuote)
return VERR_PARSE_UNBALANCED_QUOTE;
if (cPar)
break;
}
/*
* When quoted we ignore everything but the quotation char.
* We use the REXX way of escaping the quotation char, i.e. double occurence.
*/
{
if (chQuote)
{
/* end quote? */
{
psz++; /* skip the escaped quote char */
else
}
}
else
}
/*
* Parenthesis can of course be nested.
*/
else if (ch == '(')
{
cPar++;
fBinary = false;
}
else if (ch == ')')
{
if (!cPar)
cPar--;
fBinary = true;
}
{
/*
* Encountering blanks may mean the end of it all. A binary operator
* will force continued parsing.
*/
{
psz++;
break; /* the end. */
psz++;
fBinary = false;
continue;
}
/*
* Look for operators without a space up front.
*/
if (dbgcIsOpChar(*psz))
{
if (pOp)
{
{
/** @todo this is a parsing error really. */
break; /* the end. */
}
psz++;
fBinary = false;
continue;
}
}
fBinary = true;
}
/* next char */
psz++;
}
*pszEnd = '\0';
/* (psz = next char to process) */
/*
* Parse and evaluate the argument.
*/
if (VBOX_FAILURE(rc))
return rc;
/*
* Next.
*/
pArg++;
(*pcArgs)++;
pszArgs++;
} while (*pszArgs);
/*
* Match the arguments.
*/
return dbgcEvalSubMatchVars(pDbgc, pCmd->cArgsMin, pCmd->cArgsMax, pCmd->paArgDescs, pCmd->cArgDescs, pArg0, pArg - pArg0);
}
/**
* Process one command.
*
* @returns VBox status code. Any error indicates the termination of the console session.
* @param pDbgc Debugger console instance data.
* @param pszCmd Pointer to the command.
* @param cchCmd Length of the command.
*/
{
char *pszCmdInput = pszCmd;
/*
* Skip blanks.
*/
/* external command? */
if (fExternal)
/*
* Find arguments.
*/
pszArgs++;
{
return 0;
}
/*
* Find the command.
*/
/*
* Parse arguments (if any).
*/
unsigned cArgs;
int rc = dbgcProcessArguments(pDbgc, pCmd, pszArgs, &pDbgc->aArgs[pDbgc->iArg], ELEMENTS(pDbgc->aArgs) - pDbgc->iArg, &cArgs);
/*
* Execute the command.
*/
if (!rc)
{
}
else
{
/* report parse / eval error. */
switch (rc)
{
"Syntax error: Too few arguments. Minimum is %d for command '%s'.\n", pCmd->cArgsMin, pCmd->pszCmd);
break;
"Syntax error: Too many arguments. Maximum is %d for command '%s'.\n", pCmd->cArgsMax, pCmd->pszCmd);
break;
"Syntax error: Too many arguments.\n");
break;
"Syntax error: Unbalanced quote (argument %d).\n", cArgs);
break;
"Syntax error: Unbalanced parenthesis (argument %d).\n", cArgs);
break;
"Syntax error: An argument or subargument contains nothing useful (argument %d).\n", cArgs);
break;
"Syntax error: Invalid operator usage (argument %d).\n", cArgs);
break;
"Syntax error: Ivalid numeric value (argument %d). If a string was the intention, then quote it.\n", cArgs);
break;
"Error: Numeric overflow (argument %d).\n", cArgs);
break;
"Error: Invalid operation attempted (argument %d).\n", cArgs);
break;
"Error: Function not found (argument %d).\n", cArgs);
break;
"Error: The function specified is not a function (argument %d).\n", cArgs);
break;
case VERR_PARSE_NO_MEMORY:
"Error: Out memory in the regular heap! Expect odd stuff to happen...\n", cArgs);
break;
"Error: Incorrect argument type (argument %d?).\n", cArgs);
break;
"Error: An undefined variable was referenced (argument %d).\n", cArgs);
break;
"Error: A conversion between two types failed (argument %d).\n", cArgs);
break;
"Error: You hit a debugger feature which isn't implemented yet (argument %d).\n", cArgs);
break;
"Error: Couldn't satisfy a request for a specific result type (argument %d). (Usually applies to symbols)\n", cArgs);
break;
"Error: Cannot get symbol, it's set only (argument %d).\n", cArgs);
break;
default:
"Error: Unknown error %d!\n", rc);
return rc;
}
/*
* Parse errors are non fatal.
*/
rc = 0;
}
return rc;
}
/**
* 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.
*/
{
int rc = 0;
while (pDbgc->cInputLines)
{
/*
* Empty the log buffer if we're hooking the log.
*/
{
if (VBOX_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)
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 (VBOX_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 (VBOX_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.
*/
{
/*
* We know there's input ready, so let's read it first.
*/
if (VBOX_FAILURE(rc))
return rc;
/*
* Now execute any ready commands.
*/
if (pDbgc->cInputLines)
{
/** @todo this fReady stuff is broken. */
if ( VBOX_SUCCESS(rc)
}
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_HWACCL: 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 (VBOX_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 (VBOX_SUCCESS(rc))
break;
}
/*
* The second part is events which can occur at any time.
*/
case DBGFEVENT_FATAL_ERROR:
{
if (VBOX_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 (VBOX_SUCCESS(rc))
break;
}
{
pDbgc->fRegCtxGuest = false;
"\ndbgf event: Hypervisor Assertion! (%s)\n"
"%s"
"%s"
"\n",
if (VBOX_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 (VBOX_SUCCESS(rc))
break;
}
{
break;
}
case DBGFEVENT_TERMINATING:
{
break;
}
default:
{
rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Unknown event %d!\n", pEvent->enmType);
break;
}
}
/*
* Prompt, anyone?
*/
{
}
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;
}
/**
* Run the debugger console.
*
* @returns VBox status.
* @param pDbgc Pointer to the debugger console instance data.
*/
{
/*
* 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 = VINF_SUCCESS;
for (;;)
{
{
/*
* Wait for a debug event.
*/
if (VBOX_SUCCESS(rc))
{
if (VBOX_FAILURE(rc))
break;
}
else if (rc != VERR_TIMEOUT)
break;
/*
* Check for input.
*/
{
if (VBOX_FAILURE(rc))
break;
}
}
else
{
/*
* Wait for input. If Logging is enabled we'll only wait very briefly.
*/
{
if (VBOX_FAILURE(rc))
break;
}
}
/*
* Forward log output.
*/
{
if (VBOX_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->DisasmPos = {0};
//pDbgc->SourcePos = {0};
//pDbgc->DumpPos = {0};
//pDbgc->cbDumpElement = 0;
//pDbgc->cVars = 0;
//pDbgc->paVars = NULL;
//pDbgc->pFirstBp = NULL;
//pDbgc->uInputZero = 0;
//pDbgc->iRead = 0;
//pDbgc->iWrite = 0;
//pDbgc->cInputLines = 0;
//pDbgc->fInputOverflow = false;
//pDbgc->iArg = 0;
//pDbgc->rcOutput = 0;
return VINF_SUCCESS;
}
/**
* Destroys a DBGC instance created by dbgcCreate.
*
* @param pDbgc Pointer to the debugger console instance data.
*/
{
/* Disable log hook. */
{
}
/* 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 pVM 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.
*/
/*
* 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\n" /** @todo get and print the VM name! */
"VBoxDbg> ",
}
else
rc = pDbgc->CmdHlp.pfnVBoxError(&pDbgc->CmdHlp, rc, "When trying to attach to VM %p\n", pDbgc->pVM);
}
else
"VBoxDbg> ");
/*
* Run the main loop.
*/
if (RT_SUCCESS(rc))
/*
* Cleanup console debugger session.
*/
return rc;
}