/* $Id: tstDBGCParser.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */ /** @file * DBGC Testcase - Command Parser. */ /* * Copyright (C) 2006-2020 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * 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. */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #include #include "../DBGCInternal.h" #include #include #include /********************************************************************************************************************************* * Internal Functions * *********************************************************************************************************************************/ static DECLCALLBACK(bool) tstDBGCBackInput(PDBGCBACK pBack, uint32_t cMillies); static DECLCALLBACK(int) tstDBGCBackRead(PDBGCBACK pBack, void *pvBuf, size_t cbBuf, size_t *pcbRead); static DECLCALLBACK(int) tstDBGCBackWrite(PDBGCBACK pBack, const void *pvBuf, size_t cbBuf, size_t *pcbWritten); static DECLCALLBACK(void) tstDBGCBackSetReady(PDBGCBACK pBack, bool fReady); /********************************************************************************************************************************* * Global Variables * *********************************************************************************************************************************/ /** The test handle. */ static RTTEST g_hTest = NIL_RTTEST; /** The DBGC backend structure for use in this testcase. */ static DBGCBACK g_tstBack = { tstDBGCBackInput, tstDBGCBackRead, tstDBGCBackWrite, tstDBGCBackSetReady }; /** For keeping track of output prefixing. */ static bool g_fPendingPrefix = true; /** Pointer to the current input position. */ const char *g_pszInput = NULL; /** The output of the last command. */ static char g_szOutput[1024]; /** The current offset into g_szOutput. */ static size_t g_offOutput = 0; /** * Checks if there is input. * * @returns true if there is input ready. * @returns false if there not input ready. * @param pBack Pointer to the backend structure supplied by * the backend. The backend can use this to find * it's instance data. * @param cMillies Number of milliseconds to wait on input data. */ static DECLCALLBACK(bool) tstDBGCBackInput(PDBGCBACK pBack, uint32_t cMillies) { return g_pszInput != NULL && *g_pszInput != '\0'; } /** * Read input. * * @returns VBox status code. * @param pBack Pointer to the backend structure supplied by * the backend. The backend can use this to find * it's instance data. * @param pvBuf Where to put the bytes we read. * @param cbBuf Maximum nymber of bytes to read. * @param pcbRead Where to store the number of bytes actually read. * If NULL the entire buffer must be filled for a * successful return. */ static DECLCALLBACK(int) tstDBGCBackRead(PDBGCBACK pBack, void *pvBuf, size_t cbBuf, size_t *pcbRead) { if (g_pszInput && *g_pszInput) { size_t cb = strlen(g_pszInput); if (cb > cbBuf) cb = cbBuf; *pcbRead = cb; memcpy(pvBuf, g_pszInput, cb); g_pszInput += cb; } else *pcbRead = 0; return VINF_SUCCESS; } /** * Write (output). * * @returns VBox status code. * @param pBack Pointer to the backend structure supplied by * the backend. The backend can use this to find * it's instance data. * @param pvBuf What to write. * @param cbBuf Number of bytes to write. * @param pcbWritten Where to store the number of bytes actually written. * If NULL the entire buffer must be successfully written. */ static DECLCALLBACK(int) tstDBGCBackWrite(PDBGCBACK pBack, const void *pvBuf, size_t cbBuf, size_t *pcbWritten) { const char *pch = (const char *)pvBuf; if (pcbWritten) *pcbWritten = cbBuf; while (cbBuf-- > 0) { /* screen/log output */ if (g_fPendingPrefix) { RTTestPrintfNl(g_hTest, RTTESTLVL_ALWAYS, "OUTPUT: "); g_fPendingPrefix = false; } if (*pch == '\n') g_fPendingPrefix = true; RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "%c", *pch); /* buffer output */ if (g_offOutput < sizeof(g_szOutput) - 1) { g_szOutput[g_offOutput++] = *pch; g_szOutput[g_offOutput] = '\0'; } /* advance */ pch++; } return VINF_SUCCESS; } /** * Ready / busy notification. * * @param pBack Pointer to the backend structure supplied by * the backend. The backend can use this to find * it's instance data. * @param fReady Whether it's ready (true) or busy (false). */ static DECLCALLBACK(void) tstDBGCBackSetReady(PDBGCBACK pBack, bool fReady) { } /** * Completes the output, making sure that we're in * the 1 position of a new line. */ static void tstCompleteOutput(void) { if (!g_fPendingPrefix) RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "\n"); g_fPendingPrefix = true; } /** * Checks if two DBGC variables are identical * * @returns * @param pVar1 . * @param pVar2 . */ bool DBGCVarAreIdentical(PCDBGCVAR pVar1, PCDBGCVAR pVar2) { if (!pVar1) return false; if (pVar1 == pVar2) return true; if (pVar1->enmType != pVar2->enmType) return false; switch (pVar1->enmType) { case DBGCVAR_TYPE_GC_FLAT: if (pVar1->u.GCFlat != pVar2->u.GCFlat) return false; break; case DBGCVAR_TYPE_GC_FAR: if (pVar1->u.GCFar.off != pVar2->u.GCFar.off) return false; if (pVar1->u.GCFar.sel != pVar2->u.GCFar.sel) return false; break; case DBGCVAR_TYPE_GC_PHYS: if (pVar1->u.GCPhys != pVar2->u.GCPhys) return false; break; case DBGCVAR_TYPE_HC_FLAT: if (pVar1->u.pvHCFlat != pVar2->u.pvHCFlat) return false; break; case DBGCVAR_TYPE_HC_PHYS: if (pVar1->u.HCPhys != pVar2->u.HCPhys) return false; break; case DBGCVAR_TYPE_NUMBER: if (pVar1->u.u64Number != pVar2->u.u64Number) return false; break; case DBGCVAR_TYPE_STRING: case DBGCVAR_TYPE_SYMBOL: if (RTStrCmp(pVar1->u.pszString, pVar2->u.pszString) != 0) return false; break; default: AssertFailedReturn(false); } if (pVar1->enmRangeType != pVar2->enmRangeType) return false; switch (pVar1->enmRangeType) { case DBGCVAR_RANGE_NONE: break; case DBGCVAR_RANGE_ELEMENTS: case DBGCVAR_RANGE_BYTES: if (pVar1->u64Range != pVar2->u64Range) return false; break; default: AssertFailedReturn(false); } return true; } /** * Tries one command string. * @param pDbgc Pointer to the debugger instance. * @param pszCmds The command to test. * @param rcCmd The expected result. * @param fNoExecute When set, the command is not executed. * @param pszExpected Expected output. This does not need to include all * of the output, just the start of it. Thus the * prompt can be omitted. * @param cArgs The number of expected arguments. -1 if we don't * want to check the parsed arguments. * @param va Info about expected parsed arguments. For each * argument a DBGCVARTYPE, value (depends on type), * DBGCVARRANGETYPE and optionally range value. */ static void tstTryExV(PDBGC pDbgc, const char *pszCmds, int rcCmd, bool fNoExecute, const char *pszExpected, int32_t cArgs, va_list va) { RT_ZERO(g_szOutput); g_offOutput = 0; g_pszInput = pszCmds; if (strchr(pszCmds, '\0')[-1] == '\n') RTTestPrintfNl(g_hTest, RTTESTLVL_ALWAYS, "RUNNING: %s", pszCmds); else RTTestPrintfNl(g_hTest, RTTESTLVL_ALWAYS, "RUNNING: %s\n", pszCmds); pDbgc->rcCmd = VERR_INTERNAL_ERROR; dbgcProcessInput(pDbgc, fNoExecute); tstCompleteOutput(); if (pDbgc->rcCmd != rcCmd) RTTestFailed(g_hTest, "rcCmd=%Rrc expected =%Rrc\n", pDbgc->rcCmd, rcCmd); else if ( !fNoExecute && pszExpected && strncmp(pszExpected, g_szOutput, strlen(pszExpected))) RTTestFailed(g_hTest, "Wrong output - expected \"%s\"", pszExpected); if (cArgs >= 0) { PCDBGCVAR paArgs = pDbgc->aArgs; for (int32_t iArg = 0; iArg < cArgs; iArg++) { DBGCVAR ExpectedArg; ExpectedArg.enmType = (DBGCVARTYPE)va_arg(va, int/*DBGCVARTYPE*/); switch (ExpectedArg.enmType) { case DBGCVAR_TYPE_GC_FLAT: ExpectedArg.u.GCFlat = va_arg(va, RTGCPTR); break; case DBGCVAR_TYPE_GC_FAR: ExpectedArg.u.GCFar.sel = va_arg(va, int /*RTSEL*/); ExpectedArg.u.GCFar.off = va_arg(va, uint32_t); break; case DBGCVAR_TYPE_GC_PHYS: ExpectedArg.u.GCPhys = va_arg(va, RTGCPHYS); break; case DBGCVAR_TYPE_HC_FLAT: ExpectedArg.u.pvHCFlat = va_arg(va, void *); break; case DBGCVAR_TYPE_HC_PHYS: ExpectedArg.u.HCPhys = va_arg(va, RTHCPHYS); break; case DBGCVAR_TYPE_NUMBER: ExpectedArg.u.u64Number = va_arg(va, uint64_t); break; case DBGCVAR_TYPE_STRING: ExpectedArg.u.pszString = va_arg(va, const char *); break; case DBGCVAR_TYPE_SYMBOL: ExpectedArg.u.pszString = va_arg(va, const char *); break; default: RTTestFailed(g_hTest, "enmType=%u iArg=%u\n", ExpectedArg.enmType, iArg); ExpectedArg.u.u64Number = 0; break; } ExpectedArg.enmRangeType = (DBGCVARRANGETYPE)va_arg(va, int /*DBGCVARRANGETYPE*/); switch (ExpectedArg.enmRangeType) { case DBGCVAR_RANGE_NONE: ExpectedArg.u64Range = 0; break; case DBGCVAR_RANGE_ELEMENTS: ExpectedArg.u64Range = va_arg(va, uint64_t); break; case DBGCVAR_RANGE_BYTES: ExpectedArg.u64Range = va_arg(va, uint64_t); break; default: RTTestFailed(g_hTest, "enmRangeType=%u iArg=%u\n", ExpectedArg.enmRangeType, iArg); ExpectedArg.u64Range = 0; break; } if (!DBGCVarAreIdentical(&ExpectedArg, &paArgs[iArg])) RTTestFailed(g_hTest, "Arg #%u\n" "actual: enmType=%u u64=%#RX64 enmRangeType=%u u64Range=%#RX64\n" "expected: enmType=%u u64=%#RX64 enmRangeType=%u u64Range=%#RX64\n", iArg, paArgs[iArg].enmType, paArgs[iArg].u.u64Number, paArgs[iArg].enmRangeType, paArgs[iArg].u64Range, ExpectedArg.enmType, ExpectedArg.u.u64Number, ExpectedArg.enmRangeType, ExpectedArg.u64Range); } } } /** * Tries one command string. * * @param pDbgc Pointer to the debugger instance. * @param pszCmds The command to test. * @param rcCmd The expected result. * @param fNoExecute When set, the command is not executed. * @param pszExpected Expected output. This does not need to include all * of the output, just the start of it. Thus the * prompt can be omitted. * @param cArgs The number of expected arguments. -1 if we don't * want to check the parsed arguments. * @param ... Info about expected parsed arguments. For each * argument a DBGCVARTYPE, value (depends on type), * DBGCVARRANGETYPE and optionally range value. */ static void tstTryEx(PDBGC pDbgc, const char *pszCmds, int rcCmd, bool fNoExecute, const char *pszExpected, int32_t cArgs, ...) { va_list va; va_start(va, cArgs); tstTryExV(pDbgc, pszCmds, rcCmd, fNoExecute, pszExpected, cArgs, va); va_end(va); } /** * Tries one command string without executing it. * * @param pDbgc Pointer to the debugger instance. * @param pszCmds The command to test. * @param rcCmd The expected result. */ static void tstTry(PDBGC pDbgc, const char *pszCmds, int rcCmd) { return tstTryEx(pDbgc, pszCmds, rcCmd, true /*fNoExecute*/, NULL, -1); } #ifdef SOME_UNUSED_FUNCTION /** * Tries to execute one command string. * @param pDbgc Pointer to the debugger instance. * @param pszCmds The command to test. * @param rcCmd The expected result. * @param pszExpected Expected output. This does not need to include all * of the output, just the start of it. Thus the * prompt can be omitted. */ static void tstTryExec(PDBGC pDbgc, const char *pszCmds, int rcCmd, const char *pszExpected) { return tstTryEx(pDbgc, pszCmds, rcCmd, false /*fNoExecute*/, pszExpected, -1); } #endif /** * Test an operator on an expression resulting a plain number. * * @param pDbgc Pointer to the debugger instance. * @param pszExpr The express to test. * @param u64Expect The expected result. */ static void tstNumOp(PDBGC pDbgc, const char *pszExpr, uint64_t u64Expect) { char szCmd[80]; RTStrPrintf(szCmd, sizeof(szCmd), "format %s\n", pszExpr); char szExpected[80]; RTStrPrintf(szExpected, sizeof(szExpected), "Number: hex %llx dec 0i%lld oct 0t%llo", u64Expect, u64Expect, u64Expect); return tstTryEx(pDbgc, szCmd, VINF_SUCCESS, false /*fNoExecute*/, szExpected, -1); } /* * * CodeView emulation commands. * CodeView emulation commands. * CodeView emulation commands. * */ static void testCodeView_ba(PDBGC pDbgc) { RTTestISub("codeview - ba"); tstTry(pDbgc, "ba x 1 0f000:0000\n", VINF_SUCCESS); tstTry(pDbgc, "ba x 1 0f000:0000 0\n", VINF_SUCCESS); tstTry(pDbgc, "ba x 1 0f000:0000 0 ~0\n", VINF_SUCCESS); tstTry(pDbgc, "ba x 1 0f000:0000 0 ~0 \"command\"\n", VINF_SUCCESS); tstTry(pDbgc, "ba x 1 0f000:0000 0 ~0 \"command\" too_many\n", VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS); tstTry(pDbgc, "ba x 1\n", VERR_DBGC_PARSE_TOO_FEW_ARGUMENTS); tstTryEx(pDbgc, "ba x 1 0f000:1234 5 1000 \"command\"\n", VINF_SUCCESS, true /*fNoExecute*/, NULL /*pszExpected*/, 6 /*cArgs*/, DBGCVAR_TYPE_STRING, "x", DBGCVAR_RANGE_BYTES, UINT64_C(1), DBGCVAR_TYPE_NUMBER, UINT64_C(1), DBGCVAR_RANGE_NONE, DBGCVAR_TYPE_GC_FAR, 0xf000, UINT32_C(0x1234), DBGCVAR_RANGE_NONE, DBGCVAR_TYPE_NUMBER, UINT64_C(0x5), DBGCVAR_RANGE_NONE, DBGCVAR_TYPE_NUMBER, UINT64_C(0x1000), DBGCVAR_RANGE_NONE, DBGCVAR_TYPE_STRING, "command", DBGCVAR_RANGE_BYTES, UINT64_C(7)); tstTryEx(pDbgc, "ba x 1 %0f000:1234 5 1000 \"command\"\n", VINF_SUCCESS, true /*fNoExecute*/, NULL /*pszExpected*/, 6 /*cArgs*/, DBGCVAR_TYPE_STRING, "x", DBGCVAR_RANGE_BYTES, UINT64_C(1), DBGCVAR_TYPE_NUMBER, UINT64_C(1), DBGCVAR_RANGE_NONE, DBGCVAR_TYPE_GC_FLAT, UINT64_C(0xf1234), DBGCVAR_RANGE_NONE, DBGCVAR_TYPE_NUMBER, UINT64_C(0x5), DBGCVAR_RANGE_NONE, DBGCVAR_TYPE_NUMBER, UINT64_C(0x1000), DBGCVAR_RANGE_NONE, DBGCVAR_TYPE_STRING, "command", DBGCVAR_RANGE_BYTES, UINT64_C(7)); tstTry(pDbgc, "ba x 1 bad:bad 5 1000 \"command\"\n", VINF_SUCCESS); tstTry(pDbgc, "ba x 1 %bad:bad 5 1000 \"command\"\n", VERR_DBGC_PARSE_CONVERSION_FAILED); tstTryEx(pDbgc, "ba f 1 0f000:1234 5 1000 \"command\"\n", VINF_SUCCESS, true /*fNoExecute*/, NULL /*pszExpected*/, 6 /*cArgs*/, DBGCVAR_TYPE_STRING, "f", DBGCVAR_RANGE_BYTES, UINT64_C(1), DBGCVAR_TYPE_NUMBER, UINT64_C(1), DBGCVAR_RANGE_NONE, DBGCVAR_TYPE_GC_FAR, 0xf000, UINT32_C(0x1234), DBGCVAR_RANGE_NONE, DBGCVAR_TYPE_NUMBER, UINT64_C(0x5), DBGCVAR_RANGE_NONE, DBGCVAR_TYPE_NUMBER, UINT64_C(0x1000), DBGCVAR_RANGE_NONE, DBGCVAR_TYPE_STRING, "command", DBGCVAR_RANGE_BYTES, UINT64_C(7)); tstTry(pDbgc, "ba x 1 0f000:1234 qnx 1000 \"command\"\n", VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS); tstTry(pDbgc, "ba x 1 0f000:1234 5 qnx \"command\"\n", VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS); tstTry(pDbgc, "ba x qnx 0f000:1234 5 1000 \"command\"\n", VERR_DBGC_PARSE_INVALID_NUMBER); tstTry(pDbgc, "ba x 1 qnx 5 1000 \"command\"\n", VERR_DBGC_PARSE_INVALID_NUMBER); } static void testCodeView_bc(PDBGC pDbgc) { RTTestISub("codeview - bc"); } static void testCodeView_bd(PDBGC pDbgc) { RTTestISub("codeview - bc"); } static void testCodeView_be(PDBGC pDbgc) { RTTestISub("codeview - be"); } static void testCodeView_bl(PDBGC pDbgc) { RTTestISub("codeview - bl"); } static void testCodeView_bp(PDBGC pDbgc) { RTTestISub("codeview - bp"); } static void testCodeView_br(PDBGC pDbgc) { RTTestISub("codeview - br"); } static void testCodeView_d(PDBGC pDbgc) { RTTestISub("codeview - d"); } static void testCodeView_da(PDBGC pDbgc) { RTTestISub("codeview - da"); } static void testCodeView_db(PDBGC pDbgc) { RTTestISub("codeview - db"); } static void testCodeView_dd(PDBGC pDbgc) { RTTestISub("codeview - dd"); } static void testCodeView_dg(PDBGC pDbgc) { RTTestISub("codeview - dg"); } static void testCodeView_dga(PDBGC pDbgc) { RTTestISub("codeview - dga"); } static void testCodeView_di(PDBGC pDbgc) { RTTestISub("codeview - di"); } static void testCodeView_dia(PDBGC pDbgc) { RTTestISub("codeview - dia"); } static void testCodeView_dl(PDBGC pDbgc) { RTTestISub("codeview - dl"); } static void testCodeView_dla(PDBGC pDbgc) { RTTestISub("codeview - dla"); } static void testCodeView_dpd(PDBGC pDbgc) { RTTestISub("codeview - dpd"); } static void testCodeView_dpda(PDBGC pDbgc) { RTTestISub("codeview - dpda"); } static void testCodeView_dpdb(PDBGC pDbgc) { RTTestISub("codeview - dpdb"); } static void testCodeView_dpdg(PDBGC pDbgc) { RTTestISub("codeview - dpdg"); } static void testCodeView_dpdh(PDBGC pDbgc) { RTTestISub("codeview - dpdh"); } static void testCodeView_dph(PDBGC pDbgc) { RTTestISub("codeview - dph"); } static void testCodeView_dphg(PDBGC pDbgc) { RTTestISub("codeview - dphg"); } static void testCodeView_dphh(PDBGC pDbgc) { RTTestISub("codeview - dphh"); } static void testCodeView_dq(PDBGC pDbgc) { RTTestISub("codeview - dq"); } static void testCodeView_dt(PDBGC pDbgc) { RTTestISub("codeview - dt"); } static void testCodeView_dt16(PDBGC pDbgc) { RTTestISub("codeview - dt16"); } static void testCodeView_dt32(PDBGC pDbgc) { RTTestISub("codeview - dt32"); } static void testCodeView_dt64(PDBGC pDbgc) { RTTestISub("codeview - dt64"); } static void testCodeView_dw(PDBGC pDbgc) { RTTestISub("codeview - dw"); } static void testCodeView_eb(PDBGC pDbgc) { RTTestISub("codeview - eb"); } static void testCodeView_ew(PDBGC pDbgc) { RTTestISub("codeview - ew"); } static void testCodeView_ed(PDBGC pDbgc) { RTTestISub("codeview - ed"); } static void testCodeView_eq(PDBGC pDbgc) { RTTestISub("codeview - eq"); } static void testCodeView_g(PDBGC pDbgc) { RTTestISub("codeview - g"); } static void testCodeView_k(PDBGC pDbgc) { RTTestISub("codeview - k"); } static void testCodeView_kg(PDBGC pDbgc) { RTTestISub("codeview - kg"); } static void testCodeView_kh(PDBGC pDbgc) { RTTestISub("codeview - kh"); } static void testCodeView_lm(PDBGC pDbgc) { RTTestISub("codeview - lm"); } static void testCodeView_lmo(PDBGC pDbgc) { RTTestISub("codeview - lmo"); } static void testCodeView_ln(PDBGC pDbgc) { RTTestISub("codeview - ln"); } static void testCodeView_ls(PDBGC pDbgc) { RTTestISub("codeview - ls"); } static void testCodeView_m(PDBGC pDbgc) { RTTestISub("codeview - m"); } static void testCodeView_r(PDBGC pDbgc) { RTTestISub("codeview - r"); } static void testCodeView_rg(PDBGC pDbgc) { RTTestISub("codeview - rg"); } static void testCodeView_rg32(PDBGC pDbgc) { RTTestISub("codeview - rg32"); } static void testCodeView_rg64(PDBGC pDbgc) { RTTestISub("codeview - rg64"); } static void testCodeView_rh(PDBGC pDbgc) { RTTestISub("codeview - rh"); } static void testCodeView_rt(PDBGC pDbgc) { RTTestISub("codeview - rt"); } static void testCodeView_s(PDBGC pDbgc) { RTTestISub("codeview - s"); } static void testCodeView_sa(PDBGC pDbgc) { RTTestISub("codeview - sa"); } static void testCodeView_sb(PDBGC pDbgc) { RTTestISub("codeview - sb"); } static void testCodeView_sd(PDBGC pDbgc) { RTTestISub("codeview - sd"); } static void testCodeView_sq(PDBGC pDbgc) { RTTestISub("codeview - sq"); } static void testCodeView_su(PDBGC pDbgc) { RTTestISub("codeview - su"); } static void testCodeView_sw(PDBGC pDbgc) { RTTestISub("codeview - sw"); } static void testCodeView_t(PDBGC pDbgc) { RTTestISub("codeview - t"); } static void testCodeView_y(PDBGC pDbgc) { RTTestISub("codeview - y"); } static void testCodeView_u64(PDBGC pDbgc) { RTTestISub("codeview - u64"); } static void testCodeView_u32(PDBGC pDbgc) { RTTestISub("codeview - u32"); } static void testCodeView_u16(PDBGC pDbgc) { RTTestISub("codeview - u16"); } static void testCodeView_uv86(PDBGC pDbgc) { RTTestISub("codeview - uv86"); } /* * Common commands. */ static void testCommon_bye_exit_quit(PDBGC pDbgc) { RTTestISub("common - bye/exit/quit"); /* These have the same parameter descriptor and handler, the command really just has a couple of aliases.*/ tstTry(pDbgc, "bye\n", VINF_SUCCESS); tstTry(pDbgc, "bye x\n", VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS); tstTry(pDbgc, "bye 1\n", VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS); tstTry(pDbgc, "bye %bad:bad\n", VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS); tstTry(pDbgc, "exit\n", VINF_SUCCESS); tstTry(pDbgc, "quit\n", VINF_SUCCESS); } static void testCommon_cpu(PDBGC pDbgc) { RTTestISub("common - cpu"); tstTry(pDbgc, "cpu\n", VINF_SUCCESS); tstTry(pDbgc, "cpu 1\n", VINF_SUCCESS); tstTry(pDbgc, "cpu 1 1\n", VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS); tstTry(pDbgc, "cpu emt\n", VERR_DBGC_PARSE_INVALID_NUMBER); tstTry(pDbgc, "cpu @eax\n", VINF_SUCCESS); tstTry(pDbgc, "cpu %bad:bad\n", VERR_DBGC_PARSE_CONVERSION_FAILED); tstTry(pDbgc, "cpu '1'\n", VERR_DBGC_PARSE_INVALID_NUMBER); } static void testCommon_echo(PDBGC pDbgc) { RTTestISub("common - echo"); tstTry(pDbgc, "echo\n", VERR_DBGC_PARSE_TOO_FEW_ARGUMENTS); tstTry(pDbgc, "echo 1\n", VINF_SUCCESS); tstTryEx(pDbgc, "echo 1 2 3 4 5 6\n", VINF_SUCCESS, false, "1 2 3 4 5 6", -1); /* The idea here is that since the prefered input is a string, we definitely won't be confused by the number like beginning. */ tstTryEx(pDbgc, "echo 1234567890abcdefghijklmn\n", VINF_SUCCESS, false, "1234567890abcdefghijklmn", -1); /* The idea here is that we'll perform the + operation and then convert the result to a string (hex). */ tstTryEx(pDbgc, "echo 1 + 1\n", VINF_SUCCESS, false, "2", -1); tstTryEx(pDbgc, "echo \"1 + 1\"\n", VINF_SUCCESS, false, "1 + 1", -1); tstTryEx(pDbgc, "echo 0i10 + 6\n", VINF_SUCCESS, false, "10", -1); tstTryEx(pDbgc, "echo \"0i10 + 6\"\n", VINF_SUCCESS, false, "0i10 + 6", -1); tstTryEx(pDbgc, "echo %f000:0010\n", VINF_SUCCESS, false, "%00000000000f0010", -1); tstTryEx(pDbgc, "echo \"%f000:0010\"\n", VINF_SUCCESS, false, "%f000:0010", -1); tstTry(pDbgc, "echo %bad:bad\n", VERR_DBGC_PARSE_CONVERSION_FAILED); } static void testCommon_format(PDBGC pDbgc) { RTTestISub("common - format"); } static void testCommon_detect(PDBGC pDbgc) { RTTestISub("common - detect"); } static void testCommon_harakiri(PDBGC pDbgc) { RTTestISub("common - harakiri"); } static void testCommon_help(PDBGC pDbgc) { RTTestISub("common - help"); } static void testCommon_info(PDBGC pDbgc) { RTTestISub("common - info"); tstTry(pDbgc, "info 12fg\n", VINF_SUCCESS); tstTry(pDbgc, "info fflags argument\n", VINF_SUCCESS); } static void testCommon_loadimage(PDBGC pDbgc) { RTTestISub("common - loadimage"); } static void testCommon_loadmap(PDBGC pDbgc) { RTTestISub("common - loadmap"); } static void testCommon_loadplugin(PDBGC pDbgc) { RTTestISub("common - loadplugin"); } static void testCommon_loadseg(PDBGC pDbgc) { RTTestISub("common - loadseg"); } static void testCommon_loadsyms(PDBGC pDbgc) { RTTestISub("common - loadsyms"); } static void testCommon_loadvars(PDBGC pDbgc) { RTTestISub("common - loadvars"); } static void testCommon_log(PDBGC pDbgc) { RTTestISub("common - log"); } static void testCommon_logdest(PDBGC pDbgc) { RTTestISub("common - logdest"); } static void testCommon_logflags(PDBGC pDbgc) { RTTestISub("common - logflags"); } static void testCommon_runscript(PDBGC pDbgc) { RTTestISub("common - runscript"); } static void testCommon_set(PDBGC pDbgc) { RTTestISub("common - set"); } static void testCommon_showplugins(PDBGC pDbgc) { RTTestISub("common - showplugins"); } static void testCommon_showvars(PDBGC pDbgc) { RTTestISub("common - showvars"); } static void testCommon_stop(PDBGC pDbgc) { RTTestISub("common - stop"); } static void testCommon_unloadplugin(PDBGC pDbgc) { RTTestISub("common - unloadplugin"); } static void testCommon_unset(PDBGC pDbgc) { RTTestISub("common - unset"); } static void testCommon_writecore(PDBGC pDbgc) { RTTestISub("common - writecore"); } /* * Basic tests. */ static void testBasicsOddCases(PDBGC pDbgc) { RTTestISub("Odd cases"); tstTry(pDbgc, "r @rax\n", VINF_SUCCESS); tstTry(pDbgc, "r @eax\n", VINF_SUCCESS); tstTry(pDbgc, "r @ah\n", VINF_SUCCESS); tstTry(pDbgc, "r @notavalidregister\n", VERR_DBGF_REGISTER_NOT_FOUND); } static void testBasicsOperators(PDBGC pDbgc) { RTTestISub("Operators"); tstNumOp(pDbgc, "1", 1); tstNumOp(pDbgc, "1", 1); tstNumOp(pDbgc, "1", 1); tstNumOp(pDbgc, "+1", 1); tstNumOp(pDbgc, "++++++1", 1); tstNumOp(pDbgc, "-1", UINT64_MAX); tstNumOp(pDbgc, "--1", 1); tstNumOp(pDbgc, "---1", UINT64_MAX); tstNumOp(pDbgc, "----1", 1); tstNumOp(pDbgc, "~0", UINT64_MAX); tstNumOp(pDbgc, "~1", UINT64_MAX-1); tstNumOp(pDbgc, "~~0", 0); tstNumOp(pDbgc, "~~1", 1); tstNumOp(pDbgc, "!1", 0); tstNumOp(pDbgc, "!0", 1); tstNumOp(pDbgc, "!42", 0); tstNumOp(pDbgc, "!!42", 1); tstNumOp(pDbgc, "!!!42", 0); tstNumOp(pDbgc, "!!!!42", 1); tstNumOp(pDbgc, "1 +1", 2); tstNumOp(pDbgc, "1 + 1", 2); tstNumOp(pDbgc, "1+1", 2); tstNumOp(pDbgc, "1+ 1", 2); tstNumOp(pDbgc, "1 - 1", 0); tstNumOp(pDbgc, "99 - 90", 9); tstNumOp(pDbgc, "2 * 2", 4); tstNumOp(pDbgc, "2 / 2", 1); tstNumOp(pDbgc, "2 / 0", UINT64_MAX); tstNumOp(pDbgc, "0i1024 / 0i4", 256); tstNumOp(pDbgc, "8 mod 7", 1); tstNumOp(pDbgc, "1<<1", 2); tstNumOp(pDbgc, "1<<0i32", UINT64_C(0x0000000100000000)); tstNumOp(pDbgc, "1<<0i48", UINT64_C(0x0001000000000000)); tstNumOp(pDbgc, "1<<0i63", UINT64_C(0x8000000000000000)); tstNumOp(pDbgc, "fedcba0987654321>>0i04", UINT64_C(0x0fedcba098765432)); tstNumOp(pDbgc, "fedcba0987654321>>0i32", UINT64_C(0xfedcba09)); tstNumOp(pDbgc, "fedcba0987654321>>0i48", UINT64_C(0x0000fedc)); tstNumOp(pDbgc, "0ef & 4", 4); tstNumOp(pDbgc, "01234567891 & fff", UINT64_C(0x00000000891)); tstNumOp(pDbgc, "01234567891 & ~fff", UINT64_C(0x01234567000)); tstNumOp(pDbgc, "1 | 1", 1); tstNumOp(pDbgc, "0 | 4", 4); tstNumOp(pDbgc, "4 | 0", 4); tstNumOp(pDbgc, "4 | 4", 4); tstNumOp(pDbgc, "1 | 4 | 2", 7); tstNumOp(pDbgc, "1 ^ 1", 0); tstNumOp(pDbgc, "1 ^ 0", 1); tstNumOp(pDbgc, "0 ^ 1", 1); tstNumOp(pDbgc, "3 ^ 1", 2); tstNumOp(pDbgc, "7 ^ 3", 4); tstNumOp(pDbgc, "7 || 3", 1); tstNumOp(pDbgc, "1 || 0", 1); tstNumOp(pDbgc, "0 || 1", 1); tstNumOp(pDbgc, "0 || 0", 0); tstNumOp(pDbgc, "0 && 0", 0); tstNumOp(pDbgc, "1 && 0", 0); tstNumOp(pDbgc, "0 && 1", 0); tstNumOp(pDbgc, "1 && 1", 1); tstNumOp(pDbgc, "4 && 1", 1); } static void testBasicsFundametalParsing(PDBGC pDbgc) { RTTestISub("Fundamental parsing"); tstTry(pDbgc, "stop\n", VINF_SUCCESS); tstTry(pDbgc, "format 1\n", VINF_SUCCESS); tstTry(pDbgc, "format \n", VERR_DBGC_PARSE_TOO_FEW_ARGUMENTS); tstTry(pDbgc, "format 0 1 23 4\n", VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS); tstTry(pDbgc, "format 'x'\n", VINF_SUCCESS); tstTry(pDbgc, "format 'x' 'x'\n", VERR_DBGC_PARSE_TOO_MANY_ARGUMENTS); tstTry(pDbgc, "format 'x''x'\n", VINF_SUCCESS); tstTry(pDbgc, "format 'x'\"x\"\n", VERR_DBGC_PARSE_EXPECTED_BINARY_OP); tstTry(pDbgc, "format 'x'1\n", VERR_DBGC_PARSE_EXPECTED_BINARY_OP); tstTry(pDbgc, "format (1)1\n", VERR_DBGC_PARSE_EXPECTED_BINARY_OP); tstTry(pDbgc, "format (1)(1)\n", VERR_DBGC_PARSE_EXPECTED_BINARY_OP); tstTry(pDbgc, "format (1)''\n", VERR_DBGC_PARSE_EXPECTED_BINARY_OP); tstTry(pDbgc, "format nosuchfunction(1)\n", VERR_DBGC_PARSE_FUNCTION_NOT_FOUND); tstTry(pDbgc, "format nosuchfunction(1,2,3)\n", VERR_DBGC_PARSE_FUNCTION_NOT_FOUND); tstTry(pDbgc, "format nosuchfunction()\n", VERR_DBGC_PARSE_FUNCTION_NOT_FOUND); tstTry(pDbgc, "format randu32()\n", VINF_SUCCESS); tstTryEx(pDbgc, "format %0\n", VINF_SUCCESS, false, "Guest flat address: %00000000", -1); tstTryEx(pDbgc, "format %eax\n", VINF_SUCCESS, false, "Guest flat address: %cafebabe", -1); tstTry(pDbgc, "sa 3 23 4 'q' \"21123123\" 'b' \n", VINF_SUCCESS); tstTry(pDbgc, "sa 3,23, 4,'q' ,\"21123123\" , 'b' \n", VINF_SUCCESS); } int main() { /* * Init. */ int rc = RTTestInitAndCreate("tstDBGCParser", &g_hTest); if (rc) return rc; RTTestBanner(g_hTest); /* * Create a DBGC instance. */ RTTestSub(g_hTest, "dbgcCreate"); PDBGC pDbgc; rc = dbgcCreate(&pDbgc, &g_tstBack, 0); if (RT_SUCCESS(rc)) { pDbgc->pVM = (PVM)pDbgc; rc = dbgcProcessInput(pDbgc, true /* fNoExecute */); tstCompleteOutput(); if (RT_SUCCESS(rc)) { /* * Perform basic tests first. */ testBasicsFundametalParsing(pDbgc); if (RTTestErrorCount(g_hTest) == 0) testBasicsOperators(pDbgc); if (RTTestErrorCount(g_hTest) == 0) testBasicsOddCases(pDbgc); /* * Test commands. */ if (RTTestErrorCount(g_hTest) == 0) { testCodeView_ba(pDbgc); testCodeView_bc(pDbgc); testCodeView_bd(pDbgc); testCodeView_be(pDbgc); testCodeView_bl(pDbgc); testCodeView_bp(pDbgc); testCodeView_br(pDbgc); testCodeView_d(pDbgc); testCodeView_da(pDbgc); testCodeView_db(pDbgc); testCodeView_dd(pDbgc); testCodeView_dg(pDbgc); testCodeView_dga(pDbgc); testCodeView_di(pDbgc); testCodeView_dia(pDbgc); testCodeView_dl(pDbgc); testCodeView_dla(pDbgc); testCodeView_dpd(pDbgc); testCodeView_dpda(pDbgc); testCodeView_dpdb(pDbgc); testCodeView_dpdg(pDbgc); testCodeView_dpdh(pDbgc); testCodeView_dph(pDbgc); testCodeView_dphg(pDbgc); testCodeView_dphh(pDbgc); testCodeView_dq(pDbgc); testCodeView_dt(pDbgc); testCodeView_dt16(pDbgc); testCodeView_dt32(pDbgc); testCodeView_dt64(pDbgc); testCodeView_dw(pDbgc); testCodeView_eb(pDbgc); testCodeView_ew(pDbgc); testCodeView_ed(pDbgc); testCodeView_eq(pDbgc); testCodeView_g(pDbgc); testCodeView_k(pDbgc); testCodeView_kg(pDbgc); testCodeView_kh(pDbgc); testCodeView_lm(pDbgc); testCodeView_lmo(pDbgc); testCodeView_ln(pDbgc); testCodeView_ls(pDbgc); testCodeView_m(pDbgc); testCodeView_r(pDbgc); testCodeView_rg(pDbgc); testCodeView_rg32(pDbgc); testCodeView_rg64(pDbgc); testCodeView_rh(pDbgc); testCodeView_rt(pDbgc); testCodeView_s(pDbgc); testCodeView_sa(pDbgc); testCodeView_sb(pDbgc); testCodeView_sd(pDbgc); testCodeView_sq(pDbgc); testCodeView_su(pDbgc); testCodeView_sw(pDbgc); testCodeView_t(pDbgc); testCodeView_y(pDbgc); testCodeView_u64(pDbgc); testCodeView_u32(pDbgc); testCodeView_u16(pDbgc); testCodeView_uv86(pDbgc); testCommon_bye_exit_quit(pDbgc); testCommon_cpu(pDbgc); testCommon_echo(pDbgc); testCommon_format(pDbgc); testCommon_detect(pDbgc); testCommon_harakiri(pDbgc); testCommon_help(pDbgc); testCommon_info(pDbgc); testCommon_loadimage(pDbgc); testCommon_loadmap(pDbgc); testCommon_loadplugin(pDbgc); testCommon_loadseg(pDbgc); testCommon_loadsyms(pDbgc); testCommon_loadvars(pDbgc); testCommon_log(pDbgc); testCommon_logdest(pDbgc); testCommon_logflags(pDbgc); testCommon_runscript(pDbgc); testCommon_set(pDbgc); testCommon_showplugins(pDbgc); testCommon_showvars(pDbgc); testCommon_stop(pDbgc); testCommon_unloadplugin(pDbgc); testCommon_unset(pDbgc); testCommon_writecore(pDbgc); } } dbgcDestroy(pDbgc); } /* * Summary */ return RTTestSummaryAndDestroy(g_hTest); }