VirtualBox

source: vbox/trunk/src/VBox/Debugger/DBGConsole.cpp@ 5677

Last change on this file since 5677 was 5677, checked in by vboxsync, 18 years ago

Split out command worker routines and stuffed the variable manipulation routines in with them.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 61.5 KB
Line 
1/** $Id: DBGConsole.cpp 5677 2007-11-11 05:54:53Z vboxsync $ */
2/** @file
3 * DBGC - Debugger Console.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/** @page pg_dbgc DBGC - The Debug Console
20 *
21 * The debugger console is a first attempt to make some interactive
22 * debugging facilities for the VirtualBox backend (i.e. the VM). At a later
23 * stage we'll make a fancy gui around this, but for the present a telnet (or
24 * serial terminal) will have to suffice.
25 *
26 * The debugger is only built into the VM with debug builds or when
27 * VBOX_WITH_DEBUGGER is defined. There might be need for \#ifdef'ing on this
28 * define to enable special debugger hooks, but the general approach is to
29 * make generic interfaces. The individual components also can register
30 * 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 and
36 * windows kernel debuggers. This means ';' is a command separator and
37 * that when possible we'll use the same command names as these two uses.
38 *
39 *
40 * @subsection sec_dbg_op_numbers Numbers
41 *
42 * Numbers are hexadecimal unless specified with a prefix indicating
43 * elsewise. Prefixes:
44 * - '0x' - hexadecimal.
45 * - '0i' - decimal
46 * - '0t' - octal.
47 * - '0y' - binary.
48 *
49 *
50 * @subsection sec_dbg_op_address Addressing modes
51 *
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 Evaluation
60 *
61 * As time permits support will be implemented support for a subset of the C
62 * binary operators, starting with '+', '-', '*' and '/'. Support for variables
63 * are provided thru commands 'set' and 'unset' and the unary operator '$'. The
64 * unary '@' operator will indicate function calls. The debugger needs a set of
65 * memory read functions, but we might later extend this to allow registration of
66 * external functions too.
67 *
68 * A special command '?' will then be added which evalutates a given expression
69 * and prints it in all the different formats.
70 *
71 *
72 * @subsection sec_dbg_op_registers Registers
73 *
74 * Registers are addressed using their name. Some registers which have several fields
75 * (like gdtr) will have separate names indicating the different fields. The default
76 * register set is the guest one. To access the hypervisor register one have to
77 * prefix the register names with '.'.
78 *
79 *
80 * @subsection sec_dbg_op_commands Commands
81 *
82 * The commands are all lowercase, case sensitive, and starting with a letter. We will
83 * later add some special commands ('?' for evaulation) and perhaps command classes ('.', '!')
84 *
85 *
86 * @section sec_dbg_tasks Tasks
87 *
88 * To implement DBGT and instrument VMM for basic state inspection and log
89 * 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
102/*******************************************************************************
103* Header Files *
104*******************************************************************************/
105#define LOG_GROUP LOG_GROUP_DBGC
106#include <VBox/dbg.h>
107#include <VBox/dbgf.h>
108#include <VBox/vm.h>
109#include <VBox/vmm.h>
110#include <VBox/mm.h>
111#include <VBox/pgm.h>
112#include <VBox/selm.h>
113#include <VBox/dis.h>
114#include <VBox/param.h>
115#include <VBox/err.h>
116#include <VBox/log.h>
117
118#include <iprt/alloc.h>
119#include <iprt/alloca.h>
120#include <iprt/string.h>
121#include <iprt/assert.h>
122#include <iprt/ctype.h>
123
124#include <stdlib.h>
125#include <stdio.h>
126
127#include "DBGCInternal.h"
128
129
130/*******************************************************************************
131* Global Variables *
132*******************************************************************************/
133/** Bitmap where set bits indicates the characters the may start an operator name. */
134static uint32_t g_bmOperatorChars[256 / (4*8)];
135
136
137
138/**
139 * Initalizes g_bmOperatorChars.
140 */
141static void dbgcInitOpCharBitMap(void)
142{
143 memset(g_bmOperatorChars, 0, sizeof(g_bmOperatorChars));
144 for (unsigned iOp = 0; iOp < g_cOps; iOp++)
145 ASMBitSet(&g_bmOperatorChars[0], (uint8_t)g_aOps[iOp].szName[0]);
146}
147
148
149/**
150 * Checks whether the character may be the start of an operator.
151 *
152 * @returns true/false.
153 * @param ch The character.
154 */
155DECLINLINE(bool) dbgcIsOpChar(char ch)
156{
157 return ASMBitTest(&g_bmOperatorChars[0], (uint8_t)ch);
158}
159
160
161/**
162 * Prints any log lines from the log buffer.
163 *
164 * The caller must not call function this unless pDbgc->fLog is set.
165 *
166 * @returns VBox status. (output related)
167 * @param pDbgc Debugger console instance data.
168 */
169static int dbgcProcessLog(PDBGC pDbgc)
170{
171 /** @todo */
172 NOREF(pDbgc);
173 return 0;
174}
175
176
177
178/**
179 * Handle input buffer overflow.
180 *
181 * Will read any available input looking for a '\n' to reset the buffer on.
182 *
183 * @returns VBox status.
184 * @param pDbgc Debugger console instance data.
185 */
186static int dbgcInputOverflow(PDBGC pDbgc)
187{
188 /*
189 * Assert overflow status and reset the input buffer.
190 */
191 if (!pDbgc->fInputOverflow)
192 {
193 pDbgc->fInputOverflow = true;
194 pDbgc->iRead = pDbgc->iWrite = 0;
195 pDbgc->cInputLines = 0;
196 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "Input overflow!!\n");
197 }
198
199 /*
200 * Eat input till no more or there is a '\n'.
201 * When finding a '\n' we'll continue normal processing.
202 */
203 while (pDbgc->pBack->pfnInput(pDbgc->pBack, 0))
204 {
205 size_t cbRead;
206 int rc = pDbgc->pBack->pfnRead(pDbgc->pBack, &pDbgc->achInput[0], sizeof(pDbgc->achInput) - 1, &cbRead);
207 if (VBOX_FAILURE(rc))
208 return rc;
209 char *psz = (char *)memchr(&pDbgc->achInput[0], '\n', cbRead);
210 if (psz)
211 {
212 pDbgc->fInputOverflow = false;
213 pDbgc->iRead = psz - &pDbgc->achInput[0] + 1;
214 pDbgc->iWrite = (unsigned)cbRead;
215 pDbgc->cInputLines = 0;
216 break;
217 }
218 }
219
220 return 0;
221}
222
223
224
225/**
226 * Read input and do some preprocessing.
227 *
228 * @returns VBox status.
229 * In addition to the iWrite and achInput, cInputLines is maintained.
230 * In case of an input overflow the fInputOverflow flag will be set.
231 * @param pDbgc Debugger console instance data.
232 */
233static int dbgcInputRead(PDBGC pDbgc)
234{
235 /*
236 * We have ready input.
237 * Read it till we don't have any or we have a full input buffer.
238 */
239 int rc = 0;
240 do
241 {
242 /*
243 * More available buffer space?
244 */
245 size_t cbLeft;
246 if (pDbgc->iWrite > pDbgc->iRead)
247 cbLeft = sizeof(pDbgc->achInput) - pDbgc->iWrite - (pDbgc->iRead == 0);
248 else
249 cbLeft = pDbgc->iRead - pDbgc->iWrite - 1;
250 if (!cbLeft)
251 {
252 /* overflow? */
253 if (!pDbgc->cInputLines)
254 rc = dbgcInputOverflow(pDbgc);
255 break;
256 }
257
258 /*
259 * Read one char and interpret it.
260 */
261 char achRead[128];
262 size_t cbRead;
263 rc = pDbgc->pBack->pfnRead(pDbgc->pBack, &achRead[0], RT_MIN(cbLeft, sizeof(achRead)), &cbRead);
264 if (VBOX_FAILURE(rc))
265 return rc;
266 char *psz = &achRead[0];
267 while (cbRead-- > 0)
268 {
269 char ch = *psz++;
270 switch (ch)
271 {
272 /*
273 * Ignore.
274 */
275 case '\0':
276 case '\r':
277 case '\a':
278 break;
279
280 /*
281 * Backspace.
282 */
283 case '\b':
284 Log2(("DBGC: backspace\n"));
285 if (pDbgc->iRead != pDbgc->iWrite)
286 {
287 unsigned iWriteUndo = pDbgc->iWrite;
288 if (pDbgc->iWrite)
289 pDbgc->iWrite--;
290 else
291 pDbgc->iWrite = sizeof(pDbgc->achInput) - 1;
292
293 if (pDbgc->achInput[pDbgc->iWrite] == '\n')
294 pDbgc->iWrite = iWriteUndo;
295 }
296 break;
297
298 /*
299 * Add char to buffer.
300 */
301 case '\t':
302 case '\n':
303 case ';':
304 switch (ch)
305 {
306 case '\t': ch = ' '; break;
307 case '\n': pDbgc->cInputLines++; break;
308 }
309 default:
310 Log2(("DBGC: ch=%02x\n", (unsigned char)ch));
311 pDbgc->achInput[pDbgc->iWrite] = ch;
312 if (++pDbgc->iWrite >= sizeof(pDbgc->achInput))
313 pDbgc->iWrite = 0;
314 break;
315 }
316 }
317
318 /* Terminate it to make it easier to read in the debugger. */
319 pDbgc->achInput[pDbgc->iWrite] = '\0';
320 } while (pDbgc->pBack->pfnInput(pDbgc->pBack, 0));
321
322 return rc;
323}
324
325
326
327/**
328 * Resolves a symbol (or tries to do so at least).
329 *
330 * @returns 0 on success.
331 * @returns VBox status on failure.
332 * @param pDbgc The debug console instance.
333 * @param pszSymbol The symbol name.
334 * @param enmType The result type.
335 * @param pResult Where to store the result.
336 */
337int dbgcSymbolGet(PDBGC pDbgc, const char *pszSymbol, DBGCVARTYPE enmType, PDBGCVAR pResult)
338{
339 /*
340 * Builtin?
341 */
342 PCDBGCSYM pSymDesc = dbgcLookupRegisterSymbol(pDbgc, pszSymbol);
343 if (pSymDesc)
344 {
345 if (!pSymDesc->pfnGet)
346 return VERR_PARSE_WRITEONLY_SYMBOL;
347 return pSymDesc->pfnGet(pSymDesc, &pDbgc->CmdHlp, enmType, pResult);
348 }
349
350
351 /*
352 * Ask PDM.
353 */
354 /** @todo resolve symbols using PDM. */
355
356
357 /*
358 * Ask the debug info manager.
359 */
360 DBGFSYMBOL Symbol;
361 int rc = DBGFR3SymbolByName(pDbgc->pVM, pszSymbol, &Symbol);
362 if (VBOX_SUCCESS(rc))
363 {
364 /*
365 * Default return is a flat gc address.
366 */
367 memset(pResult, 0, sizeof(*pResult));
368 pResult->enmRangeType = Symbol.cb ? DBGCVAR_RANGE_BYTES : DBGCVAR_RANGE_NONE;
369 pResult->u64Range = Symbol.cb;
370 pResult->enmType = DBGCVAR_TYPE_GC_FLAT;
371 pResult->u.GCFlat = Symbol.Value;
372 DBGCVAR VarTmp;
373 switch (enmType)
374 {
375 /* nothing to do. */
376 case DBGCVAR_TYPE_GC_FLAT:
377 case DBGCVAR_TYPE_GC_FAR:
378 case DBGCVAR_TYPE_ANY:
379 return VINF_SUCCESS;
380
381 /* simply make it numeric. */
382 case DBGCVAR_TYPE_NUMBER:
383 pResult->enmType = DBGCVAR_TYPE_NUMBER;
384 pResult->u.u64Number = Symbol.Value;
385 return VINF_SUCCESS;
386
387 /* cast it. */
388
389 case DBGCVAR_TYPE_GC_PHYS:
390 VarTmp = *pResult;
391 return dbgcOpAddrPhys(pDbgc, &VarTmp, pResult);
392
393 case DBGCVAR_TYPE_HC_FAR:
394 case DBGCVAR_TYPE_HC_FLAT:
395 VarTmp = *pResult;
396 return dbgcOpAddrHost(pDbgc, &VarTmp, pResult);
397
398 case DBGCVAR_TYPE_HC_PHYS:
399 VarTmp = *pResult;
400 return dbgcOpAddrHostPhys(pDbgc, &VarTmp, pResult);
401
402 default:
403 AssertMsgFailed(("Internal error enmType=%d\n", enmType));
404 return VERR_INVALID_PARAMETER;
405 }
406 }
407
408 return VERR_PARSE_NOT_IMPLEMENTED;
409}
410
411
412static int dbgcEvalSubString(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pArg)
413{
414 Log2(("dbgcEvalSubString: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));
415
416 /*
417 * Removing any quoting and escapings.
418 */
419 char ch = *pszExpr;
420 if (ch == '"' || ch == '\'' || ch == '`')
421 {
422 if (pszExpr[--cchExpr] != ch)
423 return VERR_PARSE_UNBALANCED_QUOTE;
424 cchExpr--;
425 pszExpr++;
426
427 /** @todo string unescaping. */
428 }
429 pszExpr[cchExpr] = '\0';
430
431 /*
432 * Make the argument.
433 */
434 pArg->pDesc = NULL;
435 pArg->pNext = NULL;
436 pArg->enmType = DBGCVAR_TYPE_STRING;
437 pArg->u.pszString = pszExpr;
438 pArg->enmRangeType = DBGCVAR_RANGE_BYTES;
439 pArg->u64Range = cchExpr;
440
441 NOREF(pDbgc);
442 return 0;
443}
444
445
446static int dbgcEvalSubNum(char *pszExpr, unsigned uBase, PDBGCVAR pArg)
447{
448 Log2(("dbgcEvalSubNum: uBase=%d pszExpr=%s\n", uBase, pszExpr));
449 /*
450 * Convert to number.
451 */
452 uint64_t u64 = 0;
453 char ch;
454 while ((ch = *pszExpr) != '\0')
455 {
456 uint64_t u64Prev = u64;
457 unsigned u = ch - '0';
458 if (u < 10 && u < uBase)
459 u64 = u64 * uBase + u;
460 else if (ch >= 'a' && (u = ch - ('a' - 10)) < uBase)
461 u64 = u64 * uBase + u;
462 else if (ch >= 'A' && (u = ch - ('A' - 10)) < uBase)
463 u64 = u64 * uBase + u;
464 else
465 return VERR_PARSE_INVALID_NUMBER;
466
467 /* check for overflow - ARG!!! How to detect overflow correctly!?!?!? */
468 if (u64Prev != u64 / uBase)
469 return VERR_PARSE_NUMBER_TOO_BIG;
470
471 /* next */
472 pszExpr++;
473 }
474
475 /*
476 * Initialize the argument.
477 */
478 pArg->pDesc = NULL;
479 pArg->pNext = NULL;
480 pArg->enmType = DBGCVAR_TYPE_NUMBER;
481 pArg->u.u64Number = u64;
482 pArg->enmRangeType = DBGCVAR_RANGE_NONE;
483 pArg->u64Range = 0;
484
485 return 0;
486}
487
488
489/**
490 * Match variable and variable descriptor, promoting the variable if necessary.
491 *
492 * @returns VBox status code.
493 * @param pDbgc Debug console instanace.
494 * @param pVar Variable.
495 * @param pVarDesc Variable descriptor.
496 */
497static int dbgcEvalSubMatchVar(PDBGC pDbgc, PDBGCVAR pVar, PCDBGCVARDESC pVarDesc)
498{
499 /*
500 * (If match or promoted to match, return, else break.)
501 */
502 switch (pVarDesc->enmCategory)
503 {
504 /*
505 * Anything goes
506 */
507 case DBGCVAR_CAT_ANY:
508 return VINF_SUCCESS;
509
510 /*
511 * Pointer with and without range.
512 * We can try resolve strings and symbols as symbols and
513 * promote numbers to flat GC pointers.
514 */
515 case DBGCVAR_CAT_POINTER_NO_RANGE:
516 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
517 return VERR_PARSE_NO_RANGE_ALLOWED;
518 /* fallthru */
519 case DBGCVAR_CAT_POINTER:
520 switch (pVar->enmType)
521 {
522 case DBGCVAR_TYPE_GC_FLAT:
523 case DBGCVAR_TYPE_GC_FAR:
524 case DBGCVAR_TYPE_GC_PHYS:
525 case DBGCVAR_TYPE_HC_FLAT:
526 case DBGCVAR_TYPE_HC_FAR:
527 case DBGCVAR_TYPE_HC_PHYS:
528 return VINF_SUCCESS;
529
530 case DBGCVAR_TYPE_SYMBOL:
531 case DBGCVAR_TYPE_STRING:
532 {
533 DBGCVAR Var;
534 int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_GC_FLAT, &Var);
535 if (VBOX_SUCCESS(rc))
536 {
537 /* deal with range */
538 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
539 {
540 Var.enmRangeType = pVar->enmRangeType;
541 Var.u64Range = pVar->u64Range;
542 }
543 else if (pVarDesc->enmCategory == DBGCVAR_CAT_POINTER_NO_RANGE)
544 Var.enmRangeType = DBGCVAR_RANGE_NONE;
545 *pVar = Var;
546 return rc;
547 }
548 break;
549 }
550
551 case DBGCVAR_TYPE_NUMBER:
552 {
553 RTGCPTR GCPtr = (RTGCPTR)pVar->u.u64Number;
554 pVar->enmType = DBGCVAR_TYPE_GC_FLAT;
555 pVar->u.GCFlat = GCPtr;
556 return VINF_SUCCESS;
557 }
558
559 default:
560 break;
561 }
562 break;
563
564 /*
565 * GC pointer with and without range.
566 * We can try resolve strings and symbols as symbols and
567 * promote numbers to flat GC pointers.
568 */
569 case DBGCVAR_CAT_GC_POINTER_NO_RANGE:
570 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
571 return VERR_PARSE_NO_RANGE_ALLOWED;
572 /* fallthru */
573 case DBGCVAR_CAT_GC_POINTER:
574 switch (pVar->enmType)
575 {
576 case DBGCVAR_TYPE_GC_FLAT:
577 case DBGCVAR_TYPE_GC_FAR:
578 case DBGCVAR_TYPE_GC_PHYS:
579 return VINF_SUCCESS;
580
581 case DBGCVAR_TYPE_HC_FLAT:
582 case DBGCVAR_TYPE_HC_FAR:
583 case DBGCVAR_TYPE_HC_PHYS:
584 return VERR_PARSE_CONVERSION_FAILED;
585
586 case DBGCVAR_TYPE_SYMBOL:
587 case DBGCVAR_TYPE_STRING:
588 {
589 DBGCVAR Var;
590 int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_GC_FLAT, &Var);
591 if (VBOX_SUCCESS(rc))
592 {
593 /* deal with range */
594 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
595 {
596 Var.enmRangeType = pVar->enmRangeType;
597 Var.u64Range = pVar->u64Range;
598 }
599 else if (pVarDesc->enmCategory == DBGCVAR_CAT_POINTER_NO_RANGE)
600 Var.enmRangeType = DBGCVAR_RANGE_NONE;
601 *pVar = Var;
602 return rc;
603 }
604 break;
605 }
606
607 case DBGCVAR_TYPE_NUMBER:
608 {
609 RTGCPTR GCPtr = (RTGCPTR)pVar->u.u64Number;
610 pVar->enmType = DBGCVAR_TYPE_GC_FLAT;
611 pVar->u.GCFlat = GCPtr;
612 return VINF_SUCCESS;
613 }
614
615 default:
616 break;
617 }
618 break;
619
620 /*
621 * Number with or without a range.
622 * Numbers can be resolved from symbols, but we cannot demote a pointer
623 * to a number.
624 */
625 case DBGCVAR_CAT_NUMBER_NO_RANGE:
626 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
627 return VERR_PARSE_NO_RANGE_ALLOWED;
628 /* fallthru */
629 case DBGCVAR_CAT_NUMBER:
630 switch (pVar->enmType)
631 {
632 case DBGCVAR_TYPE_NUMBER:
633 return VINF_SUCCESS;
634
635 case DBGCVAR_TYPE_SYMBOL:
636 case DBGCVAR_TYPE_STRING:
637 {
638 DBGCVAR Var;
639 int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_NUMBER, &Var);
640 if (VBOX_SUCCESS(rc))
641 {
642 *pVar = Var;
643 return rc;
644 }
645 break;
646 }
647 default:
648 break;
649 }
650 break;
651
652 /*
653 * Strings can easily be made from symbols (and of course strings).
654 * We could consider reformatting the addresses and numbers into strings later...
655 */
656 case DBGCVAR_CAT_STRING:
657 switch (pVar->enmType)
658 {
659 case DBGCVAR_TYPE_SYMBOL:
660 pVar->enmType = DBGCVAR_TYPE_STRING;
661 /* fallthru */
662 case DBGCVAR_TYPE_STRING:
663 return VINF_SUCCESS;
664 default:
665 break;
666 }
667 break;
668
669 /*
670 * Symol is pretty much the same thing as a string (at least until we actually implement it).
671 */
672 case DBGCVAR_CAT_SYMBOL:
673 switch (pVar->enmType)
674 {
675 case DBGCVAR_TYPE_STRING:
676 pVar->enmType = DBGCVAR_TYPE_SYMBOL;
677 /* fallthru */
678 case DBGCVAR_TYPE_SYMBOL:
679 return VINF_SUCCESS;
680 default:
681 break;
682 }
683 break;
684
685 /*
686 * Anything else is illegal.
687 */
688 default:
689 AssertMsgFailed(("enmCategory=%d\n", pVar->enmType));
690 break;
691 }
692
693 return VERR_PARSE_NO_ARGUMENT_MATCH;
694}
695
696
697/**
698 * Matches a set of variables with a description set.
699 *
700 * This is typically used for routine arguments before a call. The effects in
701 * addition to the validation, is that some variables might be propagated to
702 * other types in order to match the description. The following transformations
703 * are supported:
704 * - String reinterpreted as a symbol and resolved to a number or pointer.
705 * - Number to a pointer.
706 * - Pointer to a number.
707 * @returns 0 on success with paVars.
708 * @returns VBox error code for match errors.
709 */
710static int dbgcEvalSubMatchVars(PDBGC pDbgc, unsigned cVarsMin, unsigned cVarsMax,
711 PCDBGCVARDESC paVarDescs, unsigned cVarDescs,
712 PDBGCVAR paVars, unsigned cVars)
713{
714 /*
715 * Just do basic min / max checks first.
716 */
717 if (cVars < cVarsMin)
718 return VERR_PARSE_TOO_FEW_ARGUMENTS;
719 if (cVars > cVarsMax)
720 return VERR_PARSE_TOO_MANY_ARGUMENTS;
721
722 /*
723 * Match the descriptors and actual variables.
724 */
725 PCDBGCVARDESC pPrevDesc = NULL;
726 unsigned cCurDesc = 0;
727 unsigned iVar = 0;
728 unsigned iVarDesc = 0;
729 while (iVar < cVars)
730 {
731 /* walk the descriptors */
732 if (iVarDesc >= cVarDescs)
733 return VERR_PARSE_TOO_MANY_ARGUMENTS;
734 if ( ( paVarDescs[iVarDesc].fFlags & DBGCVD_FLAGS_DEP_PREV
735 && &paVarDescs[iVarDesc - 1] != pPrevDesc)
736 || cCurDesc >= paVarDescs[iVarDesc].cTimesMax)
737 {
738 iVarDesc++;
739 if (iVarDesc >= cVarDescs)
740 return VERR_PARSE_TOO_MANY_ARGUMENTS;
741 cCurDesc = 0;
742 }
743
744 /*
745 * Skip thru optional arguments until we find something which matches
746 * or can easily be promoted to what the descriptor want.
747 */
748 for (;;)
749 {
750 int rc = dbgcEvalSubMatchVar(pDbgc, &paVars[iVar], &paVarDescs[iVarDesc]);
751 if (VBOX_SUCCESS(rc))
752 {
753 paVars[iVar].pDesc = &paVarDescs[iVarDesc];
754 cCurDesc++;
755 break;
756 }
757
758 /* can we advance? */
759 if (paVarDescs[iVarDesc].cTimesMin > cCurDesc)
760 return VERR_PARSE_ARGUMENT_TYPE_MISMATCH;
761 if (++iVarDesc >= cVarDescs)
762 return VERR_PARSE_ARGUMENT_TYPE_MISMATCH;
763 cCurDesc = 0;
764 }
765
766 /* next var */
767 iVar++;
768 }
769
770 /*
771 * Check that the rest of the descriptors are optional.
772 */
773 while (iVarDesc < cVarDescs)
774 {
775 if (paVarDescs[iVarDesc].cTimesMin > cCurDesc)
776 return VERR_PARSE_TOO_FEW_ARGUMENTS;
777 cCurDesc = 0;
778
779 /* next */
780 iVarDesc++;
781 }
782
783 return 0;
784}
785
786
787/**
788 * Evaluates one argument with respect to unary operators.
789 *
790 * @returns 0 on success. pResult contains the result.
791 * @returns VBox error code on parse or other evaluation error.
792 *
793 * @param pDbgc Debugger console instance data.
794 * @param pszExpr The expression string.
795 * @param pResult Where to store the result of the expression evaluation.
796 */
797static int dbgcEvalSubUnary(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pResult)
798{
799 Log2(("dbgcEvalSubUnary: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));
800
801 /*
802 * The state of the expression is now such that it will start by zero or more
803 * unary operators and being followed by an expression of some kind.
804 * The expression is either plain or in parenthesis.
805 *
806 * Being in a lazy, recursive mode today, the parsing is done as simple as possible. :-)
807 * ASSUME: unary operators are all of equal precedence.
808 */
809 int rc = 0;
810 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, pszExpr, false, ' ');
811 if (pOp)
812 {
813 /* binary operators means syntax error. */
814 if (pOp->fBinary)
815 return VERR_PARSE_UNEXPECTED_OPERATOR;
816
817 /*
818 * If the next expression (the one following the unary operator) is in a
819 * parenthesis a full eval is needed. If not the unary eval will suffice.
820 */
821 /* calc and strip next expr. */
822 char *pszExpr2 = pszExpr + pOp->cchName;
823 while (isblank(*pszExpr2))
824 pszExpr2++;
825
826 if (!*pszExpr2)
827 rc = VERR_PARSE_EMPTY_ARGUMENT;
828 else
829 {
830 DBGCVAR Arg;
831 if (*pszExpr2 == '(')
832 rc = dbgcEvalSub(pDbgc, pszExpr2, cchExpr - (pszExpr2 - pszExpr), &Arg);
833 else
834 rc = dbgcEvalSubUnary(pDbgc, pszExpr2, cchExpr - (pszExpr2 - pszExpr), &Arg);
835 if (VBOX_SUCCESS(rc))
836 rc = pOp->pfnHandlerUnary(pDbgc, &Arg, pResult);
837 }
838 }
839 else
840 {
841 /*
842 * Didn't find any operators, so it we have to check if this can be an
843 * function call before assuming numeric or string expression.
844 *
845 * (ASSUMPTIONS:)
846 * A function name only contains alphanumerical chars and it can not start
847 * with a numerical character.
848 * Immediately following the name is a parenthesis which must over
849 * the remaining part of the expression.
850 */
851 bool fExternal = *pszExpr == '.';
852 char *pszFun = fExternal ? pszExpr + 1 : pszExpr;
853 char *pszFunEnd = NULL;
854 if (pszExpr[cchExpr - 1] == ')' && isalpha(*pszFun))
855 {
856 pszFunEnd = pszExpr + 1;
857 while (*pszFunEnd != '(' && isalnum(*pszFunEnd))
858 pszFunEnd++;
859 if (*pszFunEnd != '(')
860 pszFunEnd = NULL;
861 }
862
863 if (pszFunEnd)
864 {
865 /*
866 * Ok, it's a function call.
867 */
868 if (fExternal)
869 pszExpr++, cchExpr--;
870 PCDBGCCMD pFun = dbgcRoutineLookup(pDbgc, pszExpr, pszFunEnd - pszExpr, fExternal);
871 if (!pFun)
872 return VERR_PARSE_FUNCTION_NOT_FOUND;
873 if (!pFun->pResultDesc)
874 return VERR_PARSE_NOT_A_FUNCTION;
875
876 /*
877 * Parse the expression in parenthesis.
878 */
879 cchExpr -= pszFunEnd - pszExpr;
880 pszExpr = pszFunEnd;
881 /** @todo implement multiple arguments. */
882 DBGCVAR Arg;
883 rc = dbgcEvalSub(pDbgc, pszExpr, cchExpr, &Arg);
884 if (!rc)
885 {
886 rc = dbgcEvalSubMatchVars(pDbgc, pFun->cArgsMin, pFun->cArgsMax, pFun->paArgDescs, pFun->cArgDescs, &Arg, 1);
887 if (!rc)
888 rc = pFun->pfnHandler(pFun, &pDbgc->CmdHlp, pDbgc->pVM, &Arg, 1, pResult);
889 }
890 else if (rc == VERR_PARSE_EMPTY_ARGUMENT && pFun->cArgsMin == 0)
891 rc = pFun->pfnHandler(pFun, &pDbgc->CmdHlp, pDbgc->pVM, NULL, 0, pResult);
892 }
893 else
894 {
895 /*
896 * Didn't find any operators, so it must be a plain expression.
897 * This might be numeric or a string expression.
898 */
899 char ch = pszExpr[0];
900 char ch2 = pszExpr[1];
901 if (ch == '0' && (ch2 == 'x' || ch2 == 'X'))
902 rc = dbgcEvalSubNum(pszExpr + 2, 16, pResult);
903 else if (ch == '0' && (ch2 == 'i' || ch2 == 'i'))
904 rc = dbgcEvalSubNum(pszExpr + 2, 10, pResult);
905 else if (ch == '0' && (ch2 == 't' || ch2 == 'T'))
906 rc = dbgcEvalSubNum(pszExpr + 2, 8, pResult);
907 /// @todo 0b doesn't work as a binary prefix, we confuse it with 0bf8:0123 and stuff.
908 //else if (ch == '0' && (ch2 == 'b' || ch2 == 'b'))
909 // rc = dbgcEvalSubNum(pszExpr + 2, 2, pResult);
910 else
911 {
912 /*
913 * Hexadecimal number or a string?
914 */
915 char *psz = pszExpr;
916 while (isxdigit(*psz))
917 psz++;
918 if (!*psz)
919 rc = dbgcEvalSubNum(pszExpr, 16, pResult);
920 else if ((*psz == 'h' || *psz == 'H') && !psz[1])
921 {
922 *psz = '\0';
923 rc = dbgcEvalSubNum(pszExpr, 16, pResult);
924 }
925 else
926 rc = dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult);
927 }
928 }
929 }
930
931 return rc;
932}
933
934
935/**
936 * Evaluates one argument.
937 *
938 * @returns 0 on success. pResult contains the result.
939 * @returns VBox error code on parse or other evaluation error.
940 *
941 * @param pDbgc Debugger console instance data.
942 * @param pszExpr The expression string.
943 * @param pResult Where to store the result of the expression evaluation.
944 */
945int dbgcEvalSub(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pResult)
946{
947 Log2(("dbgcEvalSub: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));
948 /*
949 * First we need to remove blanks in both ends.
950 * ASSUMES: There is no quoting unless the entire expression is a string.
951 */
952
953 /* stripping. */
954 while (cchExpr > 0 && isblank(pszExpr[cchExpr - 1]))
955 pszExpr[--cchExpr] = '\0';
956 while (isblank(*pszExpr))
957 pszExpr++, cchExpr--;
958 if (!*pszExpr)
959 return VERR_PARSE_EMPTY_ARGUMENT;
960
961 /* it there is any kind of quoting in the expression, it's string meat. */
962 if (strpbrk(pszExpr, "\"'`"))
963 return dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult);
964
965 /*
966 * Check if there are any parenthesis which needs removing.
967 */
968 if (pszExpr[0] == '(' && pszExpr[cchExpr - 1] == ')')
969 {
970 do
971 {
972 unsigned cPar = 1;
973 char *psz = pszExpr + 1;
974 char ch;
975 while ((ch = *psz) != '\0')
976 {
977 if (ch == '(')
978 cPar++;
979 else if (ch == ')')
980 {
981 if (cPar <= 0)
982 return VERR_PARSE_UNBALANCED_PARENTHESIS;
983 cPar--;
984 if (cPar == 0 && psz[1]) /* If not at end, there's nothing to do. */
985 break;
986 }
987 /* next */
988 psz++;
989 }
990 if (ch)
991 break;
992
993 /* remove the parenthesis. */
994 pszExpr++;
995 cchExpr -= 2;
996 pszExpr[cchExpr] = '\0';
997
998 /* strip blanks. */
999 while (cchExpr > 0 && isblank(pszExpr[cchExpr - 1]))
1000 pszExpr[--cchExpr] = '\0';
1001 while (isblank(*pszExpr))
1002 pszExpr++, cchExpr--;
1003 if (!*pszExpr)
1004 return VERR_PARSE_EMPTY_ARGUMENT;
1005 } while (pszExpr[0] == '(' && pszExpr[cchExpr - 1] == ')');
1006 }
1007
1008 /* tabs to spaces. */
1009 char *psz = pszExpr;
1010 while ((psz = strchr(psz, '\t')) != NULL)
1011 *psz = ' ';
1012
1013 /*
1014 * Now, we need to look for the binary operator with the lowest precedence.
1015 *
1016 * If there are no operators we're left with a simple expression which we
1017 * evaluate with respect to unary operators
1018 */
1019 char *pszOpSplit = NULL;
1020 PCDBGCOP pOpSplit = NULL;
1021 unsigned cBinaryOps = 0;
1022 unsigned cPar = 0;
1023 char ch;
1024 char chPrev = ' ';
1025 bool fBinary = false;
1026 psz = pszExpr;
1027
1028 while ((ch = *psz) != '\0')
1029 {
1030 //Log2(("ch=%c cPar=%d fBinary=%d\n", ch, cPar, fBinary));
1031 /*
1032 * Parenthesis.
1033 */
1034 if (ch == '(')
1035 {
1036 cPar++;
1037 fBinary = false;
1038 }
1039 else if (ch == ')')
1040 {
1041 if (cPar <= 0)
1042 return VERR_PARSE_UNBALANCED_PARENTHESIS;
1043 cPar--;
1044 fBinary = true;
1045 }
1046 /*
1047 * Potential operator.
1048 */
1049 else if (cPar == 0 && !isblank(ch))
1050 {
1051 PCDBGCOP pOp = dbgcIsOpChar(ch)
1052 ? dbgcOperatorLookup(pDbgc, psz, fBinary, chPrev)
1053 : NULL;
1054 if (pOp)
1055 {
1056 /* If not the right kind of operator we've got a syntax error. */
1057 if (pOp->fBinary != fBinary)
1058 return VERR_PARSE_UNEXPECTED_OPERATOR;
1059
1060 /*
1061 * Update the parse state and skip the operator.
1062 */
1063 if (!pOpSplit)
1064 {
1065 pOpSplit = pOp;
1066 pszOpSplit = psz;
1067 cBinaryOps = fBinary;
1068 }
1069 else if (fBinary)
1070 {
1071 cBinaryOps++;
1072 if (pOp->iPrecedence >= pOpSplit->iPrecedence)
1073 {
1074 pOpSplit = pOp;
1075 pszOpSplit = psz;
1076 }
1077 }
1078
1079 psz += pOp->cchName - 1;
1080 fBinary = false;
1081 }
1082 else
1083 fBinary = true;
1084 }
1085
1086 /* next */
1087 psz++;
1088 chPrev = ch;
1089 } /* parse loop. */
1090
1091
1092 /*
1093 * Either we found an operator to divide the expression by
1094 * or we didn't find any. In the first case it's divide and
1095 * conquer. In the latter it's a single expression which
1096 * needs dealing with its unary operators if any.
1097 */
1098 int rc;
1099 if ( cBinaryOps
1100 && pOpSplit->fBinary)
1101 {
1102 /* process 1st sub expression. */
1103 *pszOpSplit = '\0';
1104 DBGCVAR Arg1;
1105 rc = dbgcEvalSub(pDbgc, pszExpr, pszOpSplit - pszExpr, &Arg1);
1106 if (VBOX_SUCCESS(rc))
1107 {
1108 /* process 2nd sub expression. */
1109 char *psz2 = pszOpSplit + pOpSplit->cchName;
1110 DBGCVAR Arg2;
1111 rc = dbgcEvalSub(pDbgc, psz2, cchExpr - (psz2 - pszExpr), &Arg2);
1112 if (VBOX_SUCCESS(rc))
1113 /* apply the operator. */
1114 rc = pOpSplit->pfnHandlerBinary(pDbgc, &Arg1, &Arg2, pResult);
1115 }
1116 }
1117 else if (cBinaryOps)
1118 {
1119 /* process sub expression. */
1120 pszOpSplit += pOpSplit->cchName;
1121 DBGCVAR Arg;
1122 rc = dbgcEvalSub(pDbgc, pszOpSplit, cchExpr - (pszOpSplit - pszExpr), &Arg);
1123 if (VBOX_SUCCESS(rc))
1124 /* apply the operator. */
1125 rc = pOpSplit->pfnHandlerUnary(pDbgc, &Arg, pResult);
1126 }
1127 else
1128 /* plain expression or using unary operators perhaps with paratheses. */
1129 rc = dbgcEvalSubUnary(pDbgc, pszExpr, cchExpr, pResult);
1130
1131 return rc;
1132}
1133
1134
1135/**
1136 * Parses the arguments of one command.
1137 *
1138 * @returns 0 on success.
1139 * @returns VBox error code on parse error with *pcArg containing the argument causing trouble.
1140 * @param pDbgc Debugger console instance data.
1141 * @param pCmd Pointer to the command descriptor.
1142 * @param pszArg Pointer to the arguments to parse.
1143 * @param paArgs Where to store the parsed arguments.
1144 * @param cArgs Size of the paArgs array.
1145 * @param pcArgs Where to store the number of arguments.
1146 * In the event of an error this is used to store the index of the offending argument.
1147 */
1148static int dbgcProcessArguments(PDBGC pDbgc, PCDBGCCMD pCmd, char *pszArgs, PDBGCVAR paArgs, unsigned cArgs, unsigned *pcArgs)
1149{
1150 Log2(("dbgcProcessArguments: pCmd=%s pszArgs='%s'\n", pCmd->pszCmd, pszArgs));
1151 /*
1152 * Check if we have any argument and if the command takes any.
1153 */
1154 *pcArgs = 0;
1155 /* strip leading blanks. */
1156 while (*pszArgs && isblank(*pszArgs))
1157 pszArgs++;
1158 if (!*pszArgs)
1159 {
1160 if (!pCmd->cArgsMin)
1161 return 0;
1162 return VERR_PARSE_TOO_FEW_ARGUMENTS;
1163 }
1164 /** @todo fixme - foo() doesn't work. */
1165 if (!pCmd->cArgsMax)
1166 return VERR_PARSE_TOO_MANY_ARGUMENTS;
1167
1168 /*
1169 * This is a hack, it's "temporary" and should go away "when" the parser is
1170 * modified to match arguments while parsing.
1171 */
1172 if ( pCmd->cArgsMax == 1
1173 && pCmd->cArgsMin == 1
1174 && pCmd->cArgDescs == 1
1175 && pCmd->paArgDescs[0].enmCategory == DBGCVAR_CAT_STRING
1176 && cArgs >= 1)
1177 {
1178 *pcArgs = 1;
1179 RTStrStripR(pszArgs);
1180 return dbgcEvalSubString(pDbgc, pszArgs, strlen(pszArgs), &paArgs[0]);
1181 }
1182
1183
1184 /*
1185 * The parse loop.
1186 */
1187 PDBGCVAR pArg0 = &paArgs[0];
1188 PDBGCVAR pArg = pArg0;
1189 *pcArgs = 0;
1190 do
1191 {
1192 /*
1193 * Can we have another argument?
1194 */
1195 if (*pcArgs >= pCmd->cArgsMax)
1196 return VERR_PARSE_TOO_MANY_ARGUMENTS;
1197 if (pArg >= &paArgs[cArgs])
1198 return VERR_PARSE_ARGUMENT_OVERFLOW;
1199
1200 /*
1201 * Find the end of the argument.
1202 */
1203 int cPar = 0;
1204 char chQuote = '\0';
1205 char *pszEnd = NULL;
1206 char *psz = pszArgs;
1207 char ch;
1208 bool fBinary = false;
1209 for (;;)
1210 {
1211 /*
1212 * Check for the end.
1213 */
1214 if ((ch = *psz) == '\0')
1215 {
1216 if (chQuote)
1217 return VERR_PARSE_UNBALANCED_QUOTE;
1218 if (cPar)
1219 return VERR_PARSE_UNBALANCED_PARENTHESIS;
1220 pszEnd = psz;
1221 break;
1222 }
1223 /*
1224 * When quoted we ignore everything but the quotation char.
1225 * We use the REXX way of escaping the quotation char, i.e. double occurence.
1226 */
1227 else if (ch == '\'' || ch == '"' || ch == '`')
1228 {
1229 if (chQuote)
1230 {
1231 /* end quote? */
1232 if (ch == chQuote)
1233 {
1234 if (psz[1] == ch)
1235 psz++; /* skip the escaped quote char */
1236 else
1237 chQuote = '\0'; /* end of quoted string. */
1238 }
1239 }
1240 else
1241 chQuote = ch; /* open new quote */
1242 }
1243 /*
1244 * Parenthesis can of course be nested.
1245 */
1246 else if (ch == '(')
1247 {
1248 cPar++;
1249 fBinary = false;
1250 }
1251 else if (ch == ')')
1252 {
1253 if (!cPar)
1254 return VERR_PARSE_UNBALANCED_PARENTHESIS;
1255 cPar--;
1256 fBinary = true;
1257 }
1258 else if (!chQuote && !cPar)
1259 {
1260 /*
1261 * Encountering blanks may mean the end of it all. A binary operator
1262 * will force continued parsing.
1263 */
1264 if (isblank(*psz))
1265 {
1266 pszEnd = psz++; /* just in case. */
1267 while (isblank(*psz))
1268 psz++;
1269 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, psz, fBinary, ' ');
1270 if (!pOp || pOp->fBinary != fBinary)
1271 break; /* the end. */
1272 psz += pOp->cchName;
1273 while (isblank(*psz)) /* skip blanks so we don't get here again */
1274 psz++;
1275 fBinary = false;
1276 continue;
1277 }
1278
1279 /*
1280 * Look for operators without a space up front.
1281 */
1282 if (dbgcIsOpChar(*psz))
1283 {
1284 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, psz, fBinary, ' ');
1285 if (pOp)
1286 {
1287 if (pOp->fBinary != fBinary)
1288 {
1289 pszEnd = psz;
1290 /** @todo this is a parsing error really. */
1291 break; /* the end. */
1292 }
1293 psz += pOp->cchName;
1294 while (isblank(*psz)) /* skip blanks so we don't get here again */
1295 psz++;
1296 fBinary = false;
1297 continue;
1298 }
1299 }
1300 fBinary = true;
1301 }
1302
1303 /* next char */
1304 psz++;
1305 }
1306 *pszEnd = '\0';
1307 /* (psz = next char to process) */
1308
1309 /*
1310 * Parse and evaluate the argument.
1311 */
1312 int rc = dbgcEvalSub(pDbgc, pszArgs, strlen(pszArgs), pArg);
1313 if (VBOX_FAILURE(rc))
1314 return rc;
1315
1316 /*
1317 * Next.
1318 */
1319 pArg++;
1320 (*pcArgs)++;
1321 pszArgs = psz;
1322 while (*pszArgs && isblank(*pszArgs))
1323 pszArgs++;
1324 } while (*pszArgs);
1325
1326 /*
1327 * Match the arguments.
1328 */
1329 return dbgcEvalSubMatchVars(pDbgc, pCmd->cArgsMin, pCmd->cArgsMax, pCmd->paArgDescs, pCmd->cArgDescs, pArg0, pArg - pArg0);
1330}
1331
1332
1333/**
1334 * Process one command.
1335 *
1336 * @returns VBox status code. Any error indicates the termination of the console session.
1337 * @param pDbgc Debugger console instance data.
1338 * @param pszCmd Pointer to the command.
1339 * @param cchCmd Length of the command.
1340 */
1341int dbgcProcessCommand(PDBGC pDbgc, char *pszCmd, size_t cchCmd)
1342{
1343 char *pszCmdInput = pszCmd;
1344
1345 /*
1346 * Skip blanks.
1347 */
1348 while (isblank(*pszCmd))
1349 pszCmd++, cchCmd--;
1350
1351 /* external command? */
1352 bool fExternal = *pszCmd == '.';
1353 if (fExternal)
1354 pszCmd++, cchCmd--;
1355
1356 /*
1357 * Find arguments.
1358 */
1359 char *pszArgs = pszCmd;
1360 while (isalnum(*pszArgs))
1361 pszArgs++;
1362 if (*pszArgs && (!isblank(*pszArgs) || pszArgs == pszCmd))
1363 {
1364 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "Syntax error in command '%s'!\n", pszCmdInput);
1365 return 0;
1366 }
1367
1368 /*
1369 * Find the command.
1370 */
1371 PCDBGCCMD pCmd = dbgcRoutineLookup(pDbgc, pszCmd, pszArgs - pszCmd, fExternal);
1372 if (!pCmd || (pCmd->fFlags & DBGCCMD_FLAGS_FUNCTION))
1373 return pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "Unknown command '%s'!\n", pszCmdInput);
1374
1375 /*
1376 * Parse arguments (if any).
1377 */
1378 unsigned cArgs;
1379 int rc = dbgcProcessArguments(pDbgc, pCmd, pszArgs, &pDbgc->aArgs[pDbgc->iArg], ELEMENTS(pDbgc->aArgs) - pDbgc->iArg, &cArgs);
1380
1381 /*
1382 * Execute the command.
1383 */
1384 if (!rc)
1385 {
1386 rc = pCmd->pfnHandler(pCmd, &pDbgc->CmdHlp, pDbgc->pVM, &pDbgc->aArgs[0], cArgs, NULL);
1387 }
1388 else
1389 {
1390 /* report parse / eval error. */
1391 switch (rc)
1392 {
1393 case VERR_PARSE_TOO_FEW_ARGUMENTS:
1394 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1395 "Syntax error: Too few arguments. Minimum is %d for command '%s'.\n", pCmd->cArgsMin, pCmd->pszCmd);
1396 break;
1397 case VERR_PARSE_TOO_MANY_ARGUMENTS:
1398 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1399 "Syntax error: Too many arguments. Maximum is %d for command '%s'.\n", pCmd->cArgsMax, pCmd->pszCmd);
1400 break;
1401 case VERR_PARSE_ARGUMENT_OVERFLOW:
1402 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1403 "Syntax error: Too many arguments.\n");
1404 break;
1405 case VERR_PARSE_UNBALANCED_QUOTE:
1406 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1407 "Syntax error: Unbalanced quote (argument %d).\n", cArgs);
1408 break;
1409 case VERR_PARSE_UNBALANCED_PARENTHESIS:
1410 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1411 "Syntax error: Unbalanced parenthesis (argument %d).\n", cArgs);
1412 break;
1413 case VERR_PARSE_EMPTY_ARGUMENT:
1414 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1415 "Syntax error: An argument or subargument contains nothing useful (argument %d).\n", cArgs);
1416 break;
1417 case VERR_PARSE_UNEXPECTED_OPERATOR:
1418 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1419 "Syntax error: Invalid operator usage (argument %d).\n", cArgs);
1420 break;
1421 case VERR_PARSE_INVALID_NUMBER:
1422 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1423 "Syntax error: Ivalid numeric value (argument %d). If a string was the intention, then quote it.\n", cArgs);
1424 break;
1425 case VERR_PARSE_NUMBER_TOO_BIG:
1426 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1427 "Error: Numeric overflow (argument %d).\n", cArgs);
1428 break;
1429 case VERR_PARSE_INVALID_OPERATION:
1430 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1431 "Error: Invalid operation attempted (argument %d).\n", cArgs);
1432 break;
1433 case VERR_PARSE_FUNCTION_NOT_FOUND:
1434 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1435 "Error: Function not found (argument %d).\n", cArgs);
1436 break;
1437 case VERR_PARSE_NOT_A_FUNCTION:
1438 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1439 "Error: The function specified is not a function (argument %d).\n", cArgs);
1440 break;
1441 case VERR_PARSE_NO_MEMORY:
1442 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1443 "Error: Out memory in the regular heap! Expect odd stuff to happen...\n", cArgs);
1444 break;
1445 case VERR_PARSE_INCORRECT_ARG_TYPE:
1446 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1447 "Error: Incorrect argument type (argument %d?).\n", cArgs);
1448 break;
1449 case VERR_PARSE_VARIABLE_NOT_FOUND:
1450 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1451 "Error: An undefined variable was referenced (argument %d).\n", cArgs);
1452 break;
1453 case VERR_PARSE_CONVERSION_FAILED:
1454 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1455 "Error: A conversion between two types failed (argument %d).\n", cArgs);
1456 break;
1457 case VERR_PARSE_NOT_IMPLEMENTED:
1458 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1459 "Error: You hit a debugger feature which isn't implemented yet (argument %d).\n", cArgs);
1460 break;
1461 case VERR_PARSE_BAD_RESULT_TYPE:
1462 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1463 "Error: Couldn't satisfy a request for a specific result type (argument %d). (Usually applies to symbols)\n", cArgs);
1464 break;
1465 case VERR_PARSE_WRITEONLY_SYMBOL:
1466 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1467 "Error: Cannot get symbol, it's set only (argument %d).\n", cArgs);
1468 break;
1469
1470 default:
1471 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1472 "Error: Unknown error %d!\n", rc);
1473 return rc;
1474 }
1475
1476 /*
1477 * Parse errors are non fatal.
1478 */
1479 if (rc >= VERR_PARSE_FIRST && rc < VERR_PARSE_LAST)
1480 rc = 0;
1481 }
1482
1483 return rc;
1484}
1485
1486
1487/**
1488 * Process all commands current in the buffer.
1489 *
1490 * @returns VBox status code. Any error indicates the termination of the console session.
1491 * @param pDbgc Debugger console instance data.
1492 */
1493static int dbgcProcessCommands(PDBGC pDbgc)
1494{
1495 int rc = 0;
1496 while (pDbgc->cInputLines)
1497 {
1498 /*
1499 * Empty the log buffer if we're hooking the log.
1500 */
1501 if (pDbgc->fLog)
1502 {
1503 rc = dbgcProcessLog(pDbgc);
1504 if (VBOX_FAILURE(rc))
1505 break;
1506 }
1507
1508 if (pDbgc->iRead == pDbgc->iWrite)
1509 {
1510 AssertMsgFailed(("The input buffer is empty while cInputLines=%d!\n", pDbgc->cInputLines));
1511 pDbgc->cInputLines = 0;
1512 return 0;
1513 }
1514
1515 /*
1516 * Copy the command to the parse buffer.
1517 */
1518 char ch;
1519 char *psz = &pDbgc->achInput[pDbgc->iRead];
1520 char *pszTrg = &pDbgc->achScratch[0];
1521 while ((*pszTrg = ch = *psz++) != ';' && ch != '\n' )
1522 {
1523 if (psz == &pDbgc->achInput[sizeof(pDbgc->achInput)])
1524 psz = &pDbgc->achInput[0];
1525
1526 if (psz == &pDbgc->achInput[pDbgc->iWrite])
1527 {
1528 AssertMsgFailed(("The buffer contains no commands while cInputLines=%d!\n", pDbgc->cInputLines));
1529 pDbgc->cInputLines = 0;
1530 return 0;
1531 }
1532
1533 pszTrg++;
1534 }
1535 *pszTrg = '\0';
1536
1537 /*
1538 * Advance the buffer.
1539 */
1540 pDbgc->iRead = psz - &pDbgc->achInput[0];
1541 if (ch == '\n')
1542 pDbgc->cInputLines--;
1543
1544 /*
1545 * Parse and execute this command.
1546 */
1547 pDbgc->pszScratch = psz;
1548 pDbgc->iArg = 0;
1549 rc = dbgcProcessCommand(pDbgc, &pDbgc->achScratch[0], psz - &pDbgc->achScratch[0] - 1);
1550 if (rc)
1551 break;
1552 }
1553
1554 return rc;
1555}
1556
1557
1558/**
1559 * Reads input, parses it and executes commands on '\n'.
1560 *
1561 * @returns VBox status.
1562 * @param pDbgc Debugger console instance data.
1563 */
1564static int dbgcProcessInput(PDBGC pDbgc)
1565{
1566 /*
1567 * We know there's input ready, so let's read it first.
1568 */
1569 int rc = dbgcInputRead(pDbgc);
1570 if (VBOX_FAILURE(rc))
1571 return rc;
1572
1573 /*
1574 * Now execute any ready commands.
1575 */
1576 if (pDbgc->cInputLines)
1577 {
1578 /** @todo this fReady stuff is broken. */
1579 pDbgc->fReady = false;
1580 rc = dbgcProcessCommands(pDbgc);
1581 if (VBOX_SUCCESS(rc) && rc != VWRN_DBGC_CMD_PENDING)
1582 pDbgc->fReady = true;
1583 if ( VBOX_SUCCESS(rc)
1584 && pDbgc->iRead == pDbgc->iWrite
1585 && pDbgc->fReady)
1586 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "VBoxDbg> ");
1587 }
1588
1589 return rc;
1590}
1591
1592
1593/**
1594 * Gets the event context identifier string.
1595 * @returns Read only string.
1596 * @param enmCtx The context.
1597 */
1598static const char *dbgcGetEventCtx(DBGFEVENTCTX enmCtx)
1599{
1600 switch (enmCtx)
1601 {
1602 case DBGFEVENTCTX_RAW: return "raw";
1603 case DBGFEVENTCTX_REM: return "rem";
1604 case DBGFEVENTCTX_HWACCL: return "hwaccl";
1605 case DBGFEVENTCTX_HYPER: return "hyper";
1606 case DBGFEVENTCTX_OTHER: return "other";
1607
1608 case DBGFEVENTCTX_INVALID: return "!Invalid Event Ctx!";
1609 default:
1610 AssertMsgFailed(("enmCtx=%d\n", enmCtx));
1611 return "!Unknown Event Ctx!";
1612 }
1613}
1614
1615
1616/**
1617 * Processes debugger events.
1618 *
1619 * @returns VBox status.
1620 * @param pDbgc DBGC Instance data.
1621 * @param pEvent Pointer to event data.
1622 */
1623static int dbgcProcessEvent(PDBGC pDbgc, PCDBGFEVENT pEvent)
1624{
1625 /*
1626 * Flush log first.
1627 */
1628 if (pDbgc->fLog)
1629 {
1630 int rc = dbgcProcessLog(pDbgc);
1631 if (VBOX_FAILURE(rc))
1632 return rc;
1633 }
1634
1635 /*
1636 * Process the event.
1637 */
1638 pDbgc->pszScratch = &pDbgc->achInput[0];
1639 pDbgc->iArg = 0;
1640 bool fPrintPrompt = true;
1641 int rc = VINF_SUCCESS;
1642 switch (pEvent->enmType)
1643 {
1644 /*
1645 * The first part is events we have initiated with commands.
1646 */
1647 case DBGFEVENT_HALT_DONE:
1648 {
1649 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: VM %p is halted! (%s)\n",
1650 pDbgc->pVM, dbgcGetEventCtx(pEvent->enmCtx));
1651 pDbgc->fRegCtxGuest = true; /* we're always in guest context when halted. */
1652 if (VBOX_SUCCESS(rc))
1653 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
1654 break;
1655 }
1656
1657
1658 /*
1659 * The second part is events which can occur at any time.
1660 */
1661 case DBGFEVENT_FATAL_ERROR:
1662 {
1663 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbf event: Fatal error! (%s)\n",
1664 dbgcGetEventCtx(pEvent->enmCtx));
1665 pDbgc->fRegCtxGuest = false; /* fatal errors are always in hypervisor. */
1666 if (VBOX_SUCCESS(rc))
1667 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
1668 break;
1669 }
1670
1671 case DBGFEVENT_BREAKPOINT:
1672 case DBGFEVENT_BREAKPOINT_HYPER:
1673 {
1674 bool fRegCtxGuest = pDbgc->fRegCtxGuest;
1675 pDbgc->fRegCtxGuest = pEvent->enmType == DBGFEVENT_BREAKPOINT;
1676
1677 rc = dbgcBpExec(pDbgc, pEvent->u.Bp.iBp);
1678 switch (rc)
1679 {
1680 case VERR_DBGC_BP_NOT_FOUND:
1681 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Unknown breakpoint %u! (%s)\n",
1682 pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));
1683 break;
1684
1685 case VINF_DBGC_BP_NO_COMMAND:
1686 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Breakpoint %u! (%s)\n",
1687 pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));
1688 break;
1689
1690 case VINF_BUFFER_OVERFLOW:
1691 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Breakpoint %u! Command too long to execute! (%s)\n",
1692 pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));
1693 break;
1694
1695 default:
1696 break;
1697 }
1698 if (VBOX_SUCCESS(rc) && DBGFR3IsHalted(pDbgc->pVM))
1699 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
1700 else
1701 pDbgc->fRegCtxGuest = fRegCtxGuest;
1702 break;
1703 }
1704
1705 case DBGFEVENT_STEPPED:
1706 case DBGFEVENT_STEPPED_HYPER:
1707 {
1708 pDbgc->fRegCtxGuest = pEvent->enmType == DBGFEVENT_STEPPED;
1709
1710 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Single step! (%s)\n", dbgcGetEventCtx(pEvent->enmCtx));
1711 if (VBOX_SUCCESS(rc))
1712 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
1713 break;
1714 }
1715
1716 case DBGFEVENT_ASSERTION_HYPER:
1717 {
1718 pDbgc->fRegCtxGuest = false;
1719
1720 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1721 "\ndbgf event: Hypervisor Assertion! (%s)\n"
1722 "%s"
1723 "%s"
1724 "\n",
1725 dbgcGetEventCtx(pEvent->enmCtx),
1726 pEvent->u.Assert.pszMsg1,
1727 pEvent->u.Assert.pszMsg2);
1728 if (VBOX_SUCCESS(rc))
1729 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
1730 break;
1731 }
1732
1733 case DBGFEVENT_DEV_STOP:
1734 {
1735 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1736 "\n"
1737 "dbgf event: DBGFSTOP (%s)\n"
1738 "File: %s\n"
1739 "Line: %d\n"
1740 "Function: %s\n",
1741 dbgcGetEventCtx(pEvent->enmCtx),
1742 pEvent->u.Src.pszFile,
1743 pEvent->u.Src.uLine,
1744 pEvent->u.Src.pszFunction);
1745 if (VBOX_SUCCESS(rc) && pEvent->u.Src.pszMessage && *pEvent->u.Src.pszMessage)
1746 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1747 "Message: %s\n",
1748 pEvent->u.Src.pszMessage);
1749 if (VBOX_SUCCESS(rc))
1750 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
1751 break;
1752 }
1753
1754
1755 case DBGFEVENT_INVALID_COMMAND:
1756 {
1757 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Invalid command event!\n");
1758 fPrintPrompt = !pDbgc->fReady;
1759 break;
1760 }
1761
1762 case DBGFEVENT_TERMINATING:
1763 {
1764 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\nVM is terminating!\n");
1765 rc = VERR_GENERAL_FAILURE;
1766 break;
1767 }
1768
1769
1770 default:
1771 {
1772 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Unknown event %d!\n", pEvent->enmType);
1773 fPrintPrompt = !pDbgc->fReady;
1774 break;
1775 }
1776 }
1777
1778 /*
1779 * Prompt, anyone?
1780 */
1781 if (fPrintPrompt && VBOX_SUCCESS(rc))
1782 {
1783 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "VBoxDbg> ");
1784 }
1785
1786 return rc;
1787}
1788
1789
1790
1791
1792
1793/**
1794 * Make a console instance.
1795 *
1796 * This will not return until either an 'exit' command is issued or a error code
1797 * indicating connection loss is encountered.
1798 *
1799 * @returns VINF_SUCCESS if console termination caused by the 'exit' command.
1800 * @returns The VBox status code causing the console termination.
1801 *
1802 * @param pVM VM Handle.
1803 * @param pBack Pointer to the backend structure. This must contain
1804 * a full set of function pointers to service the console.
1805 * @param fFlags Reserved, must be zero.
1806 * @remark A forced termination of the console is easiest done by forcing the
1807 * callbacks to return fatal failures.
1808 */
1809DBGDECL(int) DBGCCreate(PVM pVM, PDBGCBACK pBack, unsigned fFlags)
1810{
1811 /*
1812 * Validate input.
1813 */
1814 AssertReturn(VALID_PTR(pVM), VERR_INVALID_PARAMETER);
1815 AssertReturn(VALID_PTR(pBack), VERR_INVALID_PARAMETER);
1816 AssertMsgReturn(!fFlags, ("%#x", fFlags), VERR_INVALID_PARAMETER);
1817
1818 /*
1819 * Allocate and initialize instance data
1820 */
1821 PDBGC pDbgc = (PDBGC)RTMemAllocZ(sizeof(*pDbgc));
1822 if (!pDbgc)
1823 return VERR_NO_MEMORY;
1824
1825 dbgcInitCmdHlp(pDbgc);
1826 pDbgc->pBack = pBack;
1827 pDbgc->pVM = NULL;
1828 pDbgc->pszEmulation = "CodeView/WinDbg";
1829 pDbgc->paEmulationCmds = &g_aCmdsCodeView[0];
1830 pDbgc->cEmulationCmds = g_cCmdsCodeView;
1831 //pDbgc->fLog = false;
1832 pDbgc->fRegCtxGuest = true;
1833 pDbgc->fRegTerse = true;
1834 //pDbgc->DisasmPos = {0};
1835 //pDbgc->SourcePos = {0};
1836 //pDbgc->DumpPos = {0};
1837 //pDbgc->cbDumpElement = 0;
1838 //pDbgc->cVars = 0;
1839 //pDbgc->paVars = NULL;
1840 //pDbgc->pFirstBp = NULL;
1841 //pDbgc->uInputZero = 0;
1842 //pDbgc->iRead = 0;
1843 //pDbgc->iWrite = 0;
1844 //pDbgc->cInputLines = 0;
1845 //pDbgc->fInputOverflow = false;
1846 pDbgc->fReady = true;
1847 pDbgc->pszScratch = &pDbgc->achScratch[0];
1848 //pDbgc->iArg = 0;
1849 //pDbgc->rcOutput = 0;
1850
1851 dbgcInitOpCharBitMap();
1852
1853 /*
1854 * Print welcome message.
1855 */
1856 int rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1857 "Welcome to the VirtualBox Debugger!\n");
1858 if (VBOX_FAILURE(rc))
1859 goto l_failure;
1860
1861 /*
1862 * Attach to the VM.
1863 */
1864 rc = DBGFR3Attach(pVM);
1865 if (VBOX_FAILURE(rc))
1866 {
1867 rc = pDbgc->CmdHlp.pfnVBoxError(&pDbgc->CmdHlp, rc, "When trying to attach to VM %p\n", pDbgc->pVM);
1868 goto l_failure;
1869 }
1870 pDbgc->pVM = pVM;
1871
1872 /*
1873 * Print commandline and auto select result.
1874 */
1875 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1876 "Current VM is %08x\n" /** @todo get and print the VM name! */
1877 "VBoxDbg> ",
1878 pDbgc->pVM);
1879 if (VBOX_FAILURE(rc))
1880 goto l_failure;
1881
1882 /*
1883 * Main Debugger Loop.
1884 *
1885 * This loop will either block on waiting for input or on waiting on
1886 * debug events. If we're forwarding the log we cannot wait for long
1887 * before we must flush the log.
1888 */
1889 for (rc = 0;;)
1890 {
1891 if (pDbgc->pVM && DBGFR3CanWait(pDbgc->pVM))
1892 {
1893 /*
1894 * Wait for a debug event.
1895 */
1896 PCDBGFEVENT pEvent;
1897 rc = DBGFR3EventWait(pDbgc->pVM, pDbgc->fLog ? 1 : 32, &pEvent);
1898 if (VBOX_SUCCESS(rc))
1899 {
1900 rc = dbgcProcessEvent(pDbgc, pEvent);
1901 if (VBOX_FAILURE(rc))
1902 break;
1903 }
1904 else if (rc != VERR_TIMEOUT)
1905 break;
1906
1907 /*
1908 * Check for input.
1909 */
1910 if (pBack->pfnInput(pDbgc->pBack, 0))
1911 {
1912 rc = dbgcProcessInput(pDbgc);
1913 if (VBOX_FAILURE(rc))
1914 break;
1915 }
1916 }
1917 else
1918 {
1919 /*
1920 * Wait for input. If Logging is enabled we'll only wait very briefly.
1921 */
1922 if (pBack->pfnInput(pDbgc->pBack, pDbgc->fLog ? 1 : 1000))
1923 {
1924 rc = dbgcProcessInput(pDbgc);
1925 if (VBOX_FAILURE(rc))
1926 break;
1927 }
1928 }
1929
1930 /*
1931 * Forward log output.
1932 */
1933 if (pDbgc->fLog)
1934 {
1935 rc = dbgcProcessLog(pDbgc);
1936 if (VBOX_FAILURE(rc))
1937 break;
1938 }
1939 }
1940
1941
1942l_failure:
1943 /*
1944 * Cleanup console debugger session.
1945 */
1946 /* Disable log hook. */
1947 if (pDbgc->fLog)
1948 {
1949
1950 }
1951
1952 /* Detach from the VM. */
1953 if (pDbgc->pVM)
1954 DBGFR3Detach(pDbgc->pVM);
1955
1956 /* finally, free the instance memory. */
1957 RTMemFree(pDbgc);
1958
1959 return rc;
1960}
1961
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette