VirtualBox

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

Last change on this file since 35627 was 35627, checked in by vboxsync, 14 years ago

Debugger Console: cleaning up...

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