VirtualBox

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

Last change on this file since 5675 was 5675, checked in by vboxsync, 17 years ago

Split out the commands.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 91.9 KB
Line 
1/** $Id: DBGConsole.cpp 5675 2007-11-11 05:42:00Z 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.virtualbox.org. 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* Internal Functions *
132*******************************************************************************/
133static int dbgcEvalSub(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pResult);
134static int dbgcProcessCommand(PDBGC pDbgc, char *pszCmd, size_t cchCmd);
135
136
137/*******************************************************************************
138* Global Variables *
139*******************************************************************************/
140/** Bitmap where set bits indicates the characters the may start an operator name. */
141static uint32_t g_bmOperatorChars[256 / (4*8)];
142
143
144
145
146
147
148
149//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
150//
151//
152// C a l l b a c k H e l p e r s
153//
154//
155//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
156
157
158
159/**
160 * Command helper for writing text to the debug console.
161 *
162 * @returns VBox status.
163 * @param pCmdHlp Pointer to the command callback structure.
164 * @param pvBuf What to write.
165 * @param cbBuf Number of bytes to write.
166 * @param pcbWritten Where to store the number of bytes actually written.
167 * If NULL the entire buffer must be successfully written.
168 */
169static DECLCALLBACK(int) dbgcHlpWrite(PDBGCCMDHLP pCmdHlp, const void *pvBuf, size_t cbBuf, size_t *pcbWritten)
170{
171 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
172 return pDbgc->pBack->pfnWrite(pDbgc->pBack, pvBuf, cbBuf, pcbWritten);
173}
174
175
176/**
177 * Command helper for writing formatted text to the debug console.
178 *
179 * @returns VBox status.
180 * @param pCmdHlp Pointer to the command callback structure.
181 * @param pcb Where to store the number of bytes written.
182 * @param pszFormat The format string.
183 * This is using the log formatter, so it's format extensions can be used.
184 * @param ... Arguments specified in the format string.
185 */
186static DECLCALLBACK(int) dbgcHlpPrintf(PDBGCCMDHLP pCmdHlp, size_t *pcbWritten, const char *pszFormat, ...)
187{
188 /*
189 * Do the formatting and output.
190 */
191 va_list args;
192 va_start(args, pszFormat);
193 int rc = pCmdHlp->pfnPrintfV(pCmdHlp, pcbWritten, pszFormat, args);
194 va_end(args);
195
196 return rc;
197}
198
199/**
200 * Callback to format non-standard format specifiers.
201 *
202 * @returns The number of bytes formatted.
203 * @param pvArg Formatter argument.
204 * @param pfnOutput Pointer to output function.
205 * @param pvArgOutput Argument for the output function.
206 * @param ppszFormat Pointer to the format string pointer. Advance this till the char
207 * after the format specifier.
208 * @param pArgs Pointer to the argument list. Use this to fetch the arguments.
209 * @param cchWidth Format Width. -1 if not specified.
210 * @param cchPrecision Format Precision. -1 if not specified.
211 * @param fFlags Flags (RTSTR_NTFS_*).
212 * @param chArgSize The argument size specifier, 'l' or 'L'.
213 */
214static DECLCALLBACK(size_t) dbgcStringFormatter(void *pvArg, PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
215 const char **ppszFormat, va_list *pArgs, int cchWidth,
216 int cchPrecision, unsigned fFlags, char chArgSize)
217{
218 NOREF(cchWidth); NOREF(cchPrecision); NOREF(fFlags); NOREF(chArgSize); NOREF(pvArg);
219 if (**ppszFormat != 'D')
220 {
221 (*ppszFormat)++;
222 return 0;
223 }
224
225 (*ppszFormat)++;
226 switch (**ppszFormat)
227 {
228 /*
229 * Print variable without range.
230 * The argument is a const pointer to the variable.
231 */
232 case 'V':
233 {
234 (*ppszFormat)++;
235 PCDBGCVAR pVar = va_arg(*pArgs, PCDBGCVAR);
236 switch (pVar->enmType)
237 {
238 case DBGCVAR_TYPE_GC_FLAT:
239 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%%%VGv", pVar->u.GCFlat);
240 case DBGCVAR_TYPE_GC_FAR:
241 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%04x:%08x", pVar->u.GCFar.sel, pVar->u.GCFar.off);
242 case DBGCVAR_TYPE_GC_PHYS:
243 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%%%%%VGp", pVar->u.GCPhys);
244 case DBGCVAR_TYPE_HC_FLAT:
245 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%%#%VHv", (uintptr_t)pVar->u.pvHCFlat);
246 case DBGCVAR_TYPE_HC_FAR:
247 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "#%04x:%08x", pVar->u.HCFar.sel, pVar->u.HCFar.off);
248 case DBGCVAR_TYPE_HC_PHYS:
249 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "#%%%%%VHp", pVar->u.HCPhys);
250 case DBGCVAR_TYPE_STRING:
251 return pfnOutput(pvArgOutput, pVar->u.pszString, (size_t)pVar->u64Range);
252 case DBGCVAR_TYPE_NUMBER:
253 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%llx", pVar->u.u64Number);
254
255 case DBGCVAR_TYPE_UNKNOWN:
256 default:
257 return pfnOutput(pvArgOutput, "??", 2);
258 }
259 }
260
261 /*
262 * Print variable with range.
263 * The argument is a const pointer to the variable.
264 */
265 case 'v':
266 {
267 (*ppszFormat)++;
268 PCDBGCVAR pVar = va_arg(*pArgs, PCDBGCVAR);
269
270 char szRange[32];
271 switch (pVar->enmRangeType)
272 {
273 case DBGCVAR_RANGE_NONE:
274 szRange[0] = '\0';
275 break;
276 case DBGCVAR_RANGE_ELEMENTS:
277 RTStrPrintf(szRange, sizeof(szRange), " L %llx", pVar->u64Range);
278 break;
279 case DBGCVAR_RANGE_BYTES:
280 RTStrPrintf(szRange, sizeof(szRange), " LB %llx", pVar->u64Range);
281 break;
282 }
283
284 switch (pVar->enmType)
285 {
286 case DBGCVAR_TYPE_GC_FLAT:
287 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%%%VGv%s", pVar->u.GCFlat, szRange);
288 case DBGCVAR_TYPE_GC_FAR:
289 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%04x:%08x%s", pVar->u.GCFar.sel, pVar->u.GCFar.off, szRange);
290 case DBGCVAR_TYPE_GC_PHYS:
291 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%%%%%VGp%s", pVar->u.GCPhys, szRange);
292 case DBGCVAR_TYPE_HC_FLAT:
293 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%%#%VHv%s", (uintptr_t)pVar->u.pvHCFlat, szRange);
294 case DBGCVAR_TYPE_HC_FAR:
295 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "#%04x:%08x%s", pVar->u.HCFar.sel, pVar->u.HCFar.off, szRange);
296 case DBGCVAR_TYPE_HC_PHYS:
297 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "#%%%%%VHp%s", pVar->u.HCPhys, szRange);
298 case DBGCVAR_TYPE_STRING:
299 return pfnOutput(pvArgOutput, pVar->u.pszString, (size_t)pVar->u64Range);
300 case DBGCVAR_TYPE_NUMBER:
301 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%llx%s", pVar->u.u64Number, szRange);
302
303 case DBGCVAR_TYPE_UNKNOWN:
304 default:
305 return pfnOutput(pvArgOutput, "??", 2);
306 }
307 }
308
309 default:
310 AssertMsgFailed(("Invalid format type '%s'!\n", **ppszFormat));
311 return 0;
312 }
313}
314
315
316/**
317 * Output callback.
318 *
319 * @returns number of bytes written.
320 * @param pvArg User argument.
321 * @param pachChars Pointer to an array of utf-8 characters.
322 * @param cbChars Number of bytes in the character array pointed to by pachChars.
323 */
324static DECLCALLBACK(size_t) dbgcFormatOutput(void *pvArg, const char *pachChars, size_t cbChars)
325{
326 PDBGC pDbgc = (PDBGC)pvArg;
327 if (cbChars)
328 {
329 int rc = pDbgc->pBack->pfnWrite(pDbgc->pBack, pachChars, cbChars, NULL);
330 if (VBOX_FAILURE(rc))
331 {
332 pDbgc->rcOutput = rc;
333 cbChars = 0;
334 }
335 }
336
337 return cbChars;
338}
339
340
341
342/**
343 * Command helper for writing formatted text to the debug console.
344 *
345 * @returns VBox status.
346 * @param pCmdHlp Pointer to the command callback structure.
347 * @param pcb Where to store the number of bytes written.
348 * @param pszFormat The format string.
349 * This is using the log formatter, so it's format extensions can be used.
350 * @param args Arguments specified in the format string.
351 */
352static DECLCALLBACK(int) dbgcHlpPrintfV(PDBGCCMDHLP pCmdHlp, size_t *pcbWritten, const char *pszFormat, va_list args)
353{
354 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
355
356 /*
357 * Do the formatting and output.
358 */
359 pDbgc->rcOutput = 0;
360 size_t cb = RTStrFormatV(dbgcFormatOutput, pDbgc, dbgcStringFormatter, pDbgc, pszFormat, args);
361
362 if (pcbWritten)
363 *pcbWritten = cb;
364
365 return pDbgc->rcOutput;
366}
367
368
369/**
370 * Reports an error from a DBGF call.
371 *
372 * @returns VBox status code appropriate to return from a command.
373 * @param pCmdHlp Pointer to command helpers.
374 * @param rc The VBox status code returned by a DBGF call.
375 * @param pszFormat Format string for additional messages. Can be NULL.
376 * @param ... Format arguments, optional.
377 */
378static DECLCALLBACK(int) dbgcHlpVBoxErrorV(PDBGCCMDHLP pCmdHlp, int rc, const char *pszFormat, va_list args)
379{
380 switch (rc)
381 {
382 case VINF_SUCCESS:
383 break;
384
385 default:
386 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: %Vrc: %s", rc, pszFormat ? " " : "\n");
387 if (VBOX_SUCCESS(rc) && pszFormat)
388 rc = pCmdHlp->pfnPrintfV(pCmdHlp, NULL, pszFormat, args);
389 break;
390 }
391 return rc;
392}
393
394
395/**
396 * Reports an error from a DBGF call.
397 *
398 * @returns VBox status code appropriate to return from a command.
399 * @param pCmdHlp Pointer to command helpers.
400 * @param rc The VBox status code returned by a DBGF call.
401 * @param pszFormat Format string for additional messages. Can be NULL.
402 * @param ... Format arguments, optional.
403 */
404static DECLCALLBACK(int) dbgcHlpVBoxError(PDBGCCMDHLP pCmdHlp, int rc, const char *pszFormat, ...)
405{
406 va_list args;
407 va_start(args, pszFormat);
408 int rcRet = pCmdHlp->pfnVBoxErrorV(pCmdHlp, rc, pszFormat, args);
409 va_end(args);
410 return rcRet;
411}
412
413
414/**
415 * Command helper for reading memory specified by a DBGC variable.
416 *
417 * @returns VBox status code appropriate to return from a command.
418 * @param pCmdHlp Pointer to the command callback structure.
419 * @param pVM VM handle if GC or physical HC address.
420 * @param pvBuffer Where to store the read data.
421 * @param cbRead Number of bytes to read.
422 * @param pVarPointer DBGC variable specifying where to start reading.
423 * @param pcbRead Where to store the number of bytes actually read.
424 * This optional, but it's useful when read GC virtual memory where a
425 * page in the requested range might not be present.
426 * If not specified not-present failure or end of a HC physical page
427 * will cause failure.
428 */
429static DECLCALLBACK(int) dbgcHlpMemRead(PDBGCCMDHLP pCmdHlp, PVM pVM, void *pvBuffer, size_t cbRead, PCDBGCVAR pVarPointer, size_t *pcbRead)
430{
431 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
432
433 /*
434 * Dummy check.
435 */
436 if (cbRead == 0)
437 {
438 if (*pcbRead)
439 *pcbRead = 0;
440 return VINF_SUCCESS;
441 }
442
443 /*
444 * Convert Far addresses getting size and the correct base address.
445 * Getting and checking the size is what makes this messy and slow.
446 */
447 DBGCVAR Var = *pVarPointer;
448 switch (pVarPointer->enmType)
449 {
450 case DBGCVAR_TYPE_GC_FAR:
451 {
452 /* Use DBGFR3AddrFromSelOff for the conversion. */
453 Assert(pDbgc->pVM);
454 DBGFADDRESS Address;
455 int rc = DBGFR3AddrFromSelOff(pDbgc->pVM, &Address, Var.u.GCFar.sel, Var.u.GCFar.off);
456 if (VBOX_FAILURE(rc))
457 return rc;
458
459 /* don't bother with flat selectors (for now). */
460 if (!DBGFADDRESS_IS_FLAT(&Address))
461 {
462 SELMSELINFO SelInfo;
463 rc = SELMR3GetSelectorInfo(pDbgc->pVM, Address.Sel, &SelInfo);
464 if (VBOX_SUCCESS(rc))
465 {
466 RTGCUINTPTR cb; /* -1 byte */
467 if (SELMSelInfoIsExpandDown(&SelInfo))
468 {
469 if ( !SelInfo.Raw.Gen.u1Granularity
470 && Address.off > UINT16_C(0xffff))
471 return VERR_OUT_OF_SELECTOR_BOUNDS;
472 if (Address.off <= SelInfo.cbLimit)
473 return VERR_OUT_OF_SELECTOR_BOUNDS;
474 cb = (SelInfo.Raw.Gen.u1Granularity ? UINT32_C(0xffffffff) : UINT32_C(0xffff)) - Address.off;
475 }
476 else
477 {
478 if (Address.off > SelInfo.cbLimit)
479 return VERR_OUT_OF_SELECTOR_BOUNDS;
480 cb = SelInfo.cbLimit - Address.off;
481 }
482 if (cbRead - 1 > cb)
483 {
484 if (!pcbRead)
485 return VERR_OUT_OF_SELECTOR_BOUNDS;
486 cbRead = cb + 1;
487 }
488 }
489
490 Var.enmType = DBGCVAR_TYPE_GC_FLAT;
491 Var.u.GCFlat = Address.FlatPtr;
492 }
493 break;
494 }
495
496 case DBGCVAR_TYPE_GC_FLAT:
497 case DBGCVAR_TYPE_GC_PHYS:
498 case DBGCVAR_TYPE_HC_FLAT:
499 case DBGCVAR_TYPE_HC_PHYS:
500 break;
501
502 case DBGCVAR_TYPE_HC_FAR: /* not supported yet! */
503 default:
504 return VERR_NOT_IMPLEMENTED;
505 }
506
507
508
509 /*
510 * Copy page by page.
511 */
512 size_t cbLeft = cbRead;
513 for (;;)
514 {
515 /*
516 * Calc read size.
517 */
518 size_t cb = RT_MIN(PAGE_SIZE, cbLeft);
519 switch (pVarPointer->enmType)
520 {
521 case DBGCVAR_TYPE_GC_FLAT: cb = RT_MIN(cb, PAGE_SIZE - (Var.u.GCFlat & PAGE_OFFSET_MASK)); break;
522 case DBGCVAR_TYPE_GC_PHYS: cb = RT_MIN(cb, PAGE_SIZE - (Var.u.GCPhys & PAGE_OFFSET_MASK)); break;
523 case DBGCVAR_TYPE_HC_FLAT: cb = RT_MIN(cb, PAGE_SIZE - ((uintptr_t)Var.u.pvHCFlat & PAGE_OFFSET_MASK)); break;
524 case DBGCVAR_TYPE_HC_PHYS: cb = RT_MIN(cb, PAGE_SIZE - ((size_t)Var.u.HCPhys & PAGE_OFFSET_MASK)); break; /* size_t: MSC has braindead loss of data warnings! */
525 default: break;
526 }
527
528 /*
529 * Perform read.
530 */
531 int rc;
532 switch (Var.enmType)
533 {
534 case DBGCVAR_TYPE_GC_FLAT:
535 rc = MMR3ReadGCVirt(pVM, pvBuffer, Var.u.GCFlat, cb);
536 break;
537 case DBGCVAR_TYPE_GC_PHYS:
538 rc = PGMPhysReadGCPhys(pVM, pvBuffer, Var.u.GCPhys, cb);
539 break;
540
541 case DBGCVAR_TYPE_HC_PHYS:
542 case DBGCVAR_TYPE_HC_FLAT:
543 case DBGCVAR_TYPE_HC_FAR:
544 {
545 DBGCVAR Var2;
546 rc = dbgcOpAddrFlat(pDbgc, &Var, &Var2);
547 if (VBOX_SUCCESS(rc))
548 {
549 /** @todo protect this!!! */
550 memcpy(pvBuffer, Var2.u.pvHCFlat, cb);
551 rc = 0;
552 }
553 else
554 rc = VERR_INVALID_POINTER;
555 break;
556 }
557
558 default:
559 rc = VERR_PARSE_INCORRECT_ARG_TYPE;
560 }
561
562 /*
563 * Check for failure.
564 */
565 if (VBOX_FAILURE(rc))
566 {
567 if (pcbRead && (*pcbRead = cbRead - cbLeft) > 0)
568 return VINF_SUCCESS;
569 return rc;
570 }
571
572 /*
573 * Next.
574 */
575 cbLeft -= cb;
576 if (!cbLeft)
577 break;
578 pvBuffer = (char *)pvBuffer + cb;
579 rc = pCmdHlp->pfnEval(pCmdHlp, &Var, "%DV + %d", &Var, cb);
580 if (VBOX_FAILURE(rc))
581 {
582 if (pcbRead && (*pcbRead = cbRead - cbLeft) > 0)
583 return VINF_SUCCESS;
584 return rc;
585 }
586 }
587
588 /*
589 * Done
590 */
591 if (pcbRead)
592 *pcbRead = cbRead;
593 return 0;
594}
595
596/**
597 * Command helper for writing memory specified by a DBGC variable.
598 *
599 * @returns VBox status code appropriate to return from a command.
600 * @param pCmdHlp Pointer to the command callback structure.
601 * @param pVM VM handle if GC or physical HC address.
602 * @param pvBuffer What to write.
603 * @param cbWrite Number of bytes to write.
604 * @param pVarPointer DBGC variable specifying where to start reading.
605 * @param pcbWritten Where to store the number of bytes written.
606 * This is optional. If NULL be aware that some of the buffer
607 * might have been written to the specified address.
608 */
609static DECLCALLBACK(int) dbgcHlpMemWrite(PDBGCCMDHLP pCmdHlp, PVM pVM, const void *pvBuffer, size_t cbWrite, PCDBGCVAR pVarPointer, size_t *pcbWritten)
610{
611 NOREF(pCmdHlp); NOREF(pVM); NOREF(pvBuffer); NOREF(cbWrite); NOREF(pVarPointer); NOREF(pcbWritten);
612 return VERR_NOT_IMPLEMENTED;
613}
614
615
616/**
617 * Evaluates an expression.
618 * (Hopefully the parser and functions are fully reentrant.)
619 *
620 * @returns VBox status code appropriate to return from a command.
621 * @param pCmdHlp Pointer to the command callback structure.
622 * @param pResult Where to store the result.
623 * @param pszExpr The expression. Format string with the format DBGC extensions.
624 * @param ... Format arguments.
625 */
626static DECLCALLBACK(int) dbgcHlpEval(PDBGCCMDHLP pCmdHlp, PDBGCVAR pResult, const char *pszExpr, ...)
627{
628 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
629
630 /*
631 * Format the expression.
632 */
633 char szExprFormatted[2048];
634 va_list args;
635 va_start(args, pszExpr);
636 size_t cb = RTStrPrintfExV(dbgcStringFormatter, pDbgc, szExprFormatted, sizeof(szExprFormatted), pszExpr, args);
637 va_end(args);
638 /* ignore overflows. */
639
640 return dbgcEvalSub(pDbgc, &szExprFormatted[0], cb, pResult);
641}
642
643
644/**
645 * Executes one command expression.
646 * (Hopefully the parser and functions are fully reentrant.)
647 *
648 * @returns VBox status code appropriate to return from a command.
649 * @param pCmdHlp Pointer to the command callback structure.
650 * @param pszExpr The expression. Format string with the format DBGC extensions.
651 * @param ... Format arguments.
652 */
653static DECLCALLBACK(int) dbgcHlpExec(PDBGCCMDHLP pCmdHlp, const char *pszExpr, ...)
654{
655 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
656 /* Save the scratch state. */
657 char *pszScratch = pDbgc->pszScratch;
658 unsigned iArg = pDbgc->iArg;
659
660 /*
661 * Format the expression.
662 */
663 va_list args;
664 va_start(args, pszExpr);
665 size_t cbScratch = sizeof(pDbgc->achScratch) - (pDbgc->pszScratch - &pDbgc->achScratch[0]);
666 size_t cb = RTStrPrintfExV(dbgcStringFormatter, pDbgc, pDbgc->pszScratch, cbScratch, pszExpr, args);
667 va_end(args);
668 if (cb >= cbScratch)
669 return VERR_BUFFER_OVERFLOW;
670
671 /*
672 * Execute the command.
673 * We save and restore the arg index and scratch buffer pointer.
674 */
675 pDbgc->pszScratch = pDbgc->pszScratch + cb + 1;
676 int rc = dbgcProcessCommand(pDbgc, pszScratch, cb);
677
678 /* Restore the scratch state. */
679 pDbgc->iArg = iArg;
680 pDbgc->pszScratch = pszScratch;
681
682 return rc;
683}
684
685
686/**
687 * Converts a DBGC variable to a DBGF address structure.
688 *
689 * @returns VBox status code.
690 * @param pCmdHlp Pointer to the command callback structure.
691 * @param pVar The variable to convert.
692 * @param pAddress The target address.
693 */
694static DECLCALLBACK(int) dbgcHlpVarToDbgfAddr(PDBGCCMDHLP pCmdHlp, PCDBGCVAR pVar, PDBGFADDRESS pAddress)
695{
696 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
697 return dbgcVarToDbgfAddr(pDbgc, pVar, pAddress);
698}
699
700
701/**
702 * Converts a DBGC variable to a boolean.
703 *
704 * @returns VBox status code.
705 * @param pCmdHlp Pointer to the command callback structure.
706 * @param pVar The variable to convert.
707 * @param pf Where to store the boolean.
708 */
709static DECLCALLBACK(int) dbgcHlpVarToBool(PDBGCCMDHLP pCmdHlp, PCDBGCVAR pVar, bool *pf)
710{
711 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
712 NOREF(pDbgc);
713
714 switch (pVar->enmType)
715 {
716 case DBGCVAR_TYPE_STRING:
717 /** @todo add strcasecmp / stricmp wrappers to iprt/string.h. */
718 if ( !strcmp(pVar->u.pszString, "true")
719 || !strcmp(pVar->u.pszString, "True")
720 || !strcmp(pVar->u.pszString, "TRUE")
721 || !strcmp(pVar->u.pszString, "on")
722 || !strcmp(pVar->u.pszString, "On")
723 || !strcmp(pVar->u.pszString, "oN")
724 || !strcmp(pVar->u.pszString, "ON")
725 || !strcmp(pVar->u.pszString, "enabled")
726 || !strcmp(pVar->u.pszString, "Enabled")
727 || !strcmp(pVar->u.pszString, "DISABLED"))
728 {
729 *pf = true;
730 return VINF_SUCCESS;
731 }
732 if ( !strcmp(pVar->u.pszString, "false")
733 || !strcmp(pVar->u.pszString, "False")
734 || !strcmp(pVar->u.pszString, "FALSE")
735 || !strcmp(pVar->u.pszString, "off")
736 || !strcmp(pVar->u.pszString, "Off")
737 || !strcmp(pVar->u.pszString, "OFF")
738 || !strcmp(pVar->u.pszString, "disabled")
739 || !strcmp(pVar->u.pszString, "Disabled")
740 || !strcmp(pVar->u.pszString, "DISABLED"))
741 {
742 *pf = false;
743 return VINF_SUCCESS;
744 }
745 return VERR_PARSE_INCORRECT_ARG_TYPE; /** @todo better error code! */
746
747 case DBGCVAR_TYPE_GC_FLAT:
748 case DBGCVAR_TYPE_GC_PHYS:
749 case DBGCVAR_TYPE_HC_FLAT:
750 case DBGCVAR_TYPE_HC_PHYS:
751 case DBGCVAR_TYPE_NUMBER:
752 *pf = pVar->u.u64Number != 0;
753 return VINF_SUCCESS;
754
755 case DBGCVAR_TYPE_HC_FAR:
756 case DBGCVAR_TYPE_GC_FAR:
757 case DBGCVAR_TYPE_SYMBOL:
758 default:
759 return VERR_PARSE_INCORRECT_ARG_TYPE;
760 }
761}
762
763
764
765
766
767//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
768//
769//
770// V a r i a b l e M a n i p u l a t i o n
771//
772//
773//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
774
775
776
777/** @todo move me!*/
778void dbgcVarSetGCFlat(PDBGCVAR pVar, RTGCPTR GCFlat)
779{
780 if (pVar)
781 {
782 pVar->enmType = DBGCVAR_TYPE_GC_FLAT;
783 pVar->u.GCFlat = GCFlat;
784 pVar->enmRangeType = DBGCVAR_RANGE_NONE;
785 pVar->u64Range = 0;
786 }
787}
788
789
790/** @todo move me!*/
791void dbgcVarSetGCFlatByteRange(PDBGCVAR pVar, RTGCPTR GCFlat, uint64_t cb)
792{
793 if (pVar)
794 {
795 pVar->enmType = DBGCVAR_TYPE_GC_FLAT;
796 pVar->u.GCFlat = GCFlat;
797 pVar->enmRangeType = DBGCVAR_RANGE_BYTES;
798 pVar->u64Range = cb;
799 }
800}
801
802
803/** @todo move me!*/
804void dbgcVarSetVar(PDBGCVAR pVar, PCDBGCVAR pVar2)
805{
806 if (pVar)
807 {
808 if (pVar2)
809 *pVar = *pVar2;
810 else
811 {
812 pVar->enmType = DBGCVAR_TYPE_UNKNOWN;
813 memset(&pVar->u, 0, sizeof(pVar->u));
814 pVar->enmRangeType = DBGCVAR_RANGE_NONE;
815 pVar->u64Range = 0;
816 }
817 }
818}
819
820
821/** @todo move me!*/
822void dbgcVarSetByteRange(PDBGCVAR pVar, uint64_t cb)
823{
824 if (pVar)
825 {
826 pVar->enmRangeType = DBGCVAR_RANGE_BYTES;
827 pVar->u64Range = cb;
828 }
829}
830
831
832/** @todo move me!*/
833void dbgcVarSetNoRange(PDBGCVAR pVar)
834{
835 if (pVar)
836 {
837 pVar->enmRangeType = DBGCVAR_RANGE_NONE;
838 pVar->u64Range = 0;
839 }
840}
841
842
843/**
844 * Converts a DBGC variable to a DBGF address.
845 *
846 * @returns VBox status code.
847 * @param pDbgc The DBGC instance.
848 * @param pVar The variable.
849 * @param pAddress Where to store the address.
850 */
851int dbgcVarToDbgfAddr(PDBGC pDbgc, PCDBGCVAR pVar, PDBGFADDRESS pAddress)
852{
853 AssertReturn(pVar, VERR_INVALID_PARAMETER);
854 switch (pVar->enmType)
855 {
856 case DBGCVAR_TYPE_GC_FLAT:
857 DBGFR3AddrFromFlat(pDbgc->pVM, pAddress, pVar->u.GCFlat);
858 return VINF_SUCCESS;
859
860 case DBGCVAR_TYPE_NUMBER:
861 DBGFR3AddrFromFlat(pDbgc->pVM, pAddress, (RTGCUINTPTR)pVar->u.u64Number);
862 return VINF_SUCCESS;
863
864 case DBGCVAR_TYPE_GC_FAR:
865 return DBGFR3AddrFromSelOff(pDbgc->pVM, pAddress, pVar->u.GCFar.sel, pVar->u.GCFar.sel);
866
867 case DBGCVAR_TYPE_STRING:
868 case DBGCVAR_TYPE_SYMBOL:
869 {
870 DBGCVAR Var;
871 int rc = pDbgc->CmdHlp.pfnEval(&pDbgc->CmdHlp, &Var, "%%(%DV)", pVar);
872 if (VBOX_FAILURE(rc))
873 return rc;
874 return dbgcVarToDbgfAddr(pDbgc, &Var, pAddress);
875 }
876
877 case DBGCVAR_TYPE_GC_PHYS:
878 case DBGCVAR_TYPE_HC_FLAT:
879 case DBGCVAR_TYPE_HC_FAR:
880 case DBGCVAR_TYPE_HC_PHYS:
881 default:
882 return VERR_PARSE_CONVERSION_FAILED;
883 }
884}
885
886
887
888//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
889//
890//
891// B r e a k p o i n t M a n a g e m e n t
892//
893//
894//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
895
896
897/**
898 * Adds a breakpoint to the DBGC breakpoint list.
899 */
900int dbgcBpAdd(PDBGC pDbgc, RTUINT iBp, const char *pszCmd)
901{
902 /*
903 * Check if it already exists.
904 */
905 PDBGCBP pBp = dbgcBpGet(pDbgc, iBp);
906 if (pBp)
907 return VERR_DBGC_BP_EXISTS;
908
909 /*
910 * Add the breakpoint.
911 */
912 if (pszCmd)
913 pszCmd = RTStrStripL(pszCmd);
914 size_t cchCmd = pszCmd ? strlen(pszCmd) : 0;
915 pBp = (PDBGCBP)RTMemAlloc(RT_OFFSETOF(DBGCBP, szCmd[cchCmd + 1]));
916 if (!pBp)
917 return VERR_NO_MEMORY;
918 if (cchCmd)
919 memcpy(pBp->szCmd, pszCmd, cchCmd + 1);
920 else
921 pBp->szCmd[0] = '\0';
922 pBp->cchCmd = cchCmd;
923 pBp->iBp = iBp;
924 pBp->pNext = pDbgc->pFirstBp;
925 pDbgc->pFirstBp = pBp;
926
927 return VINF_SUCCESS;
928}
929
930/**
931 * Updates the a breakpoint.
932 *
933 * @returns VBox status code.
934 * @param pDbgc The DBGC instance.
935 * @param iBp The breakpoint to update.
936 * @param pszCmd The new command.
937 */
938int dbgcBpUpdate(PDBGC pDbgc, RTUINT iBp, const char *pszCmd)
939{
940 /*
941 * Find the breakpoint.
942 */
943 PDBGCBP pBp = dbgcBpGet(pDbgc, iBp);
944 if (!pBp)
945 return VERR_DBGC_BP_NOT_FOUND;
946
947 /*
948 * Do we need to reallocate?
949 */
950 if (pszCmd)
951 pszCmd = RTStrStripL(pszCmd);
952 if (!pszCmd || !*pszCmd)
953 pBp->szCmd[0] = '\0';
954 else
955 {
956 size_t cchCmd = strlen(pszCmd);
957 if (strlen(pBp->szCmd) >= cchCmd)
958 {
959 memcpy(pBp->szCmd, pszCmd, cchCmd + 1);
960 pBp->cchCmd = cchCmd;
961 }
962 else
963 {
964 /*
965 * Yes, let's do it the simple way...
966 */
967 int rc = dbgcBpDelete(pDbgc, iBp);
968 AssertRC(rc);
969 return dbgcBpAdd(pDbgc, iBp, pszCmd);
970 }
971 }
972 return VINF_SUCCESS;
973}
974
975
976/**
977 * Deletes a breakpoint.
978 *
979 * @returns VBox status code.
980 * @param pDbgc The DBGC instance.
981 * @param iBp The breakpoint to delete.
982 */
983int dbgcBpDelete(PDBGC pDbgc, RTUINT iBp)
984{
985 /*
986 * Search thru the list, when found unlink and free it.
987 */
988 PDBGCBP pBpPrev = NULL;
989 PDBGCBP pBp = pDbgc->pFirstBp;
990 for (; pBp; pBp = pBp->pNext)
991 {
992 if (pBp->iBp == iBp)
993 {
994 if (pBpPrev)
995 pBpPrev->pNext = pBp->pNext;
996 else
997 pDbgc->pFirstBp = pBp->pNext;
998 RTMemFree(pBp);
999 return VINF_SUCCESS;
1000 }
1001 pBpPrev = pBp;
1002 }
1003
1004 return VERR_DBGC_BP_NOT_FOUND;
1005}
1006
1007
1008/**
1009 * Get a breakpoint.
1010 *
1011 * @returns Pointer to the breakpoint.
1012 * @returns NULL if the breakpoint wasn't found.
1013 * @param pDbgc The DBGC instance.
1014 * @param iBp The breakpoint to get.
1015 */
1016PDBGCBP dbgcBpGet(PDBGC pDbgc, RTUINT iBp)
1017{
1018 /*
1019 * Enumerate the list.
1020 */
1021 PDBGCBP pBp = pDbgc->pFirstBp;
1022 for (; pBp; pBp = pBp->pNext)
1023 if (pBp->iBp == iBp)
1024 return pBp;
1025 return NULL;
1026}
1027
1028
1029/**
1030 * Executes the command of a breakpoint.
1031 *
1032 * @returns VINF_DBGC_BP_NO_COMMAND if there is no command associated with the breakpoint.
1033 * @returns VERR_DBGC_BP_NOT_FOUND if the breakpoint wasn't found.
1034 * @returns VERR_BUFFER_OVERFLOW if the is not enough space in the scratch buffer for the command.
1035 * @returns VBox status code from dbgcProcessCommand() other wise.
1036 * @param pDbgc The DBGC instance.
1037 * @param iBp The breakpoint to execute.
1038 */
1039int dbgcBpExec(PDBGC pDbgc, RTUINT iBp)
1040{
1041 /*
1042 * Find the breakpoint.
1043 */
1044 PDBGCBP pBp = dbgcBpGet(pDbgc, iBp);
1045 if (!pBp)
1046 return VERR_DBGC_BP_NOT_FOUND;
1047
1048 /*
1049 * Anything to do?
1050 */
1051 if (!pBp->cchCmd)
1052 return VINF_DBGC_BP_NO_COMMAND;
1053
1054 /*
1055 * Execute the command.
1056 * This means copying it to the scratch buffer and process it as if it
1057 * were user input. We must save and restore the state of the scratch buffer.
1058 */
1059 /* Save the scratch state. */
1060 char *pszScratch = pDbgc->pszScratch;
1061 unsigned iArg = pDbgc->iArg;
1062
1063 /* Copy the command to the scratch buffer. */
1064 size_t cbScratch = sizeof(pDbgc->achScratch) - (pDbgc->pszScratch - &pDbgc->achScratch[0]);
1065 if (pBp->cchCmd >= cbScratch)
1066 return VERR_BUFFER_OVERFLOW;
1067 memcpy(pDbgc->pszScratch, pBp->szCmd, pBp->cchCmd + 1);
1068
1069 /* Execute the command. */
1070 pDbgc->pszScratch = pDbgc->pszScratch + pBp->cchCmd + 1;
1071 int rc = dbgcProcessCommand(pDbgc, pszScratch, pBp->cchCmd);
1072
1073 /* Restore the scratch state. */
1074 pDbgc->iArg = iArg;
1075 pDbgc->pszScratch = pszScratch;
1076
1077 return rc;
1078}
1079
1080
1081
1082
1083
1084//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
1085//
1086//
1087// I n p u t , p a r s i n g a n d l o g g i n g
1088//
1089//
1090//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
1091
1092
1093
1094/**
1095 * Prints any log lines from the log buffer.
1096 *
1097 * The caller must not call function this unless pDbgc->fLog is set.
1098 *
1099 * @returns VBox status. (output related)
1100 * @param pDbgc Debugger console instance data.
1101 */
1102static int dbgcProcessLog(PDBGC pDbgc)
1103{
1104 /** @todo */
1105 NOREF(pDbgc);
1106 return 0;
1107}
1108
1109
1110
1111/**
1112 * Handle input buffer overflow.
1113 *
1114 * Will read any available input looking for a '\n' to reset the buffer on.
1115 *
1116 * @returns VBox status.
1117 * @param pDbgc Debugger console instance data.
1118 */
1119static int dbgcInputOverflow(PDBGC pDbgc)
1120{
1121 /*
1122 * Assert overflow status and reset the input buffer.
1123 */
1124 if (!pDbgc->fInputOverflow)
1125 {
1126 pDbgc->fInputOverflow = true;
1127 pDbgc->iRead = pDbgc->iWrite = 0;
1128 pDbgc->cInputLines = 0;
1129 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "Input overflow!!\n");
1130 }
1131
1132 /*
1133 * Eat input till no more or there is a '\n'.
1134 * When finding a '\n' we'll continue normal processing.
1135 */
1136 while (pDbgc->pBack->pfnInput(pDbgc->pBack, 0))
1137 {
1138 size_t cbRead;
1139 int rc = pDbgc->pBack->pfnRead(pDbgc->pBack, &pDbgc->achInput[0], sizeof(pDbgc->achInput) - 1, &cbRead);
1140 if (VBOX_FAILURE(rc))
1141 return rc;
1142 char *psz = (char *)memchr(&pDbgc->achInput[0], '\n', cbRead);
1143 if (psz)
1144 {
1145 pDbgc->fInputOverflow = false;
1146 pDbgc->iRead = psz - &pDbgc->achInput[0] + 1;
1147 pDbgc->iWrite = (unsigned)cbRead;
1148 pDbgc->cInputLines = 0;
1149 break;
1150 }
1151 }
1152
1153 return 0;
1154}
1155
1156
1157
1158/**
1159 * Read input and do some preprocessing.
1160 *
1161 * @returns VBox status.
1162 * In addition to the iWrite and achInput, cInputLines is maintained.
1163 * In case of an input overflow the fInputOverflow flag will be set.
1164 * @param pDbgc Debugger console instance data.
1165 */
1166static int dbgcInputRead(PDBGC pDbgc)
1167{
1168 /*
1169 * We have ready input.
1170 * Read it till we don't have any or we have a full input buffer.
1171 */
1172 int rc = 0;
1173 do
1174 {
1175 /*
1176 * More available buffer space?
1177 */
1178 size_t cbLeft;
1179 if (pDbgc->iWrite > pDbgc->iRead)
1180 cbLeft = sizeof(pDbgc->achInput) - pDbgc->iWrite - (pDbgc->iRead == 0);
1181 else
1182 cbLeft = pDbgc->iRead - pDbgc->iWrite - 1;
1183 if (!cbLeft)
1184 {
1185 /* overflow? */
1186 if (!pDbgc->cInputLines)
1187 rc = dbgcInputOverflow(pDbgc);
1188 break;
1189 }
1190
1191 /*
1192 * Read one char and interpret it.
1193 */
1194 char achRead[128];
1195 size_t cbRead;
1196 rc = pDbgc->pBack->pfnRead(pDbgc->pBack, &achRead[0], RT_MIN(cbLeft, sizeof(achRead)), &cbRead);
1197 if (VBOX_FAILURE(rc))
1198 return rc;
1199 char *psz = &achRead[0];
1200 while (cbRead-- > 0)
1201 {
1202 char ch = *psz++;
1203 switch (ch)
1204 {
1205 /*
1206 * Ignore.
1207 */
1208 case '\0':
1209 case '\r':
1210 case '\a':
1211 break;
1212
1213 /*
1214 * Backspace.
1215 */
1216 case '\b':
1217 Log2(("DBGC: backspace\n"));
1218 if (pDbgc->iRead != pDbgc->iWrite)
1219 {
1220 unsigned iWriteUndo = pDbgc->iWrite;
1221 if (pDbgc->iWrite)
1222 pDbgc->iWrite--;
1223 else
1224 pDbgc->iWrite = sizeof(pDbgc->achInput) - 1;
1225
1226 if (pDbgc->achInput[pDbgc->iWrite] == '\n')
1227 pDbgc->iWrite = iWriteUndo;
1228 }
1229 break;
1230
1231 /*
1232 * Add char to buffer.
1233 */
1234 case '\t':
1235 case '\n':
1236 case ';':
1237 switch (ch)
1238 {
1239 case '\t': ch = ' '; break;
1240 case '\n': pDbgc->cInputLines++; break;
1241 }
1242 default:
1243 Log2(("DBGC: ch=%02x\n", (unsigned char)ch));
1244 pDbgc->achInput[pDbgc->iWrite] = ch;
1245 if (++pDbgc->iWrite >= sizeof(pDbgc->achInput))
1246 pDbgc->iWrite = 0;
1247 break;
1248 }
1249 }
1250
1251 /* Terminate it to make it easier to read in the debugger. */
1252 pDbgc->achInput[pDbgc->iWrite] = '\0';
1253 } while (pDbgc->pBack->pfnInput(pDbgc->pBack, 0));
1254
1255 return rc;
1256}
1257
1258
1259
1260/**
1261 * Resolves a symbol (or tries to do so at least).
1262 *
1263 * @returns 0 on success.
1264 * @returns VBox status on failure.
1265 * @param pDbgc The debug console instance.
1266 * @param pszSymbol The symbol name.
1267 * @param enmType The result type.
1268 * @param pResult Where to store the result.
1269 */
1270int dbgcSymbolGet(PDBGC pDbgc, const char *pszSymbol, DBGCVARTYPE enmType, PDBGCVAR pResult)
1271{
1272 /*
1273 * Builtin?
1274 */
1275 PCDBGCSYM pSymDesc = dbgcLookupRegisterSymbol(pDbgc, pszSymbol);
1276 if (pSymDesc)
1277 {
1278 if (!pSymDesc->pfnGet)
1279 return VERR_PARSE_WRITEONLY_SYMBOL;
1280 return pSymDesc->pfnGet(pSymDesc, &pDbgc->CmdHlp, enmType, pResult);
1281 }
1282
1283
1284 /*
1285 * Ask PDM.
1286 */
1287 /** @todo resolve symbols using PDM. */
1288
1289
1290 /*
1291 * Ask the debug info manager.
1292 */
1293 DBGFSYMBOL Symbol;
1294 int rc = DBGFR3SymbolByName(pDbgc->pVM, pszSymbol, &Symbol);
1295 if (VBOX_SUCCESS(rc))
1296 {
1297 /*
1298 * Default return is a flat gc address.
1299 */
1300 memset(pResult, 0, sizeof(*pResult));
1301 pResult->enmRangeType = Symbol.cb ? DBGCVAR_RANGE_BYTES : DBGCVAR_RANGE_NONE;
1302 pResult->u64Range = Symbol.cb;
1303 pResult->enmType = DBGCVAR_TYPE_GC_FLAT;
1304 pResult->u.GCFlat = Symbol.Value;
1305 DBGCVAR VarTmp;
1306 switch (enmType)
1307 {
1308 /* nothing to do. */
1309 case DBGCVAR_TYPE_GC_FLAT:
1310 case DBGCVAR_TYPE_GC_FAR:
1311 case DBGCVAR_TYPE_ANY:
1312 return VINF_SUCCESS;
1313
1314 /* simply make it numeric. */
1315 case DBGCVAR_TYPE_NUMBER:
1316 pResult->enmType = DBGCVAR_TYPE_NUMBER;
1317 pResult->u.u64Number = Symbol.Value;
1318 return VINF_SUCCESS;
1319
1320 /* cast it. */
1321
1322 case DBGCVAR_TYPE_GC_PHYS:
1323 VarTmp = *pResult;
1324 return dbgcOpAddrPhys(pDbgc, &VarTmp, pResult);
1325
1326 case DBGCVAR_TYPE_HC_FAR:
1327 case DBGCVAR_TYPE_HC_FLAT:
1328 VarTmp = *pResult;
1329 return dbgcOpAddrHost(pDbgc, &VarTmp, pResult);
1330
1331 case DBGCVAR_TYPE_HC_PHYS:
1332 VarTmp = *pResult;
1333 return dbgcOpAddrHostPhys(pDbgc, &VarTmp, pResult);
1334
1335 default:
1336 AssertMsgFailed(("Internal error enmType=%d\n", enmType));
1337 return VERR_INVALID_PARAMETER;
1338 }
1339 }
1340
1341 return VERR_PARSE_NOT_IMPLEMENTED;
1342}
1343
1344
1345/**
1346 * Initalizes g_bmOperatorChars.
1347 */
1348static void dbgcInitOpCharBitMap(void)
1349{
1350 memset(g_bmOperatorChars, 0, sizeof(g_bmOperatorChars));
1351 for (unsigned iOp = 0; iOp < g_cOps; iOp++)
1352 ASMBitSet(&g_bmOperatorChars[0], (uint8_t)g_aOps[iOp].szName[0]);
1353}
1354
1355
1356/**
1357 * Checks whether the character may be the start of an operator.
1358 *
1359 * @returns true/false.
1360 * @param ch The character.
1361 */
1362DECLINLINE(bool) dbgcIsOpChar(char ch)
1363{
1364 return ASMBitTest(&g_bmOperatorChars[0], (uint8_t)ch);
1365}
1366
1367
1368static int dbgcEvalSubString(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pArg)
1369{
1370 Log2(("dbgcEvalSubString: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));
1371
1372 /*
1373 * Removing any quoting and escapings.
1374 */
1375 char ch = *pszExpr;
1376 if (ch == '"' || ch == '\'' || ch == '`')
1377 {
1378 if (pszExpr[--cchExpr] != ch)
1379 return VERR_PARSE_UNBALANCED_QUOTE;
1380 cchExpr--;
1381 pszExpr++;
1382
1383 /** @todo string unescaping. */
1384 }
1385 pszExpr[cchExpr] = '\0';
1386
1387 /*
1388 * Make the argument.
1389 */
1390 pArg->pDesc = NULL;
1391 pArg->pNext = NULL;
1392 pArg->enmType = DBGCVAR_TYPE_STRING;
1393 pArg->u.pszString = pszExpr;
1394 pArg->enmRangeType = DBGCVAR_RANGE_BYTES;
1395 pArg->u64Range = cchExpr;
1396
1397 NOREF(pDbgc);
1398 return 0;
1399}
1400
1401
1402static int dbgcEvalSubNum(char *pszExpr, unsigned uBase, PDBGCVAR pArg)
1403{
1404 Log2(("dbgcEvalSubNum: uBase=%d pszExpr=%s\n", uBase, pszExpr));
1405 /*
1406 * Convert to number.
1407 */
1408 uint64_t u64 = 0;
1409 char ch;
1410 while ((ch = *pszExpr) != '\0')
1411 {
1412 uint64_t u64Prev = u64;
1413 unsigned u = ch - '0';
1414 if (u < 10 && u < uBase)
1415 u64 = u64 * uBase + u;
1416 else if (ch >= 'a' && (u = ch - ('a' - 10)) < uBase)
1417 u64 = u64 * uBase + u;
1418 else if (ch >= 'A' && (u = ch - ('A' - 10)) < uBase)
1419 u64 = u64 * uBase + u;
1420 else
1421 return VERR_PARSE_INVALID_NUMBER;
1422
1423 /* check for overflow - ARG!!! How to detect overflow correctly!?!?!? */
1424 if (u64Prev != u64 / uBase)
1425 return VERR_PARSE_NUMBER_TOO_BIG;
1426
1427 /* next */
1428 pszExpr++;
1429 }
1430
1431 /*
1432 * Initialize the argument.
1433 */
1434 pArg->pDesc = NULL;
1435 pArg->pNext = NULL;
1436 pArg->enmType = DBGCVAR_TYPE_NUMBER;
1437 pArg->u.u64Number = u64;
1438 pArg->enmRangeType = DBGCVAR_RANGE_NONE;
1439 pArg->u64Range = 0;
1440
1441 return 0;
1442}
1443
1444
1445/**
1446 * Match variable and variable descriptor, promoting the variable if necessary.
1447 *
1448 * @returns VBox status code.
1449 * @param pDbgc Debug console instanace.
1450 * @param pVar Variable.
1451 * @param pVarDesc Variable descriptor.
1452 */
1453static int dbgcEvalSubMatchVar(PDBGC pDbgc, PDBGCVAR pVar, PCDBGCVARDESC pVarDesc)
1454{
1455 /*
1456 * (If match or promoted to match, return, else break.)
1457 */
1458 switch (pVarDesc->enmCategory)
1459 {
1460 /*
1461 * Anything goes
1462 */
1463 case DBGCVAR_CAT_ANY:
1464 return VINF_SUCCESS;
1465
1466 /*
1467 * Pointer with and without range.
1468 * We can try resolve strings and symbols as symbols and
1469 * promote numbers to flat GC pointers.
1470 */
1471 case DBGCVAR_CAT_POINTER_NO_RANGE:
1472 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
1473 return VERR_PARSE_NO_RANGE_ALLOWED;
1474 /* fallthru */
1475 case DBGCVAR_CAT_POINTER:
1476 switch (pVar->enmType)
1477 {
1478 case DBGCVAR_TYPE_GC_FLAT:
1479 case DBGCVAR_TYPE_GC_FAR:
1480 case DBGCVAR_TYPE_GC_PHYS:
1481 case DBGCVAR_TYPE_HC_FLAT:
1482 case DBGCVAR_TYPE_HC_FAR:
1483 case DBGCVAR_TYPE_HC_PHYS:
1484 return VINF_SUCCESS;
1485
1486 case DBGCVAR_TYPE_SYMBOL:
1487 case DBGCVAR_TYPE_STRING:
1488 {
1489 DBGCVAR Var;
1490 int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_GC_FLAT, &Var);
1491 if (VBOX_SUCCESS(rc))
1492 {
1493 /* deal with range */
1494 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
1495 {
1496 Var.enmRangeType = pVar->enmRangeType;
1497 Var.u64Range = pVar->u64Range;
1498 }
1499 else if (pVarDesc->enmCategory == DBGCVAR_CAT_POINTER_NO_RANGE)
1500 Var.enmRangeType = DBGCVAR_RANGE_NONE;
1501 *pVar = Var;
1502 return rc;
1503 }
1504 break;
1505 }
1506
1507 case DBGCVAR_TYPE_NUMBER:
1508 {
1509 RTGCPTR GCPtr = (RTGCPTR)pVar->u.u64Number;
1510 pVar->enmType = DBGCVAR_TYPE_GC_FLAT;
1511 pVar->u.GCFlat = GCPtr;
1512 return VINF_SUCCESS;
1513 }
1514
1515 default:
1516 break;
1517 }
1518 break;
1519
1520 /*
1521 * GC pointer with and without range.
1522 * We can try resolve strings and symbols as symbols and
1523 * promote numbers to flat GC pointers.
1524 */
1525 case DBGCVAR_CAT_GC_POINTER_NO_RANGE:
1526 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
1527 return VERR_PARSE_NO_RANGE_ALLOWED;
1528 /* fallthru */
1529 case DBGCVAR_CAT_GC_POINTER:
1530 switch (pVar->enmType)
1531 {
1532 case DBGCVAR_TYPE_GC_FLAT:
1533 case DBGCVAR_TYPE_GC_FAR:
1534 case DBGCVAR_TYPE_GC_PHYS:
1535 return VINF_SUCCESS;
1536
1537 case DBGCVAR_TYPE_HC_FLAT:
1538 case DBGCVAR_TYPE_HC_FAR:
1539 case DBGCVAR_TYPE_HC_PHYS:
1540 return VERR_PARSE_CONVERSION_FAILED;
1541
1542 case DBGCVAR_TYPE_SYMBOL:
1543 case DBGCVAR_TYPE_STRING:
1544 {
1545 DBGCVAR Var;
1546 int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_GC_FLAT, &Var);
1547 if (VBOX_SUCCESS(rc))
1548 {
1549 /* deal with range */
1550 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
1551 {
1552 Var.enmRangeType = pVar->enmRangeType;
1553 Var.u64Range = pVar->u64Range;
1554 }
1555 else if (pVarDesc->enmCategory == DBGCVAR_CAT_POINTER_NO_RANGE)
1556 Var.enmRangeType = DBGCVAR_RANGE_NONE;
1557 *pVar = Var;
1558 return rc;
1559 }
1560 break;
1561 }
1562
1563 case DBGCVAR_TYPE_NUMBER:
1564 {
1565 RTGCPTR GCPtr = (RTGCPTR)pVar->u.u64Number;
1566 pVar->enmType = DBGCVAR_TYPE_GC_FLAT;
1567 pVar->u.GCFlat = GCPtr;
1568 return VINF_SUCCESS;
1569 }
1570
1571 default:
1572 break;
1573 }
1574 break;
1575
1576 /*
1577 * Number with or without a range.
1578 * Numbers can be resolved from symbols, but we cannot demote a pointer
1579 * to a number.
1580 */
1581 case DBGCVAR_CAT_NUMBER_NO_RANGE:
1582 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
1583 return VERR_PARSE_NO_RANGE_ALLOWED;
1584 /* fallthru */
1585 case DBGCVAR_CAT_NUMBER:
1586 switch (pVar->enmType)
1587 {
1588 case DBGCVAR_TYPE_NUMBER:
1589 return VINF_SUCCESS;
1590
1591 case DBGCVAR_TYPE_SYMBOL:
1592 case DBGCVAR_TYPE_STRING:
1593 {
1594 DBGCVAR Var;
1595 int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_NUMBER, &Var);
1596 if (VBOX_SUCCESS(rc))
1597 {
1598 *pVar = Var;
1599 return rc;
1600 }
1601 break;
1602 }
1603 default:
1604 break;
1605 }
1606 break;
1607
1608 /*
1609 * Strings can easily be made from symbols (and of course strings).
1610 * We could consider reformatting the addresses and numbers into strings later...
1611 */
1612 case DBGCVAR_CAT_STRING:
1613 switch (pVar->enmType)
1614 {
1615 case DBGCVAR_TYPE_SYMBOL:
1616 pVar->enmType = DBGCVAR_TYPE_STRING;
1617 /* fallthru */
1618 case DBGCVAR_TYPE_STRING:
1619 return VINF_SUCCESS;
1620 default:
1621 break;
1622 }
1623 break;
1624
1625 /*
1626 * Symol is pretty much the same thing as a string (at least until we actually implement it).
1627 */
1628 case DBGCVAR_CAT_SYMBOL:
1629 switch (pVar->enmType)
1630 {
1631 case DBGCVAR_TYPE_STRING:
1632 pVar->enmType = DBGCVAR_TYPE_SYMBOL;
1633 /* fallthru */
1634 case DBGCVAR_TYPE_SYMBOL:
1635 return VINF_SUCCESS;
1636 default:
1637 break;
1638 }
1639 break;
1640
1641 /*
1642 * Anything else is illegal.
1643 */
1644 default:
1645 AssertMsgFailed(("enmCategory=%d\n", pVar->enmType));
1646 break;
1647 }
1648
1649 return VERR_PARSE_NO_ARGUMENT_MATCH;
1650}
1651
1652
1653/**
1654 * Matches a set of variables with a description set.
1655 *
1656 * This is typically used for routine arguments before a call. The effects in
1657 * addition to the validation, is that some variables might be propagated to
1658 * other types in order to match the description. The following transformations
1659 * are supported:
1660 * - String reinterpreted as a symbol and resolved to a number or pointer.
1661 * - Number to a pointer.
1662 * - Pointer to a number.
1663 * @returns 0 on success with paVars.
1664 * @returns VBox error code for match errors.
1665 */
1666static int dbgcEvalSubMatchVars(PDBGC pDbgc, unsigned cVarsMin, unsigned cVarsMax,
1667 PCDBGCVARDESC paVarDescs, unsigned cVarDescs,
1668 PDBGCVAR paVars, unsigned cVars)
1669{
1670 /*
1671 * Just do basic min / max checks first.
1672 */
1673 if (cVars < cVarsMin)
1674 return VERR_PARSE_TOO_FEW_ARGUMENTS;
1675 if (cVars > cVarsMax)
1676 return VERR_PARSE_TOO_MANY_ARGUMENTS;
1677
1678 /*
1679 * Match the descriptors and actual variables.
1680 */
1681 PCDBGCVARDESC pPrevDesc = NULL;
1682 unsigned cCurDesc = 0;
1683 unsigned iVar = 0;
1684 unsigned iVarDesc = 0;
1685 while (iVar < cVars)
1686 {
1687 /* walk the descriptors */
1688 if (iVarDesc >= cVarDescs)
1689 return VERR_PARSE_TOO_MANY_ARGUMENTS;
1690 if ( ( paVarDescs[iVarDesc].fFlags & DBGCVD_FLAGS_DEP_PREV
1691 && &paVarDescs[iVarDesc - 1] != pPrevDesc)
1692 || cCurDesc >= paVarDescs[iVarDesc].cTimesMax)
1693 {
1694 iVarDesc++;
1695 if (iVarDesc >= cVarDescs)
1696 return VERR_PARSE_TOO_MANY_ARGUMENTS;
1697 cCurDesc = 0;
1698 }
1699
1700 /*
1701 * Skip thru optional arguments until we find something which matches
1702 * or can easily be promoted to what the descriptor want.
1703 */
1704 for (;;)
1705 {
1706 int rc = dbgcEvalSubMatchVar(pDbgc, &paVars[iVar], &paVarDescs[iVarDesc]);
1707 if (VBOX_SUCCESS(rc))
1708 {
1709 paVars[iVar].pDesc = &paVarDescs[iVarDesc];
1710 cCurDesc++;
1711 break;
1712 }
1713
1714 /* can we advance? */
1715 if (paVarDescs[iVarDesc].cTimesMin > cCurDesc)
1716 return VERR_PARSE_ARGUMENT_TYPE_MISMATCH;
1717 if (++iVarDesc >= cVarDescs)
1718 return VERR_PARSE_ARGUMENT_TYPE_MISMATCH;
1719 cCurDesc = 0;
1720 }
1721
1722 /* next var */
1723 iVar++;
1724 }
1725
1726 /*
1727 * Check that the rest of the descriptors are optional.
1728 */
1729 while (iVarDesc < cVarDescs)
1730 {
1731 if (paVarDescs[iVarDesc].cTimesMin > cCurDesc)
1732 return VERR_PARSE_TOO_FEW_ARGUMENTS;
1733 cCurDesc = 0;
1734
1735 /* next */
1736 iVarDesc++;
1737 }
1738
1739 return 0;
1740}
1741
1742
1743/**
1744 * Evaluates one argument with respect to unary operators.
1745 *
1746 * @returns 0 on success. pResult contains the result.
1747 * @returns VBox error code on parse or other evaluation error.
1748 *
1749 * @param pDbgc Debugger console instance data.
1750 * @param pszExpr The expression string.
1751 * @param pResult Where to store the result of the expression evaluation.
1752 */
1753static int dbgcEvalSubUnary(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pResult)
1754{
1755 Log2(("dbgcEvalSubUnary: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));
1756
1757 /*
1758 * The state of the expression is now such that it will start by zero or more
1759 * unary operators and being followed by an expression of some kind.
1760 * The expression is either plain or in parenthesis.
1761 *
1762 * Being in a lazy, recursive mode today, the parsing is done as simple as possible. :-)
1763 * ASSUME: unary operators are all of equal precedence.
1764 */
1765 int rc = 0;
1766 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, pszExpr, false, ' ');
1767 if (pOp)
1768 {
1769 /* binary operators means syntax error. */
1770 if (pOp->fBinary)
1771 return VERR_PARSE_UNEXPECTED_OPERATOR;
1772
1773 /*
1774 * If the next expression (the one following the unary operator) is in a
1775 * parenthesis a full eval is needed. If not the unary eval will suffice.
1776 */
1777 /* calc and strip next expr. */
1778 char *pszExpr2 = pszExpr + pOp->cchName;
1779 while (isblank(*pszExpr2))
1780 pszExpr2++;
1781
1782 if (!*pszExpr2)
1783 rc = VERR_PARSE_EMPTY_ARGUMENT;
1784 else
1785 {
1786 DBGCVAR Arg;
1787 if (*pszExpr2 == '(')
1788 rc = dbgcEvalSub(pDbgc, pszExpr2, cchExpr - (pszExpr2 - pszExpr), &Arg);
1789 else
1790 rc = dbgcEvalSubUnary(pDbgc, pszExpr2, cchExpr - (pszExpr2 - pszExpr), &Arg);
1791 if (VBOX_SUCCESS(rc))
1792 rc = pOp->pfnHandlerUnary(pDbgc, &Arg, pResult);
1793 }
1794 }
1795 else
1796 {
1797 /*
1798 * Didn't find any operators, so it we have to check if this can be an
1799 * function call before assuming numeric or string expression.
1800 *
1801 * (ASSUMPTIONS:)
1802 * A function name only contains alphanumerical chars and it can not start
1803 * with a numerical character.
1804 * Immediately following the name is a parenthesis which must over
1805 * the remaining part of the expression.
1806 */
1807 bool fExternal = *pszExpr == '.';
1808 char *pszFun = fExternal ? pszExpr + 1 : pszExpr;
1809 char *pszFunEnd = NULL;
1810 if (pszExpr[cchExpr - 1] == ')' && isalpha(*pszFun))
1811 {
1812 pszFunEnd = pszExpr + 1;
1813 while (*pszFunEnd != '(' && isalnum(*pszFunEnd))
1814 pszFunEnd++;
1815 if (*pszFunEnd != '(')
1816 pszFunEnd = NULL;
1817 }
1818
1819 if (pszFunEnd)
1820 {
1821 /*
1822 * Ok, it's a function call.
1823 */
1824 if (fExternal)
1825 pszExpr++, cchExpr--;
1826 PCDBGCCMD pFun = dbgcRoutineLookup(pDbgc, pszExpr, pszFunEnd - pszExpr, fExternal);
1827 if (!pFun)
1828 return VERR_PARSE_FUNCTION_NOT_FOUND;
1829 if (!pFun->pResultDesc)
1830 return VERR_PARSE_NOT_A_FUNCTION;
1831
1832 /*
1833 * Parse the expression in parenthesis.
1834 */
1835 cchExpr -= pszFunEnd - pszExpr;
1836 pszExpr = pszFunEnd;
1837 /** @todo implement multiple arguments. */
1838 DBGCVAR Arg;
1839 rc = dbgcEvalSub(pDbgc, pszExpr, cchExpr, &Arg);
1840 if (!rc)
1841 {
1842 rc = dbgcEvalSubMatchVars(pDbgc, pFun->cArgsMin, pFun->cArgsMax, pFun->paArgDescs, pFun->cArgDescs, &Arg, 1);
1843 if (!rc)
1844 rc = pFun->pfnHandler(pFun, &pDbgc->CmdHlp, pDbgc->pVM, &Arg, 1, pResult);
1845 }
1846 else if (rc == VERR_PARSE_EMPTY_ARGUMENT && pFun->cArgsMin == 0)
1847 rc = pFun->pfnHandler(pFun, &pDbgc->CmdHlp, pDbgc->pVM, NULL, 0, pResult);
1848 }
1849 else
1850 {
1851 /*
1852 * Didn't find any operators, so it must be a plain expression.
1853 * This might be numeric or a string expression.
1854 */
1855 char ch = pszExpr[0];
1856 char ch2 = pszExpr[1];
1857 if (ch == '0' && (ch2 == 'x' || ch2 == 'X'))
1858 rc = dbgcEvalSubNum(pszExpr + 2, 16, pResult);
1859 else if (ch == '0' && (ch2 == 'i' || ch2 == 'i'))
1860 rc = dbgcEvalSubNum(pszExpr + 2, 10, pResult);
1861 else if (ch == '0' && (ch2 == 't' || ch2 == 'T'))
1862 rc = dbgcEvalSubNum(pszExpr + 2, 8, pResult);
1863 /// @todo 0b doesn't work as a binary prefix, we confuse it with 0bf8:0123 and stuff.
1864 //else if (ch == '0' && (ch2 == 'b' || ch2 == 'b'))
1865 // rc = dbgcEvalSubNum(pszExpr + 2, 2, pResult);
1866 else
1867 {
1868 /*
1869 * Hexadecimal number or a string?
1870 */
1871 char *psz = pszExpr;
1872 while (isxdigit(*psz))
1873 psz++;
1874 if (!*psz)
1875 rc = dbgcEvalSubNum(pszExpr, 16, pResult);
1876 else if ((*psz == 'h' || *psz == 'H') && !psz[1])
1877 {
1878 *psz = '\0';
1879 rc = dbgcEvalSubNum(pszExpr, 16, pResult);
1880 }
1881 else
1882 rc = dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult);
1883 }
1884 }
1885 }
1886
1887 return rc;
1888}
1889
1890
1891/**
1892 * Evaluates one argument.
1893 *
1894 * @returns 0 on success. pResult contains the result.
1895 * @returns VBox error code on parse or other evaluation error.
1896 *
1897 * @param pDbgc Debugger console instance data.
1898 * @param pszExpr The expression string.
1899 * @param pResult Where to store the result of the expression evaluation.
1900 */
1901static int dbgcEvalSub(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pResult)
1902{
1903 Log2(("dbgcEvalSub: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));
1904 /*
1905 * First we need to remove blanks in both ends.
1906 * ASSUMES: There is no quoting unless the entire expression is a string.
1907 */
1908
1909 /* stripping. */
1910 while (cchExpr > 0 && isblank(pszExpr[cchExpr - 1]))
1911 pszExpr[--cchExpr] = '\0';
1912 while (isblank(*pszExpr))
1913 pszExpr++, cchExpr--;
1914 if (!*pszExpr)
1915 return VERR_PARSE_EMPTY_ARGUMENT;
1916
1917 /* it there is any kind of quoting in the expression, it's string meat. */
1918 if (strpbrk(pszExpr, "\"'`"))
1919 return dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult);
1920
1921 /*
1922 * Check if there are any parenthesis which needs removing.
1923 */
1924 if (pszExpr[0] == '(' && pszExpr[cchExpr - 1] == ')')
1925 {
1926 do
1927 {
1928 unsigned cPar = 1;
1929 char *psz = pszExpr + 1;
1930 char ch;
1931 while ((ch = *psz) != '\0')
1932 {
1933 if (ch == '(')
1934 cPar++;
1935 else if (ch == ')')
1936 {
1937 if (cPar <= 0)
1938 return VERR_PARSE_UNBALANCED_PARENTHESIS;
1939 cPar--;
1940 if (cPar == 0 && psz[1]) /* If not at end, there's nothing to do. */
1941 break;
1942 }
1943 /* next */
1944 psz++;
1945 }
1946 if (ch)
1947 break;
1948
1949 /* remove the parenthesis. */
1950 pszExpr++;
1951 cchExpr -= 2;
1952 pszExpr[cchExpr] = '\0';
1953
1954 /* strip blanks. */
1955 while (cchExpr > 0 && isblank(pszExpr[cchExpr - 1]))
1956 pszExpr[--cchExpr] = '\0';
1957 while (isblank(*pszExpr))
1958 pszExpr++, cchExpr--;
1959 if (!*pszExpr)
1960 return VERR_PARSE_EMPTY_ARGUMENT;
1961 } while (pszExpr[0] == '(' && pszExpr[cchExpr - 1] == ')');
1962 }
1963
1964 /* tabs to spaces. */
1965 char *psz = pszExpr;
1966 while ((psz = strchr(psz, '\t')) != NULL)
1967 *psz = ' ';
1968
1969 /*
1970 * Now, we need to look for the binary operator with the lowest precedence.
1971 *
1972 * If there are no operators we're left with a simple expression which we
1973 * evaluate with respect to unary operators
1974 */
1975 char *pszOpSplit = NULL;
1976 PCDBGCOP pOpSplit = NULL;
1977 unsigned cBinaryOps = 0;
1978 unsigned cPar = 0;
1979 char ch;
1980 char chPrev = ' ';
1981 bool fBinary = false;
1982 psz = pszExpr;
1983
1984 while ((ch = *psz) != '\0')
1985 {
1986 //Log2(("ch=%c cPar=%d fBinary=%d\n", ch, cPar, fBinary));
1987 /*
1988 * Parenthesis.
1989 */
1990 if (ch == '(')
1991 {
1992 cPar++;
1993 fBinary = false;
1994 }
1995 else if (ch == ')')
1996 {
1997 if (cPar <= 0)
1998 return VERR_PARSE_UNBALANCED_PARENTHESIS;
1999 cPar--;
2000 fBinary = true;
2001 }
2002 /*
2003 * Potential operator.
2004 */
2005 else if (cPar == 0 && !isblank(ch))
2006 {
2007 PCDBGCOP pOp = dbgcIsOpChar(ch)
2008 ? dbgcOperatorLookup(pDbgc, psz, fBinary, chPrev)
2009 : NULL;
2010 if (pOp)
2011 {
2012 /* If not the right kind of operator we've got a syntax error. */
2013 if (pOp->fBinary != fBinary)
2014 return VERR_PARSE_UNEXPECTED_OPERATOR;
2015
2016 /*
2017 * Update the parse state and skip the operator.
2018 */
2019 if (!pOpSplit)
2020 {
2021 pOpSplit = pOp;
2022 pszOpSplit = psz;
2023 cBinaryOps = fBinary;
2024 }
2025 else if (fBinary)
2026 {
2027 cBinaryOps++;
2028 if (pOp->iPrecedence >= pOpSplit->iPrecedence)
2029 {
2030 pOpSplit = pOp;
2031 pszOpSplit = psz;
2032 }
2033 }
2034
2035 psz += pOp->cchName - 1;
2036 fBinary = false;
2037 }
2038 else
2039 fBinary = true;
2040 }
2041
2042 /* next */
2043 psz++;
2044 chPrev = ch;
2045 } /* parse loop. */
2046
2047
2048 /*
2049 * Either we found an operator to divide the expression by
2050 * or we didn't find any. In the first case it's divide and
2051 * conquer. In the latter it's a single expression which
2052 * needs dealing with its unary operators if any.
2053 */
2054 int rc;
2055 if ( cBinaryOps
2056 && pOpSplit->fBinary)
2057 {
2058 /* process 1st sub expression. */
2059 *pszOpSplit = '\0';
2060 DBGCVAR Arg1;
2061 rc = dbgcEvalSub(pDbgc, pszExpr, pszOpSplit - pszExpr, &Arg1);
2062 if (VBOX_SUCCESS(rc))
2063 {
2064 /* process 2nd sub expression. */
2065 char *psz2 = pszOpSplit + pOpSplit->cchName;
2066 DBGCVAR Arg2;
2067 rc = dbgcEvalSub(pDbgc, psz2, cchExpr - (psz2 - pszExpr), &Arg2);
2068 if (VBOX_SUCCESS(rc))
2069 /* apply the operator. */
2070 rc = pOpSplit->pfnHandlerBinary(pDbgc, &Arg1, &Arg2, pResult);
2071 }
2072 }
2073 else if (cBinaryOps)
2074 {
2075 /* process sub expression. */
2076 pszOpSplit += pOpSplit->cchName;
2077 DBGCVAR Arg;
2078 rc = dbgcEvalSub(pDbgc, pszOpSplit, cchExpr - (pszOpSplit - pszExpr), &Arg);
2079 if (VBOX_SUCCESS(rc))
2080 /* apply the operator. */
2081 rc = pOpSplit->pfnHandlerUnary(pDbgc, &Arg, pResult);
2082 }
2083 else
2084 /* plain expression or using unary operators perhaps with paratheses. */
2085 rc = dbgcEvalSubUnary(pDbgc, pszExpr, cchExpr, pResult);
2086
2087 return rc;
2088}
2089
2090
2091/**
2092 * Parses the arguments of one command.
2093 *
2094 * @returns 0 on success.
2095 * @returns VBox error code on parse error with *pcArg containing the argument causing trouble.
2096 * @param pDbgc Debugger console instance data.
2097 * @param pCmd Pointer to the command descriptor.
2098 * @param pszArg Pointer to the arguments to parse.
2099 * @param paArgs Where to store the parsed arguments.
2100 * @param cArgs Size of the paArgs array.
2101 * @param pcArgs Where to store the number of arguments.
2102 * In the event of an error this is used to store the index of the offending argument.
2103 */
2104static int dbgcProcessArguments(PDBGC pDbgc, PCDBGCCMD pCmd, char *pszArgs, PDBGCVAR paArgs, unsigned cArgs, unsigned *pcArgs)
2105{
2106 Log2(("dbgcProcessArguments: pCmd=%s pszArgs='%s'\n", pCmd->pszCmd, pszArgs));
2107 /*
2108 * Check if we have any argument and if the command takes any.
2109 */
2110 *pcArgs = 0;
2111 /* strip leading blanks. */
2112 while (*pszArgs && isblank(*pszArgs))
2113 pszArgs++;
2114 if (!*pszArgs)
2115 {
2116 if (!pCmd->cArgsMin)
2117 return 0;
2118 return VERR_PARSE_TOO_FEW_ARGUMENTS;
2119 }
2120 /** @todo fixme - foo() doesn't work. */
2121 if (!pCmd->cArgsMax)
2122 return VERR_PARSE_TOO_MANY_ARGUMENTS;
2123
2124 /*
2125 * This is a hack, it's "temporary" and should go away "when" the parser is
2126 * modified to match arguments while parsing.
2127 */
2128 if ( pCmd->cArgsMax == 1
2129 && pCmd->cArgsMin == 1
2130 && pCmd->cArgDescs == 1
2131 && pCmd->paArgDescs[0].enmCategory == DBGCVAR_CAT_STRING
2132 && cArgs >= 1)
2133 {
2134 *pcArgs = 1;
2135 RTStrStripR(pszArgs);
2136 return dbgcEvalSubString(pDbgc, pszArgs, strlen(pszArgs), &paArgs[0]);
2137 }
2138
2139
2140 /*
2141 * The parse loop.
2142 */
2143 PDBGCVAR pArg0 = &paArgs[0];
2144 PDBGCVAR pArg = pArg0;
2145 *pcArgs = 0;
2146 do
2147 {
2148 /*
2149 * Can we have another argument?
2150 */
2151 if (*pcArgs >= pCmd->cArgsMax)
2152 return VERR_PARSE_TOO_MANY_ARGUMENTS;
2153 if (pArg >= &paArgs[cArgs])
2154 return VERR_PARSE_ARGUMENT_OVERFLOW;
2155
2156 /*
2157 * Find the end of the argument.
2158 */
2159 int cPar = 0;
2160 char chQuote = '\0';
2161 char *pszEnd = NULL;
2162 char *psz = pszArgs;
2163 char ch;
2164 bool fBinary = false;
2165 for (;;)
2166 {
2167 /*
2168 * Check for the end.
2169 */
2170 if ((ch = *psz) == '\0')
2171 {
2172 if (chQuote)
2173 return VERR_PARSE_UNBALANCED_QUOTE;
2174 if (cPar)
2175 return VERR_PARSE_UNBALANCED_PARENTHESIS;
2176 pszEnd = psz;
2177 break;
2178 }
2179 /*
2180 * When quoted we ignore everything but the quotation char.
2181 * We use the REXX way of escaping the quotation char, i.e. double occurence.
2182 */
2183 else if (ch == '\'' || ch == '"' || ch == '`')
2184 {
2185 if (chQuote)
2186 {
2187 /* end quote? */
2188 if (ch == chQuote)
2189 {
2190 if (psz[1] == ch)
2191 psz++; /* skip the escaped quote char */
2192 else
2193 chQuote = '\0'; /* end of quoted string. */
2194 }
2195 }
2196 else
2197 chQuote = ch; /* open new quote */
2198 }
2199 /*
2200 * Parenthesis can of course be nested.
2201 */
2202 else if (ch == '(')
2203 {
2204 cPar++;
2205 fBinary = false;
2206 }
2207 else if (ch == ')')
2208 {
2209 if (!cPar)
2210 return VERR_PARSE_UNBALANCED_PARENTHESIS;
2211 cPar--;
2212 fBinary = true;
2213 }
2214 else if (!chQuote && !cPar)
2215 {
2216 /*
2217 * Encountering blanks may mean the end of it all. A binary operator
2218 * will force continued parsing.
2219 */
2220 if (isblank(*psz))
2221 {
2222 pszEnd = psz++; /* just in case. */
2223 while (isblank(*psz))
2224 psz++;
2225 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, psz, fBinary, ' ');
2226 if (!pOp || pOp->fBinary != fBinary)
2227 break; /* the end. */
2228 psz += pOp->cchName;
2229 while (isblank(*psz)) /* skip blanks so we don't get here again */
2230 psz++;
2231 fBinary = false;
2232 continue;
2233 }
2234
2235 /*
2236 * Look for operators without a space up front.
2237 */
2238 if (dbgcIsOpChar(*psz))
2239 {
2240 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, psz, fBinary, ' ');
2241 if (pOp)
2242 {
2243 if (pOp->fBinary != fBinary)
2244 {
2245 pszEnd = psz;
2246 /** @todo this is a parsing error really. */
2247 break; /* the end. */
2248 }
2249 psz += pOp->cchName;
2250 while (isblank(*psz)) /* skip blanks so we don't get here again */
2251 psz++;
2252 fBinary = false;
2253 continue;
2254 }
2255 }
2256 fBinary = true;
2257 }
2258
2259 /* next char */
2260 psz++;
2261 }
2262 *pszEnd = '\0';
2263 /* (psz = next char to process) */
2264
2265 /*
2266 * Parse and evaluate the argument.
2267 */
2268 int rc = dbgcEvalSub(pDbgc, pszArgs, strlen(pszArgs), pArg);
2269 if (VBOX_FAILURE(rc))
2270 return rc;
2271
2272 /*
2273 * Next.
2274 */
2275 pArg++;
2276 (*pcArgs)++;
2277 pszArgs = psz;
2278 while (*pszArgs && isblank(*pszArgs))
2279 pszArgs++;
2280 } while (*pszArgs);
2281
2282 /*
2283 * Match the arguments.
2284 */
2285 return dbgcEvalSubMatchVars(pDbgc, pCmd->cArgsMin, pCmd->cArgsMax, pCmd->paArgDescs, pCmd->cArgDescs, pArg0, pArg - pArg0);
2286}
2287
2288
2289/**
2290 * Process one command.
2291 *
2292 * @returns VBox status code. Any error indicates the termination of the console session.
2293 * @param pDbgc Debugger console instance data.
2294 * @param pszCmd Pointer to the command.
2295 * @param cchCmd Length of the command.
2296 */
2297static int dbgcProcessCommand(PDBGC pDbgc, char *pszCmd, size_t cchCmd)
2298{
2299 char *pszCmdInput = pszCmd;
2300
2301 /*
2302 * Skip blanks.
2303 */
2304 while (isblank(*pszCmd))
2305 pszCmd++, cchCmd--;
2306
2307 /* external command? */
2308 bool fExternal = *pszCmd == '.';
2309 if (fExternal)
2310 pszCmd++, cchCmd--;
2311
2312 /*
2313 * Find arguments.
2314 */
2315 char *pszArgs = pszCmd;
2316 while (isalnum(*pszArgs))
2317 pszArgs++;
2318 if (*pszArgs && (!isblank(*pszArgs) || pszArgs == pszCmd))
2319 {
2320 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "Syntax error in command '%s'!\n", pszCmdInput);
2321 return 0;
2322 }
2323
2324 /*
2325 * Find the command.
2326 */
2327 PCDBGCCMD pCmd = dbgcRoutineLookup(pDbgc, pszCmd, pszArgs - pszCmd, fExternal);
2328 if (!pCmd || (pCmd->fFlags & DBGCCMD_FLAGS_FUNCTION))
2329 return pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "Unknown command '%s'!\n", pszCmdInput);
2330
2331 /*
2332 * Parse arguments (if any).
2333 */
2334 unsigned cArgs;
2335 int rc = dbgcProcessArguments(pDbgc, pCmd, pszArgs, &pDbgc->aArgs[pDbgc->iArg], ELEMENTS(pDbgc->aArgs) - pDbgc->iArg, &cArgs);
2336
2337 /*
2338 * Execute the command.
2339 */
2340 if (!rc)
2341 {
2342 rc = pCmd->pfnHandler(pCmd, &pDbgc->CmdHlp, pDbgc->pVM, &pDbgc->aArgs[0], cArgs, NULL);
2343 }
2344 else
2345 {
2346 /* report parse / eval error. */
2347 switch (rc)
2348 {
2349 case VERR_PARSE_TOO_FEW_ARGUMENTS:
2350 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2351 "Syntax error: Too few arguments. Minimum is %d for command '%s'.\n", pCmd->cArgsMin, pCmd->pszCmd);
2352 break;
2353 case VERR_PARSE_TOO_MANY_ARGUMENTS:
2354 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2355 "Syntax error: Too many arguments. Maximum is %d for command '%s'.\n", pCmd->cArgsMax, pCmd->pszCmd);
2356 break;
2357 case VERR_PARSE_ARGUMENT_OVERFLOW:
2358 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2359 "Syntax error: Too many arguments.\n");
2360 break;
2361 case VERR_PARSE_UNBALANCED_QUOTE:
2362 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2363 "Syntax error: Unbalanced quote (argument %d).\n", cArgs);
2364 break;
2365 case VERR_PARSE_UNBALANCED_PARENTHESIS:
2366 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2367 "Syntax error: Unbalanced parenthesis (argument %d).\n", cArgs);
2368 break;
2369 case VERR_PARSE_EMPTY_ARGUMENT:
2370 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2371 "Syntax error: An argument or subargument contains nothing useful (argument %d).\n", cArgs);
2372 break;
2373 case VERR_PARSE_UNEXPECTED_OPERATOR:
2374 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2375 "Syntax error: Invalid operator usage (argument %d).\n", cArgs);
2376 break;
2377 case VERR_PARSE_INVALID_NUMBER:
2378 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2379 "Syntax error: Ivalid numeric value (argument %d). If a string was the intention, then quote it.\n", cArgs);
2380 break;
2381 case VERR_PARSE_NUMBER_TOO_BIG:
2382 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2383 "Error: Numeric overflow (argument %d).\n", cArgs);
2384 break;
2385 case VERR_PARSE_INVALID_OPERATION:
2386 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2387 "Error: Invalid operation attempted (argument %d).\n", cArgs);
2388 break;
2389 case VERR_PARSE_FUNCTION_NOT_FOUND:
2390 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2391 "Error: Function not found (argument %d).\n", cArgs);
2392 break;
2393 case VERR_PARSE_NOT_A_FUNCTION:
2394 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2395 "Error: The function specified is not a function (argument %d).\n", cArgs);
2396 break;
2397 case VERR_PARSE_NO_MEMORY:
2398 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2399 "Error: Out memory in the regular heap! Expect odd stuff to happen...\n", cArgs);
2400 break;
2401 case VERR_PARSE_INCORRECT_ARG_TYPE:
2402 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2403 "Error: Incorrect argument type (argument %d?).\n", cArgs);
2404 break;
2405 case VERR_PARSE_VARIABLE_NOT_FOUND:
2406 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2407 "Error: An undefined variable was referenced (argument %d).\n", cArgs);
2408 break;
2409 case VERR_PARSE_CONVERSION_FAILED:
2410 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2411 "Error: A conversion between two types failed (argument %d).\n", cArgs);
2412 break;
2413 case VERR_PARSE_NOT_IMPLEMENTED:
2414 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2415 "Error: You hit a debugger feature which isn't implemented yet (argument %d).\n", cArgs);
2416 break;
2417 case VERR_PARSE_BAD_RESULT_TYPE:
2418 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2419 "Error: Couldn't satisfy a request for a specific result type (argument %d). (Usually applies to symbols)\n", cArgs);
2420 break;
2421 case VERR_PARSE_WRITEONLY_SYMBOL:
2422 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2423 "Error: Cannot get symbol, it's set only (argument %d).\n", cArgs);
2424 break;
2425
2426 default:
2427 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2428 "Error: Unknown error %d!\n", rc);
2429 return rc;
2430 }
2431
2432 /*
2433 * Parse errors are non fatal.
2434 */
2435 if (rc >= VERR_PARSE_FIRST && rc < VERR_PARSE_LAST)
2436 rc = 0;
2437 }
2438
2439 return rc;
2440}
2441
2442
2443/**
2444 * Process all commands current in the buffer.
2445 *
2446 * @returns VBox status code. Any error indicates the termination of the console session.
2447 * @param pDbgc Debugger console instance data.
2448 */
2449static int dbgcProcessCommands(PDBGC pDbgc)
2450{
2451 int rc = 0;
2452 while (pDbgc->cInputLines)
2453 {
2454 /*
2455 * Empty the log buffer if we're hooking the log.
2456 */
2457 if (pDbgc->fLog)
2458 {
2459 rc = dbgcProcessLog(pDbgc);
2460 if (VBOX_FAILURE(rc))
2461 break;
2462 }
2463
2464 if (pDbgc->iRead == pDbgc->iWrite)
2465 {
2466 AssertMsgFailed(("The input buffer is empty while cInputLines=%d!\n", pDbgc->cInputLines));
2467 pDbgc->cInputLines = 0;
2468 return 0;
2469 }
2470
2471 /*
2472 * Copy the command to the parse buffer.
2473 */
2474 char ch;
2475 char *psz = &pDbgc->achInput[pDbgc->iRead];
2476 char *pszTrg = &pDbgc->achScratch[0];
2477 while ((*pszTrg = ch = *psz++) != ';' && ch != '\n' )
2478 {
2479 if (psz == &pDbgc->achInput[sizeof(pDbgc->achInput)])
2480 psz = &pDbgc->achInput[0];
2481
2482 if (psz == &pDbgc->achInput[pDbgc->iWrite])
2483 {
2484 AssertMsgFailed(("The buffer contains no commands while cInputLines=%d!\n", pDbgc->cInputLines));
2485 pDbgc->cInputLines = 0;
2486 return 0;
2487 }
2488
2489 pszTrg++;
2490 }
2491 *pszTrg = '\0';
2492
2493 /*
2494 * Advance the buffer.
2495 */
2496 pDbgc->iRead = psz - &pDbgc->achInput[0];
2497 if (ch == '\n')
2498 pDbgc->cInputLines--;
2499
2500 /*
2501 * Parse and execute this command.
2502 */
2503 pDbgc->pszScratch = psz;
2504 pDbgc->iArg = 0;
2505 rc = dbgcProcessCommand(pDbgc, &pDbgc->achScratch[0], psz - &pDbgc->achScratch[0] - 1);
2506 if (rc)
2507 break;
2508 }
2509
2510 return rc;
2511}
2512
2513
2514/**
2515 * Reads input, parses it and executes commands on '\n'.
2516 *
2517 * @returns VBox status.
2518 * @param pDbgc Debugger console instance data.
2519 */
2520static int dbgcProcessInput(PDBGC pDbgc)
2521{
2522 /*
2523 * We know there's input ready, so let's read it first.
2524 */
2525 int rc = dbgcInputRead(pDbgc);
2526 if (VBOX_FAILURE(rc))
2527 return rc;
2528
2529 /*
2530 * Now execute any ready commands.
2531 */
2532 if (pDbgc->cInputLines)
2533 {
2534 /** @todo this fReady stuff is broken. */
2535 pDbgc->fReady = false;
2536 rc = dbgcProcessCommands(pDbgc);
2537 if (VBOX_SUCCESS(rc) && rc != VWRN_DBGC_CMD_PENDING)
2538 pDbgc->fReady = true;
2539 if ( VBOX_SUCCESS(rc)
2540 && pDbgc->iRead == pDbgc->iWrite
2541 && pDbgc->fReady)
2542 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "VBoxDbg> ");
2543 }
2544
2545 return rc;
2546}
2547
2548
2549/**
2550 * Gets the event context identifier string.
2551 * @returns Read only string.
2552 * @param enmCtx The context.
2553 */
2554static const char *dbgcGetEventCtx(DBGFEVENTCTX enmCtx)
2555{
2556 switch (enmCtx)
2557 {
2558 case DBGFEVENTCTX_RAW: return "raw";
2559 case DBGFEVENTCTX_REM: return "rem";
2560 case DBGFEVENTCTX_HWACCL: return "hwaccl";
2561 case DBGFEVENTCTX_HYPER: return "hyper";
2562 case DBGFEVENTCTX_OTHER: return "other";
2563
2564 case DBGFEVENTCTX_INVALID: return "!Invalid Event Ctx!";
2565 default:
2566 AssertMsgFailed(("enmCtx=%d\n", enmCtx));
2567 return "!Unknown Event Ctx!";
2568 }
2569}
2570
2571
2572/**
2573 * Processes debugger events.
2574 *
2575 * @returns VBox status.
2576 * @param pDbgc DBGC Instance data.
2577 * @param pEvent Pointer to event data.
2578 */
2579static int dbgcProcessEvent(PDBGC pDbgc, PCDBGFEVENT pEvent)
2580{
2581 /*
2582 * Flush log first.
2583 */
2584 if (pDbgc->fLog)
2585 {
2586 int rc = dbgcProcessLog(pDbgc);
2587 if (VBOX_FAILURE(rc))
2588 return rc;
2589 }
2590
2591 /*
2592 * Process the event.
2593 */
2594 pDbgc->pszScratch = &pDbgc->achInput[0];
2595 pDbgc->iArg = 0;
2596 bool fPrintPrompt = true;
2597 int rc = VINF_SUCCESS;
2598 switch (pEvent->enmType)
2599 {
2600 /*
2601 * The first part is events we have initiated with commands.
2602 */
2603 case DBGFEVENT_HALT_DONE:
2604 {
2605 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: VM %p is halted! (%s)\n",
2606 pDbgc->pVM, dbgcGetEventCtx(pEvent->enmCtx));
2607 pDbgc->fRegCtxGuest = true; /* we're always in guest context when halted. */
2608 if (VBOX_SUCCESS(rc))
2609 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
2610 break;
2611 }
2612
2613
2614 /*
2615 * The second part is events which can occur at any time.
2616 */
2617 case DBGFEVENT_FATAL_ERROR:
2618 {
2619 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbf event: Fatal error! (%s)\n",
2620 dbgcGetEventCtx(pEvent->enmCtx));
2621 pDbgc->fRegCtxGuest = false; /* fatal errors are always in hypervisor. */
2622 if (VBOX_SUCCESS(rc))
2623 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
2624 break;
2625 }
2626
2627 case DBGFEVENT_BREAKPOINT:
2628 case DBGFEVENT_BREAKPOINT_HYPER:
2629 {
2630 bool fRegCtxGuest = pDbgc->fRegCtxGuest;
2631 pDbgc->fRegCtxGuest = pEvent->enmType == DBGFEVENT_BREAKPOINT;
2632
2633 rc = dbgcBpExec(pDbgc, pEvent->u.Bp.iBp);
2634 switch (rc)
2635 {
2636 case VERR_DBGC_BP_NOT_FOUND:
2637 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Unknown breakpoint %u! (%s)\n",
2638 pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));
2639 break;
2640
2641 case VINF_DBGC_BP_NO_COMMAND:
2642 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Breakpoint %u! (%s)\n",
2643 pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));
2644 break;
2645
2646 case VINF_BUFFER_OVERFLOW:
2647 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Breakpoint %u! Command too long to execute! (%s)\n",
2648 pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));
2649 break;
2650
2651 default:
2652 break;
2653 }
2654 if (VBOX_SUCCESS(rc) && DBGFR3IsHalted(pDbgc->pVM))
2655 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
2656 else
2657 pDbgc->fRegCtxGuest = fRegCtxGuest;
2658 break;
2659 }
2660
2661 case DBGFEVENT_STEPPED:
2662 case DBGFEVENT_STEPPED_HYPER:
2663 {
2664 pDbgc->fRegCtxGuest = pEvent->enmType == DBGFEVENT_STEPPED;
2665
2666 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Single step! (%s)\n", dbgcGetEventCtx(pEvent->enmCtx));
2667 if (VBOX_SUCCESS(rc))
2668 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
2669 break;
2670 }
2671
2672 case DBGFEVENT_ASSERTION_HYPER:
2673 {
2674 pDbgc->fRegCtxGuest = false;
2675
2676 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2677 "\ndbgf event: Hypervisor Assertion! (%s)\n"
2678 "%s"
2679 "%s"
2680 "\n",
2681 dbgcGetEventCtx(pEvent->enmCtx),
2682 pEvent->u.Assert.pszMsg1,
2683 pEvent->u.Assert.pszMsg2);
2684 if (VBOX_SUCCESS(rc))
2685 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
2686 break;
2687 }
2688
2689 case DBGFEVENT_DEV_STOP:
2690 {
2691 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2692 "\n"
2693 "dbgf event: DBGFSTOP (%s)\n"
2694 "File: %s\n"
2695 "Line: %d\n"
2696 "Function: %s\n",
2697 dbgcGetEventCtx(pEvent->enmCtx),
2698 pEvent->u.Src.pszFile,
2699 pEvent->u.Src.uLine,
2700 pEvent->u.Src.pszFunction);
2701 if (VBOX_SUCCESS(rc) && pEvent->u.Src.pszMessage && *pEvent->u.Src.pszMessage)
2702 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2703 "Message: %s\n",
2704 pEvent->u.Src.pszMessage);
2705 if (VBOX_SUCCESS(rc))
2706 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
2707 break;
2708 }
2709
2710
2711 case DBGFEVENT_INVALID_COMMAND:
2712 {
2713 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Invalid command event!\n");
2714 fPrintPrompt = !pDbgc->fReady;
2715 break;
2716 }
2717
2718 case DBGFEVENT_TERMINATING:
2719 {
2720 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\nVM is terminating!\n");
2721 rc = VERR_GENERAL_FAILURE;
2722 break;
2723 }
2724
2725
2726 default:
2727 {
2728 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Unknown event %d!\n", pEvent->enmType);
2729 fPrintPrompt = !pDbgc->fReady;
2730 break;
2731 }
2732 }
2733
2734 /*
2735 * Prompt, anyone?
2736 */
2737 if (fPrintPrompt && VBOX_SUCCESS(rc))
2738 {
2739 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "VBoxDbg> ");
2740 }
2741
2742 return rc;
2743}
2744
2745
2746
2747
2748
2749/**
2750 * Make a console instance.
2751 *
2752 * This will not return until either an 'exit' command is issued or a error code
2753 * indicating connection loss is encountered.
2754 *
2755 * @returns VINF_SUCCESS if console termination caused by the 'exit' command.
2756 * @returns The VBox status code causing the console termination.
2757 *
2758 * @param pVM VM Handle.
2759 * @param pBack Pointer to the backend structure. This must contain
2760 * a full set of function pointers to service the console.
2761 * @param fFlags Reserved, must be zero.
2762 * @remark A forced termination of the console is easiest done by forcing the
2763 * callbacks to return fatal failures.
2764 */
2765DBGDECL(int) DBGCCreate(PVM pVM, PDBGCBACK pBack, unsigned fFlags)
2766{
2767 /*
2768 * Validate input.
2769 */
2770 AssertReturn(VALID_PTR(pVM), VERR_INVALID_PARAMETER);
2771 AssertReturn(VALID_PTR(pBack), VERR_INVALID_PARAMETER);
2772 AssertMsgReturn(!fFlags, ("%#x", fFlags), VERR_INVALID_PARAMETER);
2773
2774 /*
2775 * Allocate and initialize instance data
2776 */
2777 PDBGC pDbgc = (PDBGC)RTMemAllocZ(sizeof(*pDbgc));
2778 if (!pDbgc)
2779 return VERR_NO_MEMORY;
2780
2781 pDbgc->CmdHlp.pfnWrite = dbgcHlpWrite;
2782 pDbgc->CmdHlp.pfnPrintfV = dbgcHlpPrintfV;
2783 pDbgc->CmdHlp.pfnPrintf = dbgcHlpPrintf;
2784 pDbgc->CmdHlp.pfnVBoxErrorV = dbgcHlpVBoxErrorV;
2785 pDbgc->CmdHlp.pfnVBoxError = dbgcHlpVBoxError;
2786 pDbgc->CmdHlp.pfnMemRead = dbgcHlpMemRead;
2787 pDbgc->CmdHlp.pfnMemWrite = dbgcHlpMemWrite;
2788 pDbgc->CmdHlp.pfnEval = dbgcHlpEval;
2789 pDbgc->CmdHlp.pfnExec = dbgcHlpExec;
2790 pDbgc->CmdHlp.pfnVarToDbgfAddr = dbgcHlpVarToDbgfAddr;
2791 pDbgc->CmdHlp.pfnVarToBool = dbgcHlpVarToBool;
2792 pDbgc->pBack = pBack;
2793 pDbgc->pVM = NULL;
2794 pDbgc->pszEmulation = "CodeView/WinDbg";
2795 pDbgc->paEmulationCmds = &g_aCmdsCodeView[0];
2796 pDbgc->cEmulationCmds = g_cCmdsCodeView;
2797 //pDbgc->fLog = false;
2798 pDbgc->fRegCtxGuest = true;
2799 pDbgc->fRegTerse = true;
2800 //pDbgc->DisasmPos = {0};
2801 //pDbgc->SourcePos = {0};
2802 //pDbgc->DumpPos = {0};
2803 //pDbgc->cbDumpElement = 0;
2804 //pDbgc->cVars = 0;
2805 //pDbgc->paVars = NULL;
2806 //pDbgc->pFirstBp = NULL;
2807 //pDbgc->uInputZero = 0;
2808 //pDbgc->iRead = 0;
2809 //pDbgc->iWrite = 0;
2810 //pDbgc->cInputLines = 0;
2811 //pDbgc->fInputOverflow = false;
2812 pDbgc->fReady = true;
2813 pDbgc->pszScratch = &pDbgc->achScratch[0];
2814 //pDbgc->iArg = 0;
2815 //pDbgc->rcOutput = 0;
2816
2817 dbgcInitOpCharBitMap();
2818
2819 /*
2820 * Print welcome message.
2821 */
2822 int rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2823 "Welcome to the VirtualBox Debugger!\n");
2824 if (VBOX_FAILURE(rc))
2825 goto l_failure;
2826
2827 /*
2828 * Attach to the VM.
2829 */
2830 rc = DBGFR3Attach(pVM);
2831 if (VBOX_FAILURE(rc))
2832 {
2833 rc = pDbgc->CmdHlp.pfnVBoxError(&pDbgc->CmdHlp, rc, "When trying to attach to VM %p\n", pDbgc->pVM);
2834 goto l_failure;
2835 }
2836 pDbgc->pVM = pVM;
2837
2838 /*
2839 * Print commandline and auto select result.
2840 */
2841 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2842 "Current VM is %08x\n" /** @todo get and print the VM name! */
2843 "VBoxDbg> ",
2844 pDbgc->pVM);
2845 if (VBOX_FAILURE(rc))
2846 goto l_failure;
2847
2848 /*
2849 * Main Debugger Loop.
2850 *
2851 * This loop will either block on waiting for input or on waiting on
2852 * debug events. If we're forwarding the log we cannot wait for long
2853 * before we must flush the log.
2854 */
2855 for (rc = 0;;)
2856 {
2857 if (pDbgc->pVM && DBGFR3CanWait(pDbgc->pVM))
2858 {
2859 /*
2860 * Wait for a debug event.
2861 */
2862 PCDBGFEVENT pEvent;
2863 rc = DBGFR3EventWait(pDbgc->pVM, pDbgc->fLog ? 1 : 32, &pEvent);
2864 if (VBOX_SUCCESS(rc))
2865 {
2866 rc = dbgcProcessEvent(pDbgc, pEvent);
2867 if (VBOX_FAILURE(rc))
2868 break;
2869 }
2870 else if (rc != VERR_TIMEOUT)
2871 break;
2872
2873 /*
2874 * Check for input.
2875 */
2876 if (pBack->pfnInput(pDbgc->pBack, 0))
2877 {
2878 rc = dbgcProcessInput(pDbgc);
2879 if (VBOX_FAILURE(rc))
2880 break;
2881 }
2882 }
2883 else
2884 {
2885 /*
2886 * Wait for input. If Logging is enabled we'll only wait very briefly.
2887 */
2888 if (pBack->pfnInput(pDbgc->pBack, pDbgc->fLog ? 1 : 1000))
2889 {
2890 rc = dbgcProcessInput(pDbgc);
2891 if (VBOX_FAILURE(rc))
2892 break;
2893 }
2894 }
2895
2896 /*
2897 * Forward log output.
2898 */
2899 if (pDbgc->fLog)
2900 {
2901 rc = dbgcProcessLog(pDbgc);
2902 if (VBOX_FAILURE(rc))
2903 break;
2904 }
2905 }
2906
2907
2908l_failure:
2909 /*
2910 * Cleanup console debugger session.
2911 */
2912 /* Disable log hook. */
2913 if (pDbgc->fLog)
2914 {
2915
2916 }
2917
2918 /* Detach from the VM. */
2919 if (pDbgc->pVM)
2920 DBGFR3Detach(pDbgc->pVM);
2921
2922 /* finally, free the instance memory. */
2923 RTMemFree(pDbgc);
2924
2925 return rc;
2926}
2927
Note: See TracBrowser for help on using the repository browser.

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