diff options
Diffstat (limited to 'src/VBox/Debugger/DBGConsole.cpp')
-rw-r--r-- | src/VBox/Debugger/DBGConsole.cpp | 1232 |
1 files changed, 70 insertions, 1162 deletions
diff --git a/src/VBox/Debugger/DBGConsole.cpp b/src/VBox/Debugger/DBGConsole.cpp index 03115487e..e8804020b 100644 --- a/src/VBox/Debugger/DBGConsole.cpp +++ b/src/VBox/Debugger/DBGConsole.cpp @@ -1,10 +1,10 @@ -/* $Id: DBGConsole.cpp $ */ +/* $Id: DBGConsole.cpp 35829 2011-02-03 10:18:53Z vboxsync $ */ /** @file * DBGC - Debugger Console. */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -133,67 +133,24 @@ #define LOG_GROUP LOG_GROUP_DBGC #include <VBox/dbg.h> #include <VBox/vmm/dbgf.h> -#include <VBox/vmm/vm.h> -#include <VBox/vmm/vmm.h> -#include <VBox/vmm/mm.h> -#include <VBox/vmm/pgm.h> -#include <VBox/vmm/selm.h> -#include <VBox/dis.h> -#include <VBox/param.h> #include <VBox/err.h> #include <VBox/log.h> #include <iprt/asm.h> -#include <iprt/alloca.h> #include <iprt/assert.h> #include <iprt/mem.h> #include <iprt/string.h> -#include <iprt/ctype.h> - -#include <stdlib.h> -#include <stdio.h> #include "DBGCInternal.h" #include "DBGPlugIns.h" /******************************************************************************* -* Global Variables * -*******************************************************************************/ -/** Bitmap where set bits indicates the characters the may start an operator name. */ -static uint32_t g_bmOperatorChars[256 / (4*8)]; - - -/******************************************************************************* * Internal Functions * *******************************************************************************/ static int dbgcProcessLog(PDBGC pDbgc); - -/** - * Initializes g_bmOperatorChars. - */ -static void dbgcInitOpCharBitMap(void) -{ - memset(g_bmOperatorChars, 0, sizeof(g_bmOperatorChars)); - for (unsigned iOp = 0; iOp < g_cOps; iOp++) - ASMBitSet(&g_bmOperatorChars[0], (uint8_t)g_aOps[iOp].szName[0]); -} - - -/** - * Checks whether the character may be the start of an operator. - * - * @returns true/false. - * @param ch The character. - */ -DECLINLINE(bool) dbgcIsOpChar(char ch) -{ - return ASMBitTest(&g_bmOperatorChars[0], (uint8_t)ch); -} - - /** * Resolves a symbol (or tries to do so at least). * @@ -201,15 +158,18 @@ DECLINLINE(bool) dbgcIsOpChar(char ch) * @returns VBox status on failure. * @param pDbgc The debug console instance. * @param pszSymbol The symbol name. - * @param enmType The result type. + * @param enmType The result type. Specifying DBGCVAR_TYPE_GC_FAR may + * cause failure, avoid it. * @param pResult Where to store the result. */ int dbgcSymbolGet(PDBGC pDbgc, const char *pszSymbol, DBGCVARTYPE enmType, PDBGCVAR pResult) { + int rc; + /* * Builtin? */ - PCDBGCSYM pSymDesc = dbgcLookupRegisterSymbol(pDbgc, pszSymbol); + PCDBGCSYM pSymDesc = dbgcLookupRegisterSymbol(pDbgc, pszSymbol); if (pSymDesc) { if (!pSymDesc->pfnGet) @@ -217,37 +177,80 @@ int dbgcSymbolGet(PDBGC pDbgc, const char *pszSymbol, DBGCVARTYPE enmType, PDBGC return pSymDesc->pfnGet(pSymDesc, &pDbgc->CmdHlp, enmType, pResult); } + /* + * 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;" + ; + size_t const cchSymbol = strlen(pszSymbol); + if ( (cchSymbol == 2 && strstr(s_szTwoLetterRegisters, pszSymbol)) + || (cchSymbol == 3 && strstr(s_szThreeLetterRegisters, pszSymbol)) + || (cchSymbol == 6 && strstr(s_szSixLetterRegisters, pszSymbol))) + { + if (!strchr(pszSymbol, ';')) + { + DBGCVAR Var; + DBGCVAR_INIT_STRING(&Var, pszSymbol); + rc = dbgcOpRegister(pDbgc, &Var, pResult); + if (RT_SUCCESS(rc)) + return DBGCCmdHlpConvert(&pDbgc->CmdHlp, &Var, enmType, false /*fConvSyms*/, pResult); + } + } /* * Ask PDM. */ /** @todo resolve symbols using PDM. */ - /* * Ask the debug info manager. */ RTDBGSYMBOL Symbol; - int rc = DBGFR3AsSymbolByName(pDbgc->pVM, pDbgc->hDbgAs, pszSymbol, &Symbol, NULL); + rc = DBGFR3AsSymbolByName(pDbgc->pVM, pDbgc->hDbgAs, pszSymbol, &Symbol, NULL); if (RT_SUCCESS(rc)) { /* * Default return is a flat gc address. */ - memset(pResult, 0, sizeof(*pResult)); - pResult->enmRangeType = Symbol.cb ? DBGCVAR_RANGE_BYTES : DBGCVAR_RANGE_NONE; - pResult->u64Range = Symbol.cb; - pResult->enmType = DBGCVAR_TYPE_GC_FLAT; - pResult->u.GCFlat = Symbol.Value; - DBGCVAR VarTmp; + DBGCVAR_INIT_GC_FLAT(pResult, Symbol.Value); + if (Symbol.cb) + DBGCVAR_SET_RANGE(pResult, DBGCVAR_RANGE_BYTES, Symbol.cb); + switch (enmType) { /* nothing to do. */ case DBGCVAR_TYPE_GC_FLAT: - case DBGCVAR_TYPE_GC_FAR: case DBGCVAR_TYPE_ANY: return VINF_SUCCESS; + /* impossible at the moment. */ + case DBGCVAR_TYPE_GC_FAR: + return VERR_PARSE_CONVERSION_FAILED; + /* simply make it numeric. */ case DBGCVAR_TYPE_NUMBER: pResult->enmType = DBGCVAR_TYPE_NUMBER; @@ -255,19 +258,10 @@ int dbgcSymbolGet(PDBGC pDbgc, const char *pszSymbol, DBGCVARTYPE enmType, PDBGC return VINF_SUCCESS; /* cast it. */ - case DBGCVAR_TYPE_GC_PHYS: - VarTmp = *pResult; - return dbgcOpAddrPhys(pDbgc, &VarTmp, pResult); - - case DBGCVAR_TYPE_HC_FAR: case DBGCVAR_TYPE_HC_FLAT: - VarTmp = *pResult; - return dbgcOpAddrHost(pDbgc, &VarTmp, pResult); - case DBGCVAR_TYPE_HC_PHYS: - VarTmp = *pResult; - return dbgcOpAddrHostPhys(pDbgc, &VarTmp, pResult); + return DBGCCmdHlpConvert(&pDbgc->CmdHlp, pResult, enmType, false /*fConvSyms*/, pResult); default: AssertMsgFailed(("Internal error enmType=%d\n", enmType)); @@ -279,1096 +273,6 @@ int dbgcSymbolGet(PDBGC pDbgc, const char *pszSymbol, DBGCVARTYPE enmType, PDBGC } -static int dbgcEvalSubString(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pArg) -{ - Log2(("dbgcEvalSubString: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr)); - - /* - * Removing any quoting and escapings. - */ - char ch = *pszExpr; - if (ch == '"' || ch == '\'' || ch == '`') - { - if (pszExpr[--cchExpr] != ch) - return VERR_PARSE_UNBALANCED_QUOTE; - cchExpr--; - pszExpr++; - - /** @todo string unescaping. */ - } - pszExpr[cchExpr] = '\0'; - - /* - * Make the argument. - */ - pArg->pDesc = NULL; - pArg->pNext = NULL; - pArg->enmType = DBGCVAR_TYPE_STRING; - pArg->u.pszString = pszExpr; - pArg->enmRangeType = DBGCVAR_RANGE_BYTES; - pArg->u64Range = cchExpr; - - NOREF(pDbgc); - return 0; -} - - -static int dbgcEvalSubNum(char *pszExpr, unsigned uBase, PDBGCVAR pArg) -{ - Log2(("dbgcEvalSubNum: uBase=%d pszExpr=%s\n", uBase, pszExpr)); - /* - * Convert to number. - */ - uint64_t u64 = 0; - char ch; - while ((ch = *pszExpr) != '\0') - { - uint64_t u64Prev = u64; - unsigned u = ch - '0'; - if (u < 10 && u < uBase) - u64 = u64 * uBase + u; - else if (ch >= 'a' && (u = ch - ('a' - 10)) < uBase) - u64 = u64 * uBase + u; - else if (ch >= 'A' && (u = ch - ('A' - 10)) < uBase) - u64 = u64 * uBase + u; - else - return VERR_PARSE_INVALID_NUMBER; - - /* check for overflow - ARG!!! How to detect overflow correctly!?!?!? */ - if (u64Prev != u64 / uBase) - return VERR_PARSE_NUMBER_TOO_BIG; - - /* next */ - pszExpr++; - } - - /* - * Initialize the argument. - */ - pArg->pDesc = NULL; - pArg->pNext = NULL; - pArg->enmType = DBGCVAR_TYPE_NUMBER; - pArg->u.u64Number = u64; - pArg->enmRangeType = DBGCVAR_RANGE_NONE; - pArg->u64Range = 0; - - 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. - */ -static int dbgcEvalSubMatchVar(PDBGC pDbgc, PDBGCVAR pVar, PCDBGCVARDESC pVarDesc) -{ - /* - * (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. - */ - case DBGCVAR_CAT_POINTER_NO_RANGE: - if (pVar->enmRangeType != DBGCVAR_RANGE_NONE) - return VERR_PARSE_NO_RANGE_ALLOWED; - /* fallthru */ - case DBGCVAR_CAT_POINTER: - switch (pVar->enmType) - { - 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: - { - DBGCVAR Var; - int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_GC_FLAT, &Var); - if (RT_SUCCESS(rc)) - { - /* deal with range */ - if (pVar->enmRangeType != DBGCVAR_RANGE_NONE) - { - Var.enmRangeType = pVar->enmRangeType; - Var.u64Range = pVar->u64Range; - } - else if (pVarDesc->enmCategory == DBGCVAR_CAT_POINTER_NO_RANGE) - Var.enmRangeType = DBGCVAR_RANGE_NONE; - *pVar = Var; - return rc; - } - break; - } - - case DBGCVAR_TYPE_NUMBER: - { - RTGCPTR GCPtr = (RTGCPTR)pVar->u.u64Number; - pVar->enmType = DBGCVAR_TYPE_GC_FLAT; - pVar->u.GCFlat = GCPtr; - 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. - */ - case DBGCVAR_CAT_GC_POINTER_NO_RANGE: - if (pVar->enmRangeType != DBGCVAR_RANGE_NONE) - return VERR_PARSE_NO_RANGE_ALLOWED; - /* fallthru */ - case DBGCVAR_CAT_GC_POINTER: - switch (pVar->enmType) - { - 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: - { - DBGCVAR Var; - int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_GC_FLAT, &Var); - if (RT_SUCCESS(rc)) - { - /* deal with range */ - if (pVar->enmRangeType != DBGCVAR_RANGE_NONE) - { - Var.enmRangeType = pVar->enmRangeType; - Var.u64Range = pVar->u64Range; - } - else if (pVarDesc->enmCategory == DBGCVAR_CAT_POINTER_NO_RANGE) - Var.enmRangeType = DBGCVAR_RANGE_NONE; - *pVar = Var; - return rc; - } - break; - } - - case DBGCVAR_TYPE_NUMBER: - { - RTGCPTR GCPtr = (RTGCPTR)pVar->u.u64Number; - pVar->enmType = DBGCVAR_TYPE_GC_FLAT; - pVar->u.GCFlat = GCPtr; - 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. - */ - case DBGCVAR_CAT_NUMBER_NO_RANGE: - if (pVar->enmRangeType != DBGCVAR_RANGE_NONE) - return VERR_PARSE_NO_RANGE_ALLOWED; - /* fallthru */ - case DBGCVAR_CAT_NUMBER: - switch (pVar->enmType) - { - case DBGCVAR_TYPE_NUMBER: - return VINF_SUCCESS; - - case DBGCVAR_TYPE_SYMBOL: - case DBGCVAR_TYPE_STRING: - { - DBGCVAR Var; - int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_NUMBER, &Var); - if (RT_SUCCESS(rc)) - { - *pVar = Var; - 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: - switch (pVar->enmType) - { - case DBGCVAR_TYPE_SYMBOL: - pVar->enmType = DBGCVAR_TYPE_STRING; - /* 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: - switch (pVar->enmType) - { - case DBGCVAR_TYPE_STRING: - pVar->enmType = DBGCVAR_TYPE_SYMBOL; - /* fallthru */ - case DBGCVAR_TYPE_SYMBOL: - return VINF_SUCCESS; - default: - break; - } - break; - - /* - * Anything else is illegal. - */ - default: - AssertMsgFailed(("enmCategory=%d\n", pVar->enmType)); - 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. - */ -static int dbgcEvalSubMatchVars(PDBGC pDbgc, unsigned cVarsMin, unsigned cVarsMax, - PCDBGCVARDESC paVarDescs, unsigned cVarDescs, - PDBGCVAR paVars, unsigned cVars) -{ - /* - * Just do basic min / max checks first. - */ - if (cVars < cVarsMin) - return VERR_PARSE_TOO_FEW_ARGUMENTS; - if (cVars > cVarsMax) - return VERR_PARSE_TOO_MANY_ARGUMENTS; - - /* - * Match the descriptors and actual variables. - */ - PCDBGCVARDESC pPrevDesc = NULL; - unsigned cCurDesc = 0; - unsigned iVar = 0; - unsigned iVarDesc = 0; - while (iVar < cVars) - { - /* walk the descriptors */ - if (iVarDesc >= cVarDescs) - return VERR_PARSE_TOO_MANY_ARGUMENTS; - if ( ( paVarDescs[iVarDesc].fFlags & DBGCVD_FLAGS_DEP_PREV - && &paVarDescs[iVarDesc - 1] != pPrevDesc) - || cCurDesc >= paVarDescs[iVarDesc].cTimesMax) - { - iVarDesc++; - if (iVarDesc >= cVarDescs) - 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 (;;) - { - int rc = dbgcEvalSubMatchVar(pDbgc, &paVars[iVar], &paVarDescs[iVarDesc]); - if (RT_SUCCESS(rc)) - { - paVars[iVar].pDesc = &paVarDescs[iVarDesc]; - cCurDesc++; - break; - } - - /* can we advance? */ - if (paVarDescs[iVarDesc].cTimesMin > cCurDesc) - return VERR_PARSE_ARGUMENT_TYPE_MISMATCH; - if (++iVarDesc >= cVarDescs) - return VERR_PARSE_ARGUMENT_TYPE_MISMATCH; - cCurDesc = 0; - } - - /* next var */ - iVar++; - } - - /* - * Check that the rest of the descriptors are optional. - */ - while (iVarDesc < cVarDescs) - { - if (paVarDescs[iVarDesc].cTimesMin > cCurDesc) - 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. - */ -static int dbgcEvalSubUnary(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pResult) -{ - Log2(("dbgcEvalSubUnary: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr)); - - /* - * 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; - PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, pszExpr, false, ' '); - if (pOp) - { - /* binary operators means syntax error. */ - if (pOp->fBinary) - 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. */ - char *pszExpr2 = pszExpr + pOp->cchName; - while (RT_C_IS_BLANK(*pszExpr2)) - pszExpr2++; - - if (!*pszExpr2) - rc = VERR_PARSE_EMPTY_ARGUMENT; - else - { - DBGCVAR Arg; - if (*pszExpr2 == '(') - rc = dbgcEvalSub(pDbgc, pszExpr2, cchExpr - (pszExpr2 - pszExpr), &Arg); - else - rc = dbgcEvalSubUnary(pDbgc, pszExpr2, cchExpr - (pszExpr2 - pszExpr), &Arg); - if (RT_SUCCESS(rc)) - rc = pOp->pfnHandlerUnary(pDbgc, &Arg, pResult); - } - } - 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. - */ - bool fExternal = *pszExpr == '.'; - char *pszFun = fExternal ? pszExpr + 1 : pszExpr; - char *pszFunEnd = NULL; - if (pszExpr[cchExpr - 1] == ')' && RT_C_IS_ALPHA(*pszFun)) - { - pszFunEnd = pszExpr + 1; - while (*pszFunEnd != '(' && RT_C_IS_ALNUM(*pszFunEnd)) - pszFunEnd++; - if (*pszFunEnd != '(') - pszFunEnd = NULL; - } - - if (pszFunEnd) - { - /* - * Ok, it's a function call. - */ - if (fExternal) - pszExpr++, cchExpr--; - PCDBGCCMD pFun = dbgcRoutineLookup(pDbgc, pszExpr, pszFunEnd - pszExpr, fExternal); - if (!pFun) - return VERR_PARSE_FUNCTION_NOT_FOUND; - if (!pFun->pResultDesc) - return VERR_PARSE_NOT_A_FUNCTION; - - /* - * Parse the expression in parenthesis. - */ - cchExpr -= pszFunEnd - pszExpr; - pszExpr = pszFunEnd; - /** @todo implement multiple arguments. */ - DBGCVAR Arg; - rc = dbgcEvalSub(pDbgc, pszExpr, cchExpr, &Arg); - if (!rc) - { - rc = dbgcEvalSubMatchVars(pDbgc, pFun->cArgsMin, pFun->cArgsMax, pFun->paArgDescs, pFun->cArgDescs, &Arg, 1); - if (!rc) - rc = pFun->pfnHandler(pFun, &pDbgc->CmdHlp, pDbgc->pVM, &Arg, 1, pResult); - } - else if (rc == VERR_PARSE_EMPTY_ARGUMENT && pFun->cArgsMin == 0) - rc = pFun->pfnHandler(pFun, &pDbgc->CmdHlp, pDbgc->pVM, NULL, 0, pResult); - } - else - { - /* - * Didn't find any operators, so it must be a plain expression. - * This might be numeric or a string expression. - */ - char ch = pszExpr[0]; - char ch2 = pszExpr[1]; - if (ch == '0' && (ch2 == 'x' || ch2 == 'X')) - rc = dbgcEvalSubNum(pszExpr + 2, 16, pResult); - else if (ch == '0' && (ch2 == 'i' || ch2 == 'i')) - rc = dbgcEvalSubNum(pszExpr + 2, 10, pResult); - else if (ch == '0' && (ch2 == 't' || ch2 == 'T')) - rc = dbgcEvalSubNum(pszExpr + 2, 8, pResult); - /// @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? - */ - char *psz = pszExpr; - while (RT_C_IS_XDIGIT(*psz)) - psz++; - if (!*psz) - rc = dbgcEvalSubNum(pszExpr, 16, pResult); - else if ((*psz == 'h' || *psz == 'H') && !psz[1]) - { - *psz = '\0'; - rc = dbgcEvalSubNum(pszExpr, 16, pResult); - } - else - rc = dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult); - } - } - } - - 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. - */ -int dbgcEvalSub(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pResult) -{ - Log2(("dbgcEvalSub: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr)); - /* - * First we need to remove blanks in both ends. - * ASSUMES: There is no quoting unless the entire expression is a string. - */ - - /* stripping. */ - while (cchExpr > 0 && RT_C_IS_BLANK(pszExpr[cchExpr - 1])) - pszExpr[--cchExpr] = '\0'; - while (RT_C_IS_BLANK(*pszExpr)) - pszExpr++, cchExpr--; - if (!*pszExpr) - return VERR_PARSE_EMPTY_ARGUMENT; - - /* it there is any kind of quoting in the expression, it's string meat. */ - if (strpbrk(pszExpr, "\"'`")) - return dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult); - - /* - * Check if there are any parenthesis which needs removing. - */ - if (pszExpr[0] == '(' && pszExpr[cchExpr - 1] == ')') - { - do - { - unsigned cPar = 1; - char *psz = pszExpr + 1; - char ch; - while ((ch = *psz) != '\0') - { - if (ch == '(') - cPar++; - else if (ch == ')') - { - if (cPar <= 0) - return VERR_PARSE_UNBALANCED_PARENTHESIS; - cPar--; - if (cPar == 0 && psz[1]) /* If not at end, there's nothing to do. */ - break; - } - /* next */ - psz++; - } - if (ch) - break; - - /* remove the parenthesis. */ - pszExpr++; - cchExpr -= 2; - pszExpr[cchExpr] = '\0'; - - /* strip blanks. */ - while (cchExpr > 0 && RT_C_IS_BLANK(pszExpr[cchExpr - 1])) - pszExpr[--cchExpr] = '\0'; - while (RT_C_IS_BLANK(*pszExpr)) - pszExpr++, cchExpr--; - if (!*pszExpr) - return VERR_PARSE_EMPTY_ARGUMENT; - } while (pszExpr[0] == '(' && pszExpr[cchExpr - 1] == ')'); - } - - /* tabs to spaces. */ - char *psz = pszExpr; - while ((psz = strchr(psz, '\t')) != NULL) - *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; - PCDBGCOP pOpSplit = NULL; - unsigned cBinaryOps = 0; - unsigned cPar = 0; - char ch; - char chPrev = ' '; - bool fBinary = false; - psz = pszExpr; - - while ((ch = *psz) != '\0') - { - //Log2(("ch=%c cPar=%d fBinary=%d\n", ch, cPar, fBinary)); - /* - * Parenthesis. - */ - if (ch == '(') - { - cPar++; - fBinary = false; - } - else if (ch == ')') - { - if (cPar <= 0) - return VERR_PARSE_UNBALANCED_PARENTHESIS; - cPar--; - fBinary = true; - } - /* - * Potential operator. - */ - else if (cPar == 0 && !RT_C_IS_BLANK(ch)) - { - PCDBGCOP pOp = dbgcIsOpChar(ch) - ? dbgcOperatorLookup(pDbgc, psz, fBinary, chPrev) - : NULL; - if (pOp) - { - /* If not the right kind of operator we've got a syntax error. */ - if (pOp->fBinary != fBinary) - return VERR_PARSE_UNEXPECTED_OPERATOR; - - /* - * Update the parse state and skip the operator. - */ - if (!pOpSplit) - { - pOpSplit = pOp; - pszOpSplit = psz; - cBinaryOps = fBinary; - } - else if (fBinary) - { - cBinaryOps++; - if (pOp->iPrecedence >= pOpSplit->iPrecedence) - { - pOpSplit = pOp; - pszOpSplit = psz; - } - } - - psz += pOp->cchName - 1; - fBinary = false; - } - else - fBinary = true; - } - - /* next */ - psz++; - chPrev = ch; - } /* 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 - && pOpSplit->fBinary) - { - /* process 1st sub expression. */ - *pszOpSplit = '\0'; - DBGCVAR Arg1; - rc = dbgcEvalSub(pDbgc, pszExpr, pszOpSplit - pszExpr, &Arg1); - if (RT_SUCCESS(rc)) - { - /* process 2nd sub expression. */ - char *psz2 = pszOpSplit + pOpSplit->cchName; - DBGCVAR Arg2; - rc = dbgcEvalSub(pDbgc, psz2, cchExpr - (psz2 - pszExpr), &Arg2); - if (RT_SUCCESS(rc)) - /* apply the operator. */ - rc = pOpSplit->pfnHandlerBinary(pDbgc, &Arg1, &Arg2, pResult); - } - } - else if (cBinaryOps) - { - /* process sub expression. */ - pszOpSplit += pOpSplit->cchName; - DBGCVAR Arg; - rc = dbgcEvalSub(pDbgc, pszOpSplit, cchExpr - (pszOpSplit - pszExpr), &Arg); - if (RT_SUCCESS(rc)) - /* apply the operator. */ - rc = pOpSplit->pfnHandlerUnary(pDbgc, &Arg, pResult); - } - else - /* plain expression or using unary operators perhaps with parentheses. */ - rc = dbgcEvalSubUnary(pDbgc, pszExpr, cchExpr, pResult); - - 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) -{ - Log2(("dbgcProcessArguments: pCmd=%s pszArgs='%s'\n", pCmd->pszCmd, pszArgs)); - /* - * Check if we have any argument and if the command takes any. - */ - *pcArgs = 0; - /* strip leading blanks. */ - while (*pszArgs && RT_C_IS_BLANK(*pszArgs)) - pszArgs++; - if (!*pszArgs) - { - if (!pCmd->cArgsMin) - return 0; - return VERR_PARSE_TOO_FEW_ARGUMENTS; - } - /** @todo fixme - foo() doesn't work. */ - if (!pCmd->cArgsMax) - 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. - */ - if ( pCmd->cArgsMax == 1 - && pCmd->cArgsMin == 1 - && pCmd->cArgDescs == 1 - && pCmd->paArgDescs[0].enmCategory == DBGCVAR_CAT_STRING - && cArgs >= 1) - { - *pcArgs = 1; - RTStrStripR(pszArgs); - return dbgcEvalSubString(pDbgc, pszArgs, strlen(pszArgs), &paArgs[0]); - } - - - /* - * The parse loop. - */ - PDBGCVAR pArg0 = &paArgs[0]; - PDBGCVAR pArg = pArg0; - *pcArgs = 0; - do - { - /* - * Can we have another argument? - */ - if (*pcArgs >= pCmd->cArgsMax) - return VERR_PARSE_TOO_MANY_ARGUMENTS; - if (pArg >= &paArgs[cArgs]) - return VERR_PARSE_ARGUMENT_OVERFLOW; - - /* - * Find the end of the argument. - */ - int cPar = 0; - char chQuote = '\0'; - char *pszEnd = NULL; - char *psz = pszArgs; - char ch; - bool fBinary = false; - for (;;) - { - /* - * Check for the end. - */ - if ((ch = *psz) == '\0') - { - if (chQuote) - return VERR_PARSE_UNBALANCED_QUOTE; - if (cPar) - return VERR_PARSE_UNBALANCED_PARENTHESIS; - pszEnd = psz; - break; - } - /* - * When quoted we ignore everything but the quotation char. - * We use the REXX way of escaping the quotation char, i.e. double occurrence. - */ - else if (ch == '\'' || ch == '"' || ch == '`') - { - if (chQuote) - { - /* end quote? */ - if (ch == chQuote) - { - if (psz[1] == ch) - psz++; /* skip the escaped quote char */ - else - chQuote = '\0'; /* end of quoted string. */ - } - } - else - chQuote = ch; /* open new quote */ - } - /* - * Parenthesis can of course be nested. - */ - else if (ch == '(') - { - cPar++; - fBinary = false; - } - else if (ch == ')') - { - if (!cPar) - return VERR_PARSE_UNBALANCED_PARENTHESIS; - cPar--; - fBinary = true; - } - else if (!chQuote && !cPar) - { - /* - * Encountering blanks may mean the end of it all. A binary operator - * will force continued parsing. - */ - if (RT_C_IS_BLANK(*psz)) - { - pszEnd = psz++; /* just in case. */ - while (RT_C_IS_BLANK(*psz)) - psz++; - PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, psz, fBinary, ' '); - if (!pOp || pOp->fBinary != fBinary) - break; /* the end. */ - psz += pOp->cchName; - while (RT_C_IS_BLANK(*psz)) /* skip blanks so we don't get here again */ - psz++; - fBinary = false; - continue; - } - - /* - * Look for operators without a space up front. - */ - if (dbgcIsOpChar(*psz)) - { - PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, psz, fBinary, ' '); - if (pOp) - { - if (pOp->fBinary != fBinary) - { - pszEnd = psz; - /** @todo this is a parsing error really. */ - break; /* the end. */ - } - psz += pOp->cchName; - while (RT_C_IS_BLANK(*psz)) /* skip blanks so we don't get here again */ - psz++; - fBinary = false; - continue; - } - } - fBinary = true; - } - - /* next char */ - psz++; - } - *pszEnd = '\0'; - /* (psz = next char to process) */ - - /* - * Parse and evaluate the argument. - */ - int rc = dbgcEvalSub(pDbgc, pszArgs, strlen(pszArgs), pArg); - if (RT_FAILURE(rc)) - return rc; - - /* - * Next. - */ - pArg++; - (*pcArgs)++; - pszArgs = psz; - while (*pszArgs && RT_C_IS_BLANK(*pszArgs)) - 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. - * @param fNoExecute Indicates that no commands should actually be executed. - */ -int dbgcProcessCommand(PDBGC pDbgc, char *pszCmd, size_t cchCmd, bool fNoExecute) -{ - char *pszCmdInput = pszCmd; - - /* - * Skip blanks. - */ - while (RT_C_IS_BLANK(*pszCmd)) - pszCmd++, cchCmd--; - - /* external command? */ - bool fExternal = *pszCmd == '.'; - if (fExternal) - pszCmd++, cchCmd--; - - /* - * Find arguments. - */ - char *pszArgs = pszCmd; - while (RT_C_IS_ALNUM(*pszArgs)) - pszArgs++; - if (*pszArgs && (!RT_C_IS_BLANK(*pszArgs) || pszArgs == pszCmd)) - { - pDbgc->rcCmd = VINF_PARSE_INVALD_COMMAND_NAME; - pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "Syntax error in command '%s'!\n", pszCmdInput); - return 0; - } - - /* - * Find the command. - */ - PCDBGCCMD pCmd = dbgcRoutineLookup(pDbgc, pszCmd, pszArgs - pszCmd, fExternal); - if (!pCmd || (pCmd->fFlags & DBGCCMD_FLAGS_FUNCTION)) - { - pDbgc->rcCmd = VINF_PARSE_COMMAND_NOT_FOUND; - return pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "Unknown command '%s'!\n", pszCmdInput); - } - - /* - * Parse arguments (if any). - */ - unsigned cArgs; - int rc = dbgcProcessArguments(pDbgc, pCmd, pszArgs, &pDbgc->aArgs[pDbgc->iArg], RT_ELEMENTS(pDbgc->aArgs) - pDbgc->iArg, &cArgs); - - /* - * Execute the command. - */ - if (!rc) - { - if (!fNoExecute) - rc = pCmd->pfnHandler(pCmd, &pDbgc->CmdHlp, pDbgc->pVM, &pDbgc->aArgs[0], cArgs, NULL); - pDbgc->rcCmd = rc; - if (rc == VERR_DBGC_COMMAND_FAILED) - rc = VINF_SUCCESS; - } - else - { - pDbgc->rcCmd = rc; - - /* report parse / eval error. */ - switch (rc) - { - case VERR_PARSE_TOO_FEW_ARGUMENTS: - rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, - "Syntax error: Too few arguments. Minimum is %d for command '%s'.\n", pCmd->cArgsMin, pCmd->pszCmd); - break; - case VERR_PARSE_TOO_MANY_ARGUMENTS: - rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, - "Syntax error: Too many arguments. Maximum is %d for command '%s'.\n", pCmd->cArgsMax, pCmd->pszCmd); - break; - case VERR_PARSE_ARGUMENT_OVERFLOW: - rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, - "Syntax error: Too many arguments.\n"); - break; - case VERR_PARSE_UNBALANCED_QUOTE: - rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, - "Syntax error: Unbalanced quote (argument %d).\n", cArgs); - break; - case VERR_PARSE_UNBALANCED_PARENTHESIS: - rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, - "Syntax error: Unbalanced parenthesis (argument %d).\n", cArgs); - break; - case VERR_PARSE_EMPTY_ARGUMENT: - rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, - "Syntax error: An argument or subargument contains nothing useful (argument %d).\n", cArgs); - break; - case VERR_PARSE_UNEXPECTED_OPERATOR: - rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, - "Syntax error: Invalid operator usage (argument %d).\n", cArgs); - break; - case VERR_PARSE_INVALID_NUMBER: - rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, - "Syntax error: Ivalid numeric value (argument %d). If a string was the intention, then quote it.\n", cArgs); - break; - case VERR_PARSE_NUMBER_TOO_BIG: - rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, - "Error: Numeric overflow (argument %d).\n", cArgs); - break; - case VERR_PARSE_INVALID_OPERATION: - rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, - "Error: Invalid operation attempted (argument %d).\n", cArgs); - break; - case VERR_PARSE_FUNCTION_NOT_FOUND: - rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, - "Error: Function not found (argument %d).\n", cArgs); - break; - case VERR_PARSE_NOT_A_FUNCTION: - rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, - "Error: The function specified is not a function (argument %d).\n", cArgs); - break; - case VERR_PARSE_NO_MEMORY: - rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, - "Error: Out memory in the regular heap! Expect odd stuff to happen...\n", cArgs); - break; - case VERR_PARSE_INCORRECT_ARG_TYPE: - rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, - "Error: Incorrect argument type (argument %d?).\n", cArgs); - break; - case VERR_PARSE_VARIABLE_NOT_FOUND: - rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, - "Error: An undefined variable was referenced (argument %d).\n", cArgs); - break; - case VERR_PARSE_CONVERSION_FAILED: - rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, - "Error: A conversion between two types failed (argument %d).\n", cArgs); - break; - case VERR_PARSE_NOT_IMPLEMENTED: - rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, - "Error: You hit a debugger feature which isn't implemented yet (argument %d).\n", cArgs); - break; - case VERR_PARSE_BAD_RESULT_TYPE: - rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, - "Error: Couldn't satisfy a request for a specific result type (argument %d). (Usually applies to symbols)\n", cArgs); - break; - case VERR_PARSE_WRITEONLY_SYMBOL: - rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, - "Error: Cannot get symbol, it's set only (argument %d).\n", cArgs); - break; - - case VERR_DBGC_COMMAND_FAILED: - rc = VINF_SUCCESS; - break; - - default: - rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, - "Error: Unknown error %d!\n", rc); - return rc; - } - - /* - * Parse errors are non fatal. - */ - if (rc >= VERR_PARSE_FIRST && rc < VERR_PARSE_LAST) - rc = VINF_SUCCESS; - } - - return rc; -} - - /** * Process all commands currently in the buffer. * @@ -1378,7 +282,9 @@ int dbgcProcessCommand(PDBGC pDbgc, char *pszCmd, size_t cchCmd, bool fNoExecute */ static int dbgcProcessCommands(PDBGC pDbgc, bool fNoExecute) { - int rc = 0; + /** @todo Replace this with a sh/ksh/csh/rexx like toplevel language that + * allows doing function, loops, if, cases, and such. */ + int rc = VINF_SUCCESS; while (pDbgc->cInputLines) { /* @@ -1432,9 +338,11 @@ static int dbgcProcessCommands(PDBGC pDbgc, bool fNoExecute) */ pDbgc->pszScratch = psz; pDbgc->iArg = 0; - rc = dbgcProcessCommand(pDbgc, &pDbgc->achScratch[0], psz - &pDbgc->achScratch[0] - 1, fNoExecute); - if (rc) + rc = dbgcEvalCommand(pDbgc, &pDbgc->achScratch[0], psz - &pDbgc->achScratch[0] - 1, fNoExecute); + if ( rc == VERR_DBGC_QUIT + || rc == VWRN_DBGC_CMD_PENDING) break; + rc = VINF_SUCCESS; /* ignore other statuses */ } return rc; @@ -1954,7 +862,7 @@ int dbgcCreate(PDBGC *ppDbgc, PDBGCBACK pBack, unsigned fFlags) dbgcInitCmdHlp(pDbgc); pDbgc->pBack = pBack; pDbgc->pVM = NULL; - pDbgc->idCpu = NIL_VMCPUID; + pDbgc->idCpu = 0; pDbgc->hDbgAs = DBGF_AS_GLOBAL; pDbgc->pszEmulation = "CodeView/WinDbg"; pDbgc->paEmulationCmds = &g_aCmdsCodeView[0]; @@ -1990,7 +898,7 @@ int dbgcCreate(PDBGC *ppDbgc, PDBGCBACK pBack, unsigned fFlags) //pDbgc->rcOutput = 0; //pDbgc->rcCmd = 0; - dbgcInitOpCharBitMap(); + dbgcEvalInit(); *ppDbgc = pDbgc; return VINF_SUCCESS; @@ -2068,7 +976,7 @@ DBGDECL(int) DBGCCreate(PVM pVM, PDBGCBACK pBack, unsigned fFlags) rc = DBGFR3Attach(pVM); if (RT_SUCCESS(rc)) { - pDbgc->pVM = pVM; + pDbgc->pVM = pVM; pDbgc->idCpu = 0; rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "Current VM is %08x, CPU #%u\n" /** @todo get and print the VM name! */ |