Changeset 5675 in vbox for trunk/src/VBox/Debugger/DBGCCommands.cpp
- Timestamp:
- Nov 11, 2007 5:42:00 AM (17 years ago)
- File:
-
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Debugger/DBGCCommands.cpp
r5674 r5675 1 1 /** $Id$ */ 2 2 /** @file 3 * DBGC - Debugger Console .3 * DBGC - Debugger Console, Native Commands. 4 4 */ 5 5 … … 15 15 * be useful, but WITHOUT ANY WARRANTY of any kind. 16 16 */ 17 18 19 /** @page pg_dbgc DBGC - The Debug Console20 *21 * The debugger console is a first attempt to make some interactive22 * debugging facilities for the VirtualBox backend (i.e. the VM). At a later23 * stage we'll make a fancy gui around this, but for the present a telnet (or24 * serial terminal) will have to suffice.25 *26 * The debugger is only built into the VM with debug builds or when27 * VBOX_WITH_DEBUGGER is defined. There might be need for \#ifdef'ing on this28 * define to enable special debugger hooks, but the general approach is to29 * make generic interfaces. The individual components also can register30 * external commands, and such code must be within \#ifdef.31 *32 *33 * @section sec_dbgc_op Operation (intentions)34 *35 * The console will process commands in a manner similar to the OS/2 and36 * windows kernel debuggers. This means ';' is a command separator and37 * that when possible we'll use the same command names as these two uses.38 *39 *40 * @subsection sec_dbg_op_numbers Numbers41 *42 * Numbers are hexadecimal unless specified with a prefix indicating43 * elsewise. Prefixes:44 * - '0x' - hexadecimal.45 * - '0i' - decimal46 * - '0t' - octal.47 * - '0y' - binary.48 *49 *50 * @subsection sec_dbg_op_address Addressing modes51 *52 * - Default is flat. For compatability '%' also means flat.53 * - Segmented addresses are specified selector:offset.54 * - Physical addresses are specified using '%%'.55 * - The default target for the addressing is the guest context, the '#'56 * will override this and set it to the host.57 *58 *59 * @subsection sec_dbg_op_evalution Evaluation60 *61 * As time permits support will be implemented support for a subset of the C62 * binary operators, starting with '+', '-', '*' and '/'. Support for variables63 * are provided thru commands 'set' and 'unset' and the unary operator '$'. The64 * unary '@' operator will indicate function calls. The debugger needs a set of65 * memory read functions, but we might later extend this to allow registration of66 * external functions too.67 *68 * A special command '?' will then be added which evalutates a given expression69 * and prints it in all the different formats.70 *71 *72 * @subsection sec_dbg_op_registers Registers73 *74 * Registers are addressed using their name. Some registers which have several fields75 * (like gdtr) will have separate names indicating the different fields. The default76 * register set is the guest one. To access the hypervisor register one have to77 * prefix the register names with '.'.78 *79 *80 * @subsection sec_dbg_op_commands Commands81 *82 * The commands are all lowercase, case sensitive, and starting with a letter. We will83 * later add some special commands ('?' for evaulation) and perhaps command classes ('.', '!')84 *85 *86 * @section sec_dbg_tasks Tasks87 *88 * To implement DBGT and instrument VMM for basic state inspection and log89 * viewing, the follwing task must be executed:90 *91 * -# Basic threading layer in RT.92 * -# Basic tcpip server abstration in RT.93 * -# Write DBGC.94 * -# Write DBCTCP.95 * -# Integrate with VMM and the rest.96 * -# Start writing DBGF (VMM).97 */98 99 100 101 17 102 18 /******************************************************************************* … … 148 64 static DECLCALLBACK(int) dbgcCmdRunScript(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult); 149 65 150 static int dbgcEvalSub(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pResult);151 static int dbgcProcessCommand(PDBGC pDbgc, char *pszCmd, size_t cchCmd);152 153 66 154 67 /******************************************************************************* 155 68 * Global Variables * 156 69 *******************************************************************************/ 157 /**158 * Pointer to head of the list of external commands.159 */160 static PDBGCEXTCMDS g_pExtCmdsHead; /** @todo rw protect g_pExtCmdsHead! */161 /** Locks the g_pExtCmdsHead list for reading. */162 #define DBGCEXTCMDS_LOCK_RD() do { } while (0)163 /** Locks the g_pExtCmdsHead list for writing. */164 #define DBGCEXTCMDS_LOCK_WR() do { } while (0)165 /** UnLocks the g_pExtCmdsHead list after reading. */166 #define DBGCEXTCMDS_UNLOCK_RD() do { } while (0)167 /** UnLocks the g_pExtCmdsHead list after writing. */168 #define DBGCEXTCMDS_UNLOCK_WR() do { } while (0)169 170 171 70 /** One argument of any kind. */ 172 71 static const DBGCVARDESC g_aArgAny[] = … … 257 156 258 157 /** Command descriptors for the basic commands. */ 259 staticconst DBGCCMD g_aCmds[] =158 const DBGCCMD g_aCmds[] = 260 159 { 261 160 /* pszCmd, cArgsMin, cArgsMax, paArgDescs, cArgDescs, pResultDesc, fFlags, pfnHandler pszSyntax, ....pszDescription */ … … 280 179 }; 281 180 282 283 /** Bitmap where set bits indicates the characters the may start an operator name. */ 284 static uint32_t g_bmOperatorChars[256 / (4*8)]; 285 286 287 288 181 /** The number of native commands. */ 182 const unsigned g_cCmds = RT_ELEMENTS(g_aCmds); 183 184 185 /** 186 * Pointer to head of the list of external commands. 187 */ 188 static PDBGCEXTCMDS g_pExtCmdsHead; /** @todo rw protect g_pExtCmdsHead! */ 189 /** Locks the g_pExtCmdsHead list for reading. */ 190 #define DBGCEXTCMDS_LOCK_RD() do { } while (0) 191 /** Locks the g_pExtCmdsHead list for writing. */ 192 #define DBGCEXTCMDS_LOCK_WR() do { } while (0) 193 /** UnLocks the g_pExtCmdsHead list after reading. */ 194 #define DBGCEXTCMDS_UNLOCK_RD() do { } while (0) 195 /** UnLocks the g_pExtCmdsHead list after writing. */ 196 #define DBGCEXTCMDS_UNLOCK_WR() do { } while (0) 197 198 199 200 201 /** 202 * Finds a routine. 203 * 204 * @returns Pointer to the command descriptor. 205 * If the request was for an external command, the caller is responsible for 206 * unlocking the external command list. 207 * @returns NULL if not found. 208 * @param pDbgc The debug console instance. 209 * @param pachName Pointer to the routine string (not terminated). 210 * @param cchName Length of the routine name. 211 * @param fExternal Whether or not the routine is external. 212 */ 213 PCDBGCCMD dbgcRoutineLookup(PDBGC pDbgc, const char *pachName, size_t cchName, bool fExternal) 214 { 215 if (!fExternal) 216 { 217 /* emulation first, so commands can be overloaded (info ++). */ 218 PCDBGCCMD pCmd = pDbgc->paEmulationCmds; 219 unsigned cLeft = pDbgc->cEmulationCmds; 220 while (cLeft-- > 0) 221 { 222 if ( !strncmp(pachName, pCmd->pszCmd, cchName) 223 && !pCmd->pszCmd[cchName]) 224 return pCmd; 225 pCmd++; 226 } 227 228 for (unsigned iCmd = 0; iCmd < ELEMENTS(g_aCmds); iCmd++) 229 { 230 if ( !strncmp(pachName, g_aCmds[iCmd].pszCmd, cchName) 231 && !g_aCmds[iCmd].pszCmd[cchName]) 232 return &g_aCmds[iCmd]; 233 } 234 } 235 else 236 { 237 DBGCEXTCMDS_LOCK_RD(); 238 for (PDBGCEXTCMDS pExtCmds = g_pExtCmdsHead; pExtCmds; pExtCmds = pExtCmds->pNext) 239 { 240 for (unsigned iCmd = 0; iCmd < pExtCmds->cCmds; iCmd++) 241 { 242 if ( !strncmp(pachName, pExtCmds->paCmds[iCmd].pszCmd, cchName) 243 && !pExtCmds->paCmds[iCmd].pszCmd[cchName]) 244 return &pExtCmds->paCmds[iCmd]; 245 } 246 } 247 DBGCEXTCMDS_UNLOCK_RD(); 248 } 249 250 NOREF(pDbgc); 251 return NULL; 252 } 253 254 255 /** 256 * Register one or more external commands. 257 * 258 * @returns VBox status. 259 * @param paCommands Pointer to an array of command descriptors. 260 * The commands must be unique. It's not possible 261 * to register the same commands more than once. 262 * @param cCommands Number of commands. 263 */ 264 DBGDECL(int) DBGCRegisterCommands(PCDBGCCMD paCommands, unsigned cCommands) 265 { 266 /* 267 * Lock the list. 268 */ 269 DBGCEXTCMDS_LOCK_WR(); 270 PDBGCEXTCMDS pCur = g_pExtCmdsHead; 271 while (pCur) 272 { 273 if (paCommands == pCur->paCmds) 274 { 275 DBGCEXTCMDS_UNLOCK_WR(); 276 AssertMsgFailed(("Attempt at re-registering %d command(s)!\n", cCommands)); 277 return VWRN_DBGC_ALREADY_REGISTERED; 278 } 279 pCur = pCur->pNext; 280 } 281 282 /* 283 * Allocate new chunk. 284 */ 285 int rc = 0; 286 pCur = (PDBGCEXTCMDS)RTMemAlloc(sizeof(*pCur)); 287 if (pCur) 288 { 289 pCur->cCmds = cCommands; 290 pCur->paCmds = paCommands; 291 pCur->pNext = g_pExtCmdsHead; 292 g_pExtCmdsHead = pCur; 293 } 294 else 295 rc = VERR_NO_MEMORY; 296 DBGCEXTCMDS_UNLOCK_WR(); 297 298 return rc; 299 } 300 301 302 /** 303 * Deregister one or more external commands previously registered by 304 * DBGCRegisterCommands(). 305 * 306 * @returns VBox status. 307 * @param paCommands Pointer to an array of command descriptors 308 * as given to DBGCRegisterCommands(). 309 * @param cCommands Number of commands. 310 */ 311 DBGDECL(int) DBGCDeregisterCommands(PCDBGCCMD paCommands, unsigned cCommands) 312 { 313 /* 314 * Lock the list. 315 */ 316 DBGCEXTCMDS_LOCK_WR(); 317 PDBGCEXTCMDS pPrev = NULL; 318 PDBGCEXTCMDS pCur = g_pExtCmdsHead; 319 while (pCur) 320 { 321 if (paCommands == pCur->paCmds) 322 { 323 if (pPrev) 324 pPrev->pNext = pCur->pNext; 325 else 326 g_pExtCmdsHead = pCur->pNext; 327 DBGCEXTCMDS_UNLOCK_WR(); 328 329 RTMemFree(pCur); 330 return VINF_SUCCESS; 331 } 332 pPrev = pCur; 333 pCur = pCur->pNext; 334 } 335 DBGCEXTCMDS_UNLOCK_WR(); 336 337 NOREF(cCommands); 338 return VERR_DBGC_COMMANDS_NOT_REGISTERED; 339 } 289 340 290 341 … … 1280 1331 } 1281 1332 1282 1283 1284 1285 1286 1287 //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//1288 //1289 //1290 // C a l l b a c k H e l p e r s1291 //1292 //1293 //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//1294 1295 1296 1297 /**1298 * Command helper for writing text to the debug console.1299 *1300 * @returns VBox status.1301 * @param pCmdHlp Pointer to the command callback structure.1302 * @param pvBuf What to write.1303 * @param cbBuf Number of bytes to write.1304 * @param pcbWritten Where to store the number of bytes actually written.1305 * If NULL the entire buffer must be successfully written.1306 */1307 static DECLCALLBACK(int) dbgcHlpWrite(PDBGCCMDHLP pCmdHlp, const void *pvBuf, size_t cbBuf, size_t *pcbWritten)1308 {1309 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);1310 return pDbgc->pBack->pfnWrite(pDbgc->pBack, pvBuf, cbBuf, pcbWritten);1311 }1312 1313 1314 /**1315 * Command helper for writing formatted text to the debug console.1316 *1317 * @returns VBox status.1318 * @param pCmdHlp Pointer to the command callback structure.1319 * @param pcb Where to store the number of bytes written.1320 * @param pszFormat The format string.1321 * This is using the log formatter, so it's format extensions can be used.1322 * @param ... Arguments specified in the format string.1323 */1324 static DECLCALLBACK(int) dbgcHlpPrintf(PDBGCCMDHLP pCmdHlp, size_t *pcbWritten, const char *pszFormat, ...)1325 {1326 /*1327 * Do the formatting and output.1328 */1329 va_list args;1330 va_start(args, pszFormat);1331 int rc = pCmdHlp->pfnPrintfV(pCmdHlp, pcbWritten, pszFormat, args);1332 va_end(args);1333 1334 return rc;1335 }1336 1337 /**1338 * Callback to format non-standard format specifiers.1339 *1340 * @returns The number of bytes formatted.1341 * @param pvArg Formatter argument.1342 * @param pfnOutput Pointer to output function.1343 * @param pvArgOutput Argument for the output function.1344 * @param ppszFormat Pointer to the format string pointer. Advance this till the char1345 * after the format specifier.1346 * @param pArgs Pointer to the argument list. Use this to fetch the arguments.1347 * @param cchWidth Format Width. -1 if not specified.1348 * @param cchPrecision Format Precision. -1 if not specified.1349 * @param fFlags Flags (RTSTR_NTFS_*).1350 * @param chArgSize The argument size specifier, 'l' or 'L'.1351 */1352 static DECLCALLBACK(size_t) dbgcStringFormatter(void *pvArg, PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,1353 const char **ppszFormat, va_list *pArgs, int cchWidth,1354 int cchPrecision, unsigned fFlags, char chArgSize)1355 {1356 NOREF(cchWidth); NOREF(cchPrecision); NOREF(fFlags); NOREF(chArgSize); NOREF(pvArg);1357 if (**ppszFormat != 'D')1358 {1359 (*ppszFormat)++;1360 return 0;1361 }1362 1363 (*ppszFormat)++;1364 switch (**ppszFormat)1365 {1366 /*1367 * Print variable without range.1368 * The argument is a const pointer to the variable.1369 */1370 case 'V':1371 {1372 (*ppszFormat)++;1373 PCDBGCVAR pVar = va_arg(*pArgs, PCDBGCVAR);1374 switch (pVar->enmType)1375 {1376 case DBGCVAR_TYPE_GC_FLAT:1377 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%%%VGv", pVar->u.GCFlat);1378 case DBGCVAR_TYPE_GC_FAR:1379 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%04x:%08x", pVar->u.GCFar.sel, pVar->u.GCFar.off);1380 case DBGCVAR_TYPE_GC_PHYS:1381 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%%%%%VGp", pVar->u.GCPhys);1382 case DBGCVAR_TYPE_HC_FLAT:1383 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%%#%VHv", (uintptr_t)pVar->u.pvHCFlat);1384 case DBGCVAR_TYPE_HC_FAR:1385 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "#%04x:%08x", pVar->u.HCFar.sel, pVar->u.HCFar.off);1386 case DBGCVAR_TYPE_HC_PHYS:1387 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "#%%%%%VHp", pVar->u.HCPhys);1388 case DBGCVAR_TYPE_STRING:1389 return pfnOutput(pvArgOutput, pVar->u.pszString, (size_t)pVar->u64Range);1390 case DBGCVAR_TYPE_NUMBER:1391 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%llx", pVar->u.u64Number);1392 1393 case DBGCVAR_TYPE_UNKNOWN:1394 default:1395 return pfnOutput(pvArgOutput, "??", 2);1396 }1397 }1398 1399 /*1400 * Print variable with range.1401 * The argument is a const pointer to the variable.1402 */1403 case 'v':1404 {1405 (*ppszFormat)++;1406 PCDBGCVAR pVar = va_arg(*pArgs, PCDBGCVAR);1407 1408 char szRange[32];1409 switch (pVar->enmRangeType)1410 {1411 case DBGCVAR_RANGE_NONE:1412 szRange[0] = '\0';1413 break;1414 case DBGCVAR_RANGE_ELEMENTS:1415 RTStrPrintf(szRange, sizeof(szRange), " L %llx", pVar->u64Range);1416 break;1417 case DBGCVAR_RANGE_BYTES:1418 RTStrPrintf(szRange, sizeof(szRange), " LB %llx", pVar->u64Range);1419 break;1420 }1421 1422 switch (pVar->enmType)1423 {1424 case DBGCVAR_TYPE_GC_FLAT:1425 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%%%VGv%s", pVar->u.GCFlat, szRange);1426 case DBGCVAR_TYPE_GC_FAR:1427 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%04x:%08x%s", pVar->u.GCFar.sel, pVar->u.GCFar.off, szRange);1428 case DBGCVAR_TYPE_GC_PHYS:1429 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%%%%%VGp%s", pVar->u.GCPhys, szRange);1430 case DBGCVAR_TYPE_HC_FLAT:1431 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%%#%VHv%s", (uintptr_t)pVar->u.pvHCFlat, szRange);1432 case DBGCVAR_TYPE_HC_FAR:1433 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "#%04x:%08x%s", pVar->u.HCFar.sel, pVar->u.HCFar.off, szRange);1434 case DBGCVAR_TYPE_HC_PHYS:1435 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "#%%%%%VHp%s", pVar->u.HCPhys, szRange);1436 case DBGCVAR_TYPE_STRING:1437 return pfnOutput(pvArgOutput, pVar->u.pszString, (size_t)pVar->u64Range);1438 case DBGCVAR_TYPE_NUMBER:1439 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%llx%s", pVar->u.u64Number, szRange);1440 1441 case DBGCVAR_TYPE_UNKNOWN:1442 default:1443 return pfnOutput(pvArgOutput, "??", 2);1444 }1445 }1446 1447 default:1448 AssertMsgFailed(("Invalid format type '%s'!\n", **ppszFormat));1449 return 0;1450 }1451 }1452 1453 1454 /**1455 * Output callback.1456 *1457 * @returns number of bytes written.1458 * @param pvArg User argument.1459 * @param pachChars Pointer to an array of utf-8 characters.1460 * @param cbChars Number of bytes in the character array pointed to by pachChars.1461 */1462 static DECLCALLBACK(size_t) dbgcFormatOutput(void *pvArg, const char *pachChars, size_t cbChars)1463 {1464 PDBGC pDbgc = (PDBGC)pvArg;1465 if (cbChars)1466 {1467 int rc = pDbgc->pBack->pfnWrite(pDbgc->pBack, pachChars, cbChars, NULL);1468 if (VBOX_FAILURE(rc))1469 {1470 pDbgc->rcOutput = rc;1471 cbChars = 0;1472 }1473 }1474 1475 return cbChars;1476 }1477 1478 1479 1480 /**1481 * Command helper for writing formatted text to the debug console.1482 *1483 * @returns VBox status.1484 * @param pCmdHlp Pointer to the command callback structure.1485 * @param pcb Where to store the number of bytes written.1486 * @param pszFormat The format string.1487 * This is using the log formatter, so it's format extensions can be used.1488 * @param args Arguments specified in the format string.1489 */1490 static DECLCALLBACK(int) dbgcHlpPrintfV(PDBGCCMDHLP pCmdHlp, size_t *pcbWritten, const char *pszFormat, va_list args)1491 {1492 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);1493 1494 /*1495 * Do the formatting and output.1496 */1497 pDbgc->rcOutput = 0;1498 size_t cb = RTStrFormatV(dbgcFormatOutput, pDbgc, dbgcStringFormatter, pDbgc, pszFormat, args);1499 1500 if (pcbWritten)1501 *pcbWritten = cb;1502 1503 return pDbgc->rcOutput;1504 }1505 1506 1507 /**1508 * Reports an error from a DBGF call.1509 *1510 * @returns VBox status code appropriate to return from a command.1511 * @param pCmdHlp Pointer to command helpers.1512 * @param rc The VBox status code returned by a DBGF call.1513 * @param pszFormat Format string for additional messages. Can be NULL.1514 * @param ... Format arguments, optional.1515 */1516 static DECLCALLBACK(int) dbgcHlpVBoxErrorV(PDBGCCMDHLP pCmdHlp, int rc, const char *pszFormat, va_list args)1517 {1518 switch (rc)1519 {1520 case VINF_SUCCESS:1521 break;1522 1523 default:1524 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: %Vrc: %s", rc, pszFormat ? " " : "\n");1525 if (VBOX_SUCCESS(rc) && pszFormat)1526 rc = pCmdHlp->pfnPrintfV(pCmdHlp, NULL, pszFormat, args);1527 break;1528 }1529 return rc;1530 }1531 1532 1533 /**1534 * Reports an error from a DBGF call.1535 *1536 * @returns VBox status code appropriate to return from a command.1537 * @param pCmdHlp Pointer to command helpers.1538 * @param rc The VBox status code returned by a DBGF call.1539 * @param pszFormat Format string for additional messages. Can be NULL.1540 * @param ... Format arguments, optional.1541 */1542 static DECLCALLBACK(int) dbgcHlpVBoxError(PDBGCCMDHLP pCmdHlp, int rc, const char *pszFormat, ...)1543 {1544 va_list args;1545 va_start(args, pszFormat);1546 int rcRet = pCmdHlp->pfnVBoxErrorV(pCmdHlp, rc, pszFormat, args);1547 va_end(args);1548 return rcRet;1549 }1550 1551 1552 /**1553 * Command helper for reading memory specified by a DBGC variable.1554 *1555 * @returns VBox status code appropriate to return from a command.1556 * @param pCmdHlp Pointer to the command callback structure.1557 * @param pVM VM handle if GC or physical HC address.1558 * @param pvBuffer Where to store the read data.1559 * @param cbRead Number of bytes to read.1560 * @param pVarPointer DBGC variable specifying where to start reading.1561 * @param pcbRead Where to store the number of bytes actually read.1562 * This optional, but it's useful when read GC virtual memory where a1563 * page in the requested range might not be present.1564 * If not specified not-present failure or end of a HC physical page1565 * will cause failure.1566 */1567 static DECLCALLBACK(int) dbgcHlpMemRead(PDBGCCMDHLP pCmdHlp, PVM pVM, void *pvBuffer, size_t cbRead, PCDBGCVAR pVarPointer, size_t *pcbRead)1568 {1569 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);1570 1571 /*1572 * Dummy check.1573 */1574 if (cbRead == 0)1575 {1576 if (*pcbRead)1577 *pcbRead = 0;1578 return VINF_SUCCESS;1579 }1580 1581 /*1582 * Convert Far addresses getting size and the correct base address.1583 * Getting and checking the size is what makes this messy and slow.1584 */1585 DBGCVAR Var = *pVarPointer;1586 switch (pVarPointer->enmType)1587 {1588 case DBGCVAR_TYPE_GC_FAR:1589 {1590 /* Use DBGFR3AddrFromSelOff for the conversion. */1591 Assert(pDbgc->pVM);1592 DBGFADDRESS Address;1593 int rc = DBGFR3AddrFromSelOff(pDbgc->pVM, &Address, Var.u.GCFar.sel, Var.u.GCFar.off);1594 if (VBOX_FAILURE(rc))1595 return rc;1596 1597 /* don't bother with flat selectors (for now). */1598 if (!DBGFADDRESS_IS_FLAT(&Address))1599 {1600 SELMSELINFO SelInfo;1601 rc = SELMR3GetSelectorInfo(pDbgc->pVM, Address.Sel, &SelInfo);1602 if (VBOX_SUCCESS(rc))1603 {1604 RTGCUINTPTR cb; /* -1 byte */1605 if (SELMSelInfoIsExpandDown(&SelInfo))1606 {1607 if ( !SelInfo.Raw.Gen.u1Granularity1608 && Address.off > UINT16_C(0xffff))1609 return VERR_OUT_OF_SELECTOR_BOUNDS;1610 if (Address.off <= SelInfo.cbLimit)1611 return VERR_OUT_OF_SELECTOR_BOUNDS;1612 cb = (SelInfo.Raw.Gen.u1Granularity ? UINT32_C(0xffffffff) : UINT32_C(0xffff)) - Address.off;1613 }1614 else1615 {1616 if (Address.off > SelInfo.cbLimit)1617 return VERR_OUT_OF_SELECTOR_BOUNDS;1618 cb = SelInfo.cbLimit - Address.off;1619 }1620 if (cbRead - 1 > cb)1621 {1622 if (!pcbRead)1623 return VERR_OUT_OF_SELECTOR_BOUNDS;1624 cbRead = cb + 1;1625 }1626 }1627 1628 Var.enmType = DBGCVAR_TYPE_GC_FLAT;1629 Var.u.GCFlat = Address.FlatPtr;1630 }1631 break;1632 }1633 1634 case DBGCVAR_TYPE_GC_FLAT:1635 case DBGCVAR_TYPE_GC_PHYS:1636 case DBGCVAR_TYPE_HC_FLAT:1637 case DBGCVAR_TYPE_HC_PHYS:1638 break;1639 1640 case DBGCVAR_TYPE_HC_FAR: /* not supported yet! */1641 default:1642 return VERR_NOT_IMPLEMENTED;1643 }1644 1645 1646 1647 /*1648 * Copy page by page.1649 */1650 size_t cbLeft = cbRead;1651 for (;;)1652 {1653 /*1654 * Calc read size.1655 */1656 size_t cb = RT_MIN(PAGE_SIZE, cbLeft);1657 switch (pVarPointer->enmType)1658 {1659 case DBGCVAR_TYPE_GC_FLAT: cb = RT_MIN(cb, PAGE_SIZE - (Var.u.GCFlat & PAGE_OFFSET_MASK)); break;1660 case DBGCVAR_TYPE_GC_PHYS: cb = RT_MIN(cb, PAGE_SIZE - (Var.u.GCPhys & PAGE_OFFSET_MASK)); break;1661 case DBGCVAR_TYPE_HC_FLAT: cb = RT_MIN(cb, PAGE_SIZE - ((uintptr_t)Var.u.pvHCFlat & PAGE_OFFSET_MASK)); break;1662 case DBGCVAR_TYPE_HC_PHYS: cb = RT_MIN(cb, PAGE_SIZE - ((size_t)Var.u.HCPhys & PAGE_OFFSET_MASK)); break; /* size_t: MSC has braindead loss of data warnings! */1663 default: break;1664 }1665 1666 /*1667 * Perform read.1668 */1669 int rc;1670 switch (Var.enmType)1671 {1672 case DBGCVAR_TYPE_GC_FLAT:1673 rc = MMR3ReadGCVirt(pVM, pvBuffer, Var.u.GCFlat, cb);1674 break;1675 case DBGCVAR_TYPE_GC_PHYS:1676 rc = PGMPhysReadGCPhys(pVM, pvBuffer, Var.u.GCPhys, cb);1677 break;1678 1679 case DBGCVAR_TYPE_HC_PHYS:1680 case DBGCVAR_TYPE_HC_FLAT:1681 case DBGCVAR_TYPE_HC_FAR:1682 {1683 DBGCVAR Var2;1684 rc = dbgcOpAddrFlat(pDbgc, &Var, &Var2);1685 if (VBOX_SUCCESS(rc))1686 {1687 /** @todo protect this!!! */1688 memcpy(pvBuffer, Var2.u.pvHCFlat, cb);1689 rc = 0;1690 }1691 else1692 rc = VERR_INVALID_POINTER;1693 break;1694 }1695 1696 default:1697 rc = VERR_PARSE_INCORRECT_ARG_TYPE;1698 }1699 1700 /*1701 * Check for failure.1702 */1703 if (VBOX_FAILURE(rc))1704 {1705 if (pcbRead && (*pcbRead = cbRead - cbLeft) > 0)1706 return VINF_SUCCESS;1707 return rc;1708 }1709 1710 /*1711 * Next.1712 */1713 cbLeft -= cb;1714 if (!cbLeft)1715 break;1716 pvBuffer = (char *)pvBuffer + cb;1717 rc = pCmdHlp->pfnEval(pCmdHlp, &Var, "%DV + %d", &Var, cb);1718 if (VBOX_FAILURE(rc))1719 {1720 if (pcbRead && (*pcbRead = cbRead - cbLeft) > 0)1721 return VINF_SUCCESS;1722 return rc;1723 }1724 }1725 1726 /*1727 * Done1728 */1729 if (pcbRead)1730 *pcbRead = cbRead;1731 return 0;1732 }1733 1734 /**1735 * Command helper for writing memory specified by a DBGC variable.1736 *1737 * @returns VBox status code appropriate to return from a command.1738 * @param pCmdHlp Pointer to the command callback structure.1739 * @param pVM VM handle if GC or physical HC address.1740 * @param pvBuffer What to write.1741 * @param cbWrite Number of bytes to write.1742 * @param pVarPointer DBGC variable specifying where to start reading.1743 * @param pcbWritten Where to store the number of bytes written.1744 * This is optional. If NULL be aware that some of the buffer1745 * might have been written to the specified address.1746 */1747 static DECLCALLBACK(int) dbgcHlpMemWrite(PDBGCCMDHLP pCmdHlp, PVM pVM, const void *pvBuffer, size_t cbWrite, PCDBGCVAR pVarPointer, size_t *pcbWritten)1748 {1749 NOREF(pCmdHlp); NOREF(pVM); NOREF(pvBuffer); NOREF(cbWrite); NOREF(pVarPointer); NOREF(pcbWritten);1750 return VERR_NOT_IMPLEMENTED;1751 }1752 1753 1754 /**1755 * Evaluates an expression.1756 * (Hopefully the parser and functions are fully reentrant.)1757 *1758 * @returns VBox status code appropriate to return from a command.1759 * @param pCmdHlp Pointer to the command callback structure.1760 * @param pResult Where to store the result.1761 * @param pszExpr The expression. Format string with the format DBGC extensions.1762 * @param ... Format arguments.1763 */1764 static DECLCALLBACK(int) dbgcHlpEval(PDBGCCMDHLP pCmdHlp, PDBGCVAR pResult, const char *pszExpr, ...)1765 {1766 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);1767 1768 /*1769 * Format the expression.1770 */1771 char szExprFormatted[2048];1772 va_list args;1773 va_start(args, pszExpr);1774 size_t cb = RTStrPrintfExV(dbgcStringFormatter, pDbgc, szExprFormatted, sizeof(szExprFormatted), pszExpr, args);1775 va_end(args);1776 /* ignore overflows. */1777 1778 return dbgcEvalSub(pDbgc, &szExprFormatted[0], cb, pResult);1779 }1780 1781 1782 /**1783 * Executes one command expression.1784 * (Hopefully the parser and functions are fully reentrant.)1785 *1786 * @returns VBox status code appropriate to return from a command.1787 * @param pCmdHlp Pointer to the command callback structure.1788 * @param pszExpr The expression. Format string with the format DBGC extensions.1789 * @param ... Format arguments.1790 */1791 static DECLCALLBACK(int) dbgcHlpExec(PDBGCCMDHLP pCmdHlp, const char *pszExpr, ...)1792 {1793 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);1794 /* Save the scratch state. */1795 char *pszScratch = pDbgc->pszScratch;1796 unsigned iArg = pDbgc->iArg;1797 1798 /*1799 * Format the expression.1800 */1801 va_list args;1802 va_start(args, pszExpr);1803 size_t cbScratch = sizeof(pDbgc->achScratch) - (pDbgc->pszScratch - &pDbgc->achScratch[0]);1804 size_t cb = RTStrPrintfExV(dbgcStringFormatter, pDbgc, pDbgc->pszScratch, cbScratch, pszExpr, args);1805 va_end(args);1806 if (cb >= cbScratch)1807 return VERR_BUFFER_OVERFLOW;1808 1809 /*1810 * Execute the command.1811 * We save and restore the arg index and scratch buffer pointer.1812 */1813 pDbgc->pszScratch = pDbgc->pszScratch + cb + 1;1814 int rc = dbgcProcessCommand(pDbgc, pszScratch, cb);1815 1816 /* Restore the scratch state. */1817 pDbgc->iArg = iArg;1818 pDbgc->pszScratch = pszScratch;1819 1820 return rc;1821 }1822 1823 1824 /**1825 * Converts a DBGC variable to a DBGF address structure.1826 *1827 * @returns VBox status code.1828 * @param pCmdHlp Pointer to the command callback structure.1829 * @param pVar The variable to convert.1830 * @param pAddress The target address.1831 */1832 static DECLCALLBACK(int) dbgcHlpVarToDbgfAddr(PDBGCCMDHLP pCmdHlp, PCDBGCVAR pVar, PDBGFADDRESS pAddress)1833 {1834 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);1835 return dbgcVarToDbgfAddr(pDbgc, pVar, pAddress);1836 }1837 1838 1839 /**1840 * Converts a DBGC variable to a boolean.1841 *1842 * @returns VBox status code.1843 * @param pCmdHlp Pointer to the command callback structure.1844 * @param pVar The variable to convert.1845 * @param pf Where to store the boolean.1846 */1847 static DECLCALLBACK(int) dbgcHlpVarToBool(PDBGCCMDHLP pCmdHlp, PCDBGCVAR pVar, bool *pf)1848 {1849 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);1850 NOREF(pDbgc);1851 1852 switch (pVar->enmType)1853 {1854 case DBGCVAR_TYPE_STRING:1855 /** @todo add strcasecmp / stricmp wrappers to iprt/string.h. */1856 if ( !strcmp(pVar->u.pszString, "true")1857 || !strcmp(pVar->u.pszString, "True")1858 || !strcmp(pVar->u.pszString, "TRUE")1859 || !strcmp(pVar->u.pszString, "on")1860 || !strcmp(pVar->u.pszString, "On")1861 || !strcmp(pVar->u.pszString, "oN")1862 || !strcmp(pVar->u.pszString, "ON")1863 || !strcmp(pVar->u.pszString, "enabled")1864 || !strcmp(pVar->u.pszString, "Enabled")1865 || !strcmp(pVar->u.pszString, "DISABLED"))1866 {1867 *pf = true;1868 return VINF_SUCCESS;1869 }1870 if ( !strcmp(pVar->u.pszString, "false")1871 || !strcmp(pVar->u.pszString, "False")1872 || !strcmp(pVar->u.pszString, "FALSE")1873 || !strcmp(pVar->u.pszString, "off")1874 || !strcmp(pVar->u.pszString, "Off")1875 || !strcmp(pVar->u.pszString, "OFF")1876 || !strcmp(pVar->u.pszString, "disabled")1877 || !strcmp(pVar->u.pszString, "Disabled")1878 || !strcmp(pVar->u.pszString, "DISABLED"))1879 {1880 *pf = false;1881 return VINF_SUCCESS;1882 }1883 return VERR_PARSE_INCORRECT_ARG_TYPE; /** @todo better error code! */1884 1885 case DBGCVAR_TYPE_GC_FLAT:1886 case DBGCVAR_TYPE_GC_PHYS:1887 case DBGCVAR_TYPE_HC_FLAT:1888 case DBGCVAR_TYPE_HC_PHYS:1889 case DBGCVAR_TYPE_NUMBER:1890 *pf = pVar->u.u64Number != 0;1891 return VINF_SUCCESS;1892 1893 case DBGCVAR_TYPE_HC_FAR:1894 case DBGCVAR_TYPE_GC_FAR:1895 case DBGCVAR_TYPE_SYMBOL:1896 default:1897 return VERR_PARSE_INCORRECT_ARG_TYPE;1898 }1899 }1900 1901 1902 1903 1904 1905 //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//1906 //1907 //1908 // V a r i a b l e M a n i p u l a t i o n1909 //1910 //1911 //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//1912 1913 1914 1915 /** @todo move me!*/1916 void dbgcVarSetGCFlat(PDBGCVAR pVar, RTGCPTR GCFlat)1917 {1918 if (pVar)1919 {1920 pVar->enmType = DBGCVAR_TYPE_GC_FLAT;1921 pVar->u.GCFlat = GCFlat;1922 pVar->enmRangeType = DBGCVAR_RANGE_NONE;1923 pVar->u64Range = 0;1924 }1925 }1926 1927 1928 /** @todo move me!*/1929 void dbgcVarSetGCFlatByteRange(PDBGCVAR pVar, RTGCPTR GCFlat, uint64_t cb)1930 {1931 if (pVar)1932 {1933 pVar->enmType = DBGCVAR_TYPE_GC_FLAT;1934 pVar->u.GCFlat = GCFlat;1935 pVar->enmRangeType = DBGCVAR_RANGE_BYTES;1936 pVar->u64Range = cb;1937 }1938 }1939 1940 1941 /** @todo move me!*/1942 void dbgcVarSetVar(PDBGCVAR pVar, PCDBGCVAR pVar2)1943 {1944 if (pVar)1945 {1946 if (pVar2)1947 *pVar = *pVar2;1948 else1949 {1950 pVar->enmType = DBGCVAR_TYPE_UNKNOWN;1951 memset(&pVar->u, 0, sizeof(pVar->u));1952 pVar->enmRangeType = DBGCVAR_RANGE_NONE;1953 pVar->u64Range = 0;1954 }1955 }1956 }1957 1958 1959 /** @todo move me!*/1960 void dbgcVarSetByteRange(PDBGCVAR pVar, uint64_t cb)1961 {1962 if (pVar)1963 {1964 pVar->enmRangeType = DBGCVAR_RANGE_BYTES;1965 pVar->u64Range = cb;1966 }1967 }1968 1969 1970 /** @todo move me!*/1971 void dbgcVarSetNoRange(PDBGCVAR pVar)1972 {1973 if (pVar)1974 {1975 pVar->enmRangeType = DBGCVAR_RANGE_NONE;1976 pVar->u64Range = 0;1977 }1978 }1979 1980 1981 /**1982 * Converts a DBGC variable to a DBGF address.1983 *1984 * @returns VBox status code.1985 * @param pDbgc The DBGC instance.1986 * @param pVar The variable.1987 * @param pAddress Where to store the address.1988 */1989 int dbgcVarToDbgfAddr(PDBGC pDbgc, PCDBGCVAR pVar, PDBGFADDRESS pAddress)1990 {1991 AssertReturn(pVar, VERR_INVALID_PARAMETER);1992 switch (pVar->enmType)1993 {1994 case DBGCVAR_TYPE_GC_FLAT:1995 DBGFR3AddrFromFlat(pDbgc->pVM, pAddress, pVar->u.GCFlat);1996 return VINF_SUCCESS;1997 1998 case DBGCVAR_TYPE_NUMBER:1999 DBGFR3AddrFromFlat(pDbgc->pVM, pAddress, (RTGCUINTPTR)pVar->u.u64Number);2000 return VINF_SUCCESS;2001 2002 case DBGCVAR_TYPE_GC_FAR:2003 return DBGFR3AddrFromSelOff(pDbgc->pVM, pAddress, pVar->u.GCFar.sel, pVar->u.GCFar.sel);2004 2005 case DBGCVAR_TYPE_STRING:2006 case DBGCVAR_TYPE_SYMBOL:2007 {2008 DBGCVAR Var;2009 int rc = pDbgc->CmdHlp.pfnEval(&pDbgc->CmdHlp, &Var, "%%(%DV)", pVar);2010 if (VBOX_FAILURE(rc))2011 return rc;2012 return dbgcVarToDbgfAddr(pDbgc, &Var, pAddress);2013 }2014 2015 case DBGCVAR_TYPE_GC_PHYS:2016 case DBGCVAR_TYPE_HC_FLAT:2017 case DBGCVAR_TYPE_HC_FAR:2018 case DBGCVAR_TYPE_HC_PHYS:2019 default:2020 return VERR_PARSE_CONVERSION_FAILED;2021 }2022 }2023 2024 2025 2026 //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//2027 //2028 //2029 // B r e a k p o i n t M a n a g e m e n t2030 //2031 //2032 //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//2033 2034 2035 /**2036 * Adds a breakpoint to the DBGC breakpoint list.2037 */2038 int dbgcBpAdd(PDBGC pDbgc, RTUINT iBp, const char *pszCmd)2039 {2040 /*2041 * Check if it already exists.2042 */2043 PDBGCBP pBp = dbgcBpGet(pDbgc, iBp);2044 if (pBp)2045 return VERR_DBGC_BP_EXISTS;2046 2047 /*2048 * Add the breakpoint.2049 */2050 if (pszCmd)2051 pszCmd = RTStrStripL(pszCmd);2052 size_t cchCmd = pszCmd ? strlen(pszCmd) : 0;2053 pBp = (PDBGCBP)RTMemAlloc(RT_OFFSETOF(DBGCBP, szCmd[cchCmd + 1]));2054 if (!pBp)2055 return VERR_NO_MEMORY;2056 if (cchCmd)2057 memcpy(pBp->szCmd, pszCmd, cchCmd + 1);2058 else2059 pBp->szCmd[0] = '\0';2060 pBp->cchCmd = cchCmd;2061 pBp->iBp = iBp;2062 pBp->pNext = pDbgc->pFirstBp;2063 pDbgc->pFirstBp = pBp;2064 2065 return VINF_SUCCESS;2066 }2067 2068 /**2069 * Updates the a breakpoint.2070 *2071 * @returns VBox status code.2072 * @param pDbgc The DBGC instance.2073 * @param iBp The breakpoint to update.2074 * @param pszCmd The new command.2075 */2076 int dbgcBpUpdate(PDBGC pDbgc, RTUINT iBp, const char *pszCmd)2077 {2078 /*2079 * Find the breakpoint.2080 */2081 PDBGCBP pBp = dbgcBpGet(pDbgc, iBp);2082 if (!pBp)2083 return VERR_DBGC_BP_NOT_FOUND;2084 2085 /*2086 * Do we need to reallocate?2087 */2088 if (pszCmd)2089 pszCmd = RTStrStripL(pszCmd);2090 if (!pszCmd || !*pszCmd)2091 pBp->szCmd[0] = '\0';2092 else2093 {2094 size_t cchCmd = strlen(pszCmd);2095 if (strlen(pBp->szCmd) >= cchCmd)2096 {2097 memcpy(pBp->szCmd, pszCmd, cchCmd + 1);2098 pBp->cchCmd = cchCmd;2099 }2100 else2101 {2102 /*2103 * Yes, let's do it the simple way...2104 */2105 int rc = dbgcBpDelete(pDbgc, iBp);2106 AssertRC(rc);2107 return dbgcBpAdd(pDbgc, iBp, pszCmd);2108 }2109 }2110 return VINF_SUCCESS;2111 }2112 2113 2114 /**2115 * Deletes a breakpoint.2116 *2117 * @returns VBox status code.2118 * @param pDbgc The DBGC instance.2119 * @param iBp The breakpoint to delete.2120 */2121 int dbgcBpDelete(PDBGC pDbgc, RTUINT iBp)2122 {2123 /*2124 * Search thru the list, when found unlink and free it.2125 */2126 PDBGCBP pBpPrev = NULL;2127 PDBGCBP pBp = pDbgc->pFirstBp;2128 for (; pBp; pBp = pBp->pNext)2129 {2130 if (pBp->iBp == iBp)2131 {2132 if (pBpPrev)2133 pBpPrev->pNext = pBp->pNext;2134 else2135 pDbgc->pFirstBp = pBp->pNext;2136 RTMemFree(pBp);2137 return VINF_SUCCESS;2138 }2139 pBpPrev = pBp;2140 }2141 2142 return VERR_DBGC_BP_NOT_FOUND;2143 }2144 2145 2146 /**2147 * Get a breakpoint.2148 *2149 * @returns Pointer to the breakpoint.2150 * @returns NULL if the breakpoint wasn't found.2151 * @param pDbgc The DBGC instance.2152 * @param iBp The breakpoint to get.2153 */2154 PDBGCBP dbgcBpGet(PDBGC pDbgc, RTUINT iBp)2155 {2156 /*2157 * Enumerate the list.2158 */2159 PDBGCBP pBp = pDbgc->pFirstBp;2160 for (; pBp; pBp = pBp->pNext)2161 if (pBp->iBp == iBp)2162 return pBp;2163 return NULL;2164 }2165 2166 2167 /**2168 * Executes the command of a breakpoint.2169 *2170 * @returns VINF_DBGC_BP_NO_COMMAND if there is no command associated with the breakpoint.2171 * @returns VERR_DBGC_BP_NOT_FOUND if the breakpoint wasn't found.2172 * @returns VERR_BUFFER_OVERFLOW if the is not enough space in the scratch buffer for the command.2173 * @returns VBox status code from dbgcProcessCommand() other wise.2174 * @param pDbgc The DBGC instance.2175 * @param iBp The breakpoint to execute.2176 */2177 int dbgcBpExec(PDBGC pDbgc, RTUINT iBp)2178 {2179 /*2180 * Find the breakpoint.2181 */2182 PDBGCBP pBp = dbgcBpGet(pDbgc, iBp);2183 if (!pBp)2184 return VERR_DBGC_BP_NOT_FOUND;2185 2186 /*2187 * Anything to do?2188 */2189 if (!pBp->cchCmd)2190 return VINF_DBGC_BP_NO_COMMAND;2191 2192 /*2193 * Execute the command.2194 * This means copying it to the scratch buffer and process it as if it2195 * were user input. We must save and restore the state of the scratch buffer.2196 */2197 /* Save the scratch state. */2198 char *pszScratch = pDbgc->pszScratch;2199 unsigned iArg = pDbgc->iArg;2200 2201 /* Copy the command to the scratch buffer. */2202 size_t cbScratch = sizeof(pDbgc->achScratch) - (pDbgc->pszScratch - &pDbgc->achScratch[0]);2203 if (pBp->cchCmd >= cbScratch)2204 return VERR_BUFFER_OVERFLOW;2205 memcpy(pDbgc->pszScratch, pBp->szCmd, pBp->cchCmd + 1);2206 2207 /* Execute the command. */2208 pDbgc->pszScratch = pDbgc->pszScratch + pBp->cchCmd + 1;2209 int rc = dbgcProcessCommand(pDbgc, pszScratch, pBp->cchCmd);2210 2211 /* Restore the scratch state. */2212 pDbgc->iArg = iArg;2213 pDbgc->pszScratch = pszScratch;2214 2215 return rc;2216 }2217 2218 2219 2220 2221 2222 //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//2223 //2224 //2225 // I n p u t , p a r s i n g a n d l o g g i n g2226 //2227 //2228 //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//2229 2230 2231 2232 /**2233 * Prints any log lines from the log buffer.2234 *2235 * The caller must not call function this unless pDbgc->fLog is set.2236 *2237 * @returns VBox status. (output related)2238 * @param pDbgc Debugger console instance data.2239 */2240 static int dbgcProcessLog(PDBGC pDbgc)2241 {2242 /** @todo */2243 NOREF(pDbgc);2244 return 0;2245 }2246 2247 2248 2249 /**2250 * Handle input buffer overflow.2251 *2252 * Will read any available input looking for a '\n' to reset the buffer on.2253 *2254 * @returns VBox status.2255 * @param pDbgc Debugger console instance data.2256 */2257 static int dbgcInputOverflow(PDBGC pDbgc)2258 {2259 /*2260 * Assert overflow status and reset the input buffer.2261 */2262 if (!pDbgc->fInputOverflow)2263 {2264 pDbgc->fInputOverflow = true;2265 pDbgc->iRead = pDbgc->iWrite = 0;2266 pDbgc->cInputLines = 0;2267 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "Input overflow!!\n");2268 }2269 2270 /*2271 * Eat input till no more or there is a '\n'.2272 * When finding a '\n' we'll continue normal processing.2273 */2274 while (pDbgc->pBack->pfnInput(pDbgc->pBack, 0))2275 {2276 size_t cbRead;2277 int rc = pDbgc->pBack->pfnRead(pDbgc->pBack, &pDbgc->achInput[0], sizeof(pDbgc->achInput) - 1, &cbRead);2278 if (VBOX_FAILURE(rc))2279 return rc;2280 char *psz = (char *)memchr(&pDbgc->achInput[0], '\n', cbRead);2281 if (psz)2282 {2283 pDbgc->fInputOverflow = false;2284 pDbgc->iRead = psz - &pDbgc->achInput[0] + 1;2285 pDbgc->iWrite = (unsigned)cbRead;2286 pDbgc->cInputLines = 0;2287 break;2288 }2289 }2290 2291 return 0;2292 }2293 2294 2295 2296 /**2297 * Read input and do some preprocessing.2298 *2299 * @returns VBox status.2300 * In addition to the iWrite and achInput, cInputLines is maintained.2301 * In case of an input overflow the fInputOverflow flag will be set.2302 * @param pDbgc Debugger console instance data.2303 */2304 static int dbgcInputRead(PDBGC pDbgc)2305 {2306 /*2307 * We have ready input.2308 * Read it till we don't have any or we have a full input buffer.2309 */2310 int rc = 0;2311 do2312 {2313 /*2314 * More available buffer space?2315 */2316 size_t cbLeft;2317 if (pDbgc->iWrite > pDbgc->iRead)2318 cbLeft = sizeof(pDbgc->achInput) - pDbgc->iWrite - (pDbgc->iRead == 0);2319 else2320 cbLeft = pDbgc->iRead - pDbgc->iWrite - 1;2321 if (!cbLeft)2322 {2323 /* overflow? */2324 if (!pDbgc->cInputLines)2325 rc = dbgcInputOverflow(pDbgc);2326 break;2327 }2328 2329 /*2330 * Read one char and interpret it.2331 */2332 char achRead[128];2333 size_t cbRead;2334 rc = pDbgc->pBack->pfnRead(pDbgc->pBack, &achRead[0], RT_MIN(cbLeft, sizeof(achRead)), &cbRead);2335 if (VBOX_FAILURE(rc))2336 return rc;2337 char *psz = &achRead[0];2338 while (cbRead-- > 0)2339 {2340 char ch = *psz++;2341 switch (ch)2342 {2343 /*2344 * Ignore.2345 */2346 case '\0':2347 case '\r':2348 case '\a':2349 break;2350 2351 /*2352 * Backspace.2353 */2354 case '\b':2355 Log2(("DBGC: backspace\n"));2356 if (pDbgc->iRead != pDbgc->iWrite)2357 {2358 unsigned iWriteUndo = pDbgc->iWrite;2359 if (pDbgc->iWrite)2360 pDbgc->iWrite--;2361 else2362 pDbgc->iWrite = sizeof(pDbgc->achInput) - 1;2363 2364 if (pDbgc->achInput[pDbgc->iWrite] == '\n')2365 pDbgc->iWrite = iWriteUndo;2366 }2367 break;2368 2369 /*2370 * Add char to buffer.2371 */2372 case '\t':2373 case '\n':2374 case ';':2375 switch (ch)2376 {2377 case '\t': ch = ' '; break;2378 case '\n': pDbgc->cInputLines++; break;2379 }2380 default:2381 Log2(("DBGC: ch=%02x\n", (unsigned char)ch));2382 pDbgc->achInput[pDbgc->iWrite] = ch;2383 if (++pDbgc->iWrite >= sizeof(pDbgc->achInput))2384 pDbgc->iWrite = 0;2385 break;2386 }2387 }2388 2389 /* Terminate it to make it easier to read in the debugger. */2390 pDbgc->achInput[pDbgc->iWrite] = '\0';2391 } while (pDbgc->pBack->pfnInput(pDbgc->pBack, 0));2392 2393 return rc;2394 }2395 2396 2397 2398 /**2399 * Resolves a symbol (or tries to do so at least).2400 *2401 * @returns 0 on success.2402 * @returns VBox status on failure.2403 * @param pDbgc The debug console instance.2404 * @param pszSymbol The symbol name.2405 * @param enmType The result type.2406 * @param pResult Where to store the result.2407 */2408 int dbgcSymbolGet(PDBGC pDbgc, const char *pszSymbol, DBGCVARTYPE enmType, PDBGCVAR pResult)2409 {2410 /*2411 * Builtin?2412 */2413 PCDBGCSYM pSymDesc = dbgcLookupRegisterSymbol(pDbgc, pszSymbol);2414 if (pSymDesc)2415 {2416 if (!pSymDesc->pfnGet)2417 return VERR_PARSE_WRITEONLY_SYMBOL;2418 return pSymDesc->pfnGet(pSymDesc, &pDbgc->CmdHlp, enmType, pResult);2419 }2420 2421 2422 /*2423 * Ask PDM.2424 */2425 /** @todo resolve symbols using PDM. */2426 2427 2428 /*2429 * Ask the debug info manager.2430 */2431 DBGFSYMBOL Symbol;2432 int rc = DBGFR3SymbolByName(pDbgc->pVM, pszSymbol, &Symbol);2433 if (VBOX_SUCCESS(rc))2434 {2435 /*2436 * Default return is a flat gc address.2437 */2438 memset(pResult, 0, sizeof(*pResult));2439 pResult->enmRangeType = Symbol.cb ? DBGCVAR_RANGE_BYTES : DBGCVAR_RANGE_NONE;2440 pResult->u64Range = Symbol.cb;2441 pResult->enmType = DBGCVAR_TYPE_GC_FLAT;2442 pResult->u.GCFlat = Symbol.Value;2443 DBGCVAR VarTmp;2444 switch (enmType)2445 {2446 /* nothing to do. */2447 case DBGCVAR_TYPE_GC_FLAT:2448 case DBGCVAR_TYPE_GC_FAR:2449 case DBGCVAR_TYPE_ANY:2450 return VINF_SUCCESS;2451 2452 /* simply make it numeric. */2453 case DBGCVAR_TYPE_NUMBER:2454 pResult->enmType = DBGCVAR_TYPE_NUMBER;2455 pResult->u.u64Number = Symbol.Value;2456 return VINF_SUCCESS;2457 2458 /* cast it. */2459 2460 case DBGCVAR_TYPE_GC_PHYS:2461 VarTmp = *pResult;2462 return dbgcOpAddrPhys(pDbgc, &VarTmp, pResult);2463 2464 case DBGCVAR_TYPE_HC_FAR:2465 case DBGCVAR_TYPE_HC_FLAT:2466 VarTmp = *pResult;2467 return dbgcOpAddrHost(pDbgc, &VarTmp, pResult);2468 2469 case DBGCVAR_TYPE_HC_PHYS:2470 VarTmp = *pResult;2471 return dbgcOpAddrHostPhys(pDbgc, &VarTmp, pResult);2472 2473 default:2474 AssertMsgFailed(("Internal error enmType=%d\n", enmType));2475 return VERR_INVALID_PARAMETER;2476 }2477 }2478 2479 return VERR_PARSE_NOT_IMPLEMENTED;2480 }2481 2482 2483 2484 /**2485 * Finds a routine.2486 *2487 * @returns Pointer to the command descriptor.2488 * If the request was for an external command, the caller is responsible for2489 * unlocking the external command list.2490 * @returns NULL if not found.2491 * @param pDbgc The debug console instance.2492 * @param pachName Pointer to the routine string (not terminated).2493 * @param cchName Length of the routine name.2494 * @param fExternal Whether or not the routine is external.2495 */2496 static PCDBGCCMD dbgcRoutineLookup(PDBGC pDbgc, const char *pachName, size_t cchName, bool fExternal)2497 {2498 if (!fExternal)2499 {2500 /* emulation first, so commands can be overloaded (info ++). */2501 PCDBGCCMD pCmd = pDbgc->paEmulationCmds;2502 unsigned cLeft = pDbgc->cEmulationCmds;2503 while (cLeft-- > 0)2504 {2505 if ( !strncmp(pachName, pCmd->pszCmd, cchName)2506 && !pCmd->pszCmd[cchName])2507 return pCmd;2508 pCmd++;2509 }2510 2511 for (unsigned iCmd = 0; iCmd < ELEMENTS(g_aCmds); iCmd++)2512 {2513 if ( !strncmp(pachName, g_aCmds[iCmd].pszCmd, cchName)2514 && !g_aCmds[iCmd].pszCmd[cchName])2515 return &g_aCmds[iCmd];2516 }2517 }2518 else2519 {2520 DBGCEXTCMDS_LOCK_RD();2521 for (PDBGCEXTCMDS pExtCmds = g_pExtCmdsHead; pExtCmds; pExtCmds = pExtCmds->pNext)2522 {2523 for (unsigned iCmd = 0; iCmd < pExtCmds->cCmds; iCmd++)2524 {2525 if ( !strncmp(pachName, pExtCmds->paCmds[iCmd].pszCmd, cchName)2526 && !pExtCmds->paCmds[iCmd].pszCmd[cchName])2527 return &pExtCmds->paCmds[iCmd];2528 }2529 }2530 DBGCEXTCMDS_UNLOCK_RD();2531 }2532 2533 NOREF(pDbgc);2534 return NULL;2535 }2536 2537 2538 /**2539 * Initalizes g_bmOperatorChars.2540 */2541 static void dbgcInitOpCharBitMap(void)2542 {2543 memset(g_bmOperatorChars, 0, sizeof(g_bmOperatorChars));2544 for (unsigned iOp = 0; iOp < g_cOps; iOp++)2545 ASMBitSet(&g_bmOperatorChars[0], (uint8_t)g_aOps[iOp].szName[0]);2546 }2547 2548 2549 /**2550 * Checks whether the character may be the start of an operator.2551 *2552 * @returns true/false.2553 * @param ch The character.2554 */2555 DECLINLINE(bool) dbgcIsOpChar(char ch)2556 {2557 return ASMBitTest(&g_bmOperatorChars[0], (uint8_t)ch);2558 }2559 2560 2561 static int dbgcEvalSubString(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pArg)2562 {2563 Log2(("dbgcEvalSubString: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));2564 2565 /*2566 * Removing any quoting and escapings.2567 */2568 char ch = *pszExpr;2569 if (ch == '"' || ch == '\'' || ch == '`')2570 {2571 if (pszExpr[--cchExpr] != ch)2572 return VERR_PARSE_UNBALANCED_QUOTE;2573 cchExpr--;2574 pszExpr++;2575 2576 /** @todo string unescaping. */2577 }2578 pszExpr[cchExpr] = '\0';2579 2580 /*2581 * Make the argument.2582 */2583 pArg->pDesc = NULL;2584 pArg->pNext = NULL;2585 pArg->enmType = DBGCVAR_TYPE_STRING;2586 pArg->u.pszString = pszExpr;2587 pArg->enmRangeType = DBGCVAR_RANGE_BYTES;2588 pArg->u64Range = cchExpr;2589 2590 NOREF(pDbgc);2591 return 0;2592 }2593 2594 2595 static int dbgcEvalSubNum(char *pszExpr, unsigned uBase, PDBGCVAR pArg)2596 {2597 Log2(("dbgcEvalSubNum: uBase=%d pszExpr=%s\n", uBase, pszExpr));2598 /*2599 * Convert to number.2600 */2601 uint64_t u64 = 0;2602 char ch;2603 while ((ch = *pszExpr) != '\0')2604 {2605 uint64_t u64Prev = u64;2606 unsigned u = ch - '0';2607 if (u < 10 && u < uBase)2608 u64 = u64 * uBase + u;2609 else if (ch >= 'a' && (u = ch - ('a' - 10)) < uBase)2610 u64 = u64 * uBase + u;2611 else if (ch >= 'A' && (u = ch - ('A' - 10)) < uBase)2612 u64 = u64 * uBase + u;2613 else2614 return VERR_PARSE_INVALID_NUMBER;2615 2616 /* check for overflow - ARG!!! How to detect overflow correctly!?!?!? */2617 if (u64Prev != u64 / uBase)2618 return VERR_PARSE_NUMBER_TOO_BIG;2619 2620 /* next */2621 pszExpr++;2622 }2623 2624 /*2625 * Initialize the argument.2626 */2627 pArg->pDesc = NULL;2628 pArg->pNext = NULL;2629 pArg->enmType = DBGCVAR_TYPE_NUMBER;2630 pArg->u.u64Number = u64;2631 pArg->enmRangeType = DBGCVAR_RANGE_NONE;2632 pArg->u64Range = 0;2633 2634 return 0;2635 }2636 2637 2638 /**2639 * Match variable and variable descriptor, promoting the variable if necessary.2640 *2641 * @returns VBox status code.2642 * @param pDbgc Debug console instanace.2643 * @param pVar Variable.2644 * @param pVarDesc Variable descriptor.2645 */2646 static int dbgcEvalSubMatchVar(PDBGC pDbgc, PDBGCVAR pVar, PCDBGCVARDESC pVarDesc)2647 {2648 /*2649 * (If match or promoted to match, return, else break.)2650 */2651 switch (pVarDesc->enmCategory)2652 {2653 /*2654 * Anything goes2655 */2656 case DBGCVAR_CAT_ANY:2657 return VINF_SUCCESS;2658 2659 /*2660 * Pointer with and without range.2661 * We can try resolve strings and symbols as symbols and2662 * promote numbers to flat GC pointers.2663 */2664 case DBGCVAR_CAT_POINTER_NO_RANGE:2665 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)2666 return VERR_PARSE_NO_RANGE_ALLOWED;2667 /* fallthru */2668 case DBGCVAR_CAT_POINTER:2669 switch (pVar->enmType)2670 {2671 case DBGCVAR_TYPE_GC_FLAT:2672 case DBGCVAR_TYPE_GC_FAR:2673 case DBGCVAR_TYPE_GC_PHYS:2674 case DBGCVAR_TYPE_HC_FLAT:2675 case DBGCVAR_TYPE_HC_FAR:2676 case DBGCVAR_TYPE_HC_PHYS:2677 return VINF_SUCCESS;2678 2679 case DBGCVAR_TYPE_SYMBOL:2680 case DBGCVAR_TYPE_STRING:2681 {2682 DBGCVAR Var;2683 int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_GC_FLAT, &Var);2684 if (VBOX_SUCCESS(rc))2685 {2686 /* deal with range */2687 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)2688 {2689 Var.enmRangeType = pVar->enmRangeType;2690 Var.u64Range = pVar->u64Range;2691 }2692 else if (pVarDesc->enmCategory == DBGCVAR_CAT_POINTER_NO_RANGE)2693 Var.enmRangeType = DBGCVAR_RANGE_NONE;2694 *pVar = Var;2695 return rc;2696 }2697 break;2698 }2699 2700 case DBGCVAR_TYPE_NUMBER:2701 {2702 RTGCPTR GCPtr = (RTGCPTR)pVar->u.u64Number;2703 pVar->enmType = DBGCVAR_TYPE_GC_FLAT;2704 pVar->u.GCFlat = GCPtr;2705 return VINF_SUCCESS;2706 }2707 2708 default:2709 break;2710 }2711 break;2712 2713 /*2714 * GC pointer with and without range.2715 * We can try resolve strings and symbols as symbols and2716 * promote numbers to flat GC pointers.2717 */2718 case DBGCVAR_CAT_GC_POINTER_NO_RANGE:2719 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)2720 return VERR_PARSE_NO_RANGE_ALLOWED;2721 /* fallthru */2722 case DBGCVAR_CAT_GC_POINTER:2723 switch (pVar->enmType)2724 {2725 case DBGCVAR_TYPE_GC_FLAT:2726 case DBGCVAR_TYPE_GC_FAR:2727 case DBGCVAR_TYPE_GC_PHYS:2728 return VINF_SUCCESS;2729 2730 case DBGCVAR_TYPE_HC_FLAT:2731 case DBGCVAR_TYPE_HC_FAR:2732 case DBGCVAR_TYPE_HC_PHYS:2733 return VERR_PARSE_CONVERSION_FAILED;2734 2735 case DBGCVAR_TYPE_SYMBOL:2736 case DBGCVAR_TYPE_STRING:2737 {2738 DBGCVAR Var;2739 int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_GC_FLAT, &Var);2740 if (VBOX_SUCCESS(rc))2741 {2742 /* deal with range */2743 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)2744 {2745 Var.enmRangeType = pVar->enmRangeType;2746 Var.u64Range = pVar->u64Range;2747 }2748 else if (pVarDesc->enmCategory == DBGCVAR_CAT_POINTER_NO_RANGE)2749 Var.enmRangeType = DBGCVAR_RANGE_NONE;2750 *pVar = Var;2751 return rc;2752 }2753 break;2754 }2755 2756 case DBGCVAR_TYPE_NUMBER:2757 {2758 RTGCPTR GCPtr = (RTGCPTR)pVar->u.u64Number;2759 pVar->enmType = DBGCVAR_TYPE_GC_FLAT;2760 pVar->u.GCFlat = GCPtr;2761 return VINF_SUCCESS;2762 }2763 2764 default:2765 break;2766 }2767 break;2768 2769 /*2770 * Number with or without a range.2771 * Numbers can be resolved from symbols, but we cannot demote a pointer2772 * to a number.2773 */2774 case DBGCVAR_CAT_NUMBER_NO_RANGE:2775 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)2776 return VERR_PARSE_NO_RANGE_ALLOWED;2777 /* fallthru */2778 case DBGCVAR_CAT_NUMBER:2779 switch (pVar->enmType)2780 {2781 case DBGCVAR_TYPE_NUMBER:2782 return VINF_SUCCESS;2783 2784 case DBGCVAR_TYPE_SYMBOL:2785 case DBGCVAR_TYPE_STRING:2786 {2787 DBGCVAR Var;2788 int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_NUMBER, &Var);2789 if (VBOX_SUCCESS(rc))2790 {2791 *pVar = Var;2792 return rc;2793 }2794 break;2795 }2796 default:2797 break;2798 }2799 break;2800 2801 /*2802 * Strings can easily be made from symbols (and of course strings).2803 * We could consider reformatting the addresses and numbers into strings later...2804 */2805 case DBGCVAR_CAT_STRING:2806 switch (pVar->enmType)2807 {2808 case DBGCVAR_TYPE_SYMBOL:2809 pVar->enmType = DBGCVAR_TYPE_STRING;2810 /* fallthru */2811 case DBGCVAR_TYPE_STRING:2812 return VINF_SUCCESS;2813 default:2814 break;2815 }2816 break;2817 2818 /*2819 * Symol is pretty much the same thing as a string (at least until we actually implement it).2820 */2821 case DBGCVAR_CAT_SYMBOL:2822 switch (pVar->enmType)2823 {2824 case DBGCVAR_TYPE_STRING:2825 pVar->enmType = DBGCVAR_TYPE_SYMBOL;2826 /* fallthru */2827 case DBGCVAR_TYPE_SYMBOL:2828 return VINF_SUCCESS;2829 default:2830 break;2831 }2832 break;2833 2834 /*2835 * Anything else is illegal.2836 */2837 default:2838 AssertMsgFailed(("enmCategory=%d\n", pVar->enmType));2839 break;2840 }2841 2842 return VERR_PARSE_NO_ARGUMENT_MATCH;2843 }2844 2845 2846 /**2847 * Matches a set of variables with a description set.2848 *2849 * This is typically used for routine arguments before a call. The effects in2850 * addition to the validation, is that some variables might be propagated to2851 * other types in order to match the description. The following transformations2852 * are supported:2853 * - String reinterpreted as a symbol and resolved to a number or pointer.2854 * - Number to a pointer.2855 * - Pointer to a number.2856 * @returns 0 on success with paVars.2857 * @returns VBox error code for match errors.2858 */2859 static int dbgcEvalSubMatchVars(PDBGC pDbgc, unsigned cVarsMin, unsigned cVarsMax,2860 PCDBGCVARDESC paVarDescs, unsigned cVarDescs,2861 PDBGCVAR paVars, unsigned cVars)2862 {2863 /*2864 * Just do basic min / max checks first.2865 */2866 if (cVars < cVarsMin)2867 return VERR_PARSE_TOO_FEW_ARGUMENTS;2868 if (cVars > cVarsMax)2869 return VERR_PARSE_TOO_MANY_ARGUMENTS;2870 2871 /*2872 * Match the descriptors and actual variables.2873 */2874 PCDBGCVARDESC pPrevDesc = NULL;2875 unsigned cCurDesc = 0;2876 unsigned iVar = 0;2877 unsigned iVarDesc = 0;2878 while (iVar < cVars)2879 {2880 /* walk the descriptors */2881 if (iVarDesc >= cVarDescs)2882 return VERR_PARSE_TOO_MANY_ARGUMENTS;2883 if ( ( paVarDescs[iVarDesc].fFlags & DBGCVD_FLAGS_DEP_PREV2884 && &paVarDescs[iVarDesc - 1] != pPrevDesc)2885 || cCurDesc >= paVarDescs[iVarDesc].cTimesMax)2886 {2887 iVarDesc++;2888 if (iVarDesc >= cVarDescs)2889 return VERR_PARSE_TOO_MANY_ARGUMENTS;2890 cCurDesc = 0;2891 }2892 2893 /*2894 * Skip thru optional arguments until we find something which matches2895 * or can easily be promoted to what the descriptor want.2896 */2897 for (;;)2898 {2899 int rc = dbgcEvalSubMatchVar(pDbgc, &paVars[iVar], &paVarDescs[iVarDesc]);2900 if (VBOX_SUCCESS(rc))2901 {2902 paVars[iVar].pDesc = &paVarDescs[iVarDesc];2903 cCurDesc++;2904 break;2905 }2906 2907 /* can we advance? */2908 if (paVarDescs[iVarDesc].cTimesMin > cCurDesc)2909 return VERR_PARSE_ARGUMENT_TYPE_MISMATCH;2910 if (++iVarDesc >= cVarDescs)2911 return VERR_PARSE_ARGUMENT_TYPE_MISMATCH;2912 cCurDesc = 0;2913 }2914 2915 /* next var */2916 iVar++;2917 }2918 2919 /*2920 * Check that the rest of the descriptors are optional.2921 */2922 while (iVarDesc < cVarDescs)2923 {2924 if (paVarDescs[iVarDesc].cTimesMin > cCurDesc)2925 return VERR_PARSE_TOO_FEW_ARGUMENTS;2926 cCurDesc = 0;2927 2928 /* next */2929 iVarDesc++;2930 }2931 2932 return 0;2933 }2934 2935 2936 /**2937 * Evaluates one argument with respect to unary operators.2938 *2939 * @returns 0 on success. pResult contains the result.2940 * @returns VBox error code on parse or other evaluation error.2941 *2942 * @param pDbgc Debugger console instance data.2943 * @param pszExpr The expression string.2944 * @param pResult Where to store the result of the expression evaluation.2945 */2946 static int dbgcEvalSubUnary(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pResult)2947 {2948 Log2(("dbgcEvalSubUnary: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));2949 2950 /*2951 * The state of the expression is now such that it will start by zero or more2952 * unary operators and being followed by an expression of some kind.2953 * The expression is either plain or in parenthesis.2954 *2955 * Being in a lazy, recursive mode today, the parsing is done as simple as possible. :-)2956 * ASSUME: unary operators are all of equal precedence.2957 */2958 int rc = 0;2959 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, pszExpr, false, ' ');2960 if (pOp)2961 {2962 /* binary operators means syntax error. */2963 if (pOp->fBinary)2964 return VERR_PARSE_UNEXPECTED_OPERATOR;2965 2966 /*2967 * If the next expression (the one following the unary operator) is in a2968 * parenthesis a full eval is needed. If not the unary eval will suffice.2969 */2970 /* calc and strip next expr. */2971 char *pszExpr2 = pszExpr + pOp->cchName;2972 while (isblank(*pszExpr2))2973 pszExpr2++;2974 2975 if (!*pszExpr2)2976 rc = VERR_PARSE_EMPTY_ARGUMENT;2977 else2978 {2979 DBGCVAR Arg;2980 if (*pszExpr2 == '(')2981 rc = dbgcEvalSub(pDbgc, pszExpr2, cchExpr - (pszExpr2 - pszExpr), &Arg);2982 else2983 rc = dbgcEvalSubUnary(pDbgc, pszExpr2, cchExpr - (pszExpr2 - pszExpr), &Arg);2984 if (VBOX_SUCCESS(rc))2985 rc = pOp->pfnHandlerUnary(pDbgc, &Arg, pResult);2986 }2987 }2988 else2989 {2990 /*2991 * Didn't find any operators, so it we have to check if this can be an2992 * function call before assuming numeric or string expression.2993 *2994 * (ASSUMPTIONS:)2995 * A function name only contains alphanumerical chars and it can not start2996 * with a numerical character.2997 * Immediately following the name is a parenthesis which must over2998 * the remaining part of the expression.2999 */3000 bool fExternal = *pszExpr == '.';3001 char *pszFun = fExternal ? pszExpr + 1 : pszExpr;3002 char *pszFunEnd = NULL;3003 if (pszExpr[cchExpr - 1] == ')' && isalpha(*pszFun))3004 {3005 pszFunEnd = pszExpr + 1;3006 while (*pszFunEnd != '(' && isalnum(*pszFunEnd))3007 pszFunEnd++;3008 if (*pszFunEnd != '(')3009 pszFunEnd = NULL;3010 }3011 3012 if (pszFunEnd)3013 {3014 /*3015 * Ok, it's a function call.3016 */3017 if (fExternal)3018 pszExpr++, cchExpr--;3019 PCDBGCCMD pFun = dbgcRoutineLookup(pDbgc, pszExpr, pszFunEnd - pszExpr, fExternal);3020 if (!pFun)3021 return VERR_PARSE_FUNCTION_NOT_FOUND;3022 if (!pFun->pResultDesc)3023 return VERR_PARSE_NOT_A_FUNCTION;3024 3025 /*3026 * Parse the expression in parenthesis.3027 */3028 cchExpr -= pszFunEnd - pszExpr;3029 pszExpr = pszFunEnd;3030 /** @todo implement multiple arguments. */3031 DBGCVAR Arg;3032 rc = dbgcEvalSub(pDbgc, pszExpr, cchExpr, &Arg);3033 if (!rc)3034 {3035 rc = dbgcEvalSubMatchVars(pDbgc, pFun->cArgsMin, pFun->cArgsMax, pFun->paArgDescs, pFun->cArgDescs, &Arg, 1);3036 if (!rc)3037 rc = pFun->pfnHandler(pFun, &pDbgc->CmdHlp, pDbgc->pVM, &Arg, 1, pResult);3038 }3039 else if (rc == VERR_PARSE_EMPTY_ARGUMENT && pFun->cArgsMin == 0)3040 rc = pFun->pfnHandler(pFun, &pDbgc->CmdHlp, pDbgc->pVM, NULL, 0, pResult);3041 }3042 else3043 {3044 /*3045 * Didn't find any operators, so it must be a plain expression.3046 * This might be numeric or a string expression.3047 */3048 char ch = pszExpr[0];3049 char ch2 = pszExpr[1];3050 if (ch == '0' && (ch2 == 'x' || ch2 == 'X'))3051 rc = dbgcEvalSubNum(pszExpr + 2, 16, pResult);3052 else if (ch == '0' && (ch2 == 'i' || ch2 == 'i'))3053 rc = dbgcEvalSubNum(pszExpr + 2, 10, pResult);3054 else if (ch == '0' && (ch2 == 't' || ch2 == 'T'))3055 rc = dbgcEvalSubNum(pszExpr + 2, 8, pResult);3056 /// @todo 0b doesn't work as a binary prefix, we confuse it with 0bf8:0123 and stuff.3057 //else if (ch == '0' && (ch2 == 'b' || ch2 == 'b'))3058 // rc = dbgcEvalSubNum(pszExpr + 2, 2, pResult);3059 else3060 {3061 /*3062 * Hexadecimal number or a string?3063 */3064 char *psz = pszExpr;3065 while (isxdigit(*psz))3066 psz++;3067 if (!*psz)3068 rc = dbgcEvalSubNum(pszExpr, 16, pResult);3069 else if ((*psz == 'h' || *psz == 'H') && !psz[1])3070 {3071 *psz = '\0';3072 rc = dbgcEvalSubNum(pszExpr, 16, pResult);3073 }3074 else3075 rc = dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult);3076 }3077 }3078 }3079 3080 return rc;3081 }3082 3083 3084 /**3085 * Evaluates one argument.3086 *3087 * @returns 0 on success. pResult contains the result.3088 * @returns VBox error code on parse or other evaluation error.3089 *3090 * @param pDbgc Debugger console instance data.3091 * @param pszExpr The expression string.3092 * @param pResult Where to store the result of the expression evaluation.3093 */3094 static int dbgcEvalSub(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pResult)3095 {3096 Log2(("dbgcEvalSub: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));3097 /*3098 * First we need to remove blanks in both ends.3099 * ASSUMES: There is no quoting unless the entire expression is a string.3100 */3101 3102 /* stripping. */3103 while (cchExpr > 0 && isblank(pszExpr[cchExpr - 1]))3104 pszExpr[--cchExpr] = '\0';3105 while (isblank(*pszExpr))3106 pszExpr++, cchExpr--;3107 if (!*pszExpr)3108 return VERR_PARSE_EMPTY_ARGUMENT;3109 3110 /* it there is any kind of quoting in the expression, it's string meat. */3111 if (strpbrk(pszExpr, "\"'`"))3112 return dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult);3113 3114 /*3115 * Check if there are any parenthesis which needs removing.3116 */3117 if (pszExpr[0] == '(' && pszExpr[cchExpr - 1] == ')')3118 {3119 do3120 {3121 unsigned cPar = 1;3122 char *psz = pszExpr + 1;3123 char ch;3124 while ((ch = *psz) != '\0')3125 {3126 if (ch == '(')3127 cPar++;3128 else if (ch == ')')3129 {3130 if (cPar <= 0)3131 return VERR_PARSE_UNBALANCED_PARENTHESIS;3132 cPar--;3133 if (cPar == 0 && psz[1]) /* If not at end, there's nothing to do. */3134 break;3135 }3136 /* next */3137 psz++;3138 }3139 if (ch)3140 break;3141 3142 /* remove the parenthesis. */3143 pszExpr++;3144 cchExpr -= 2;3145 pszExpr[cchExpr] = '\0';3146 3147 /* strip blanks. */3148 while (cchExpr > 0 && isblank(pszExpr[cchExpr - 1]))3149 pszExpr[--cchExpr] = '\0';3150 while (isblank(*pszExpr))3151 pszExpr++, cchExpr--;3152 if (!*pszExpr)3153 return VERR_PARSE_EMPTY_ARGUMENT;3154 } while (pszExpr[0] == '(' && pszExpr[cchExpr - 1] == ')');3155 }3156 3157 /* tabs to spaces. */3158 char *psz = pszExpr;3159 while ((psz = strchr(psz, '\t')) != NULL)3160 *psz = ' ';3161 3162 /*3163 * Now, we need to look for the binary operator with the lowest precedence.3164 *3165 * If there are no operators we're left with a simple expression which we3166 * evaluate with respect to unary operators3167 */3168 char *pszOpSplit = NULL;3169 PCDBGCOP pOpSplit = NULL;3170 unsigned cBinaryOps = 0;3171 unsigned cPar = 0;3172 char ch;3173 char chPrev = ' ';3174 bool fBinary = false;3175 psz = pszExpr;3176 3177 while ((ch = *psz) != '\0')3178 {3179 //Log2(("ch=%c cPar=%d fBinary=%d\n", ch, cPar, fBinary));3180 /*3181 * Parenthesis.3182 */3183 if (ch == '(')3184 {3185 cPar++;3186 fBinary = false;3187 }3188 else if (ch == ')')3189 {3190 if (cPar <= 0)3191 return VERR_PARSE_UNBALANCED_PARENTHESIS;3192 cPar--;3193 fBinary = true;3194 }3195 /*3196 * Potential operator.3197 */3198 else if (cPar == 0 && !isblank(ch))3199 {3200 PCDBGCOP pOp = dbgcIsOpChar(ch)3201 ? dbgcOperatorLookup(pDbgc, psz, fBinary, chPrev)3202 : NULL;3203 if (pOp)3204 {3205 /* If not the right kind of operator we've got a syntax error. */3206 if (pOp->fBinary != fBinary)3207 return VERR_PARSE_UNEXPECTED_OPERATOR;3208 3209 /*3210 * Update the parse state and skip the operator.3211 */3212 if (!pOpSplit)3213 {3214 pOpSplit = pOp;3215 pszOpSplit = psz;3216 cBinaryOps = fBinary;3217 }3218 else if (fBinary)3219 {3220 cBinaryOps++;3221 if (pOp->iPrecedence >= pOpSplit->iPrecedence)3222 {3223 pOpSplit = pOp;3224 pszOpSplit = psz;3225 }3226 }3227 3228 psz += pOp->cchName - 1;3229 fBinary = false;3230 }3231 else3232 fBinary = true;3233 }3234 3235 /* next */3236 psz++;3237 chPrev = ch;3238 } /* parse loop. */3239 3240 3241 /*3242 * Either we found an operator to divide the expression by3243 * or we didn't find any. In the first case it's divide and3244 * conquer. In the latter it's a single expression which3245 * needs dealing with its unary operators if any.3246 */3247 int rc;3248 if ( cBinaryOps3249 && pOpSplit->fBinary)3250 {3251 /* process 1st sub expression. */3252 *pszOpSplit = '\0';3253 DBGCVAR Arg1;3254 rc = dbgcEvalSub(pDbgc, pszExpr, pszOpSplit - pszExpr, &Arg1);3255 if (VBOX_SUCCESS(rc))3256 {3257 /* process 2nd sub expression. */3258 char *psz2 = pszOpSplit + pOpSplit->cchName;3259 DBGCVAR Arg2;3260 rc = dbgcEvalSub(pDbgc, psz2, cchExpr - (psz2 - pszExpr), &Arg2);3261 if (VBOX_SUCCESS(rc))3262 /* apply the operator. */3263 rc = pOpSplit->pfnHandlerBinary(pDbgc, &Arg1, &Arg2, pResult);3264 }3265 }3266 else if (cBinaryOps)3267 {3268 /* process sub expression. */3269 pszOpSplit += pOpSplit->cchName;3270 DBGCVAR Arg;3271 rc = dbgcEvalSub(pDbgc, pszOpSplit, cchExpr - (pszOpSplit - pszExpr), &Arg);3272 if (VBOX_SUCCESS(rc))3273 /* apply the operator. */3274 rc = pOpSplit->pfnHandlerUnary(pDbgc, &Arg, pResult);3275 }3276 else3277 /* plain expression or using unary operators perhaps with paratheses. */3278 rc = dbgcEvalSubUnary(pDbgc, pszExpr, cchExpr, pResult);3279 3280 return rc;3281 }3282 3283 3284 /**3285 * Parses the arguments of one command.3286 *3287 * @returns 0 on success.3288 * @returns VBox error code on parse error with *pcArg containing the argument causing trouble.3289 * @param pDbgc Debugger console instance data.3290 * @param pCmd Pointer to the command descriptor.3291 * @param pszArg Pointer to the arguments to parse.3292 * @param paArgs Where to store the parsed arguments.3293 * @param cArgs Size of the paArgs array.3294 * @param pcArgs Where to store the number of arguments.3295 * In the event of an error this is used to store the index of the offending argument.3296 */3297 static int dbgcProcessArguments(PDBGC pDbgc, PCDBGCCMD pCmd, char *pszArgs, PDBGCVAR paArgs, unsigned cArgs, unsigned *pcArgs)3298 {3299 Log2(("dbgcProcessArguments: pCmd=%s pszArgs='%s'\n", pCmd->pszCmd, pszArgs));3300 /*3301 * Check if we have any argument and if the command takes any.3302 */3303 *pcArgs = 0;3304 /* strip leading blanks. */3305 while (*pszArgs && isblank(*pszArgs))3306 pszArgs++;3307 if (!*pszArgs)3308 {3309 if (!pCmd->cArgsMin)3310 return 0;3311 return VERR_PARSE_TOO_FEW_ARGUMENTS;3312 }3313 /** @todo fixme - foo() doesn't work. */3314 if (!pCmd->cArgsMax)3315 return VERR_PARSE_TOO_MANY_ARGUMENTS;3316 3317 /*3318 * This is a hack, it's "temporary" and should go away "when" the parser is3319 * modified to match arguments while parsing.3320 */3321 if ( pCmd->cArgsMax == 13322 && pCmd->cArgsMin == 13323 && pCmd->cArgDescs == 13324 && pCmd->paArgDescs[0].enmCategory == DBGCVAR_CAT_STRING3325 && cArgs >= 1)3326 {3327 *pcArgs = 1;3328 RTStrStripR(pszArgs);3329 return dbgcEvalSubString(pDbgc, pszArgs, strlen(pszArgs), &paArgs[0]);3330 }3331 3332 3333 /*3334 * The parse loop.3335 */3336 PDBGCVAR pArg0 = &paArgs[0];3337 PDBGCVAR pArg = pArg0;3338 *pcArgs = 0;3339 do3340 {3341 /*3342 * Can we have another argument?3343 */3344 if (*pcArgs >= pCmd->cArgsMax)3345 return VERR_PARSE_TOO_MANY_ARGUMENTS;3346 if (pArg >= &paArgs[cArgs])3347 return VERR_PARSE_ARGUMENT_OVERFLOW;3348 3349 /*3350 * Find the end of the argument.3351 */3352 int cPar = 0;3353 char chQuote = '\0';3354 char *pszEnd = NULL;3355 char *psz = pszArgs;3356 char ch;3357 bool fBinary = false;3358 for (;;)3359 {3360 /*3361 * Check for the end.3362 */3363 if ((ch = *psz) == '\0')3364 {3365 if (chQuote)3366 return VERR_PARSE_UNBALANCED_QUOTE;3367 if (cPar)3368 return VERR_PARSE_UNBALANCED_PARENTHESIS;3369 pszEnd = psz;3370 break;3371 }3372 /*3373 * When quoted we ignore everything but the quotation char.3374 * We use the REXX way of escaping the quotation char, i.e. double occurence.3375 */3376 else if (ch == '\'' || ch == '"' || ch == '`')3377 {3378 if (chQuote)3379 {3380 /* end quote? */3381 if (ch == chQuote)3382 {3383 if (psz[1] == ch)3384 psz++; /* skip the escaped quote char */3385 else3386 chQuote = '\0'; /* end of quoted string. */3387 }3388 }3389 else3390 chQuote = ch; /* open new quote */3391 }3392 /*3393 * Parenthesis can of course be nested.3394 */3395 else if (ch == '(')3396 {3397 cPar++;3398 fBinary = false;3399 }3400 else if (ch == ')')3401 {3402 if (!cPar)3403 return VERR_PARSE_UNBALANCED_PARENTHESIS;3404 cPar--;3405 fBinary = true;3406 }3407 else if (!chQuote && !cPar)3408 {3409 /*3410 * Encountering blanks may mean the end of it all. A binary operator3411 * will force continued parsing.3412 */3413 if (isblank(*psz))3414 {3415 pszEnd = psz++; /* just in case. */3416 while (isblank(*psz))3417 psz++;3418 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, psz, fBinary, ' ');3419 if (!pOp || pOp->fBinary != fBinary)3420 break; /* the end. */3421 psz += pOp->cchName;3422 while (isblank(*psz)) /* skip blanks so we don't get here again */3423 psz++;3424 fBinary = false;3425 continue;3426 }3427 3428 /*3429 * Look for operators without a space up front.3430 */3431 if (dbgcIsOpChar(*psz))3432 {3433 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, psz, fBinary, ' ');3434 if (pOp)3435 {3436 if (pOp->fBinary != fBinary)3437 {3438 pszEnd = psz;3439 /** @todo this is a parsing error really. */3440 break; /* the end. */3441 }3442 psz += pOp->cchName;3443 while (isblank(*psz)) /* skip blanks so we don't get here again */3444 psz++;3445 fBinary = false;3446 continue;3447 }3448 }3449 fBinary = true;3450 }3451 3452 /* next char */3453 psz++;3454 }3455 *pszEnd = '\0';3456 /* (psz = next char to process) */3457 3458 /*3459 * Parse and evaluate the argument.3460 */3461 int rc = dbgcEvalSub(pDbgc, pszArgs, strlen(pszArgs), pArg);3462 if (VBOX_FAILURE(rc))3463 return rc;3464 3465 /*3466 * Next.3467 */3468 pArg++;3469 (*pcArgs)++;3470 pszArgs = psz;3471 while (*pszArgs && isblank(*pszArgs))3472 pszArgs++;3473 } while (*pszArgs);3474 3475 /*3476 * Match the arguments.3477 */3478 return dbgcEvalSubMatchVars(pDbgc, pCmd->cArgsMin, pCmd->cArgsMax, pCmd->paArgDescs, pCmd->cArgDescs, pArg0, pArg - pArg0);3479 }3480 3481 3482 /**3483 * Process one command.3484 *3485 * @returns VBox status code. Any error indicates the termination of the console session.3486 * @param pDbgc Debugger console instance data.3487 * @param pszCmd Pointer to the command.3488 * @param cchCmd Length of the command.3489 */3490 static int dbgcProcessCommand(PDBGC pDbgc, char *pszCmd, size_t cchCmd)3491 {3492 char *pszCmdInput = pszCmd;3493 3494 /*3495 * Skip blanks.3496 */3497 while (isblank(*pszCmd))3498 pszCmd++, cchCmd--;3499 3500 /* external command? */3501 bool fExternal = *pszCmd == '.';3502 if (fExternal)3503 pszCmd++, cchCmd--;3504 3505 /*3506 * Find arguments.3507 */3508 char *pszArgs = pszCmd;3509 while (isalnum(*pszArgs))3510 pszArgs++;3511 if (*pszArgs && (!isblank(*pszArgs) || pszArgs == pszCmd))3512 {3513 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "Syntax error in command '%s'!\n", pszCmdInput);3514 return 0;3515 }3516 3517 /*3518 * Find the command.3519 */3520 PCDBGCCMD pCmd = dbgcRoutineLookup(pDbgc, pszCmd, pszArgs - pszCmd, fExternal);3521 if (!pCmd || (pCmd->fFlags & DBGCCMD_FLAGS_FUNCTION))3522 return pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "Unknown command '%s'!\n", pszCmdInput);3523 3524 /*3525 * Parse arguments (if any).3526 */3527 unsigned cArgs;3528 int rc = dbgcProcessArguments(pDbgc, pCmd, pszArgs, &pDbgc->aArgs[pDbgc->iArg], ELEMENTS(pDbgc->aArgs) - pDbgc->iArg, &cArgs);3529 3530 /*3531 * Execute the command.3532 */3533 if (!rc)3534 {3535 rc = pCmd->pfnHandler(pCmd, &pDbgc->CmdHlp, pDbgc->pVM, &pDbgc->aArgs[0], cArgs, NULL);3536 }3537 else3538 {3539 /* report parse / eval error. */3540 switch (rc)3541 {3542 case VERR_PARSE_TOO_FEW_ARGUMENTS:3543 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3544 "Syntax error: Too few arguments. Minimum is %d for command '%s'.\n", pCmd->cArgsMin, pCmd->pszCmd);3545 break;3546 case VERR_PARSE_TOO_MANY_ARGUMENTS:3547 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3548 "Syntax error: Too many arguments. Maximum is %d for command '%s'.\n", pCmd->cArgsMax, pCmd->pszCmd);3549 break;3550 case VERR_PARSE_ARGUMENT_OVERFLOW:3551 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3552 "Syntax error: Too many arguments.\n");3553 break;3554 case VERR_PARSE_UNBALANCED_QUOTE:3555 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3556 "Syntax error: Unbalanced quote (argument %d).\n", cArgs);3557 break;3558 case VERR_PARSE_UNBALANCED_PARENTHESIS:3559 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3560 "Syntax error: Unbalanced parenthesis (argument %d).\n", cArgs);3561 break;3562 case VERR_PARSE_EMPTY_ARGUMENT:3563 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3564 "Syntax error: An argument or subargument contains nothing useful (argument %d).\n", cArgs);3565 break;3566 case VERR_PARSE_UNEXPECTED_OPERATOR:3567 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3568 "Syntax error: Invalid operator usage (argument %d).\n", cArgs);3569 break;3570 case VERR_PARSE_INVALID_NUMBER:3571 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3572 "Syntax error: Ivalid numeric value (argument %d). If a string was the intention, then quote it.\n", cArgs);3573 break;3574 case VERR_PARSE_NUMBER_TOO_BIG:3575 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3576 "Error: Numeric overflow (argument %d).\n", cArgs);3577 break;3578 case VERR_PARSE_INVALID_OPERATION:3579 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3580 "Error: Invalid operation attempted (argument %d).\n", cArgs);3581 break;3582 case VERR_PARSE_FUNCTION_NOT_FOUND:3583 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3584 "Error: Function not found (argument %d).\n", cArgs);3585 break;3586 case VERR_PARSE_NOT_A_FUNCTION:3587 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3588 "Error: The function specified is not a function (argument %d).\n", cArgs);3589 break;3590 case VERR_PARSE_NO_MEMORY:3591 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3592 "Error: Out memory in the regular heap! Expect odd stuff to happen...\n", cArgs);3593 break;3594 case VERR_PARSE_INCORRECT_ARG_TYPE:3595 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3596 "Error: Incorrect argument type (argument %d?).\n", cArgs);3597 break;3598 case VERR_PARSE_VARIABLE_NOT_FOUND:3599 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3600 "Error: An undefined variable was referenced (argument %d).\n", cArgs);3601 break;3602 case VERR_PARSE_CONVERSION_FAILED:3603 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3604 "Error: A conversion between two types failed (argument %d).\n", cArgs);3605 break;3606 case VERR_PARSE_NOT_IMPLEMENTED:3607 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3608 "Error: You hit a debugger feature which isn't implemented yet (argument %d).\n", cArgs);3609 break;3610 case VERR_PARSE_BAD_RESULT_TYPE:3611 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3612 "Error: Couldn't satisfy a request for a specific result type (argument %d). (Usually applies to symbols)\n", cArgs);3613 break;3614 case VERR_PARSE_WRITEONLY_SYMBOL:3615 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3616 "Error: Cannot get symbol, it's set only (argument %d).\n", cArgs);3617 break;3618 3619 default:3620 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3621 "Error: Unknown error %d!\n", rc);3622 return rc;3623 }3624 3625 /*3626 * Parse errors are non fatal.3627 */3628 if (rc >= VERR_PARSE_FIRST && rc < VERR_PARSE_LAST)3629 rc = 0;3630 }3631 3632 return rc;3633 }3634 3635 3636 /**3637 * Process all commands current in the buffer.3638 *3639 * @returns VBox status code. Any error indicates the termination of the console session.3640 * @param pDbgc Debugger console instance data.3641 */3642 static int dbgcProcessCommands(PDBGC pDbgc)3643 {3644 int rc = 0;3645 while (pDbgc->cInputLines)3646 {3647 /*3648 * Empty the log buffer if we're hooking the log.3649 */3650 if (pDbgc->fLog)3651 {3652 rc = dbgcProcessLog(pDbgc);3653 if (VBOX_FAILURE(rc))3654 break;3655 }3656 3657 if (pDbgc->iRead == pDbgc->iWrite)3658 {3659 AssertMsgFailed(("The input buffer is empty while cInputLines=%d!\n", pDbgc->cInputLines));3660 pDbgc->cInputLines = 0;3661 return 0;3662 }3663 3664 /*3665 * Copy the command to the parse buffer.3666 */3667 char ch;3668 char *psz = &pDbgc->achInput[pDbgc->iRead];3669 char *pszTrg = &pDbgc->achScratch[0];3670 while ((*pszTrg = ch = *psz++) != ';' && ch != '\n' )3671 {3672 if (psz == &pDbgc->achInput[sizeof(pDbgc->achInput)])3673 psz = &pDbgc->achInput[0];3674 3675 if (psz == &pDbgc->achInput[pDbgc->iWrite])3676 {3677 AssertMsgFailed(("The buffer contains no commands while cInputLines=%d!\n", pDbgc->cInputLines));3678 pDbgc->cInputLines = 0;3679 return 0;3680 }3681 3682 pszTrg++;3683 }3684 *pszTrg = '\0';3685 3686 /*3687 * Advance the buffer.3688 */3689 pDbgc->iRead = psz - &pDbgc->achInput[0];3690 if (ch == '\n')3691 pDbgc->cInputLines--;3692 3693 /*3694 * Parse and execute this command.3695 */3696 pDbgc->pszScratch = psz;3697 pDbgc->iArg = 0;3698 rc = dbgcProcessCommand(pDbgc, &pDbgc->achScratch[0], psz - &pDbgc->achScratch[0] - 1);3699 if (rc)3700 break;3701 }3702 3703 return rc;3704 }3705 3706 3707 /**3708 * Reads input, parses it and executes commands on '\n'.3709 *3710 * @returns VBox status.3711 * @param pDbgc Debugger console instance data.3712 */3713 static int dbgcProcessInput(PDBGC pDbgc)3714 {3715 /*3716 * We know there's input ready, so let's read it first.3717 */3718 int rc = dbgcInputRead(pDbgc);3719 if (VBOX_FAILURE(rc))3720 return rc;3721 3722 /*3723 * Now execute any ready commands.3724 */3725 if (pDbgc->cInputLines)3726 {3727 /** @todo this fReady stuff is broken. */3728 pDbgc->fReady = false;3729 rc = dbgcProcessCommands(pDbgc);3730 if (VBOX_SUCCESS(rc) && rc != VWRN_DBGC_CMD_PENDING)3731 pDbgc->fReady = true;3732 if ( VBOX_SUCCESS(rc)3733 && pDbgc->iRead == pDbgc->iWrite3734 && pDbgc->fReady)3735 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "VBoxDbg> ");3736 }3737 3738 return rc;3739 }3740 3741 3742 /**3743 * Gets the event context identifier string.3744 * @returns Read only string.3745 * @param enmCtx The context.3746 */3747 static const char *dbgcGetEventCtx(DBGFEVENTCTX enmCtx)3748 {3749 switch (enmCtx)3750 {3751 case DBGFEVENTCTX_RAW: return "raw";3752 case DBGFEVENTCTX_REM: return "rem";3753 case DBGFEVENTCTX_HWACCL: return "hwaccl";3754 case DBGFEVENTCTX_HYPER: return "hyper";3755 case DBGFEVENTCTX_OTHER: return "other";3756 3757 case DBGFEVENTCTX_INVALID: return "!Invalid Event Ctx!";3758 default:3759 AssertMsgFailed(("enmCtx=%d\n", enmCtx));3760 return "!Unknown Event Ctx!";3761 }3762 }3763 3764 3765 /**3766 * Processes debugger events.3767 *3768 * @returns VBox status.3769 * @param pDbgc DBGC Instance data.3770 * @param pEvent Pointer to event data.3771 */3772 static int dbgcProcessEvent(PDBGC pDbgc, PCDBGFEVENT pEvent)3773 {3774 /*3775 * Flush log first.3776 */3777 if (pDbgc->fLog)3778 {3779 int rc = dbgcProcessLog(pDbgc);3780 if (VBOX_FAILURE(rc))3781 return rc;3782 }3783 3784 /*3785 * Process the event.3786 */3787 pDbgc->pszScratch = &pDbgc->achInput[0];3788 pDbgc->iArg = 0;3789 bool fPrintPrompt = true;3790 int rc = VINF_SUCCESS;3791 switch (pEvent->enmType)3792 {3793 /*3794 * The first part is events we have initiated with commands.3795 */3796 case DBGFEVENT_HALT_DONE:3797 {3798 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: VM %p is halted! (%s)\n",3799 pDbgc->pVM, dbgcGetEventCtx(pEvent->enmCtx));3800 pDbgc->fRegCtxGuest = true; /* we're always in guest context when halted. */3801 if (VBOX_SUCCESS(rc))3802 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");3803 break;3804 }3805 3806 3807 /*3808 * The second part is events which can occur at any time.3809 */3810 case DBGFEVENT_FATAL_ERROR:3811 {3812 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbf event: Fatal error! (%s)\n",3813 dbgcGetEventCtx(pEvent->enmCtx));3814 pDbgc->fRegCtxGuest = false; /* fatal errors are always in hypervisor. */3815 if (VBOX_SUCCESS(rc))3816 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");3817 break;3818 }3819 3820 case DBGFEVENT_BREAKPOINT:3821 case DBGFEVENT_BREAKPOINT_HYPER:3822 {3823 bool fRegCtxGuest = pDbgc->fRegCtxGuest;3824 pDbgc->fRegCtxGuest = pEvent->enmType == DBGFEVENT_BREAKPOINT;3825 3826 rc = dbgcBpExec(pDbgc, pEvent->u.Bp.iBp);3827 switch (rc)3828 {3829 case VERR_DBGC_BP_NOT_FOUND:3830 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Unknown breakpoint %u! (%s)\n",3831 pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));3832 break;3833 3834 case VINF_DBGC_BP_NO_COMMAND:3835 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Breakpoint %u! (%s)\n",3836 pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));3837 break;3838 3839 case VINF_BUFFER_OVERFLOW:3840 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Breakpoint %u! Command too long to execute! (%s)\n",3841 pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));3842 break;3843 3844 default:3845 break;3846 }3847 if (VBOX_SUCCESS(rc) && DBGFR3IsHalted(pDbgc->pVM))3848 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");3849 else3850 pDbgc->fRegCtxGuest = fRegCtxGuest;3851 break;3852 }3853 3854 case DBGFEVENT_STEPPED:3855 case DBGFEVENT_STEPPED_HYPER:3856 {3857 pDbgc->fRegCtxGuest = pEvent->enmType == DBGFEVENT_STEPPED;3858 3859 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Single step! (%s)\n", dbgcGetEventCtx(pEvent->enmCtx));3860 if (VBOX_SUCCESS(rc))3861 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");3862 break;3863 }3864 3865 case DBGFEVENT_ASSERTION_HYPER:3866 {3867 pDbgc->fRegCtxGuest = false;3868 3869 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3870 "\ndbgf event: Hypervisor Assertion! (%s)\n"3871 "%s"3872 "%s"3873 "\n",3874 dbgcGetEventCtx(pEvent->enmCtx),3875 pEvent->u.Assert.pszMsg1,3876 pEvent->u.Assert.pszMsg2);3877 if (VBOX_SUCCESS(rc))3878 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");3879 break;3880 }3881 3882 case DBGFEVENT_DEV_STOP:3883 {3884 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3885 "\n"3886 "dbgf event: DBGFSTOP (%s)\n"3887 "File: %s\n"3888 "Line: %d\n"3889 "Function: %s\n",3890 dbgcGetEventCtx(pEvent->enmCtx),3891 pEvent->u.Src.pszFile,3892 pEvent->u.Src.uLine,3893 pEvent->u.Src.pszFunction);3894 if (VBOX_SUCCESS(rc) && pEvent->u.Src.pszMessage && *pEvent->u.Src.pszMessage)3895 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,3896 "Message: %s\n",3897 pEvent->u.Src.pszMessage);3898 if (VBOX_SUCCESS(rc))3899 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");3900 break;3901 }3902 3903 3904 case DBGFEVENT_INVALID_COMMAND:3905 {3906 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Invalid command event!\n");3907 fPrintPrompt = !pDbgc->fReady;3908 break;3909 }3910 3911 case DBGFEVENT_TERMINATING:3912 {3913 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\nVM is terminating!\n");3914 rc = VERR_GENERAL_FAILURE;3915 break;3916 }3917 3918 3919 default:3920 {3921 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Unknown event %d!\n", pEvent->enmType);3922 fPrintPrompt = !pDbgc->fReady;3923 break;3924 }3925 }3926 3927 /*3928 * Prompt, anyone?3929 */3930 if (fPrintPrompt && VBOX_SUCCESS(rc))3931 {3932 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "VBoxDbg> ");3933 }3934 3935 return rc;3936 }3937 3938 3939 3940 3941 3942 /**3943 * Make a console instance.3944 *3945 * This will not return until either an 'exit' command is issued or a error code3946 * indicating connection loss is encountered.3947 *3948 * @returns VINF_SUCCESS if console termination caused by the 'exit' command.3949 * @returns The VBox status code causing the console termination.3950 *3951 * @param pVM VM Handle.3952 * @param pBack Pointer to the backend structure. This must contain3953 * a full set of function pointers to service the console.3954 * @param fFlags Reserved, must be zero.3955 * @remark A forced termination of the console is easiest done by forcing the3956 * callbacks to return fatal failures.3957 */3958 DBGDECL(int) DBGCCreate(PVM pVM, PDBGCBACK pBack, unsigned fFlags)3959 {3960 /*3961 * Validate input.3962 */3963 AssertReturn(VALID_PTR(pVM), VERR_INVALID_PARAMETER);3964 AssertReturn(VALID_PTR(pBack), VERR_INVALID_PARAMETER);3965 AssertMsgReturn(!fFlags, ("%#x", fFlags), VERR_INVALID_PARAMETER);3966 3967 /*3968 * Allocate and initialize instance data3969 */3970 PDBGC pDbgc = (PDBGC)RTMemAllocZ(sizeof(*pDbgc));3971 if (!pDbgc)3972 return VERR_NO_MEMORY;3973 3974 pDbgc->CmdHlp.pfnWrite = dbgcHlpWrite;3975 pDbgc->CmdHlp.pfnPrintfV = dbgcHlpPrintfV;3976 pDbgc->CmdHlp.pfnPrintf = dbgcHlpPrintf;3977 pDbgc->CmdHlp.pfnVBoxErrorV = dbgcHlpVBoxErrorV;3978 pDbgc->CmdHlp.pfnVBoxError = dbgcHlpVBoxError;3979 pDbgc->CmdHlp.pfnMemRead = dbgcHlpMemRead;3980 pDbgc->CmdHlp.pfnMemWrite = dbgcHlpMemWrite;3981 pDbgc->CmdHlp.pfnEval = dbgcHlpEval;3982 pDbgc->CmdHlp.pfnExec = dbgcHlpExec;3983 pDbgc->CmdHlp.pfnVarToDbgfAddr = dbgcHlpVarToDbgfAddr;3984 pDbgc->CmdHlp.pfnVarToBool = dbgcHlpVarToBool;3985 pDbgc->pBack = pBack;3986 pDbgc->pVM = NULL;3987 pDbgc->pszEmulation = "CodeView/WinDbg";3988 pDbgc->paEmulationCmds = &g_aCmdsCodeView[0];3989 pDbgc->cEmulationCmds = g_cCmdsCodeView;3990 //pDbgc->fLog = false;3991 pDbgc->fRegCtxGuest = true;3992 pDbgc->fRegTerse = true;3993 //pDbgc->DisasmPos = {0};3994 //pDbgc->SourcePos = {0};3995 //pDbgc->DumpPos = {0};3996 //pDbgc->cbDumpElement = 0;3997 //pDbgc->cVars = 0;3998 //pDbgc->paVars = NULL;3999 //pDbgc->pFirstBp = NULL;4000 //pDbgc->uInputZero = 0;4001 //pDbgc->iRead = 0;4002 //pDbgc->iWrite = 0;4003 //pDbgc->cInputLines = 0;4004 //pDbgc->fInputOverflow = false;4005 pDbgc->fReady = true;4006 pDbgc->pszScratch = &pDbgc->achScratch[0];4007 //pDbgc->iArg = 0;4008 //pDbgc->rcOutput = 0;4009 4010 dbgcInitOpCharBitMap();4011 4012 /*4013 * Print welcome message.4014 */4015 int rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,4016 "Welcome to the VirtualBox Debugger!\n");4017 if (VBOX_FAILURE(rc))4018 goto l_failure;4019 4020 /*4021 * Attach to the VM.4022 */4023 rc = DBGFR3Attach(pVM);4024 if (VBOX_FAILURE(rc))4025 {4026 rc = pDbgc->CmdHlp.pfnVBoxError(&pDbgc->CmdHlp, rc, "When trying to attach to VM %p\n", pDbgc->pVM);4027 goto l_failure;4028 }4029 pDbgc->pVM = pVM;4030 4031 /*4032 * Print commandline and auto select result.4033 */4034 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,4035 "Current VM is %08x\n" /** @todo get and print the VM name! */4036 "VBoxDbg> ",4037 pDbgc->pVM);4038 if (VBOX_FAILURE(rc))4039 goto l_failure;4040 4041 /*4042 * Main Debugger Loop.4043 *4044 * This loop will either block on waiting for input or on waiting on4045 * debug events. If we're forwarding the log we cannot wait for long4046 * before we must flush the log.4047 */4048 for (rc = 0;;)4049 {4050 if (pDbgc->pVM && DBGFR3CanWait(pDbgc->pVM))4051 {4052 /*4053 * Wait for a debug event.4054 */4055 PCDBGFEVENT pEvent;4056 rc = DBGFR3EventWait(pDbgc->pVM, pDbgc->fLog ? 1 : 32, &pEvent);4057 if (VBOX_SUCCESS(rc))4058 {4059 rc = dbgcProcessEvent(pDbgc, pEvent);4060 if (VBOX_FAILURE(rc))4061 break;4062 }4063 else if (rc != VERR_TIMEOUT)4064 break;4065 4066 /*4067 * Check for input.4068 */4069 if (pBack->pfnInput(pDbgc->pBack, 0))4070 {4071 rc = dbgcProcessInput(pDbgc);4072 if (VBOX_FAILURE(rc))4073 break;4074 }4075 }4076 else4077 {4078 /*4079 * Wait for input. If Logging is enabled we'll only wait very briefly.4080 */4081 if (pBack->pfnInput(pDbgc->pBack, pDbgc->fLog ? 1 : 1000))4082 {4083 rc = dbgcProcessInput(pDbgc);4084 if (VBOX_FAILURE(rc))4085 break;4086 }4087 }4088 4089 /*4090 * Forward log output.4091 */4092 if (pDbgc->fLog)4093 {4094 rc = dbgcProcessLog(pDbgc);4095 if (VBOX_FAILURE(rc))4096 break;4097 }4098 }4099 4100 4101 l_failure:4102 /*4103 * Cleanup console debugger session.4104 */4105 /* Disable log hook. */4106 if (pDbgc->fLog)4107 {4108 4109 }4110 4111 /* Detach from the VM. */4112 if (pDbgc->pVM)4113 DBGFR3Detach(pDbgc->pVM);4114 4115 /* finally, free the instance memory. */4116 RTMemFree(pDbgc);4117 4118 return rc;4119 }4120 4121 4122 4123 /**4124 * Register one or more external commands.4125 *4126 * @returns VBox status.4127 * @param paCommands Pointer to an array of command descriptors.4128 * The commands must be unique. It's not possible4129 * to register the same commands more than once.4130 * @param cCommands Number of commands.4131 */4132 DBGDECL(int) DBGCRegisterCommands(PCDBGCCMD paCommands, unsigned cCommands)4133 {4134 /*4135 * Lock the list.4136 */4137 DBGCEXTCMDS_LOCK_WR();4138 PDBGCEXTCMDS pCur = g_pExtCmdsHead;4139 while (pCur)4140 {4141 if (paCommands == pCur->paCmds)4142 {4143 DBGCEXTCMDS_UNLOCK_WR();4144 AssertMsgFailed(("Attempt at re-registering %d command(s)!\n", cCommands));4145 return VWRN_DBGC_ALREADY_REGISTERED;4146 }4147 pCur = pCur->pNext;4148 }4149 4150 /*4151 * Allocate new chunk.4152 */4153 int rc = 0;4154 pCur = (PDBGCEXTCMDS)RTMemAlloc(sizeof(*pCur));4155 if (pCur)4156 {4157 pCur->cCmds = cCommands;4158 pCur->paCmds = paCommands;4159 pCur->pNext = g_pExtCmdsHead;4160 g_pExtCmdsHead = pCur;4161 }4162 else4163 rc = VERR_NO_MEMORY;4164 DBGCEXTCMDS_UNLOCK_WR();4165 4166 return rc;4167 }4168 4169 4170 /**4171 * Deregister one or more external commands previously registered by4172 * DBGCRegisterCommands().4173 *4174 * @returns VBox status.4175 * @param paCommands Pointer to an array of command descriptors4176 * as given to DBGCRegisterCommands().4177 * @param cCommands Number of commands.4178 */4179 DBGDECL(int) DBGCDeregisterCommands(PCDBGCCMD paCommands, unsigned cCommands)4180 {4181 /*4182 * Lock the list.4183 */4184 DBGCEXTCMDS_LOCK_WR();4185 PDBGCEXTCMDS pPrev = NULL;4186 PDBGCEXTCMDS pCur = g_pExtCmdsHead;4187 while (pCur)4188 {4189 if (paCommands == pCur->paCmds)4190 {4191 if (pPrev)4192 pPrev->pNext = pCur->pNext;4193 else4194 g_pExtCmdsHead = pCur->pNext;4195 DBGCEXTCMDS_UNLOCK_WR();4196 4197 RTMemFree(pCur);4198 return VINF_SUCCESS;4199 }4200 pPrev = pCur;4201 pCur = pCur->pNext;4202 }4203 DBGCEXTCMDS_UNLOCK_WR();4204 4205 NOREF(cCommands);4206 return VERR_DBGC_COMMANDS_NOT_REGISTERED;4207 }4208
Note:
See TracChangeset
for help on using the changeset viewer.