VirtualBox

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

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

Split out the built-in symbols.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 142.4 KB
Line 
1/** $Id: DBGConsole.cpp 5674 2007-11-11 05:34:02Z 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 DECLCALLBACK(int) dbgcCmdHelp(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
134static DECLCALLBACK(int) dbgcCmdQuit(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
135static DECLCALLBACK(int) dbgcCmdStop(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
136static DECLCALLBACK(int) dbgcCmdInfo(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
137static DECLCALLBACK(int) dbgcCmdLog(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
138static DECLCALLBACK(int) dbgcCmdLogDest(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
139static DECLCALLBACK(int) dbgcCmdLogFlags(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
140static DECLCALLBACK(int) dbgcCmdFormat(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
141static DECLCALLBACK(int) dbgcCmdLoadSyms(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
142static DECLCALLBACK(int) dbgcCmdSet(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
143static DECLCALLBACK(int) dbgcCmdUnset(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
144static DECLCALLBACK(int) dbgcCmdLoadVars(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
145static DECLCALLBACK(int) dbgcCmdShowVars(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
146static DECLCALLBACK(int) dbgcCmdHarakiri(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
147static DECLCALLBACK(int) dbgcCmdEcho(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
148static DECLCALLBACK(int) dbgcCmdRunScript(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
149
150static int dbgcEvalSub(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pResult);
151static int dbgcProcessCommand(PDBGC pDbgc, char *pszCmd, size_t cchCmd);
152
153
154/*******************************************************************************
155* Global Variables *
156*******************************************************************************/
157/**
158 * Pointer to head of the list of external commands.
159 */
160static PDBGCEXTCMDS g_pExtCmdsHead; /** @todo rw protect g_pExtCmdsHead! */
161/** Locks the g_pExtCmdsHead list for reading. */
162#define DBGCEXTCMDS_LOCK_RD() do { } while (0)
163/** Locks the g_pExtCmdsHead list for writing. */
164#define DBGCEXTCMDS_LOCK_WR() do { } while (0)
165/** UnLocks the g_pExtCmdsHead list after reading. */
166#define DBGCEXTCMDS_UNLOCK_RD() do { } while (0)
167/** UnLocks the g_pExtCmdsHead list after writing. */
168#define DBGCEXTCMDS_UNLOCK_WR() do { } while (0)
169
170
171/** One argument of any kind. */
172static const DBGCVARDESC g_aArgAny[] =
173{
174 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
175 { 0, 1, DBGCVAR_CAT_ANY, 0, "var", "Any type of argument." },
176};
177
178/** Multiple string arguments (min 1). */
179static const DBGCVARDESC g_aArgMultiStr[] =
180{
181 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
182 { 1, ~0, DBGCVAR_CAT_STRING, 0, "strings", "One or more strings." },
183};
184
185/** Filename string. */
186static const DBGCVARDESC g_aArgFilename[] =
187{
188 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
189 { 1, 1, DBGCVAR_CAT_STRING, 0, "path", "Filename string." },
190};
191
192
193/** 'help' arguments. */
194static const DBGCVARDESC g_aArgHelp[] =
195{
196 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
197 { 0, ~0, DBGCVAR_CAT_STRING, 0, "cmd/op", "Zero or more command or operator names." },
198};
199
200
201/** 'info' arguments. */
202static const DBGCVARDESC g_aArgInfo[] =
203{
204 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
205 { 1, 1, DBGCVAR_CAT_STRING, 0, "info", "The name of the info to display." },
206 { 0, 1, DBGCVAR_CAT_STRING, 0, "args", "String arguments to the handler." },
207};
208
209
210/** loadsyms arguments. */
211static const DBGCVARDESC g_aArgLoadSyms[] =
212{
213 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
214 { 1, 1, DBGCVAR_CAT_STRING, 0, "path", "Filename string." },
215 { 0, 1, DBGCVAR_CAT_NUMBER, 0, "delta", "Delta to add to the loaded symbols. (optional)" },
216 { 0, 1, DBGCVAR_CAT_STRING, 0, "module name", "Module name." },
217 { 0, 1, DBGCVAR_CAT_POINTER, DBGCVD_FLAGS_DEP_PREV, "module address", "Module address." },
218 { 0, 1, DBGCVAR_CAT_NUMBER, 0, "module size", "The module size. (optional)" },
219};
220
221
222/** log arguments. */
223static const DBGCVARDESC g_aArgLog[] =
224{
225 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
226 { 1, 1, DBGCVAR_CAT_STRING, 0, "groups", "Group modifier string (quote it!)." }
227};
228
229
230/** logdest arguments. */
231static const DBGCVARDESC g_aArgLogDest[] =
232{
233 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
234 { 1, 1, DBGCVAR_CAT_STRING, 0, "dests", "Destination modifier string (quote it!)." }
235};
236
237
238/** logflags arguments. */
239static const DBGCVARDESC g_aArgLogFlags[] =
240{
241 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
242 { 1, 1, DBGCVAR_CAT_STRING, 0, "flags", "Flag modifier string (quote it!)." }
243};
244
245
246/** 'set' arguments */
247static const DBGCVARDESC g_aArgSet[] =
248{
249 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
250 { 1, 1, DBGCVAR_CAT_STRING, 0, "var", "Variable name." },
251 { 1, 1, DBGCVAR_CAT_ANY, 0, "value", "Value to assign to the variable." },
252};
253
254
255
256
257
258/** Command descriptors for the basic commands. */
259static const DBGCCMD g_aCmds[] =
260{
261 /* pszCmd, cArgsMin, cArgsMax, paArgDescs, cArgDescs, pResultDesc, fFlags, pfnHandler pszSyntax, ....pszDescription */
262 { "bye", 0, 0, NULL, 0, NULL, 0, dbgcCmdQuit, "", "Exits the debugger." },
263 { "echo", 1, ~0, &g_aArgMultiStr[0], ELEMENTS(g_aArgMultiStr), NULL, 0, dbgcCmdEcho, "<str1> [str2..[strN]]", "Displays the strings separated by one blank space and the last one followed by a newline." },
264 { "exit", 0, 0, NULL, 0, NULL, 0, dbgcCmdQuit, "", "Exits the debugger." },
265 { "format", 1, 1, &g_aArgAny[0], ELEMENTS(g_aArgAny), NULL, 0, dbgcCmdFormat, "", "Evaluates an expression and formats it." },
266 { "harakiri", 0, 0, NULL, 0, NULL, 0, dbgcCmdHarakiri, "", "Kills debugger process." },
267 { "help", 0, ~0, &g_aArgHelp[0], ELEMENTS(g_aArgHelp), NULL, 0, dbgcCmdHelp, "[cmd/op [..]]", "Display help. For help about info items try 'info help'." },
268 { "info", 1, 2, &g_aArgInfo[0], ELEMENTS(g_aArgInfo), NULL, 0, dbgcCmdInfo, "<info> [args]", "Display info register in the DBGF. For a list of info items try 'info help'." },
269 { "loadsyms", 1, 5, &g_aArgLoadSyms[0], ELEMENTS(g_aArgLoadSyms), NULL, 0, dbgcCmdLoadSyms, "<filename> [delta] [module] [module address]", "Loads symbols from a text file. Optionally giving a delta and a module." },
270 { "loadvars", 1, 1, &g_aArgFilename[0], ELEMENTS(g_aArgFilename), NULL, 0, dbgcCmdLoadVars, "<filename>", "Load variables from file. One per line, same as the args to the set command." },
271 { "log", 1, 1, &g_aArgLog[0], ELEMENTS(g_aArgLog), NULL, 0, dbgcCmdLog, "<group string>", "Modifies the logging group settings (VBOX_LOG)" },
272 { "logdest", 1, 1, &g_aArgLogDest[0], ELEMENTS(g_aArgLogDest), NULL, 0, dbgcCmdLogDest, "<dest string>", "Modifies the logging destination (VBOX_LOG_DEST)." },
273 { "logflags", 1, 1, &g_aArgLogFlags[0], ELEMENTS(g_aArgLogFlags), NULL, 0, dbgcCmdLogFlags, "<flags string>", "Modifies the logging flags (VBOX_LOG_FLAGS)." },
274 { "quit", 0, 0, NULL, 0, NULL, 0, dbgcCmdQuit, "", "Exits the debugger." },
275 { "runscript", 1, 1, &g_aArgFilename[0], ELEMENTS(g_aArgFilename), NULL, 0, dbgcCmdRunScript, "<filename>", "Runs the command listed in the script. Lines starting with '#' (after removing blanks) are comment. blank lines are ignored. Stops on failure." },
276 { "set", 2, 2, &g_aArgSet[0], ELEMENTS(g_aArgSet), NULL, 0, dbgcCmdSet, "<var> <value>", "Sets a global variable." },
277 { "showvars", 0, 0, NULL, 0, NULL, 0, dbgcCmdShowVars, "", "List all the defined variables." },
278 { "stop", 0, 0, NULL, 0, NULL, 0, dbgcCmdStop, "", "Stop execution." },
279 { "unset", 1, ~0, &g_aArgMultiStr[0], ELEMENTS(g_aArgMultiStr), NULL, 0, dbgcCmdUnset, "<var1> [var1..[varN]]", "Unsets (delete) one or more global variables." },
280};
281
282
283/** Bitmap where set bits indicates the characters the may start an operator name. */
284static uint32_t g_bmOperatorChars[256 / (4*8)];
285
286
287
288
289
290
291
292
293/**
294 * Prints full command help.
295 */
296static int dbgcPrintHelp(PDBGCCMDHLP pCmdHlp, PCDBGCCMD pCmd, bool fExternal)
297{
298 int rc;
299
300 /* the command */
301 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
302 "%s%-*s %-30s %s",
303 fExternal ? "." : "",
304 fExternal ? 10 : 11,
305 pCmd->pszCmd,
306 pCmd->pszSyntax,
307 pCmd->pszDescription);
308 if (!pCmd->cArgsMin && pCmd->cArgsMin == pCmd->cArgsMax)
309 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, " <no args>\n");
310 else if (pCmd->cArgsMin == pCmd->cArgsMax)
311 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, " <%u args>\n", pCmd->cArgsMin);
312 else if (pCmd->cArgsMax == ~0U)
313 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, " <%u+ args>\n", pCmd->cArgsMin);
314 else
315 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, " <%u to %u args>\n", pCmd->cArgsMin, pCmd->cArgsMax);
316
317 /* argument descriptions. */
318 for (unsigned i = 0; i < pCmd->cArgDescs; i++)
319 {
320 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
321 " %-12s %s",
322 pCmd->paArgDescs[i].pszName,
323 pCmd->paArgDescs[i].pszDescription);
324 if (!pCmd->paArgDescs[i].cTimesMin)
325 {
326 if (pCmd->paArgDescs[i].cTimesMax == ~0U)
327 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, " <optional+>\n");
328 else
329 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, " <optional-%u>\n", pCmd->paArgDescs[i].cTimesMax);
330 }
331 else
332 {
333 if (pCmd->paArgDescs[i].cTimesMax == ~0U)
334 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, " <%u+>\n", pCmd->paArgDescs[i].cTimesMin);
335 else
336 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, " <%u-%u>\n", pCmd->paArgDescs[i].cTimesMin, pCmd->paArgDescs[i].cTimesMax);
337 }
338 }
339 return rc;
340}
341
342
343/**
344 * The 'help' command.
345 *
346 * @returns VBox status.
347 * @param pCmd Pointer to the command descriptor (as registered).
348 * @param pCmdHlp Pointer to command helper functions.
349 * @param pVM Pointer to the current VM (if any).
350 * @param paArgs Pointer to (readonly) array of arguments.
351 * @param cArgs Number of arguments in the array.
352 */
353static DECLCALLBACK(int) dbgcCmdHelp(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
354{
355 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
356 int rc = VINF_SUCCESS;
357 unsigned i;
358
359 if (!cArgs)
360 {
361 /*
362 * All the stuff.
363 */
364 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
365 "VirtualBox Debugger\n"
366 "-------------------\n"
367 "\n"
368 "Commands and Functions:\n");
369 for (i = 0; i < ELEMENTS(g_aCmds); i++)
370 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
371 "%-11s %-30s %s\n",
372 g_aCmds[i].pszCmd,
373 g_aCmds[i].pszSyntax,
374 g_aCmds[i].pszDescription);
375 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
376 "\n"
377 "Emulation: %s\n", pDbgc->pszEmulation);
378 PCDBGCCMD pCmd = pDbgc->paEmulationCmds;
379 for (i = 0; i < pDbgc->cEmulationCmds; i++, pCmd++)
380 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
381 "%-11s %-30s %s\n",
382 pCmd->pszCmd,
383 pCmd->pszSyntax,
384 pCmd->pszDescription);
385
386 if (g_pExtCmdsHead)
387 {
388 DBGCEXTCMDS_LOCK_RD();
389 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
390 "\n"
391 "External Commands and Functions:\n");
392 for (PDBGCEXTCMDS pExtCmd = g_pExtCmdsHead; pExtCmd; pExtCmd = pExtCmd->pNext)
393 for (i = 0; i < pExtCmd->cCmds; i++)
394 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
395 ".%-10s %-30s %s\n",
396 pExtCmd->paCmds[i].pszCmd,
397 pExtCmd->paCmds[i].pszSyntax,
398 pExtCmd->paCmds[i].pszDescription);
399 DBGCEXTCMDS_UNLOCK_RD();
400 }
401
402 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
403 "\n"
404 "Operators:\n");
405 unsigned iPrecedence = 0;
406 unsigned cLeft = g_cOps;
407 while (cLeft > 0)
408 {
409 for (i = 0; i < g_cOps; i++)
410 if (g_aOps[i].iPrecedence == iPrecedence)
411 {
412 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
413 "%-10s %s %s\n",
414 g_aOps[i].szName,
415 g_aOps[i].fBinary ? "Binary" : "Unary ",
416 g_aOps[i].pszDescription);
417 cLeft--;
418 }
419 iPrecedence++;
420 }
421 }
422 else
423 {
424 /*
425 * Search for the arguments (strings).
426 */
427 for (unsigned iArg = 0; iArg < cArgs; iArg++)
428 {
429 Assert(paArgs[iArg].enmType == DBGCVAR_TYPE_STRING);
430 bool fFound = false;
431
432 /* lookup in the emulation command list first */
433 for (i = 0; i < pDbgc->cEmulationCmds; i++)
434 if (!strcmp(pDbgc->paEmulationCmds[i].pszCmd, paArgs[iArg].u.pszString))
435 {
436 rc = dbgcPrintHelp(pCmdHlp, &pDbgc->paEmulationCmds[i], false);
437 fFound = true;
438 break;
439 }
440
441 /* lookup in the command list (even when found in the emulation) */
442 for (i = 0; i < ELEMENTS(g_aCmds); i++)
443 if (!strcmp(g_aCmds[i].pszCmd, paArgs[iArg].u.pszString))
444 {
445 rc = dbgcPrintHelp(pCmdHlp, &g_aCmds[i], false);
446 fFound = true;
447 break;
448 }
449
450 /* external commands */
451 if ( !fFound
452 && g_pExtCmdsHead
453 && paArgs[iArg].u.pszString[0] == '.')
454 {
455 DBGCEXTCMDS_LOCK_RD();
456 for (PDBGCEXTCMDS pExtCmd = g_pExtCmdsHead; pExtCmd; pExtCmd = pExtCmd->pNext)
457 for (i = 0; i < pExtCmd->cCmds; i++)
458 if (!strcmp(pExtCmd->paCmds[i].pszCmd, paArgs[iArg].u.pszString + 1))
459 {
460 rc = dbgcPrintHelp(pCmdHlp, &g_aCmds[i], true);
461 fFound = true;
462 break;
463 }
464 DBGCEXTCMDS_UNLOCK_RD();
465 }
466
467 /* operators */
468 if (!fFound && strlen(paArgs[iArg].u.pszString) < sizeof(g_aOps[i].szName))
469 {
470 for (i = 0; i < g_cOps; i++)
471 if (!strcmp(g_aOps[i].szName, paArgs[iArg].u.pszString))
472 {
473 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
474 "%-10s %s %s\n",
475 g_aOps[i].szName,
476 g_aOps[i].fBinary ? "Binary" : "Unary ",
477 g_aOps[i].pszDescription);
478 fFound = true;
479 break;
480 }
481 }
482
483 /* found? */
484 if (!fFound)
485 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
486 "error: '%s' was not found!\n",
487 paArgs[iArg].u.pszString);
488 } /* foreach argument */
489 }
490
491 NOREF(pCmd);
492 NOREF(pVM);
493 NOREF(pResult);
494 return rc;
495}
496
497
498/**
499 * The 'quit', 'exit' and 'bye' commands.
500 *
501 * @returns VBox status.
502 * @param pCmd Pointer to the command descriptor (as registered).
503 * @param pCmdHlp Pointer to command helper functions.
504 * @param pVM Pointer to the current VM (if any).
505 * @param paArgs Pointer to (readonly) array of arguments.
506 * @param cArgs Number of arguments in the array.
507 */
508static DECLCALLBACK(int) dbgcCmdQuit(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
509{
510 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Quitting console...\n");
511 NOREF(pCmd);
512 NOREF(pVM);
513 NOREF(paArgs);
514 NOREF(cArgs);
515 NOREF(pResult);
516 return VERR_DBGC_QUIT;
517}
518
519
520/**
521 * The 'stop' command.
522 *
523 * @returns VBox status.
524 * @param pCmd Pointer to the command descriptor (as registered).
525 * @param pCmdHlp Pointer to command helper functions.
526 * @param pVM Pointer to the current VM (if any).
527 * @param paArgs Pointer to (readonly) array of arguments.
528 * @param cArgs Number of arguments in the array.
529 */
530static DECLCALLBACK(int) dbgcCmdStop(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
531{
532 /*
533 * Check if the VM is halted or not before trying to halt it.
534 */
535 int rc;
536 if (DBGFR3IsHalted(pVM))
537 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "warning: The VM is already halted...\n");
538 else
539 {
540 rc = DBGFR3Halt(pVM);
541 if (VBOX_SUCCESS(rc))
542 rc = VWRN_DBGC_CMD_PENDING;
543 else
544 rc = pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Executing DBGFR3Halt().");
545 }
546
547 NOREF(pCmd); NOREF(paArgs); NOREF(cArgs); NOREF(pResult);
548 return rc;
549}
550
551
552/**
553 * The 'echo' command.
554 *
555 * @returns VBox status.
556 * @param pCmd Pointer to the command descriptor (as registered).
557 * @param pCmdHlp Pointer to command helper functions.
558 * @param pVM Pointer to the current VM (if any).
559 * @param paArgs Pointer to (readonly) array of arguments.
560 * @param cArgs Number of arguments in the array.
561 */
562static DECLCALLBACK(int) dbgcCmdEcho(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
563{
564 /*
565 * Loop thru the arguments and print them with one space between.
566 */
567 int rc = 0;
568 for (unsigned i = 0; i < cArgs; i++)
569 {
570 if (paArgs[i].enmType == DBGCVAR_TYPE_STRING)
571 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, i ? " %s" : "%s", paArgs[i].u.pszString);
572 else
573 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, i ? " <parser error>" : "<parser error>");
574 if (VBOX_FAILURE(rc))
575 return rc;
576 }
577 NOREF(pCmd); NOREF(pResult); NOREF(pVM);
578 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "\n");
579}
580
581
582/**
583 * The 'runscript' command.
584 *
585 * @returns VBox status.
586 * @param pCmd Pointer to the command descriptor (as registered).
587 * @param pCmdHlp Pointer to command helper functions.
588 * @param pVM Pointer to the current VM (if any).
589 * @param paArgs Pointer to (readonly) array of arguments.
590 * @param cArgs Number of arguments in the array.
591 */
592static DECLCALLBACK(int) dbgcCmdRunScript(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
593{
594 /* check that the parser did what it's supposed to do. */
595 if ( cArgs != 1
596 || paArgs[0].enmType != DBGCVAR_TYPE_STRING)
597 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "parser error\n");
598
599 /*
600 * Try open the script.
601 */
602 const char *pszFilename = paArgs[0].u.pszString;
603 FILE *pFile = fopen(pszFilename, "r");
604 if (!pFile)
605 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Failed to open '%s'.\n", pszFilename);
606
607 /*
608 * Execute it line by line.
609 */
610 int rc = 0;
611 unsigned iLine = 0;
612 char szLine[8192];
613 while (fgets(szLine, sizeof(szLine), pFile))
614 {
615 /* check that the line isn't too long. */
616 char *pszEnd = strchr(szLine, '\0');
617 if (pszEnd == &szLine[sizeof(szLine) - 1])
618 {
619 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "runscript error: Line #%u is too long\n", iLine);
620 break;
621 }
622 iLine++;
623
624 /* strip leading blanks and check for comment / blank line. */
625 char *psz = RTStrStripL(szLine);
626 if ( *psz == '\0'
627 || *psz == '\n'
628 || *psz == '#')
629 continue;
630
631 /* strip trailing blanks and check for empty line (\r case). */
632 while ( pszEnd > psz
633 && isspace(pszEnd[-1])) /* isspace includes \n and \r normally. */
634 *--pszEnd = '\0';
635
636 /** @todo check for Control-C / Cancel at this point... */
637
638 /*
639 * Execute the command.
640 *
641 * This is a bit wasteful with scratch space btw., can fix it later.
642 * The whole return code crap should be fixed too, so that it's possible
643 * to know whether a command succeeded (VBOX_SUCCESS()) or failed, and
644 * more importantly why it failed.
645 */
646 rc = pCmdHlp->pfnExec(pCmdHlp, "%s", psz);
647 if (VBOX_FAILURE(rc))
648 {
649 if (rc == VERR_BUFFER_OVERFLOW)
650 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "runscript error: Line #%u is too long (exec overflowed)\n", iLine);
651 break;
652 }
653 if (rc == VWRN_DBGC_CMD_PENDING)
654 {
655 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "runscript error: VWRN_DBGC_CMD_PENDING on line #%u, script terminated\n", iLine);
656 break;
657 }
658 }
659
660 fclose(pFile);
661
662 NOREF(pCmd); NOREF(pResult); NOREF(pVM);
663 return rc;
664}
665
666
667/**
668 * Print formatted string.
669 *
670 * @param pHlp Pointer to this structure.
671 * @param pszFormat The format string.
672 * @param ... Arguments.
673 */
674static DECLCALLBACK(void) dbgcCmdInfo_Printf(PCDBGFINFOHLP pHlp, const char *pszFormat, ...)
675{
676 PDBGCCMDHLP pCmdHlp = *(PDBGCCMDHLP *)(pHlp + 1);
677 va_list args;
678 va_start(args, pszFormat);
679 pCmdHlp->pfnPrintfV(pCmdHlp, NULL, pszFormat, args);
680 va_end(args);
681}
682
683
684/**
685 * Print formatted string.
686 *
687 * @param pHlp Pointer to this structure.
688 * @param pszFormat The format string.
689 * @param args Argument list.
690 */
691static DECLCALLBACK(void) dbgcCmdInfo_PrintfV(PCDBGFINFOHLP pHlp, const char *pszFormat, va_list args)
692{
693 PDBGCCMDHLP pCmdHlp = *(PDBGCCMDHLP *)(pHlp + 1);
694 pCmdHlp->pfnPrintfV(pCmdHlp, NULL, pszFormat, args);
695}
696
697
698/**
699 * The 'info' command.
700 *
701 * @returns VBox status.
702 * @param pCmd Pointer to the command descriptor (as registered).
703 * @param pCmdHlp Pointer to command helper functions.
704 * @param pVM Pointer to the current VM (if any).
705 * @param paArgs Pointer to (readonly) array of arguments.
706 * @param cArgs Number of arguments in the array.
707 */
708static DECLCALLBACK(int) dbgcCmdInfo(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
709{
710 /*
711 * Validate input.
712 */
713 if ( cArgs < 1
714 || cArgs > 2
715 || paArgs[0].enmType != DBGCVAR_TYPE_STRING
716 || paArgs[cArgs - 1].enmType != DBGCVAR_TYPE_STRING)
717 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "internal error: The parser doesn't do its job properly yet.. quote the string.\n");
718 if (!pVM)
719 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: No VM.\n");
720
721 /*
722 * Dump it.
723 */
724 struct
725 {
726 DBGFINFOHLP Hlp;
727 PDBGCCMDHLP pCmdHlp;
728 } Hlp = { { dbgcCmdInfo_Printf, dbgcCmdInfo_PrintfV }, pCmdHlp };
729 int rc = DBGFR3Info(pVM, paArgs[0].u.pszString, cArgs == 2 ? paArgs[1].u.pszString : NULL, &Hlp.Hlp);
730 if (VBOX_FAILURE(rc))
731 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "DBGFR3Info()\n");
732
733 NOREF(pCmd); NOREF(pResult);
734 return 0;
735}
736
737
738/**
739 * The 'log' command.
740 *
741 * @returns VBox status.
742 * @param pCmd Pointer to the command descriptor (as registered).
743 * @param pCmdHlp Pointer to command helper functions.
744 * @param pVM Pointer to the current VM (if any).
745 * @param paArgs Pointer to (readonly) array of arguments.
746 * @param cArgs Number of arguments in the array.
747 */
748static DECLCALLBACK(int) dbgcCmdLog(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
749{
750 int rc = DBGFR3LogModifyGroups(pVM, paArgs[0].u.pszString);
751 if (VBOX_SUCCESS(rc))
752 return VINF_SUCCESS;
753 NOREF(pCmd); NOREF(cArgs); NOREF(pResult);
754 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "DBGFR3LogModifyGroups(%p,'%s')\n", pVM, paArgs[0].u.pszString);
755}
756
757
758/**
759 * The 'logdest' command.
760 *
761 * @returns VBox status.
762 * @param pCmd Pointer to the command descriptor (as registered).
763 * @param pCmdHlp Pointer to command helper functions.
764 * @param pVM Pointer to the current VM (if any).
765 * @param paArgs Pointer to (readonly) array of arguments.
766 * @param cArgs Number of arguments in the array.
767 */
768static DECLCALLBACK(int) dbgcCmdLogDest(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
769{
770 int rc = DBGFR3LogModifyDestinations(pVM, paArgs[0].u.pszString);
771 if (VBOX_SUCCESS(rc))
772 return VINF_SUCCESS;
773 NOREF(pCmd); NOREF(cArgs); NOREF(pResult);
774 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "DBGFR3LogModifyDestinations(%p,'%s')\n", pVM, paArgs[0].u.pszString);
775}
776
777
778/**
779 * The 'logflags' command.
780 *
781 * @returns VBox status.
782 * @param pCmd Pointer to the command descriptor (as registered).
783 * @param pCmdHlp Pointer to command helper functions.
784 * @param pVM Pointer to the current VM (if any).
785 * @param paArgs Pointer to (readonly) array of arguments.
786 * @param cArgs Number of arguments in the array.
787 */
788static DECLCALLBACK(int) dbgcCmdLogFlags(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
789{
790 int rc = DBGFR3LogModifyFlags(pVM, paArgs[0].u.pszString);
791 if (VBOX_SUCCESS(rc))
792 return VINF_SUCCESS;
793 NOREF(pCmd); NOREF(cArgs); NOREF(pResult);
794 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "DBGFR3LogModifyFlags(%p,'%s')\n", pVM, paArgs[0].u.pszString);
795}
796
797
798/**
799 * The 'format' command.
800 *
801 * @returns VBox status.
802 * @param pCmd Pointer to the command descriptor (as registered).
803 * @param pCmdHlp Pointer to command helper functions.
804 * @param pVM Pointer to the current VM (if any).
805 * @param paArgs Pointer to (readonly) array of arguments.
806 * @param cArgs Number of arguments in the array.
807 */
808static DECLCALLBACK(int) dbgcCmdFormat(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
809{
810 LogFlow(("dbgcCmdFormat\n"));
811 static const char *apszRangeDesc[] =
812 {
813 "none", "bytes", "elements"
814 };
815 int rc;
816
817 for (unsigned iArg = 0; iArg < cArgs; iArg++)
818 {
819 switch (paArgs[iArg].enmType)
820 {
821 case DBGCVAR_TYPE_UNKNOWN:
822 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
823 "Unknown variable type!\n");
824 break;
825 case DBGCVAR_TYPE_GC_FLAT:
826 if (paArgs[iArg].enmRangeType != DBGCVAR_RANGE_NONE)
827 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
828 "Guest flat address: %%%08x range %lld %s\n",
829 paArgs[iArg].u.GCFlat,
830 paArgs[iArg].u64Range,
831 apszRangeDesc[paArgs[iArg].enmRangeType]);
832 else
833 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
834 "Guest flat address: %%%08x\n",
835 paArgs[iArg].u.GCFlat);
836 break;
837 case DBGCVAR_TYPE_GC_FAR:
838 if (paArgs[iArg].enmRangeType != DBGCVAR_RANGE_NONE)
839 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
840 "Guest far address: %04x:%08x range %lld %s\n",
841 paArgs[iArg].u.GCFar.sel,
842 paArgs[iArg].u.GCFar.off,
843 paArgs[iArg].u64Range,
844 apszRangeDesc[paArgs[iArg].enmRangeType]);
845 else
846 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
847 "Guest far address: %04x:%08x\n",
848 paArgs[iArg].u.GCFar.sel,
849 paArgs[iArg].u.GCFar.off);
850 break;
851 case DBGCVAR_TYPE_GC_PHYS:
852 if (paArgs[iArg].enmRangeType != DBGCVAR_RANGE_NONE)
853 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
854 "Guest physical address: %%%%%08x range %lld %s\n",
855 paArgs[iArg].u.GCPhys,
856 paArgs[iArg].u64Range,
857 apszRangeDesc[paArgs[iArg].enmRangeType]);
858 else
859 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
860 "Guest physical address: %%%%%08x\n",
861 paArgs[iArg].u.GCPhys);
862 break;
863 case DBGCVAR_TYPE_HC_FLAT:
864 if (paArgs[iArg].enmRangeType != DBGCVAR_RANGE_NONE)
865 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
866 "Host flat address: %%%08x range %lld %s\n",
867 paArgs[iArg].u.pvHCFlat,
868 paArgs[iArg].u64Range,
869 apszRangeDesc[paArgs[iArg].enmRangeType]);
870 else
871 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
872 "Host flat address: %%%08x\n",
873 paArgs[iArg].u.pvHCFlat);
874 break;
875 case DBGCVAR_TYPE_HC_FAR:
876 if (paArgs[iArg].enmRangeType != DBGCVAR_RANGE_NONE)
877 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
878 "Host far address: %04x:%08x range %lld %s\n",
879 paArgs[iArg].u.HCFar.sel,
880 paArgs[iArg].u.HCFar.off,
881 paArgs[iArg].u64Range,
882 apszRangeDesc[paArgs[iArg].enmRangeType]);
883 else
884 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
885 "Host far address: %04x:%08x\n",
886 paArgs[iArg].u.HCFar.sel,
887 paArgs[iArg].u.HCFar.off);
888 break;
889 case DBGCVAR_TYPE_HC_PHYS:
890 if (paArgs[iArg].enmRangeType != DBGCVAR_RANGE_NONE)
891 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
892 "Host physical address: %VHp range %lld %s\n",
893 paArgs[iArg].u.HCPhys,
894 paArgs[iArg].u64Range,
895 apszRangeDesc[paArgs[iArg].enmRangeType]);
896 else
897 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
898 "Host physical address: %VHp\n",
899 paArgs[iArg].u.HCPhys);
900 break;
901
902 case DBGCVAR_TYPE_STRING:
903 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
904 "String, %lld bytes long: %s\n",
905 paArgs[iArg].u64Range,
906 paArgs[iArg].u.pszString);
907 break;
908
909 case DBGCVAR_TYPE_NUMBER:
910 if (paArgs[iArg].enmRangeType != DBGCVAR_RANGE_NONE)
911 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
912 "Number: hex %llx dec 0i%lld oct 0t%llo range %lld %s\n",
913 paArgs[iArg].u.u64Number,
914 paArgs[iArg].u.u64Number,
915 paArgs[iArg].u.u64Number,
916 paArgs[iArg].u64Range,
917 apszRangeDesc[paArgs[iArg].enmRangeType]);
918 else
919 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
920 "Number: hex %llx dec 0i%lld oct 0t%llo\n",
921 paArgs[iArg].u.u64Number,
922 paArgs[iArg].u.u64Number,
923 paArgs[iArg].u.u64Number);
924 break;
925
926 default:
927 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL,
928 "Invalid argument type %d\n",
929 paArgs[iArg].enmType);
930 break;
931 }
932 } /* arg loop */
933
934 NOREF(pCmd); NOREF(pVM); NOREF(pResult);
935 return 0;
936}
937
938
939/**
940 * The 'loadsyms' command.
941 *
942 * @returns VBox status.
943 * @param pCmd Pointer to the command descriptor (as registered).
944 * @param pCmdHlp Pointer to command helper functions.
945 * @param pVM Pointer to the current VM (if any).
946 * @param paArgs Pointer to (readonly) array of arguments.
947 * @param cArgs Number of arguments in the array.
948 */
949static DECLCALLBACK(int) dbgcCmdLoadSyms(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
950{
951 /*
952 * Validate the parsing and make sense of the input.
953 * This is a mess as usual because we don't trust the parser yet.
954 */
955 if ( cArgs < 1
956 || paArgs[0].enmType != DBGCVAR_TYPE_STRING)
957 {
958 AssertMsgFailed(("Parse error, first argument required to be string!\n"));
959 return VERR_PARSE_INCORRECT_ARG_TYPE;
960 }
961 DBGCVAR AddrVar;
962 RTGCUINTPTR Delta = 0;
963 const char *pszModule = NULL;
964 RTGCUINTPTR ModuleAddress = 0;
965 unsigned cbModule = 0;
966 if (cArgs > 1)
967 {
968 unsigned iArg = 1;
969 if (paArgs[iArg].enmType == DBGCVAR_TYPE_NUMBER)
970 {
971 Delta = (RTGCUINTPTR)paArgs[iArg].u.u64Number;
972 iArg++;
973 }
974 if (iArg < cArgs)
975 {
976 if (paArgs[iArg].enmType != DBGCVAR_TYPE_STRING)
977 {
978 AssertMsgFailed(("Parse error, module argument required to be string!\n"));
979 return VERR_PARSE_INCORRECT_ARG_TYPE;
980 }
981 pszModule = paArgs[iArg].u.pszString;
982 iArg++;
983 if (iArg < cArgs)
984 {
985 if (DBGCVAR_ISPOINTER(paArgs[iArg].enmType))
986 {
987 AssertMsgFailed(("Parse error, module argument required to be GC pointer!\n"));
988 return VERR_PARSE_INCORRECT_ARG_TYPE;
989 }
990 int rc = pCmdHlp->pfnEval(pCmdHlp, &AddrVar, "%%(%Dv)", &paArgs[iArg]);
991 if (VBOX_FAILURE(rc))
992 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Module address cast %%(%Dv) failed.", &paArgs[iArg]);
993 ModuleAddress = paArgs[iArg].u.GCFlat;
994 iArg++;
995 if (iArg < cArgs)
996 {
997 if (paArgs[iArg].enmType != DBGCVAR_TYPE_NUMBER)
998 {
999 AssertMsgFailed(("Parse error, module argument required to be an interger!\n"));
1000 return VERR_PARSE_INCORRECT_ARG_TYPE;
1001 }
1002 cbModule = (unsigned)paArgs[iArg].u.u64Number;
1003 iArg++;
1004 if (iArg < cArgs)
1005 {
1006 AssertMsgFailed(("Parse error, too many arguments!\n"));
1007 return VERR_PARSE_TOO_MANY_ARGUMENTS;
1008 }
1009 }
1010 }
1011 }
1012 }
1013
1014 /*
1015 * Call the debug info manager about this loading...
1016 */
1017 int rc = DBGFR3ModuleLoad(pVM, paArgs[0].u.pszString, Delta, pszModule, ModuleAddress, cbModule);
1018 if (VBOX_FAILURE(rc))
1019 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "DBGInfoSymbolLoad(, '%s', %VGv, '%s', %VGv, 0)\n",
1020 paArgs[0].u.pszString, Delta, pszModule, ModuleAddress);
1021
1022 NOREF(pCmd); NOREF(pResult);
1023 return VINF_SUCCESS;
1024}
1025
1026
1027/**
1028 * The 'set' command.
1029 *
1030 * @returns VBox status.
1031 * @param pCmd Pointer to the command descriptor (as registered).
1032 * @param pCmdHlp Pointer to command helper functions.
1033 * @param pVM Pointer to the current VM (if any).
1034 * @param paArgs Pointer to (readonly) array of arguments.
1035 * @param cArgs Number of arguments in the array.
1036 */
1037static DECLCALLBACK(int) dbgcCmdSet(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
1038{
1039 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1040
1041 /* parse sanity check. */
1042 AssertMsg(paArgs[0].enmType == DBGCVAR_TYPE_STRING, ("expected string not %d as first arg!\n", paArgs[0].enmType));
1043 if (paArgs[0].enmType != DBGCVAR_TYPE_STRING)
1044 return VERR_PARSE_INCORRECT_ARG_TYPE;
1045
1046
1047 /*
1048 * A variable must start with an alpha chars and only contain alpha numerical chars.
1049 */
1050 const char *pszVar = paArgs[0].u.pszString;
1051 if (!isalpha(*pszVar) || *pszVar == '_')
1052 return pCmdHlp->pfnPrintf(pCmdHlp, NULL,
1053 "syntax error: Invalid variable name '%s'. Variable names must match regex '[_a-zA-Z][_a-zA-Z0-9*'!", paArgs[0].u.pszString);
1054
1055 while (isalnum(*pszVar) || *pszVar == '_')
1056 *pszVar++;
1057 if (*pszVar)
1058 return pCmdHlp->pfnPrintf(pCmdHlp, NULL,
1059 "syntax error: Invalid variable name '%s'. Variable names must match regex '[_a-zA-Z][_a-zA-Z0-9*]'!", paArgs[0].u.pszString);
1060
1061
1062 /*
1063 * Calc variable size.
1064 */
1065 size_t cbVar = (size_t)paArgs[0].u64Range + sizeof(DBGCNAMEDVAR);
1066 if (paArgs[1].enmType == DBGCVAR_TYPE_STRING)
1067 cbVar += 1 + (size_t)paArgs[1].u64Range;
1068
1069 /*
1070 * Look for existing one.
1071 */
1072 pszVar = paArgs[0].u.pszString;
1073 for (unsigned iVar = 0; iVar < pDbgc->cVars; iVar++)
1074 {
1075 if (!strcmp(pszVar, pDbgc->papVars[iVar]->szName))
1076 {
1077 /*
1078 * Update existing variable.
1079 */
1080 void *pv = RTMemRealloc(pDbgc->papVars[iVar], cbVar);
1081 if (!pv)
1082 return VERR_PARSE_NO_MEMORY;
1083 PDBGCNAMEDVAR pVar = pDbgc->papVars[iVar] = (PDBGCNAMEDVAR)pv;
1084
1085 pVar->Var = paArgs[1];
1086 memcpy(pVar->szName, paArgs[0].u.pszString, (size_t)paArgs[0].u64Range + 1);
1087 if (paArgs[1].enmType == DBGCVAR_TYPE_STRING)
1088 pVar->Var.u.pszString = (char *)memcpy(&pVar->szName[paArgs[0].u64Range + 1], paArgs[1].u.pszString, (size_t)paArgs[1].u64Range + 1);
1089 return 0;
1090 }
1091 }
1092
1093 /*
1094 * Allocate another.
1095 */
1096 PDBGCNAMEDVAR pVar = (PDBGCNAMEDVAR)RTMemAlloc(cbVar);
1097
1098 pVar->Var = paArgs[1];
1099 memcpy(pVar->szName, pszVar, (size_t)paArgs[0].u64Range + 1);
1100 if (paArgs[1].enmType == DBGCVAR_TYPE_STRING)
1101 pVar->Var.u.pszString = (char *)memcpy(&pVar->szName[paArgs[0].u64Range + 1], paArgs[1].u.pszString, (size_t)paArgs[1].u64Range + 1);
1102
1103 /* need to reallocate the pointer array too? */
1104 if (!(pDbgc->cVars % 0x20))
1105 {
1106 void *pv = RTMemRealloc(pDbgc->papVars, (pDbgc->cVars + 0x20) * sizeof(pDbgc->papVars[0]));
1107 if (!pv)
1108 {
1109 RTMemFree(pVar);
1110 return VERR_PARSE_NO_MEMORY;
1111 }
1112 pDbgc->papVars = (PDBGCNAMEDVAR *)pv;
1113 }
1114 pDbgc->papVars[pDbgc->cVars++] = pVar;
1115
1116 NOREF(pCmd); NOREF(pVM); NOREF(cArgs); NOREF(pResult);
1117 return 0;
1118}
1119
1120
1121/**
1122 * The 'unset' command.
1123 *
1124 * @returns VBox status.
1125 * @param pCmd Pointer to the command descriptor (as registered).
1126 * @param pCmdHlp Pointer to command helper functions.
1127 * @param pVM Pointer to the current VM (if any).
1128 * @param paArgs Pointer to (readonly) array of arguments.
1129 * @param cArgs Number of arguments in the array.
1130 */
1131static DECLCALLBACK(int) dbgcCmdUnset(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
1132{
1133 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1134
1135 /*
1136 * Don't trust the parser.
1137 */
1138 for (unsigned i = 0; i < cArgs; i++)
1139 if (paArgs[i].enmType != DBGCVAR_TYPE_STRING)
1140 {
1141 AssertMsgFailed(("expected strings only. (arg=%d)!\n", i));
1142 return VERR_PARSE_INCORRECT_ARG_TYPE;
1143 }
1144
1145 /*
1146 * Iterate the variables and unset them.
1147 */
1148 for (unsigned iArg = 0; iArg < cArgs; iArg++)
1149 {
1150 const char *pszVar = paArgs[iArg].u.pszString;
1151
1152 /*
1153 * Look up the variable.
1154 */
1155 for (unsigned iVar = 0; iVar < pDbgc->cVars; iVar++)
1156 {
1157 if (!strcmp(pszVar, pDbgc->papVars[iVar]->szName))
1158 {
1159 /*
1160 * Shuffle the array removing this entry.
1161 */
1162 void *pvFree = pDbgc->papVars[iVar];
1163 if (iVar + 1 < pDbgc->cVars)
1164 memmove(&pDbgc->papVars[iVar],
1165 &pDbgc->papVars[iVar + 1],
1166 (pDbgc->cVars - iVar - 1) * sizeof(pDbgc->papVars[0]));
1167 pDbgc->papVars[--pDbgc->cVars] = NULL;
1168
1169 RTMemFree(pvFree);
1170 }
1171 } /* lookup */
1172 } /* arg loop */
1173
1174 NOREF(pCmd); NOREF(pVM); NOREF(pResult);
1175 return 0;
1176}
1177
1178
1179/**
1180 * The 'loadvars' command.
1181 *
1182 * @returns VBox status.
1183 * @param pCmd Pointer to the command descriptor (as registered).
1184 * @param pCmdHlp Pointer to command helper functions.
1185 * @param pVM Pointer to the current VM (if any).
1186 * @param paArgs Pointer to (readonly) array of arguments.
1187 * @param cArgs Number of arguments in the array.
1188 */
1189static DECLCALLBACK(int) dbgcCmdLoadVars(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
1190{
1191 /*
1192 * Don't trust the parser.
1193 */
1194 if ( cArgs != 1
1195 || paArgs[0].enmType != DBGCVAR_TYPE_STRING)
1196 {
1197 AssertMsgFailed(("Expected one string exactly!\n"));
1198 return VERR_PARSE_INCORRECT_ARG_TYPE;
1199 }
1200
1201 /*
1202 * Iterate the variables and unset them.
1203 */
1204 FILE *pFile = fopen(paArgs[0].u.pszString, "r");
1205 if (pFile)
1206 {
1207 char szLine[4096];
1208 while (fgets(szLine, sizeof(szLine), pFile))
1209 {
1210 /* Strip it. */
1211 char *psz = szLine;
1212 while (isblank(*psz))
1213 psz++;
1214 int i = (int)strlen(psz) - 1;
1215 while (i >= 0 && isspace(psz[i]))
1216 psz[i--] ='\0';
1217 /* Execute it if not comment or empty line. */
1218 if ( *psz != '\0'
1219 && *psz != '#'
1220 && *psz != ';')
1221 {
1222 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "dbg: set %s", psz);
1223 pCmdHlp->pfnExec(pCmdHlp, "set %s", psz);
1224 }
1225 }
1226 fclose(pFile);
1227 }
1228 else
1229 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Failed to open file '%s'.\n", paArgs[0].u.pszString);
1230
1231 NOREF(pCmd); NOREF(pVM); NOREF(pResult);
1232 return 0;
1233}
1234
1235
1236/**
1237 * The 'showvars' command.
1238 *
1239 * @returns VBox status.
1240 * @param pCmd Pointer to the command descriptor (as registered).
1241 * @param pCmdHlp Pointer to command helper functions.
1242 * @param pVM Pointer to the current VM (if any).
1243 * @param paArgs Pointer to (readonly) array of arguments.
1244 * @param cArgs Number of arguments in the array.
1245 */
1246static DECLCALLBACK(int) dbgcCmdShowVars(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
1247{
1248 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1249
1250 for (unsigned iVar = 0; iVar < pDbgc->cVars; iVar++)
1251 {
1252 int rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "%-20s ", &pDbgc->papVars[iVar]->szName);
1253 if (!rc)
1254 rc = dbgcCmdFormat(pCmd, pCmdHlp, pVM, &pDbgc->papVars[iVar]->Var, 1, NULL);
1255 if (rc)
1256 return rc;
1257 }
1258
1259 NOREF(paArgs); NOREF(cArgs); NOREF(pResult);
1260 return 0;
1261}
1262
1263
1264/**
1265 * The 'harakiri' command.
1266 *
1267 * @returns VBox status.
1268 * @param pCmd Pointer to the command descriptor (as registered).
1269 * @param pCmdHlp Pointer to command helper functions.
1270 * @param pVM Pointer to the current VM (if any).
1271 * @param paArgs Pointer to (readonly) array of arguments.
1272 * @param cArgs Number of arguments in the array.
1273 */
1274static DECLCALLBACK(int) dbgcCmdHarakiri(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
1275{
1276 Log(("dbgcCmdHarakiri\n"));
1277 for (;;)
1278 exit(126);
1279 NOREF(pCmd); NOREF(pCmdHlp); NOREF(pVM); NOREF(paArgs); NOREF(cArgs); NOREF(pResult);
1280}
1281
1282
1283
1284
1285
1286
1287//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
1288//
1289//
1290// C a l l b a c k H e l p e r s
1291//
1292//
1293//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
1294
1295
1296
1297/**
1298 * Command helper for writing text to the debug console.
1299 *
1300 * @returns VBox status.
1301 * @param pCmdHlp Pointer to the command callback structure.
1302 * @param pvBuf What to write.
1303 * @param cbBuf Number of bytes to write.
1304 * @param pcbWritten Where to store the number of bytes actually written.
1305 * If NULL the entire buffer must be successfully written.
1306 */
1307static DECLCALLBACK(int) dbgcHlpWrite(PDBGCCMDHLP pCmdHlp, const void *pvBuf, size_t cbBuf, size_t *pcbWritten)
1308{
1309 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1310 return pDbgc->pBack->pfnWrite(pDbgc->pBack, pvBuf, cbBuf, pcbWritten);
1311}
1312
1313
1314/**
1315 * Command helper for writing formatted text to the debug console.
1316 *
1317 * @returns VBox status.
1318 * @param pCmdHlp Pointer to the command callback structure.
1319 * @param pcb Where to store the number of bytes written.
1320 * @param pszFormat The format string.
1321 * This is using the log formatter, so it's format extensions can be used.
1322 * @param ... Arguments specified in the format string.
1323 */
1324static DECLCALLBACK(int) dbgcHlpPrintf(PDBGCCMDHLP pCmdHlp, size_t *pcbWritten, const char *pszFormat, ...)
1325{
1326 /*
1327 * Do the formatting and output.
1328 */
1329 va_list args;
1330 va_start(args, pszFormat);
1331 int rc = pCmdHlp->pfnPrintfV(pCmdHlp, pcbWritten, pszFormat, args);
1332 va_end(args);
1333
1334 return rc;
1335}
1336
1337/**
1338 * Callback to format non-standard format specifiers.
1339 *
1340 * @returns The number of bytes formatted.
1341 * @param pvArg Formatter argument.
1342 * @param pfnOutput Pointer to output function.
1343 * @param pvArgOutput Argument for the output function.
1344 * @param ppszFormat Pointer to the format string pointer. Advance this till the char
1345 * after the format specifier.
1346 * @param pArgs Pointer to the argument list. Use this to fetch the arguments.
1347 * @param cchWidth Format Width. -1 if not specified.
1348 * @param cchPrecision Format Precision. -1 if not specified.
1349 * @param fFlags Flags (RTSTR_NTFS_*).
1350 * @param chArgSize The argument size specifier, 'l' or 'L'.
1351 */
1352static DECLCALLBACK(size_t) dbgcStringFormatter(void *pvArg, PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
1353 const char **ppszFormat, va_list *pArgs, int cchWidth,
1354 int cchPrecision, unsigned fFlags, char chArgSize)
1355{
1356 NOREF(cchWidth); NOREF(cchPrecision); NOREF(fFlags); NOREF(chArgSize); NOREF(pvArg);
1357 if (**ppszFormat != 'D')
1358 {
1359 (*ppszFormat)++;
1360 return 0;
1361 }
1362
1363 (*ppszFormat)++;
1364 switch (**ppszFormat)
1365 {
1366 /*
1367 * Print variable without range.
1368 * The argument is a const pointer to the variable.
1369 */
1370 case 'V':
1371 {
1372 (*ppszFormat)++;
1373 PCDBGCVAR pVar = va_arg(*pArgs, PCDBGCVAR);
1374 switch (pVar->enmType)
1375 {
1376 case DBGCVAR_TYPE_GC_FLAT:
1377 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%%%VGv", pVar->u.GCFlat);
1378 case DBGCVAR_TYPE_GC_FAR:
1379 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%04x:%08x", pVar->u.GCFar.sel, pVar->u.GCFar.off);
1380 case DBGCVAR_TYPE_GC_PHYS:
1381 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%%%%%VGp", pVar->u.GCPhys);
1382 case DBGCVAR_TYPE_HC_FLAT:
1383 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%%#%VHv", (uintptr_t)pVar->u.pvHCFlat);
1384 case DBGCVAR_TYPE_HC_FAR:
1385 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "#%04x:%08x", pVar->u.HCFar.sel, pVar->u.HCFar.off);
1386 case DBGCVAR_TYPE_HC_PHYS:
1387 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "#%%%%%VHp", pVar->u.HCPhys);
1388 case DBGCVAR_TYPE_STRING:
1389 return pfnOutput(pvArgOutput, pVar->u.pszString, (size_t)pVar->u64Range);
1390 case DBGCVAR_TYPE_NUMBER:
1391 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%llx", pVar->u.u64Number);
1392
1393 case DBGCVAR_TYPE_UNKNOWN:
1394 default:
1395 return pfnOutput(pvArgOutput, "??", 2);
1396 }
1397 }
1398
1399 /*
1400 * Print variable with range.
1401 * The argument is a const pointer to the variable.
1402 */
1403 case 'v':
1404 {
1405 (*ppszFormat)++;
1406 PCDBGCVAR pVar = va_arg(*pArgs, PCDBGCVAR);
1407
1408 char szRange[32];
1409 switch (pVar->enmRangeType)
1410 {
1411 case DBGCVAR_RANGE_NONE:
1412 szRange[0] = '\0';
1413 break;
1414 case DBGCVAR_RANGE_ELEMENTS:
1415 RTStrPrintf(szRange, sizeof(szRange), " L %llx", pVar->u64Range);
1416 break;
1417 case DBGCVAR_RANGE_BYTES:
1418 RTStrPrintf(szRange, sizeof(szRange), " LB %llx", pVar->u64Range);
1419 break;
1420 }
1421
1422 switch (pVar->enmType)
1423 {
1424 case DBGCVAR_TYPE_GC_FLAT:
1425 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%%%VGv%s", pVar->u.GCFlat, szRange);
1426 case DBGCVAR_TYPE_GC_FAR:
1427 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%04x:%08x%s", pVar->u.GCFar.sel, pVar->u.GCFar.off, szRange);
1428 case DBGCVAR_TYPE_GC_PHYS:
1429 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%%%%%VGp%s", pVar->u.GCPhys, szRange);
1430 case DBGCVAR_TYPE_HC_FLAT:
1431 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%%#%VHv%s", (uintptr_t)pVar->u.pvHCFlat, szRange);
1432 case DBGCVAR_TYPE_HC_FAR:
1433 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "#%04x:%08x%s", pVar->u.HCFar.sel, pVar->u.HCFar.off, szRange);
1434 case DBGCVAR_TYPE_HC_PHYS:
1435 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "#%%%%%VHp%s", pVar->u.HCPhys, szRange);
1436 case DBGCVAR_TYPE_STRING:
1437 return pfnOutput(pvArgOutput, pVar->u.pszString, (size_t)pVar->u64Range);
1438 case DBGCVAR_TYPE_NUMBER:
1439 return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%llx%s", pVar->u.u64Number, szRange);
1440
1441 case DBGCVAR_TYPE_UNKNOWN:
1442 default:
1443 return pfnOutput(pvArgOutput, "??", 2);
1444 }
1445 }
1446
1447 default:
1448 AssertMsgFailed(("Invalid format type '%s'!\n", **ppszFormat));
1449 return 0;
1450 }
1451}
1452
1453
1454/**
1455 * Output callback.
1456 *
1457 * @returns number of bytes written.
1458 * @param pvArg User argument.
1459 * @param pachChars Pointer to an array of utf-8 characters.
1460 * @param cbChars Number of bytes in the character array pointed to by pachChars.
1461 */
1462static DECLCALLBACK(size_t) dbgcFormatOutput(void *pvArg, const char *pachChars, size_t cbChars)
1463{
1464 PDBGC pDbgc = (PDBGC)pvArg;
1465 if (cbChars)
1466 {
1467 int rc = pDbgc->pBack->pfnWrite(pDbgc->pBack, pachChars, cbChars, NULL);
1468 if (VBOX_FAILURE(rc))
1469 {
1470 pDbgc->rcOutput = rc;
1471 cbChars = 0;
1472 }
1473 }
1474
1475 return cbChars;
1476}
1477
1478
1479
1480/**
1481 * Command helper for writing formatted text to the debug console.
1482 *
1483 * @returns VBox status.
1484 * @param pCmdHlp Pointer to the command callback structure.
1485 * @param pcb Where to store the number of bytes written.
1486 * @param pszFormat The format string.
1487 * This is using the log formatter, so it's format extensions can be used.
1488 * @param args Arguments specified in the format string.
1489 */
1490static DECLCALLBACK(int) dbgcHlpPrintfV(PDBGCCMDHLP pCmdHlp, size_t *pcbWritten, const char *pszFormat, va_list args)
1491{
1492 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1493
1494 /*
1495 * Do the formatting and output.
1496 */
1497 pDbgc->rcOutput = 0;
1498 size_t cb = RTStrFormatV(dbgcFormatOutput, pDbgc, dbgcStringFormatter, pDbgc, pszFormat, args);
1499
1500 if (pcbWritten)
1501 *pcbWritten = cb;
1502
1503 return pDbgc->rcOutput;
1504}
1505
1506
1507/**
1508 * Reports an error from a DBGF call.
1509 *
1510 * @returns VBox status code appropriate to return from a command.
1511 * @param pCmdHlp Pointer to command helpers.
1512 * @param rc The VBox status code returned by a DBGF call.
1513 * @param pszFormat Format string for additional messages. Can be NULL.
1514 * @param ... Format arguments, optional.
1515 */
1516static DECLCALLBACK(int) dbgcHlpVBoxErrorV(PDBGCCMDHLP pCmdHlp, int rc, const char *pszFormat, va_list args)
1517{
1518 switch (rc)
1519 {
1520 case VINF_SUCCESS:
1521 break;
1522
1523 default:
1524 rc = pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: %Vrc: %s", rc, pszFormat ? " " : "\n");
1525 if (VBOX_SUCCESS(rc) && pszFormat)
1526 rc = pCmdHlp->pfnPrintfV(pCmdHlp, NULL, pszFormat, args);
1527 break;
1528 }
1529 return rc;
1530}
1531
1532
1533/**
1534 * Reports an error from a DBGF call.
1535 *
1536 * @returns VBox status code appropriate to return from a command.
1537 * @param pCmdHlp Pointer to command helpers.
1538 * @param rc The VBox status code returned by a DBGF call.
1539 * @param pszFormat Format string for additional messages. Can be NULL.
1540 * @param ... Format arguments, optional.
1541 */
1542static DECLCALLBACK(int) dbgcHlpVBoxError(PDBGCCMDHLP pCmdHlp, int rc, const char *pszFormat, ...)
1543{
1544 va_list args;
1545 va_start(args, pszFormat);
1546 int rcRet = pCmdHlp->pfnVBoxErrorV(pCmdHlp, rc, pszFormat, args);
1547 va_end(args);
1548 return rcRet;
1549}
1550
1551
1552/**
1553 * Command helper for reading memory specified by a DBGC variable.
1554 *
1555 * @returns VBox status code appropriate to return from a command.
1556 * @param pCmdHlp Pointer to the command callback structure.
1557 * @param pVM VM handle if GC or physical HC address.
1558 * @param pvBuffer Where to store the read data.
1559 * @param cbRead Number of bytes to read.
1560 * @param pVarPointer DBGC variable specifying where to start reading.
1561 * @param pcbRead Where to store the number of bytes actually read.
1562 * This optional, but it's useful when read GC virtual memory where a
1563 * page in the requested range might not be present.
1564 * If not specified not-present failure or end of a HC physical page
1565 * will cause failure.
1566 */
1567static DECLCALLBACK(int) dbgcHlpMemRead(PDBGCCMDHLP pCmdHlp, PVM pVM, void *pvBuffer, size_t cbRead, PCDBGCVAR pVarPointer, size_t *pcbRead)
1568{
1569 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1570
1571 /*
1572 * Dummy check.
1573 */
1574 if (cbRead == 0)
1575 {
1576 if (*pcbRead)
1577 *pcbRead = 0;
1578 return VINF_SUCCESS;
1579 }
1580
1581 /*
1582 * Convert Far addresses getting size and the correct base address.
1583 * Getting and checking the size is what makes this messy and slow.
1584 */
1585 DBGCVAR Var = *pVarPointer;
1586 switch (pVarPointer->enmType)
1587 {
1588 case DBGCVAR_TYPE_GC_FAR:
1589 {
1590 /* Use DBGFR3AddrFromSelOff for the conversion. */
1591 Assert(pDbgc->pVM);
1592 DBGFADDRESS Address;
1593 int rc = DBGFR3AddrFromSelOff(pDbgc->pVM, &Address, Var.u.GCFar.sel, Var.u.GCFar.off);
1594 if (VBOX_FAILURE(rc))
1595 return rc;
1596
1597 /* don't bother with flat selectors (for now). */
1598 if (!DBGFADDRESS_IS_FLAT(&Address))
1599 {
1600 SELMSELINFO SelInfo;
1601 rc = SELMR3GetSelectorInfo(pDbgc->pVM, Address.Sel, &SelInfo);
1602 if (VBOX_SUCCESS(rc))
1603 {
1604 RTGCUINTPTR cb; /* -1 byte */
1605 if (SELMSelInfoIsExpandDown(&SelInfo))
1606 {
1607 if ( !SelInfo.Raw.Gen.u1Granularity
1608 && Address.off > UINT16_C(0xffff))
1609 return VERR_OUT_OF_SELECTOR_BOUNDS;
1610 if (Address.off <= SelInfo.cbLimit)
1611 return VERR_OUT_OF_SELECTOR_BOUNDS;
1612 cb = (SelInfo.Raw.Gen.u1Granularity ? UINT32_C(0xffffffff) : UINT32_C(0xffff)) - Address.off;
1613 }
1614 else
1615 {
1616 if (Address.off > SelInfo.cbLimit)
1617 return VERR_OUT_OF_SELECTOR_BOUNDS;
1618 cb = SelInfo.cbLimit - Address.off;
1619 }
1620 if (cbRead - 1 > cb)
1621 {
1622 if (!pcbRead)
1623 return VERR_OUT_OF_SELECTOR_BOUNDS;
1624 cbRead = cb + 1;
1625 }
1626 }
1627
1628 Var.enmType = DBGCVAR_TYPE_GC_FLAT;
1629 Var.u.GCFlat = Address.FlatPtr;
1630 }
1631 break;
1632 }
1633
1634 case DBGCVAR_TYPE_GC_FLAT:
1635 case DBGCVAR_TYPE_GC_PHYS:
1636 case DBGCVAR_TYPE_HC_FLAT:
1637 case DBGCVAR_TYPE_HC_PHYS:
1638 break;
1639
1640 case DBGCVAR_TYPE_HC_FAR: /* not supported yet! */
1641 default:
1642 return VERR_NOT_IMPLEMENTED;
1643 }
1644
1645
1646
1647 /*
1648 * Copy page by page.
1649 */
1650 size_t cbLeft = cbRead;
1651 for (;;)
1652 {
1653 /*
1654 * Calc read size.
1655 */
1656 size_t cb = RT_MIN(PAGE_SIZE, cbLeft);
1657 switch (pVarPointer->enmType)
1658 {
1659 case DBGCVAR_TYPE_GC_FLAT: cb = RT_MIN(cb, PAGE_SIZE - (Var.u.GCFlat & PAGE_OFFSET_MASK)); break;
1660 case DBGCVAR_TYPE_GC_PHYS: cb = RT_MIN(cb, PAGE_SIZE - (Var.u.GCPhys & PAGE_OFFSET_MASK)); break;
1661 case DBGCVAR_TYPE_HC_FLAT: cb = RT_MIN(cb, PAGE_SIZE - ((uintptr_t)Var.u.pvHCFlat & PAGE_OFFSET_MASK)); break;
1662 case DBGCVAR_TYPE_HC_PHYS: cb = RT_MIN(cb, PAGE_SIZE - ((size_t)Var.u.HCPhys & PAGE_OFFSET_MASK)); break; /* size_t: MSC has braindead loss of data warnings! */
1663 default: break;
1664 }
1665
1666 /*
1667 * Perform read.
1668 */
1669 int rc;
1670 switch (Var.enmType)
1671 {
1672 case DBGCVAR_TYPE_GC_FLAT:
1673 rc = MMR3ReadGCVirt(pVM, pvBuffer, Var.u.GCFlat, cb);
1674 break;
1675 case DBGCVAR_TYPE_GC_PHYS:
1676 rc = PGMPhysReadGCPhys(pVM, pvBuffer, Var.u.GCPhys, cb);
1677 break;
1678
1679 case DBGCVAR_TYPE_HC_PHYS:
1680 case DBGCVAR_TYPE_HC_FLAT:
1681 case DBGCVAR_TYPE_HC_FAR:
1682 {
1683 DBGCVAR Var2;
1684 rc = dbgcOpAddrFlat(pDbgc, &Var, &Var2);
1685 if (VBOX_SUCCESS(rc))
1686 {
1687 /** @todo protect this!!! */
1688 memcpy(pvBuffer, Var2.u.pvHCFlat, cb);
1689 rc = 0;
1690 }
1691 else
1692 rc = VERR_INVALID_POINTER;
1693 break;
1694 }
1695
1696 default:
1697 rc = VERR_PARSE_INCORRECT_ARG_TYPE;
1698 }
1699
1700 /*
1701 * Check for failure.
1702 */
1703 if (VBOX_FAILURE(rc))
1704 {
1705 if (pcbRead && (*pcbRead = cbRead - cbLeft) > 0)
1706 return VINF_SUCCESS;
1707 return rc;
1708 }
1709
1710 /*
1711 * Next.
1712 */
1713 cbLeft -= cb;
1714 if (!cbLeft)
1715 break;
1716 pvBuffer = (char *)pvBuffer + cb;
1717 rc = pCmdHlp->pfnEval(pCmdHlp, &Var, "%DV + %d", &Var, cb);
1718 if (VBOX_FAILURE(rc))
1719 {
1720 if (pcbRead && (*pcbRead = cbRead - cbLeft) > 0)
1721 return VINF_SUCCESS;
1722 return rc;
1723 }
1724 }
1725
1726 /*
1727 * Done
1728 */
1729 if (pcbRead)
1730 *pcbRead = cbRead;
1731 return 0;
1732}
1733
1734/**
1735 * Command helper for writing memory specified by a DBGC variable.
1736 *
1737 * @returns VBox status code appropriate to return from a command.
1738 * @param pCmdHlp Pointer to the command callback structure.
1739 * @param pVM VM handle if GC or physical HC address.
1740 * @param pvBuffer What to write.
1741 * @param cbWrite Number of bytes to write.
1742 * @param pVarPointer DBGC variable specifying where to start reading.
1743 * @param pcbWritten Where to store the number of bytes written.
1744 * This is optional. If NULL be aware that some of the buffer
1745 * might have been written to the specified address.
1746 */
1747static DECLCALLBACK(int) dbgcHlpMemWrite(PDBGCCMDHLP pCmdHlp, PVM pVM, const void *pvBuffer, size_t cbWrite, PCDBGCVAR pVarPointer, size_t *pcbWritten)
1748{
1749 NOREF(pCmdHlp); NOREF(pVM); NOREF(pvBuffer); NOREF(cbWrite); NOREF(pVarPointer); NOREF(pcbWritten);
1750 return VERR_NOT_IMPLEMENTED;
1751}
1752
1753
1754/**
1755 * Evaluates an expression.
1756 * (Hopefully the parser and functions are fully reentrant.)
1757 *
1758 * @returns VBox status code appropriate to return from a command.
1759 * @param pCmdHlp Pointer to the command callback structure.
1760 * @param pResult Where to store the result.
1761 * @param pszExpr The expression. Format string with the format DBGC extensions.
1762 * @param ... Format arguments.
1763 */
1764static DECLCALLBACK(int) dbgcHlpEval(PDBGCCMDHLP pCmdHlp, PDBGCVAR pResult, const char *pszExpr, ...)
1765{
1766 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1767
1768 /*
1769 * Format the expression.
1770 */
1771 char szExprFormatted[2048];
1772 va_list args;
1773 va_start(args, pszExpr);
1774 size_t cb = RTStrPrintfExV(dbgcStringFormatter, pDbgc, szExprFormatted, sizeof(szExprFormatted), pszExpr, args);
1775 va_end(args);
1776 /* ignore overflows. */
1777
1778 return dbgcEvalSub(pDbgc, &szExprFormatted[0], cb, pResult);
1779}
1780
1781
1782/**
1783 * Executes one command expression.
1784 * (Hopefully the parser and functions are fully reentrant.)
1785 *
1786 * @returns VBox status code appropriate to return from a command.
1787 * @param pCmdHlp Pointer to the command callback structure.
1788 * @param pszExpr The expression. Format string with the format DBGC extensions.
1789 * @param ... Format arguments.
1790 */
1791static DECLCALLBACK(int) dbgcHlpExec(PDBGCCMDHLP pCmdHlp, const char *pszExpr, ...)
1792{
1793 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1794 /* Save the scratch state. */
1795 char *pszScratch = pDbgc->pszScratch;
1796 unsigned iArg = pDbgc->iArg;
1797
1798 /*
1799 * Format the expression.
1800 */
1801 va_list args;
1802 va_start(args, pszExpr);
1803 size_t cbScratch = sizeof(pDbgc->achScratch) - (pDbgc->pszScratch - &pDbgc->achScratch[0]);
1804 size_t cb = RTStrPrintfExV(dbgcStringFormatter, pDbgc, pDbgc->pszScratch, cbScratch, pszExpr, args);
1805 va_end(args);
1806 if (cb >= cbScratch)
1807 return VERR_BUFFER_OVERFLOW;
1808
1809 /*
1810 * Execute the command.
1811 * We save and restore the arg index and scratch buffer pointer.
1812 */
1813 pDbgc->pszScratch = pDbgc->pszScratch + cb + 1;
1814 int rc = dbgcProcessCommand(pDbgc, pszScratch, cb);
1815
1816 /* Restore the scratch state. */
1817 pDbgc->iArg = iArg;
1818 pDbgc->pszScratch = pszScratch;
1819
1820 return rc;
1821}
1822
1823
1824/**
1825 * Converts a DBGC variable to a DBGF address structure.
1826 *
1827 * @returns VBox status code.
1828 * @param pCmdHlp Pointer to the command callback structure.
1829 * @param pVar The variable to convert.
1830 * @param pAddress The target address.
1831 */
1832static DECLCALLBACK(int) dbgcHlpVarToDbgfAddr(PDBGCCMDHLP pCmdHlp, PCDBGCVAR pVar, PDBGFADDRESS pAddress)
1833{
1834 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1835 return dbgcVarToDbgfAddr(pDbgc, pVar, pAddress);
1836}
1837
1838
1839/**
1840 * Converts a DBGC variable to a boolean.
1841 *
1842 * @returns VBox status code.
1843 * @param pCmdHlp Pointer to the command callback structure.
1844 * @param pVar The variable to convert.
1845 * @param pf Where to store the boolean.
1846 */
1847static DECLCALLBACK(int) dbgcHlpVarToBool(PDBGCCMDHLP pCmdHlp, PCDBGCVAR pVar, bool *pf)
1848{
1849 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1850 NOREF(pDbgc);
1851
1852 switch (pVar->enmType)
1853 {
1854 case DBGCVAR_TYPE_STRING:
1855 /** @todo add strcasecmp / stricmp wrappers to iprt/string.h. */
1856 if ( !strcmp(pVar->u.pszString, "true")
1857 || !strcmp(pVar->u.pszString, "True")
1858 || !strcmp(pVar->u.pszString, "TRUE")
1859 || !strcmp(pVar->u.pszString, "on")
1860 || !strcmp(pVar->u.pszString, "On")
1861 || !strcmp(pVar->u.pszString, "oN")
1862 || !strcmp(pVar->u.pszString, "ON")
1863 || !strcmp(pVar->u.pszString, "enabled")
1864 || !strcmp(pVar->u.pszString, "Enabled")
1865 || !strcmp(pVar->u.pszString, "DISABLED"))
1866 {
1867 *pf = true;
1868 return VINF_SUCCESS;
1869 }
1870 if ( !strcmp(pVar->u.pszString, "false")
1871 || !strcmp(pVar->u.pszString, "False")
1872 || !strcmp(pVar->u.pszString, "FALSE")
1873 || !strcmp(pVar->u.pszString, "off")
1874 || !strcmp(pVar->u.pszString, "Off")
1875 || !strcmp(pVar->u.pszString, "OFF")
1876 || !strcmp(pVar->u.pszString, "disabled")
1877 || !strcmp(pVar->u.pszString, "Disabled")
1878 || !strcmp(pVar->u.pszString, "DISABLED"))
1879 {
1880 *pf = false;
1881 return VINF_SUCCESS;
1882 }
1883 return VERR_PARSE_INCORRECT_ARG_TYPE; /** @todo better error code! */
1884
1885 case DBGCVAR_TYPE_GC_FLAT:
1886 case DBGCVAR_TYPE_GC_PHYS:
1887 case DBGCVAR_TYPE_HC_FLAT:
1888 case DBGCVAR_TYPE_HC_PHYS:
1889 case DBGCVAR_TYPE_NUMBER:
1890 *pf = pVar->u.u64Number != 0;
1891 return VINF_SUCCESS;
1892
1893 case DBGCVAR_TYPE_HC_FAR:
1894 case DBGCVAR_TYPE_GC_FAR:
1895 case DBGCVAR_TYPE_SYMBOL:
1896 default:
1897 return VERR_PARSE_INCORRECT_ARG_TYPE;
1898 }
1899}
1900
1901
1902
1903
1904
1905//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
1906//
1907//
1908// V a r i a b l e M a n i p u l a t i o n
1909//
1910//
1911//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
1912
1913
1914
1915/** @todo move me!*/
1916void dbgcVarSetGCFlat(PDBGCVAR pVar, RTGCPTR GCFlat)
1917{
1918 if (pVar)
1919 {
1920 pVar->enmType = DBGCVAR_TYPE_GC_FLAT;
1921 pVar->u.GCFlat = GCFlat;
1922 pVar->enmRangeType = DBGCVAR_RANGE_NONE;
1923 pVar->u64Range = 0;
1924 }
1925}
1926
1927
1928/** @todo move me!*/
1929void dbgcVarSetGCFlatByteRange(PDBGCVAR pVar, RTGCPTR GCFlat, uint64_t cb)
1930{
1931 if (pVar)
1932 {
1933 pVar->enmType = DBGCVAR_TYPE_GC_FLAT;
1934 pVar->u.GCFlat = GCFlat;
1935 pVar->enmRangeType = DBGCVAR_RANGE_BYTES;
1936 pVar->u64Range = cb;
1937 }
1938}
1939
1940
1941/** @todo move me!*/
1942void dbgcVarSetVar(PDBGCVAR pVar, PCDBGCVAR pVar2)
1943{
1944 if (pVar)
1945 {
1946 if (pVar2)
1947 *pVar = *pVar2;
1948 else
1949 {
1950 pVar->enmType = DBGCVAR_TYPE_UNKNOWN;
1951 memset(&pVar->u, 0, sizeof(pVar->u));
1952 pVar->enmRangeType = DBGCVAR_RANGE_NONE;
1953 pVar->u64Range = 0;
1954 }
1955 }
1956}
1957
1958
1959/** @todo move me!*/
1960void dbgcVarSetByteRange(PDBGCVAR pVar, uint64_t cb)
1961{
1962 if (pVar)
1963 {
1964 pVar->enmRangeType = DBGCVAR_RANGE_BYTES;
1965 pVar->u64Range = cb;
1966 }
1967}
1968
1969
1970/** @todo move me!*/
1971void dbgcVarSetNoRange(PDBGCVAR pVar)
1972{
1973 if (pVar)
1974 {
1975 pVar->enmRangeType = DBGCVAR_RANGE_NONE;
1976 pVar->u64Range = 0;
1977 }
1978}
1979
1980
1981/**
1982 * Converts a DBGC variable to a DBGF address.
1983 *
1984 * @returns VBox status code.
1985 * @param pDbgc The DBGC instance.
1986 * @param pVar The variable.
1987 * @param pAddress Where to store the address.
1988 */
1989int dbgcVarToDbgfAddr(PDBGC pDbgc, PCDBGCVAR pVar, PDBGFADDRESS pAddress)
1990{
1991 AssertReturn(pVar, VERR_INVALID_PARAMETER);
1992 switch (pVar->enmType)
1993 {
1994 case DBGCVAR_TYPE_GC_FLAT:
1995 DBGFR3AddrFromFlat(pDbgc->pVM, pAddress, pVar->u.GCFlat);
1996 return VINF_SUCCESS;
1997
1998 case DBGCVAR_TYPE_NUMBER:
1999 DBGFR3AddrFromFlat(pDbgc->pVM, pAddress, (RTGCUINTPTR)pVar->u.u64Number);
2000 return VINF_SUCCESS;
2001
2002 case DBGCVAR_TYPE_GC_FAR:
2003 return DBGFR3AddrFromSelOff(pDbgc->pVM, pAddress, pVar->u.GCFar.sel, pVar->u.GCFar.sel);
2004
2005 case DBGCVAR_TYPE_STRING:
2006 case DBGCVAR_TYPE_SYMBOL:
2007 {
2008 DBGCVAR Var;
2009 int rc = pDbgc->CmdHlp.pfnEval(&pDbgc->CmdHlp, &Var, "%%(%DV)", pVar);
2010 if (VBOX_FAILURE(rc))
2011 return rc;
2012 return dbgcVarToDbgfAddr(pDbgc, &Var, pAddress);
2013 }
2014
2015 case DBGCVAR_TYPE_GC_PHYS:
2016 case DBGCVAR_TYPE_HC_FLAT:
2017 case DBGCVAR_TYPE_HC_FAR:
2018 case DBGCVAR_TYPE_HC_PHYS:
2019 default:
2020 return VERR_PARSE_CONVERSION_FAILED;
2021 }
2022}
2023
2024
2025
2026//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
2027//
2028//
2029// B r e a k p o i n t M a n a g e m e n t
2030//
2031//
2032//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
2033
2034
2035/**
2036 * Adds a breakpoint to the DBGC breakpoint list.
2037 */
2038int dbgcBpAdd(PDBGC pDbgc, RTUINT iBp, const char *pszCmd)
2039{
2040 /*
2041 * Check if it already exists.
2042 */
2043 PDBGCBP pBp = dbgcBpGet(pDbgc, iBp);
2044 if (pBp)
2045 return VERR_DBGC_BP_EXISTS;
2046
2047 /*
2048 * Add the breakpoint.
2049 */
2050 if (pszCmd)
2051 pszCmd = RTStrStripL(pszCmd);
2052 size_t cchCmd = pszCmd ? strlen(pszCmd) : 0;
2053 pBp = (PDBGCBP)RTMemAlloc(RT_OFFSETOF(DBGCBP, szCmd[cchCmd + 1]));
2054 if (!pBp)
2055 return VERR_NO_MEMORY;
2056 if (cchCmd)
2057 memcpy(pBp->szCmd, pszCmd, cchCmd + 1);
2058 else
2059 pBp->szCmd[0] = '\0';
2060 pBp->cchCmd = cchCmd;
2061 pBp->iBp = iBp;
2062 pBp->pNext = pDbgc->pFirstBp;
2063 pDbgc->pFirstBp = pBp;
2064
2065 return VINF_SUCCESS;
2066}
2067
2068/**
2069 * Updates the a breakpoint.
2070 *
2071 * @returns VBox status code.
2072 * @param pDbgc The DBGC instance.
2073 * @param iBp The breakpoint to update.
2074 * @param pszCmd The new command.
2075 */
2076int dbgcBpUpdate(PDBGC pDbgc, RTUINT iBp, const char *pszCmd)
2077{
2078 /*
2079 * Find the breakpoint.
2080 */
2081 PDBGCBP pBp = dbgcBpGet(pDbgc, iBp);
2082 if (!pBp)
2083 return VERR_DBGC_BP_NOT_FOUND;
2084
2085 /*
2086 * Do we need to reallocate?
2087 */
2088 if (pszCmd)
2089 pszCmd = RTStrStripL(pszCmd);
2090 if (!pszCmd || !*pszCmd)
2091 pBp->szCmd[0] = '\0';
2092 else
2093 {
2094 size_t cchCmd = strlen(pszCmd);
2095 if (strlen(pBp->szCmd) >= cchCmd)
2096 {
2097 memcpy(pBp->szCmd, pszCmd, cchCmd + 1);
2098 pBp->cchCmd = cchCmd;
2099 }
2100 else
2101 {
2102 /*
2103 * Yes, let's do it the simple way...
2104 */
2105 int rc = dbgcBpDelete(pDbgc, iBp);
2106 AssertRC(rc);
2107 return dbgcBpAdd(pDbgc, iBp, pszCmd);
2108 }
2109 }
2110 return VINF_SUCCESS;
2111}
2112
2113
2114/**
2115 * Deletes a breakpoint.
2116 *
2117 * @returns VBox status code.
2118 * @param pDbgc The DBGC instance.
2119 * @param iBp The breakpoint to delete.
2120 */
2121int dbgcBpDelete(PDBGC pDbgc, RTUINT iBp)
2122{
2123 /*
2124 * Search thru the list, when found unlink and free it.
2125 */
2126 PDBGCBP pBpPrev = NULL;
2127 PDBGCBP pBp = pDbgc->pFirstBp;
2128 for (; pBp; pBp = pBp->pNext)
2129 {
2130 if (pBp->iBp == iBp)
2131 {
2132 if (pBpPrev)
2133 pBpPrev->pNext = pBp->pNext;
2134 else
2135 pDbgc->pFirstBp = pBp->pNext;
2136 RTMemFree(pBp);
2137 return VINF_SUCCESS;
2138 }
2139 pBpPrev = pBp;
2140 }
2141
2142 return VERR_DBGC_BP_NOT_FOUND;
2143}
2144
2145
2146/**
2147 * Get a breakpoint.
2148 *
2149 * @returns Pointer to the breakpoint.
2150 * @returns NULL if the breakpoint wasn't found.
2151 * @param pDbgc The DBGC instance.
2152 * @param iBp The breakpoint to get.
2153 */
2154PDBGCBP dbgcBpGet(PDBGC pDbgc, RTUINT iBp)
2155{
2156 /*
2157 * Enumerate the list.
2158 */
2159 PDBGCBP pBp = pDbgc->pFirstBp;
2160 for (; pBp; pBp = pBp->pNext)
2161 if (pBp->iBp == iBp)
2162 return pBp;
2163 return NULL;
2164}
2165
2166
2167/**
2168 * Executes the command of a breakpoint.
2169 *
2170 * @returns VINF_DBGC_BP_NO_COMMAND if there is no command associated with the breakpoint.
2171 * @returns VERR_DBGC_BP_NOT_FOUND if the breakpoint wasn't found.
2172 * @returns VERR_BUFFER_OVERFLOW if the is not enough space in the scratch buffer for the command.
2173 * @returns VBox status code from dbgcProcessCommand() other wise.
2174 * @param pDbgc The DBGC instance.
2175 * @param iBp The breakpoint to execute.
2176 */
2177int dbgcBpExec(PDBGC pDbgc, RTUINT iBp)
2178{
2179 /*
2180 * Find the breakpoint.
2181 */
2182 PDBGCBP pBp = dbgcBpGet(pDbgc, iBp);
2183 if (!pBp)
2184 return VERR_DBGC_BP_NOT_FOUND;
2185
2186 /*
2187 * Anything to do?
2188 */
2189 if (!pBp->cchCmd)
2190 return VINF_DBGC_BP_NO_COMMAND;
2191
2192 /*
2193 * Execute the command.
2194 * This means copying it to the scratch buffer and process it as if it
2195 * were user input. We must save and restore the state of the scratch buffer.
2196 */
2197 /* Save the scratch state. */
2198 char *pszScratch = pDbgc->pszScratch;
2199 unsigned iArg = pDbgc->iArg;
2200
2201 /* Copy the command to the scratch buffer. */
2202 size_t cbScratch = sizeof(pDbgc->achScratch) - (pDbgc->pszScratch - &pDbgc->achScratch[0]);
2203 if (pBp->cchCmd >= cbScratch)
2204 return VERR_BUFFER_OVERFLOW;
2205 memcpy(pDbgc->pszScratch, pBp->szCmd, pBp->cchCmd + 1);
2206
2207 /* Execute the command. */
2208 pDbgc->pszScratch = pDbgc->pszScratch + pBp->cchCmd + 1;
2209 int rc = dbgcProcessCommand(pDbgc, pszScratch, pBp->cchCmd);
2210
2211 /* Restore the scratch state. */
2212 pDbgc->iArg = iArg;
2213 pDbgc->pszScratch = pszScratch;
2214
2215 return rc;
2216}
2217
2218
2219
2220
2221
2222//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
2223//
2224//
2225// I n p u t , p a r s i n g a n d l o g g i n g
2226//
2227//
2228//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
2229
2230
2231
2232/**
2233 * Prints any log lines from the log buffer.
2234 *
2235 * The caller must not call function this unless pDbgc->fLog is set.
2236 *
2237 * @returns VBox status. (output related)
2238 * @param pDbgc Debugger console instance data.
2239 */
2240static int dbgcProcessLog(PDBGC pDbgc)
2241{
2242 /** @todo */
2243 NOREF(pDbgc);
2244 return 0;
2245}
2246
2247
2248
2249/**
2250 * Handle input buffer overflow.
2251 *
2252 * Will read any available input looking for a '\n' to reset the buffer on.
2253 *
2254 * @returns VBox status.
2255 * @param pDbgc Debugger console instance data.
2256 */
2257static int dbgcInputOverflow(PDBGC pDbgc)
2258{
2259 /*
2260 * Assert overflow status and reset the input buffer.
2261 */
2262 if (!pDbgc->fInputOverflow)
2263 {
2264 pDbgc->fInputOverflow = true;
2265 pDbgc->iRead = pDbgc->iWrite = 0;
2266 pDbgc->cInputLines = 0;
2267 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "Input overflow!!\n");
2268 }
2269
2270 /*
2271 * Eat input till no more or there is a '\n'.
2272 * When finding a '\n' we'll continue normal processing.
2273 */
2274 while (pDbgc->pBack->pfnInput(pDbgc->pBack, 0))
2275 {
2276 size_t cbRead;
2277 int rc = pDbgc->pBack->pfnRead(pDbgc->pBack, &pDbgc->achInput[0], sizeof(pDbgc->achInput) - 1, &cbRead);
2278 if (VBOX_FAILURE(rc))
2279 return rc;
2280 char *psz = (char *)memchr(&pDbgc->achInput[0], '\n', cbRead);
2281 if (psz)
2282 {
2283 pDbgc->fInputOverflow = false;
2284 pDbgc->iRead = psz - &pDbgc->achInput[0] + 1;
2285 pDbgc->iWrite = (unsigned)cbRead;
2286 pDbgc->cInputLines = 0;
2287 break;
2288 }
2289 }
2290
2291 return 0;
2292}
2293
2294
2295
2296/**
2297 * Read input and do some preprocessing.
2298 *
2299 * @returns VBox status.
2300 * In addition to the iWrite and achInput, cInputLines is maintained.
2301 * In case of an input overflow the fInputOverflow flag will be set.
2302 * @param pDbgc Debugger console instance data.
2303 */
2304static int dbgcInputRead(PDBGC pDbgc)
2305{
2306 /*
2307 * We have ready input.
2308 * Read it till we don't have any or we have a full input buffer.
2309 */
2310 int rc = 0;
2311 do
2312 {
2313 /*
2314 * More available buffer space?
2315 */
2316 size_t cbLeft;
2317 if (pDbgc->iWrite > pDbgc->iRead)
2318 cbLeft = sizeof(pDbgc->achInput) - pDbgc->iWrite - (pDbgc->iRead == 0);
2319 else
2320 cbLeft = pDbgc->iRead - pDbgc->iWrite - 1;
2321 if (!cbLeft)
2322 {
2323 /* overflow? */
2324 if (!pDbgc->cInputLines)
2325 rc = dbgcInputOverflow(pDbgc);
2326 break;
2327 }
2328
2329 /*
2330 * Read one char and interpret it.
2331 */
2332 char achRead[128];
2333 size_t cbRead;
2334 rc = pDbgc->pBack->pfnRead(pDbgc->pBack, &achRead[0], RT_MIN(cbLeft, sizeof(achRead)), &cbRead);
2335 if (VBOX_FAILURE(rc))
2336 return rc;
2337 char *psz = &achRead[0];
2338 while (cbRead-- > 0)
2339 {
2340 char ch = *psz++;
2341 switch (ch)
2342 {
2343 /*
2344 * Ignore.
2345 */
2346 case '\0':
2347 case '\r':
2348 case '\a':
2349 break;
2350
2351 /*
2352 * Backspace.
2353 */
2354 case '\b':
2355 Log2(("DBGC: backspace\n"));
2356 if (pDbgc->iRead != pDbgc->iWrite)
2357 {
2358 unsigned iWriteUndo = pDbgc->iWrite;
2359 if (pDbgc->iWrite)
2360 pDbgc->iWrite--;
2361 else
2362 pDbgc->iWrite = sizeof(pDbgc->achInput) - 1;
2363
2364 if (pDbgc->achInput[pDbgc->iWrite] == '\n')
2365 pDbgc->iWrite = iWriteUndo;
2366 }
2367 break;
2368
2369 /*
2370 * Add char to buffer.
2371 */
2372 case '\t':
2373 case '\n':
2374 case ';':
2375 switch (ch)
2376 {
2377 case '\t': ch = ' '; break;
2378 case '\n': pDbgc->cInputLines++; break;
2379 }
2380 default:
2381 Log2(("DBGC: ch=%02x\n", (unsigned char)ch));
2382 pDbgc->achInput[pDbgc->iWrite] = ch;
2383 if (++pDbgc->iWrite >= sizeof(pDbgc->achInput))
2384 pDbgc->iWrite = 0;
2385 break;
2386 }
2387 }
2388
2389 /* Terminate it to make it easier to read in the debugger. */
2390 pDbgc->achInput[pDbgc->iWrite] = '\0';
2391 } while (pDbgc->pBack->pfnInput(pDbgc->pBack, 0));
2392
2393 return rc;
2394}
2395
2396
2397
2398/**
2399 * Resolves a symbol (or tries to do so at least).
2400 *
2401 * @returns 0 on success.
2402 * @returns VBox status on failure.
2403 * @param pDbgc The debug console instance.
2404 * @param pszSymbol The symbol name.
2405 * @param enmType The result type.
2406 * @param pResult Where to store the result.
2407 */
2408int dbgcSymbolGet(PDBGC pDbgc, const char *pszSymbol, DBGCVARTYPE enmType, PDBGCVAR pResult)
2409{
2410 /*
2411 * Builtin?
2412 */
2413 PCDBGCSYM pSymDesc = dbgcLookupRegisterSymbol(pDbgc, pszSymbol);
2414 if (pSymDesc)
2415 {
2416 if (!pSymDesc->pfnGet)
2417 return VERR_PARSE_WRITEONLY_SYMBOL;
2418 return pSymDesc->pfnGet(pSymDesc, &pDbgc->CmdHlp, enmType, pResult);
2419 }
2420
2421
2422 /*
2423 * Ask PDM.
2424 */
2425 /** @todo resolve symbols using PDM. */
2426
2427
2428 /*
2429 * Ask the debug info manager.
2430 */
2431 DBGFSYMBOL Symbol;
2432 int rc = DBGFR3SymbolByName(pDbgc->pVM, pszSymbol, &Symbol);
2433 if (VBOX_SUCCESS(rc))
2434 {
2435 /*
2436 * Default return is a flat gc address.
2437 */
2438 memset(pResult, 0, sizeof(*pResult));
2439 pResult->enmRangeType = Symbol.cb ? DBGCVAR_RANGE_BYTES : DBGCVAR_RANGE_NONE;
2440 pResult->u64Range = Symbol.cb;
2441 pResult->enmType = DBGCVAR_TYPE_GC_FLAT;
2442 pResult->u.GCFlat = Symbol.Value;
2443 DBGCVAR VarTmp;
2444 switch (enmType)
2445 {
2446 /* nothing to do. */
2447 case DBGCVAR_TYPE_GC_FLAT:
2448 case DBGCVAR_TYPE_GC_FAR:
2449 case DBGCVAR_TYPE_ANY:
2450 return VINF_SUCCESS;
2451
2452 /* simply make it numeric. */
2453 case DBGCVAR_TYPE_NUMBER:
2454 pResult->enmType = DBGCVAR_TYPE_NUMBER;
2455 pResult->u.u64Number = Symbol.Value;
2456 return VINF_SUCCESS;
2457
2458 /* cast it. */
2459
2460 case DBGCVAR_TYPE_GC_PHYS:
2461 VarTmp = *pResult;
2462 return dbgcOpAddrPhys(pDbgc, &VarTmp, pResult);
2463
2464 case DBGCVAR_TYPE_HC_FAR:
2465 case DBGCVAR_TYPE_HC_FLAT:
2466 VarTmp = *pResult;
2467 return dbgcOpAddrHost(pDbgc, &VarTmp, pResult);
2468
2469 case DBGCVAR_TYPE_HC_PHYS:
2470 VarTmp = *pResult;
2471 return dbgcOpAddrHostPhys(pDbgc, &VarTmp, pResult);
2472
2473 default:
2474 AssertMsgFailed(("Internal error enmType=%d\n", enmType));
2475 return VERR_INVALID_PARAMETER;
2476 }
2477 }
2478
2479 return VERR_PARSE_NOT_IMPLEMENTED;
2480}
2481
2482
2483
2484/**
2485 * Finds a routine.
2486 *
2487 * @returns Pointer to the command descriptor.
2488 * If the request was for an external command, the caller is responsible for
2489 * unlocking the external command list.
2490 * @returns NULL if not found.
2491 * @param pDbgc The debug console instance.
2492 * @param pachName Pointer to the routine string (not terminated).
2493 * @param cchName Length of the routine name.
2494 * @param fExternal Whether or not the routine is external.
2495 */
2496static PCDBGCCMD dbgcRoutineLookup(PDBGC pDbgc, const char *pachName, size_t cchName, bool fExternal)
2497{
2498 if (!fExternal)
2499 {
2500 /* emulation first, so commands can be overloaded (info ++). */
2501 PCDBGCCMD pCmd = pDbgc->paEmulationCmds;
2502 unsigned cLeft = pDbgc->cEmulationCmds;
2503 while (cLeft-- > 0)
2504 {
2505 if ( !strncmp(pachName, pCmd->pszCmd, cchName)
2506 && !pCmd->pszCmd[cchName])
2507 return pCmd;
2508 pCmd++;
2509 }
2510
2511 for (unsigned iCmd = 0; iCmd < ELEMENTS(g_aCmds); iCmd++)
2512 {
2513 if ( !strncmp(pachName, g_aCmds[iCmd].pszCmd, cchName)
2514 && !g_aCmds[iCmd].pszCmd[cchName])
2515 return &g_aCmds[iCmd];
2516 }
2517 }
2518 else
2519 {
2520 DBGCEXTCMDS_LOCK_RD();
2521 for (PDBGCEXTCMDS pExtCmds = g_pExtCmdsHead; pExtCmds; pExtCmds = pExtCmds->pNext)
2522 {
2523 for (unsigned iCmd = 0; iCmd < pExtCmds->cCmds; iCmd++)
2524 {
2525 if ( !strncmp(pachName, pExtCmds->paCmds[iCmd].pszCmd, cchName)
2526 && !pExtCmds->paCmds[iCmd].pszCmd[cchName])
2527 return &pExtCmds->paCmds[iCmd];
2528 }
2529 }
2530 DBGCEXTCMDS_UNLOCK_RD();
2531 }
2532
2533 NOREF(pDbgc);
2534 return NULL;
2535}
2536
2537
2538/**
2539 * Initalizes g_bmOperatorChars.
2540 */
2541static void dbgcInitOpCharBitMap(void)
2542{
2543 memset(g_bmOperatorChars, 0, sizeof(g_bmOperatorChars));
2544 for (unsigned iOp = 0; iOp < g_cOps; iOp++)
2545 ASMBitSet(&g_bmOperatorChars[0], (uint8_t)g_aOps[iOp].szName[0]);
2546}
2547
2548
2549/**
2550 * Checks whether the character may be the start of an operator.
2551 *
2552 * @returns true/false.
2553 * @param ch The character.
2554 */
2555DECLINLINE(bool) dbgcIsOpChar(char ch)
2556{
2557 return ASMBitTest(&g_bmOperatorChars[0], (uint8_t)ch);
2558}
2559
2560
2561static int dbgcEvalSubString(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pArg)
2562{
2563 Log2(("dbgcEvalSubString: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));
2564
2565 /*
2566 * Removing any quoting and escapings.
2567 */
2568 char ch = *pszExpr;
2569 if (ch == '"' || ch == '\'' || ch == '`')
2570 {
2571 if (pszExpr[--cchExpr] != ch)
2572 return VERR_PARSE_UNBALANCED_QUOTE;
2573 cchExpr--;
2574 pszExpr++;
2575
2576 /** @todo string unescaping. */
2577 }
2578 pszExpr[cchExpr] = '\0';
2579
2580 /*
2581 * Make the argument.
2582 */
2583 pArg->pDesc = NULL;
2584 pArg->pNext = NULL;
2585 pArg->enmType = DBGCVAR_TYPE_STRING;
2586 pArg->u.pszString = pszExpr;
2587 pArg->enmRangeType = DBGCVAR_RANGE_BYTES;
2588 pArg->u64Range = cchExpr;
2589
2590 NOREF(pDbgc);
2591 return 0;
2592}
2593
2594
2595static int dbgcEvalSubNum(char *pszExpr, unsigned uBase, PDBGCVAR pArg)
2596{
2597 Log2(("dbgcEvalSubNum: uBase=%d pszExpr=%s\n", uBase, pszExpr));
2598 /*
2599 * Convert to number.
2600 */
2601 uint64_t u64 = 0;
2602 char ch;
2603 while ((ch = *pszExpr) != '\0')
2604 {
2605 uint64_t u64Prev = u64;
2606 unsigned u = ch - '0';
2607 if (u < 10 && u < uBase)
2608 u64 = u64 * uBase + u;
2609 else if (ch >= 'a' && (u = ch - ('a' - 10)) < uBase)
2610 u64 = u64 * uBase + u;
2611 else if (ch >= 'A' && (u = ch - ('A' - 10)) < uBase)
2612 u64 = u64 * uBase + u;
2613 else
2614 return VERR_PARSE_INVALID_NUMBER;
2615
2616 /* check for overflow - ARG!!! How to detect overflow correctly!?!?!? */
2617 if (u64Prev != u64 / uBase)
2618 return VERR_PARSE_NUMBER_TOO_BIG;
2619
2620 /* next */
2621 pszExpr++;
2622 }
2623
2624 /*
2625 * Initialize the argument.
2626 */
2627 pArg->pDesc = NULL;
2628 pArg->pNext = NULL;
2629 pArg->enmType = DBGCVAR_TYPE_NUMBER;
2630 pArg->u.u64Number = u64;
2631 pArg->enmRangeType = DBGCVAR_RANGE_NONE;
2632 pArg->u64Range = 0;
2633
2634 return 0;
2635}
2636
2637
2638/**
2639 * Match variable and variable descriptor, promoting the variable if necessary.
2640 *
2641 * @returns VBox status code.
2642 * @param pDbgc Debug console instanace.
2643 * @param pVar Variable.
2644 * @param pVarDesc Variable descriptor.
2645 */
2646static int dbgcEvalSubMatchVar(PDBGC pDbgc, PDBGCVAR pVar, PCDBGCVARDESC pVarDesc)
2647{
2648 /*
2649 * (If match or promoted to match, return, else break.)
2650 */
2651 switch (pVarDesc->enmCategory)
2652 {
2653 /*
2654 * Anything goes
2655 */
2656 case DBGCVAR_CAT_ANY:
2657 return VINF_SUCCESS;
2658
2659 /*
2660 * Pointer with and without range.
2661 * We can try resolve strings and symbols as symbols and
2662 * promote numbers to flat GC pointers.
2663 */
2664 case DBGCVAR_CAT_POINTER_NO_RANGE:
2665 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
2666 return VERR_PARSE_NO_RANGE_ALLOWED;
2667 /* fallthru */
2668 case DBGCVAR_CAT_POINTER:
2669 switch (pVar->enmType)
2670 {
2671 case DBGCVAR_TYPE_GC_FLAT:
2672 case DBGCVAR_TYPE_GC_FAR:
2673 case DBGCVAR_TYPE_GC_PHYS:
2674 case DBGCVAR_TYPE_HC_FLAT:
2675 case DBGCVAR_TYPE_HC_FAR:
2676 case DBGCVAR_TYPE_HC_PHYS:
2677 return VINF_SUCCESS;
2678
2679 case DBGCVAR_TYPE_SYMBOL:
2680 case DBGCVAR_TYPE_STRING:
2681 {
2682 DBGCVAR Var;
2683 int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_GC_FLAT, &Var);
2684 if (VBOX_SUCCESS(rc))
2685 {
2686 /* deal with range */
2687 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
2688 {
2689 Var.enmRangeType = pVar->enmRangeType;
2690 Var.u64Range = pVar->u64Range;
2691 }
2692 else if (pVarDesc->enmCategory == DBGCVAR_CAT_POINTER_NO_RANGE)
2693 Var.enmRangeType = DBGCVAR_RANGE_NONE;
2694 *pVar = Var;
2695 return rc;
2696 }
2697 break;
2698 }
2699
2700 case DBGCVAR_TYPE_NUMBER:
2701 {
2702 RTGCPTR GCPtr = (RTGCPTR)pVar->u.u64Number;
2703 pVar->enmType = DBGCVAR_TYPE_GC_FLAT;
2704 pVar->u.GCFlat = GCPtr;
2705 return VINF_SUCCESS;
2706 }
2707
2708 default:
2709 break;
2710 }
2711 break;
2712
2713 /*
2714 * GC pointer with and without range.
2715 * We can try resolve strings and symbols as symbols and
2716 * promote numbers to flat GC pointers.
2717 */
2718 case DBGCVAR_CAT_GC_POINTER_NO_RANGE:
2719 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
2720 return VERR_PARSE_NO_RANGE_ALLOWED;
2721 /* fallthru */
2722 case DBGCVAR_CAT_GC_POINTER:
2723 switch (pVar->enmType)
2724 {
2725 case DBGCVAR_TYPE_GC_FLAT:
2726 case DBGCVAR_TYPE_GC_FAR:
2727 case DBGCVAR_TYPE_GC_PHYS:
2728 return VINF_SUCCESS;
2729
2730 case DBGCVAR_TYPE_HC_FLAT:
2731 case DBGCVAR_TYPE_HC_FAR:
2732 case DBGCVAR_TYPE_HC_PHYS:
2733 return VERR_PARSE_CONVERSION_FAILED;
2734
2735 case DBGCVAR_TYPE_SYMBOL:
2736 case DBGCVAR_TYPE_STRING:
2737 {
2738 DBGCVAR Var;
2739 int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_GC_FLAT, &Var);
2740 if (VBOX_SUCCESS(rc))
2741 {
2742 /* deal with range */
2743 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
2744 {
2745 Var.enmRangeType = pVar->enmRangeType;
2746 Var.u64Range = pVar->u64Range;
2747 }
2748 else if (pVarDesc->enmCategory == DBGCVAR_CAT_POINTER_NO_RANGE)
2749 Var.enmRangeType = DBGCVAR_RANGE_NONE;
2750 *pVar = Var;
2751 return rc;
2752 }
2753 break;
2754 }
2755
2756 case DBGCVAR_TYPE_NUMBER:
2757 {
2758 RTGCPTR GCPtr = (RTGCPTR)pVar->u.u64Number;
2759 pVar->enmType = DBGCVAR_TYPE_GC_FLAT;
2760 pVar->u.GCFlat = GCPtr;
2761 return VINF_SUCCESS;
2762 }
2763
2764 default:
2765 break;
2766 }
2767 break;
2768
2769 /*
2770 * Number with or without a range.
2771 * Numbers can be resolved from symbols, but we cannot demote a pointer
2772 * to a number.
2773 */
2774 case DBGCVAR_CAT_NUMBER_NO_RANGE:
2775 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
2776 return VERR_PARSE_NO_RANGE_ALLOWED;
2777 /* fallthru */
2778 case DBGCVAR_CAT_NUMBER:
2779 switch (pVar->enmType)
2780 {
2781 case DBGCVAR_TYPE_NUMBER:
2782 return VINF_SUCCESS;
2783
2784 case DBGCVAR_TYPE_SYMBOL:
2785 case DBGCVAR_TYPE_STRING:
2786 {
2787 DBGCVAR Var;
2788 int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_NUMBER, &Var);
2789 if (VBOX_SUCCESS(rc))
2790 {
2791 *pVar = Var;
2792 return rc;
2793 }
2794 break;
2795 }
2796 default:
2797 break;
2798 }
2799 break;
2800
2801 /*
2802 * Strings can easily be made from symbols (and of course strings).
2803 * We could consider reformatting the addresses and numbers into strings later...
2804 */
2805 case DBGCVAR_CAT_STRING:
2806 switch (pVar->enmType)
2807 {
2808 case DBGCVAR_TYPE_SYMBOL:
2809 pVar->enmType = DBGCVAR_TYPE_STRING;
2810 /* fallthru */
2811 case DBGCVAR_TYPE_STRING:
2812 return VINF_SUCCESS;
2813 default:
2814 break;
2815 }
2816 break;
2817
2818 /*
2819 * Symol is pretty much the same thing as a string (at least until we actually implement it).
2820 */
2821 case DBGCVAR_CAT_SYMBOL:
2822 switch (pVar->enmType)
2823 {
2824 case DBGCVAR_TYPE_STRING:
2825 pVar->enmType = DBGCVAR_TYPE_SYMBOL;
2826 /* fallthru */
2827 case DBGCVAR_TYPE_SYMBOL:
2828 return VINF_SUCCESS;
2829 default:
2830 break;
2831 }
2832 break;
2833
2834 /*
2835 * Anything else is illegal.
2836 */
2837 default:
2838 AssertMsgFailed(("enmCategory=%d\n", pVar->enmType));
2839 break;
2840 }
2841
2842 return VERR_PARSE_NO_ARGUMENT_MATCH;
2843}
2844
2845
2846/**
2847 * Matches a set of variables with a description set.
2848 *
2849 * This is typically used for routine arguments before a call. The effects in
2850 * addition to the validation, is that some variables might be propagated to
2851 * other types in order to match the description. The following transformations
2852 * are supported:
2853 * - String reinterpreted as a symbol and resolved to a number or pointer.
2854 * - Number to a pointer.
2855 * - Pointer to a number.
2856 * @returns 0 on success with paVars.
2857 * @returns VBox error code for match errors.
2858 */
2859static int dbgcEvalSubMatchVars(PDBGC pDbgc, unsigned cVarsMin, unsigned cVarsMax,
2860 PCDBGCVARDESC paVarDescs, unsigned cVarDescs,
2861 PDBGCVAR paVars, unsigned cVars)
2862{
2863 /*
2864 * Just do basic min / max checks first.
2865 */
2866 if (cVars < cVarsMin)
2867 return VERR_PARSE_TOO_FEW_ARGUMENTS;
2868 if (cVars > cVarsMax)
2869 return VERR_PARSE_TOO_MANY_ARGUMENTS;
2870
2871 /*
2872 * Match the descriptors and actual variables.
2873 */
2874 PCDBGCVARDESC pPrevDesc = NULL;
2875 unsigned cCurDesc = 0;
2876 unsigned iVar = 0;
2877 unsigned iVarDesc = 0;
2878 while (iVar < cVars)
2879 {
2880 /* walk the descriptors */
2881 if (iVarDesc >= cVarDescs)
2882 return VERR_PARSE_TOO_MANY_ARGUMENTS;
2883 if ( ( paVarDescs[iVarDesc].fFlags & DBGCVD_FLAGS_DEP_PREV
2884 && &paVarDescs[iVarDesc - 1] != pPrevDesc)
2885 || cCurDesc >= paVarDescs[iVarDesc].cTimesMax)
2886 {
2887 iVarDesc++;
2888 if (iVarDesc >= cVarDescs)
2889 return VERR_PARSE_TOO_MANY_ARGUMENTS;
2890 cCurDesc = 0;
2891 }
2892
2893 /*
2894 * Skip thru optional arguments until we find something which matches
2895 * or can easily be promoted to what the descriptor want.
2896 */
2897 for (;;)
2898 {
2899 int rc = dbgcEvalSubMatchVar(pDbgc, &paVars[iVar], &paVarDescs[iVarDesc]);
2900 if (VBOX_SUCCESS(rc))
2901 {
2902 paVars[iVar].pDesc = &paVarDescs[iVarDesc];
2903 cCurDesc++;
2904 break;
2905 }
2906
2907 /* can we advance? */
2908 if (paVarDescs[iVarDesc].cTimesMin > cCurDesc)
2909 return VERR_PARSE_ARGUMENT_TYPE_MISMATCH;
2910 if (++iVarDesc >= cVarDescs)
2911 return VERR_PARSE_ARGUMENT_TYPE_MISMATCH;
2912 cCurDesc = 0;
2913 }
2914
2915 /* next var */
2916 iVar++;
2917 }
2918
2919 /*
2920 * Check that the rest of the descriptors are optional.
2921 */
2922 while (iVarDesc < cVarDescs)
2923 {
2924 if (paVarDescs[iVarDesc].cTimesMin > cCurDesc)
2925 return VERR_PARSE_TOO_FEW_ARGUMENTS;
2926 cCurDesc = 0;
2927
2928 /* next */
2929 iVarDesc++;
2930 }
2931
2932 return 0;
2933}
2934
2935
2936/**
2937 * Evaluates one argument with respect to unary operators.
2938 *
2939 * @returns 0 on success. pResult contains the result.
2940 * @returns VBox error code on parse or other evaluation error.
2941 *
2942 * @param pDbgc Debugger console instance data.
2943 * @param pszExpr The expression string.
2944 * @param pResult Where to store the result of the expression evaluation.
2945 */
2946static int dbgcEvalSubUnary(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pResult)
2947{
2948 Log2(("dbgcEvalSubUnary: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));
2949
2950 /*
2951 * The state of the expression is now such that it will start by zero or more
2952 * unary operators and being followed by an expression of some kind.
2953 * The expression is either plain or in parenthesis.
2954 *
2955 * Being in a lazy, recursive mode today, the parsing is done as simple as possible. :-)
2956 * ASSUME: unary operators are all of equal precedence.
2957 */
2958 int rc = 0;
2959 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, pszExpr, false, ' ');
2960 if (pOp)
2961 {
2962 /* binary operators means syntax error. */
2963 if (pOp->fBinary)
2964 return VERR_PARSE_UNEXPECTED_OPERATOR;
2965
2966 /*
2967 * If the next expression (the one following the unary operator) is in a
2968 * parenthesis a full eval is needed. If not the unary eval will suffice.
2969 */
2970 /* calc and strip next expr. */
2971 char *pszExpr2 = pszExpr + pOp->cchName;
2972 while (isblank(*pszExpr2))
2973 pszExpr2++;
2974
2975 if (!*pszExpr2)
2976 rc = VERR_PARSE_EMPTY_ARGUMENT;
2977 else
2978 {
2979 DBGCVAR Arg;
2980 if (*pszExpr2 == '(')
2981 rc = dbgcEvalSub(pDbgc, pszExpr2, cchExpr - (pszExpr2 - pszExpr), &Arg);
2982 else
2983 rc = dbgcEvalSubUnary(pDbgc, pszExpr2, cchExpr - (pszExpr2 - pszExpr), &Arg);
2984 if (VBOX_SUCCESS(rc))
2985 rc = pOp->pfnHandlerUnary(pDbgc, &Arg, pResult);
2986 }
2987 }
2988 else
2989 {
2990 /*
2991 * Didn't find any operators, so it we have to check if this can be an
2992 * function call before assuming numeric or string expression.
2993 *
2994 * (ASSUMPTIONS:)
2995 * A function name only contains alphanumerical chars and it can not start
2996 * with a numerical character.
2997 * Immediately following the name is a parenthesis which must over
2998 * the remaining part of the expression.
2999 */
3000 bool fExternal = *pszExpr == '.';
3001 char *pszFun = fExternal ? pszExpr + 1 : pszExpr;
3002 char *pszFunEnd = NULL;
3003 if (pszExpr[cchExpr - 1] == ')' && isalpha(*pszFun))
3004 {
3005 pszFunEnd = pszExpr + 1;
3006 while (*pszFunEnd != '(' && isalnum(*pszFunEnd))
3007 pszFunEnd++;
3008 if (*pszFunEnd != '(')
3009 pszFunEnd = NULL;
3010 }
3011
3012 if (pszFunEnd)
3013 {
3014 /*
3015 * Ok, it's a function call.
3016 */
3017 if (fExternal)
3018 pszExpr++, cchExpr--;
3019 PCDBGCCMD pFun = dbgcRoutineLookup(pDbgc, pszExpr, pszFunEnd - pszExpr, fExternal);
3020 if (!pFun)
3021 return VERR_PARSE_FUNCTION_NOT_FOUND;
3022 if (!pFun->pResultDesc)
3023 return VERR_PARSE_NOT_A_FUNCTION;
3024
3025 /*
3026 * Parse the expression in parenthesis.
3027 */
3028 cchExpr -= pszFunEnd - pszExpr;
3029 pszExpr = pszFunEnd;
3030 /** @todo implement multiple arguments. */
3031 DBGCVAR Arg;
3032 rc = dbgcEvalSub(pDbgc, pszExpr, cchExpr, &Arg);
3033 if (!rc)
3034 {
3035 rc = dbgcEvalSubMatchVars(pDbgc, pFun->cArgsMin, pFun->cArgsMax, pFun->paArgDescs, pFun->cArgDescs, &Arg, 1);
3036 if (!rc)
3037 rc = pFun->pfnHandler(pFun, &pDbgc->CmdHlp, pDbgc->pVM, &Arg, 1, pResult);
3038 }
3039 else if (rc == VERR_PARSE_EMPTY_ARGUMENT && pFun->cArgsMin == 0)
3040 rc = pFun->pfnHandler(pFun, &pDbgc->CmdHlp, pDbgc->pVM, NULL, 0, pResult);
3041 }
3042 else
3043 {
3044 /*
3045 * Didn't find any operators, so it must be a plain expression.
3046 * This might be numeric or a string expression.
3047 */
3048 char ch = pszExpr[0];
3049 char ch2 = pszExpr[1];
3050 if (ch == '0' && (ch2 == 'x' || ch2 == 'X'))
3051 rc = dbgcEvalSubNum(pszExpr + 2, 16, pResult);
3052 else if (ch == '0' && (ch2 == 'i' || ch2 == 'i'))
3053 rc = dbgcEvalSubNum(pszExpr + 2, 10, pResult);
3054 else if (ch == '0' && (ch2 == 't' || ch2 == 'T'))
3055 rc = dbgcEvalSubNum(pszExpr + 2, 8, pResult);
3056 /// @todo 0b doesn't work as a binary prefix, we confuse it with 0bf8:0123 and stuff.
3057 //else if (ch == '0' && (ch2 == 'b' || ch2 == 'b'))
3058 // rc = dbgcEvalSubNum(pszExpr + 2, 2, pResult);
3059 else
3060 {
3061 /*
3062 * Hexadecimal number or a string?
3063 */
3064 char *psz = pszExpr;
3065 while (isxdigit(*psz))
3066 psz++;
3067 if (!*psz)
3068 rc = dbgcEvalSubNum(pszExpr, 16, pResult);
3069 else if ((*psz == 'h' || *psz == 'H') && !psz[1])
3070 {
3071 *psz = '\0';
3072 rc = dbgcEvalSubNum(pszExpr, 16, pResult);
3073 }
3074 else
3075 rc = dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult);
3076 }
3077 }
3078 }
3079
3080 return rc;
3081}
3082
3083
3084/**
3085 * Evaluates one argument.
3086 *
3087 * @returns 0 on success. pResult contains the result.
3088 * @returns VBox error code on parse or other evaluation error.
3089 *
3090 * @param pDbgc Debugger console instance data.
3091 * @param pszExpr The expression string.
3092 * @param pResult Where to store the result of the expression evaluation.
3093 */
3094static int dbgcEvalSub(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pResult)
3095{
3096 Log2(("dbgcEvalSub: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));
3097 /*
3098 * First we need to remove blanks in both ends.
3099 * ASSUMES: There is no quoting unless the entire expression is a string.
3100 */
3101
3102 /* stripping. */
3103 while (cchExpr > 0 && isblank(pszExpr[cchExpr - 1]))
3104 pszExpr[--cchExpr] = '\0';
3105 while (isblank(*pszExpr))
3106 pszExpr++, cchExpr--;
3107 if (!*pszExpr)
3108 return VERR_PARSE_EMPTY_ARGUMENT;
3109
3110 /* it there is any kind of quoting in the expression, it's string meat. */
3111 if (strpbrk(pszExpr, "\"'`"))
3112 return dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult);
3113
3114 /*
3115 * Check if there are any parenthesis which needs removing.
3116 */
3117 if (pszExpr[0] == '(' && pszExpr[cchExpr - 1] == ')')
3118 {
3119 do
3120 {
3121 unsigned cPar = 1;
3122 char *psz = pszExpr + 1;
3123 char ch;
3124 while ((ch = *psz) != '\0')
3125 {
3126 if (ch == '(')
3127 cPar++;
3128 else if (ch == ')')
3129 {
3130 if (cPar <= 0)
3131 return VERR_PARSE_UNBALANCED_PARENTHESIS;
3132 cPar--;
3133 if (cPar == 0 && psz[1]) /* If not at end, there's nothing to do. */
3134 break;
3135 }
3136 /* next */
3137 psz++;
3138 }
3139 if (ch)
3140 break;
3141
3142 /* remove the parenthesis. */
3143 pszExpr++;
3144 cchExpr -= 2;
3145 pszExpr[cchExpr] = '\0';
3146
3147 /* strip blanks. */
3148 while (cchExpr > 0 && isblank(pszExpr[cchExpr - 1]))
3149 pszExpr[--cchExpr] = '\0';
3150 while (isblank(*pszExpr))
3151 pszExpr++, cchExpr--;
3152 if (!*pszExpr)
3153 return VERR_PARSE_EMPTY_ARGUMENT;
3154 } while (pszExpr[0] == '(' && pszExpr[cchExpr - 1] == ')');
3155 }
3156
3157 /* tabs to spaces. */
3158 char *psz = pszExpr;
3159 while ((psz = strchr(psz, '\t')) != NULL)
3160 *psz = ' ';
3161
3162 /*
3163 * Now, we need to look for the binary operator with the lowest precedence.
3164 *
3165 * If there are no operators we're left with a simple expression which we
3166 * evaluate with respect to unary operators
3167 */
3168 char *pszOpSplit = NULL;
3169 PCDBGCOP pOpSplit = NULL;
3170 unsigned cBinaryOps = 0;
3171 unsigned cPar = 0;
3172 char ch;
3173 char chPrev = ' ';
3174 bool fBinary = false;
3175 psz = pszExpr;
3176
3177 while ((ch = *psz) != '\0')
3178 {
3179 //Log2(("ch=%c cPar=%d fBinary=%d\n", ch, cPar, fBinary));
3180 /*
3181 * Parenthesis.
3182 */
3183 if (ch == '(')
3184 {
3185 cPar++;
3186 fBinary = false;
3187 }
3188 else if (ch == ')')
3189 {
3190 if (cPar <= 0)
3191 return VERR_PARSE_UNBALANCED_PARENTHESIS;
3192 cPar--;
3193 fBinary = true;
3194 }
3195 /*
3196 * Potential operator.
3197 */
3198 else if (cPar == 0 && !isblank(ch))
3199 {
3200 PCDBGCOP pOp = dbgcIsOpChar(ch)
3201 ? dbgcOperatorLookup(pDbgc, psz, fBinary, chPrev)
3202 : NULL;
3203 if (pOp)
3204 {
3205 /* If not the right kind of operator we've got a syntax error. */
3206 if (pOp->fBinary != fBinary)
3207 return VERR_PARSE_UNEXPECTED_OPERATOR;
3208
3209 /*
3210 * Update the parse state and skip the operator.
3211 */
3212 if (!pOpSplit)
3213 {
3214 pOpSplit = pOp;
3215 pszOpSplit = psz;
3216 cBinaryOps = fBinary;
3217 }
3218 else if (fBinary)
3219 {
3220 cBinaryOps++;
3221 if (pOp->iPrecedence >= pOpSplit->iPrecedence)
3222 {
3223 pOpSplit = pOp;
3224 pszOpSplit = psz;
3225 }
3226 }
3227
3228 psz += pOp->cchName - 1;
3229 fBinary = false;
3230 }
3231 else
3232 fBinary = true;
3233 }
3234
3235 /* next */
3236 psz++;
3237 chPrev = ch;
3238 } /* parse loop. */
3239
3240
3241 /*
3242 * Either we found an operator to divide the expression by
3243 * or we didn't find any. In the first case it's divide and
3244 * conquer. In the latter it's a single expression which
3245 * needs dealing with its unary operators if any.
3246 */
3247 int rc;
3248 if ( cBinaryOps
3249 && pOpSplit->fBinary)
3250 {
3251 /* process 1st sub expression. */
3252 *pszOpSplit = '\0';
3253 DBGCVAR Arg1;
3254 rc = dbgcEvalSub(pDbgc, pszExpr, pszOpSplit - pszExpr, &Arg1);
3255 if (VBOX_SUCCESS(rc))
3256 {
3257 /* process 2nd sub expression. */
3258 char *psz2 = pszOpSplit + pOpSplit->cchName;
3259 DBGCVAR Arg2;
3260 rc = dbgcEvalSub(pDbgc, psz2, cchExpr - (psz2 - pszExpr), &Arg2);
3261 if (VBOX_SUCCESS(rc))
3262 /* apply the operator. */
3263 rc = pOpSplit->pfnHandlerBinary(pDbgc, &Arg1, &Arg2, pResult);
3264 }
3265 }
3266 else if (cBinaryOps)
3267 {
3268 /* process sub expression. */
3269 pszOpSplit += pOpSplit->cchName;
3270 DBGCVAR Arg;
3271 rc = dbgcEvalSub(pDbgc, pszOpSplit, cchExpr - (pszOpSplit - pszExpr), &Arg);
3272 if (VBOX_SUCCESS(rc))
3273 /* apply the operator. */
3274 rc = pOpSplit->pfnHandlerUnary(pDbgc, &Arg, pResult);
3275 }
3276 else
3277 /* plain expression or using unary operators perhaps with paratheses. */
3278 rc = dbgcEvalSubUnary(pDbgc, pszExpr, cchExpr, pResult);
3279
3280 return rc;
3281}
3282
3283
3284/**
3285 * Parses the arguments of one command.
3286 *
3287 * @returns 0 on success.
3288 * @returns VBox error code on parse error with *pcArg containing the argument causing trouble.
3289 * @param pDbgc Debugger console instance data.
3290 * @param pCmd Pointer to the command descriptor.
3291 * @param pszArg Pointer to the arguments to parse.
3292 * @param paArgs Where to store the parsed arguments.
3293 * @param cArgs Size of the paArgs array.
3294 * @param pcArgs Where to store the number of arguments.
3295 * In the event of an error this is used to store the index of the offending argument.
3296 */
3297static int dbgcProcessArguments(PDBGC pDbgc, PCDBGCCMD pCmd, char *pszArgs, PDBGCVAR paArgs, unsigned cArgs, unsigned *pcArgs)
3298{
3299 Log2(("dbgcProcessArguments: pCmd=%s pszArgs='%s'\n", pCmd->pszCmd, pszArgs));
3300 /*
3301 * Check if we have any argument and if the command takes any.
3302 */
3303 *pcArgs = 0;
3304 /* strip leading blanks. */
3305 while (*pszArgs && isblank(*pszArgs))
3306 pszArgs++;
3307 if (!*pszArgs)
3308 {
3309 if (!pCmd->cArgsMin)
3310 return 0;
3311 return VERR_PARSE_TOO_FEW_ARGUMENTS;
3312 }
3313 /** @todo fixme - foo() doesn't work. */
3314 if (!pCmd->cArgsMax)
3315 return VERR_PARSE_TOO_MANY_ARGUMENTS;
3316
3317 /*
3318 * This is a hack, it's "temporary" and should go away "when" the parser is
3319 * modified to match arguments while parsing.
3320 */
3321 if ( pCmd->cArgsMax == 1
3322 && pCmd->cArgsMin == 1
3323 && pCmd->cArgDescs == 1
3324 && pCmd->paArgDescs[0].enmCategory == DBGCVAR_CAT_STRING
3325 && cArgs >= 1)
3326 {
3327 *pcArgs = 1;
3328 RTStrStripR(pszArgs);
3329 return dbgcEvalSubString(pDbgc, pszArgs, strlen(pszArgs), &paArgs[0]);
3330 }
3331
3332
3333 /*
3334 * The parse loop.
3335 */
3336 PDBGCVAR pArg0 = &paArgs[0];
3337 PDBGCVAR pArg = pArg0;
3338 *pcArgs = 0;
3339 do
3340 {
3341 /*
3342 * Can we have another argument?
3343 */
3344 if (*pcArgs >= pCmd->cArgsMax)
3345 return VERR_PARSE_TOO_MANY_ARGUMENTS;
3346 if (pArg >= &paArgs[cArgs])
3347 return VERR_PARSE_ARGUMENT_OVERFLOW;
3348
3349 /*
3350 * Find the end of the argument.
3351 */
3352 int cPar = 0;
3353 char chQuote = '\0';
3354 char *pszEnd = NULL;
3355 char *psz = pszArgs;
3356 char ch;
3357 bool fBinary = false;
3358 for (;;)
3359 {
3360 /*
3361 * Check for the end.
3362 */
3363 if ((ch = *psz) == '\0')
3364 {
3365 if (chQuote)
3366 return VERR_PARSE_UNBALANCED_QUOTE;
3367 if (cPar)
3368 return VERR_PARSE_UNBALANCED_PARENTHESIS;
3369 pszEnd = psz;
3370 break;
3371 }
3372 /*
3373 * When quoted we ignore everything but the quotation char.
3374 * We use the REXX way of escaping the quotation char, i.e. double occurence.
3375 */
3376 else if (ch == '\'' || ch == '"' || ch == '`')
3377 {
3378 if (chQuote)
3379 {
3380 /* end quote? */
3381 if (ch == chQuote)
3382 {
3383 if (psz[1] == ch)
3384 psz++; /* skip the escaped quote char */
3385 else
3386 chQuote = '\0'; /* end of quoted string. */
3387 }
3388 }
3389 else
3390 chQuote = ch; /* open new quote */
3391 }
3392 /*
3393 * Parenthesis can of course be nested.
3394 */
3395 else if (ch == '(')
3396 {
3397 cPar++;
3398 fBinary = false;
3399 }
3400 else if (ch == ')')
3401 {
3402 if (!cPar)
3403 return VERR_PARSE_UNBALANCED_PARENTHESIS;
3404 cPar--;
3405 fBinary = true;
3406 }
3407 else if (!chQuote && !cPar)
3408 {
3409 /*
3410 * Encountering blanks may mean the end of it all. A binary operator
3411 * will force continued parsing.
3412 */
3413 if (isblank(*psz))
3414 {
3415 pszEnd = psz++; /* just in case. */
3416 while (isblank(*psz))
3417 psz++;
3418 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, psz, fBinary, ' ');
3419 if (!pOp || pOp->fBinary != fBinary)
3420 break; /* the end. */
3421 psz += pOp->cchName;
3422 while (isblank(*psz)) /* skip blanks so we don't get here again */
3423 psz++;
3424 fBinary = false;
3425 continue;
3426 }
3427
3428 /*
3429 * Look for operators without a space up front.
3430 */
3431 if (dbgcIsOpChar(*psz))
3432 {
3433 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, psz, fBinary, ' ');
3434 if (pOp)
3435 {
3436 if (pOp->fBinary != fBinary)
3437 {
3438 pszEnd = psz;
3439 /** @todo this is a parsing error really. */
3440 break; /* the end. */
3441 }
3442 psz += pOp->cchName;
3443 while (isblank(*psz)) /* skip blanks so we don't get here again */
3444 psz++;
3445 fBinary = false;
3446 continue;
3447 }
3448 }
3449 fBinary = true;
3450 }
3451
3452 /* next char */
3453 psz++;
3454 }
3455 *pszEnd = '\0';
3456 /* (psz = next char to process) */
3457
3458 /*
3459 * Parse and evaluate the argument.
3460 */
3461 int rc = dbgcEvalSub(pDbgc, pszArgs, strlen(pszArgs), pArg);
3462 if (VBOX_FAILURE(rc))
3463 return rc;
3464
3465 /*
3466 * Next.
3467 */
3468 pArg++;
3469 (*pcArgs)++;
3470 pszArgs = psz;
3471 while (*pszArgs && isblank(*pszArgs))
3472 pszArgs++;
3473 } while (*pszArgs);
3474
3475 /*
3476 * Match the arguments.
3477 */
3478 return dbgcEvalSubMatchVars(pDbgc, pCmd->cArgsMin, pCmd->cArgsMax, pCmd->paArgDescs, pCmd->cArgDescs, pArg0, pArg - pArg0);
3479}
3480
3481
3482/**
3483 * Process one command.
3484 *
3485 * @returns VBox status code. Any error indicates the termination of the console session.
3486 * @param pDbgc Debugger console instance data.
3487 * @param pszCmd Pointer to the command.
3488 * @param cchCmd Length of the command.
3489 */
3490static int dbgcProcessCommand(PDBGC pDbgc, char *pszCmd, size_t cchCmd)
3491{
3492 char *pszCmdInput = pszCmd;
3493
3494 /*
3495 * Skip blanks.
3496 */
3497 while (isblank(*pszCmd))
3498 pszCmd++, cchCmd--;
3499
3500 /* external command? */
3501 bool fExternal = *pszCmd == '.';
3502 if (fExternal)
3503 pszCmd++, cchCmd--;
3504
3505 /*
3506 * Find arguments.
3507 */
3508 char *pszArgs = pszCmd;
3509 while (isalnum(*pszArgs))
3510 pszArgs++;
3511 if (*pszArgs && (!isblank(*pszArgs) || pszArgs == pszCmd))
3512 {
3513 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "Syntax error in command '%s'!\n", pszCmdInput);
3514 return 0;
3515 }
3516
3517 /*
3518 * Find the command.
3519 */
3520 PCDBGCCMD pCmd = dbgcRoutineLookup(pDbgc, pszCmd, pszArgs - pszCmd, fExternal);
3521 if (!pCmd || (pCmd->fFlags & DBGCCMD_FLAGS_FUNCTION))
3522 return pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "Unknown command '%s'!\n", pszCmdInput);
3523
3524 /*
3525 * Parse arguments (if any).
3526 */
3527 unsigned cArgs;
3528 int rc = dbgcProcessArguments(pDbgc, pCmd, pszArgs, &pDbgc->aArgs[pDbgc->iArg], ELEMENTS(pDbgc->aArgs) - pDbgc->iArg, &cArgs);
3529
3530 /*
3531 * Execute the command.
3532 */
3533 if (!rc)
3534 {
3535 rc = pCmd->pfnHandler(pCmd, &pDbgc->CmdHlp, pDbgc->pVM, &pDbgc->aArgs[0], cArgs, NULL);
3536 }
3537 else
3538 {
3539 /* report parse / eval error. */
3540 switch (rc)
3541 {
3542 case VERR_PARSE_TOO_FEW_ARGUMENTS:
3543 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3544 "Syntax error: Too few arguments. Minimum is %d for command '%s'.\n", pCmd->cArgsMin, pCmd->pszCmd);
3545 break;
3546 case VERR_PARSE_TOO_MANY_ARGUMENTS:
3547 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3548 "Syntax error: Too many arguments. Maximum is %d for command '%s'.\n", pCmd->cArgsMax, pCmd->pszCmd);
3549 break;
3550 case VERR_PARSE_ARGUMENT_OVERFLOW:
3551 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3552 "Syntax error: Too many arguments.\n");
3553 break;
3554 case VERR_PARSE_UNBALANCED_QUOTE:
3555 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3556 "Syntax error: Unbalanced quote (argument %d).\n", cArgs);
3557 break;
3558 case VERR_PARSE_UNBALANCED_PARENTHESIS:
3559 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3560 "Syntax error: Unbalanced parenthesis (argument %d).\n", cArgs);
3561 break;
3562 case VERR_PARSE_EMPTY_ARGUMENT:
3563 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3564 "Syntax error: An argument or subargument contains nothing useful (argument %d).\n", cArgs);
3565 break;
3566 case VERR_PARSE_UNEXPECTED_OPERATOR:
3567 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3568 "Syntax error: Invalid operator usage (argument %d).\n", cArgs);
3569 break;
3570 case VERR_PARSE_INVALID_NUMBER:
3571 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3572 "Syntax error: Ivalid numeric value (argument %d). If a string was the intention, then quote it.\n", cArgs);
3573 break;
3574 case VERR_PARSE_NUMBER_TOO_BIG:
3575 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3576 "Error: Numeric overflow (argument %d).\n", cArgs);
3577 break;
3578 case VERR_PARSE_INVALID_OPERATION:
3579 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3580 "Error: Invalid operation attempted (argument %d).\n", cArgs);
3581 break;
3582 case VERR_PARSE_FUNCTION_NOT_FOUND:
3583 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3584 "Error: Function not found (argument %d).\n", cArgs);
3585 break;
3586 case VERR_PARSE_NOT_A_FUNCTION:
3587 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3588 "Error: The function specified is not a function (argument %d).\n", cArgs);
3589 break;
3590 case VERR_PARSE_NO_MEMORY:
3591 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3592 "Error: Out memory in the regular heap! Expect odd stuff to happen...\n", cArgs);
3593 break;
3594 case VERR_PARSE_INCORRECT_ARG_TYPE:
3595 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3596 "Error: Incorrect argument type (argument %d?).\n", cArgs);
3597 break;
3598 case VERR_PARSE_VARIABLE_NOT_FOUND:
3599 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3600 "Error: An undefined variable was referenced (argument %d).\n", cArgs);
3601 break;
3602 case VERR_PARSE_CONVERSION_FAILED:
3603 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3604 "Error: A conversion between two types failed (argument %d).\n", cArgs);
3605 break;
3606 case VERR_PARSE_NOT_IMPLEMENTED:
3607 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3608 "Error: You hit a debugger feature which isn't implemented yet (argument %d).\n", cArgs);
3609 break;
3610 case VERR_PARSE_BAD_RESULT_TYPE:
3611 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3612 "Error: Couldn't satisfy a request for a specific result type (argument %d). (Usually applies to symbols)\n", cArgs);
3613 break;
3614 case VERR_PARSE_WRITEONLY_SYMBOL:
3615 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3616 "Error: Cannot get symbol, it's set only (argument %d).\n", cArgs);
3617 break;
3618
3619 default:
3620 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3621 "Error: Unknown error %d!\n", rc);
3622 return rc;
3623 }
3624
3625 /*
3626 * Parse errors are non fatal.
3627 */
3628 if (rc >= VERR_PARSE_FIRST && rc < VERR_PARSE_LAST)
3629 rc = 0;
3630 }
3631
3632 return rc;
3633}
3634
3635
3636/**
3637 * Process all commands current in the buffer.
3638 *
3639 * @returns VBox status code. Any error indicates the termination of the console session.
3640 * @param pDbgc Debugger console instance data.
3641 */
3642static int dbgcProcessCommands(PDBGC pDbgc)
3643{
3644 int rc = 0;
3645 while (pDbgc->cInputLines)
3646 {
3647 /*
3648 * Empty the log buffer if we're hooking the log.
3649 */
3650 if (pDbgc->fLog)
3651 {
3652 rc = dbgcProcessLog(pDbgc);
3653 if (VBOX_FAILURE(rc))
3654 break;
3655 }
3656
3657 if (pDbgc->iRead == pDbgc->iWrite)
3658 {
3659 AssertMsgFailed(("The input buffer is empty while cInputLines=%d!\n", pDbgc->cInputLines));
3660 pDbgc->cInputLines = 0;
3661 return 0;
3662 }
3663
3664 /*
3665 * Copy the command to the parse buffer.
3666 */
3667 char ch;
3668 char *psz = &pDbgc->achInput[pDbgc->iRead];
3669 char *pszTrg = &pDbgc->achScratch[0];
3670 while ((*pszTrg = ch = *psz++) != ';' && ch != '\n' )
3671 {
3672 if (psz == &pDbgc->achInput[sizeof(pDbgc->achInput)])
3673 psz = &pDbgc->achInput[0];
3674
3675 if (psz == &pDbgc->achInput[pDbgc->iWrite])
3676 {
3677 AssertMsgFailed(("The buffer contains no commands while cInputLines=%d!\n", pDbgc->cInputLines));
3678 pDbgc->cInputLines = 0;
3679 return 0;
3680 }
3681
3682 pszTrg++;
3683 }
3684 *pszTrg = '\0';
3685
3686 /*
3687 * Advance the buffer.
3688 */
3689 pDbgc->iRead = psz - &pDbgc->achInput[0];
3690 if (ch == '\n')
3691 pDbgc->cInputLines--;
3692
3693 /*
3694 * Parse and execute this command.
3695 */
3696 pDbgc->pszScratch = psz;
3697 pDbgc->iArg = 0;
3698 rc = dbgcProcessCommand(pDbgc, &pDbgc->achScratch[0], psz - &pDbgc->achScratch[0] - 1);
3699 if (rc)
3700 break;
3701 }
3702
3703 return rc;
3704}
3705
3706
3707/**
3708 * Reads input, parses it and executes commands on '\n'.
3709 *
3710 * @returns VBox status.
3711 * @param pDbgc Debugger console instance data.
3712 */
3713static int dbgcProcessInput(PDBGC pDbgc)
3714{
3715 /*
3716 * We know there's input ready, so let's read it first.
3717 */
3718 int rc = dbgcInputRead(pDbgc);
3719 if (VBOX_FAILURE(rc))
3720 return rc;
3721
3722 /*
3723 * Now execute any ready commands.
3724 */
3725 if (pDbgc->cInputLines)
3726 {
3727 /** @todo this fReady stuff is broken. */
3728 pDbgc->fReady = false;
3729 rc = dbgcProcessCommands(pDbgc);
3730 if (VBOX_SUCCESS(rc) && rc != VWRN_DBGC_CMD_PENDING)
3731 pDbgc->fReady = true;
3732 if ( VBOX_SUCCESS(rc)
3733 && pDbgc->iRead == pDbgc->iWrite
3734 && pDbgc->fReady)
3735 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "VBoxDbg> ");
3736 }
3737
3738 return rc;
3739}
3740
3741
3742/**
3743 * Gets the event context identifier string.
3744 * @returns Read only string.
3745 * @param enmCtx The context.
3746 */
3747static const char *dbgcGetEventCtx(DBGFEVENTCTX enmCtx)
3748{
3749 switch (enmCtx)
3750 {
3751 case DBGFEVENTCTX_RAW: return "raw";
3752 case DBGFEVENTCTX_REM: return "rem";
3753 case DBGFEVENTCTX_HWACCL: return "hwaccl";
3754 case DBGFEVENTCTX_HYPER: return "hyper";
3755 case DBGFEVENTCTX_OTHER: return "other";
3756
3757 case DBGFEVENTCTX_INVALID: return "!Invalid Event Ctx!";
3758 default:
3759 AssertMsgFailed(("enmCtx=%d\n", enmCtx));
3760 return "!Unknown Event Ctx!";
3761 }
3762}
3763
3764
3765/**
3766 * Processes debugger events.
3767 *
3768 * @returns VBox status.
3769 * @param pDbgc DBGC Instance data.
3770 * @param pEvent Pointer to event data.
3771 */
3772static int dbgcProcessEvent(PDBGC pDbgc, PCDBGFEVENT pEvent)
3773{
3774 /*
3775 * Flush log first.
3776 */
3777 if (pDbgc->fLog)
3778 {
3779 int rc = dbgcProcessLog(pDbgc);
3780 if (VBOX_FAILURE(rc))
3781 return rc;
3782 }
3783
3784 /*
3785 * Process the event.
3786 */
3787 pDbgc->pszScratch = &pDbgc->achInput[0];
3788 pDbgc->iArg = 0;
3789 bool fPrintPrompt = true;
3790 int rc = VINF_SUCCESS;
3791 switch (pEvent->enmType)
3792 {
3793 /*
3794 * The first part is events we have initiated with commands.
3795 */
3796 case DBGFEVENT_HALT_DONE:
3797 {
3798 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: VM %p is halted! (%s)\n",
3799 pDbgc->pVM, dbgcGetEventCtx(pEvent->enmCtx));
3800 pDbgc->fRegCtxGuest = true; /* we're always in guest context when halted. */
3801 if (VBOX_SUCCESS(rc))
3802 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
3803 break;
3804 }
3805
3806
3807 /*
3808 * The second part is events which can occur at any time.
3809 */
3810 case DBGFEVENT_FATAL_ERROR:
3811 {
3812 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbf event: Fatal error! (%s)\n",
3813 dbgcGetEventCtx(pEvent->enmCtx));
3814 pDbgc->fRegCtxGuest = false; /* fatal errors are always in hypervisor. */
3815 if (VBOX_SUCCESS(rc))
3816 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
3817 break;
3818 }
3819
3820 case DBGFEVENT_BREAKPOINT:
3821 case DBGFEVENT_BREAKPOINT_HYPER:
3822 {
3823 bool fRegCtxGuest = pDbgc->fRegCtxGuest;
3824 pDbgc->fRegCtxGuest = pEvent->enmType == DBGFEVENT_BREAKPOINT;
3825
3826 rc = dbgcBpExec(pDbgc, pEvent->u.Bp.iBp);
3827 switch (rc)
3828 {
3829 case VERR_DBGC_BP_NOT_FOUND:
3830 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Unknown breakpoint %u! (%s)\n",
3831 pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));
3832 break;
3833
3834 case VINF_DBGC_BP_NO_COMMAND:
3835 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Breakpoint %u! (%s)\n",
3836 pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));
3837 break;
3838
3839 case VINF_BUFFER_OVERFLOW:
3840 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Breakpoint %u! Command too long to execute! (%s)\n",
3841 pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));
3842 break;
3843
3844 default:
3845 break;
3846 }
3847 if (VBOX_SUCCESS(rc) && DBGFR3IsHalted(pDbgc->pVM))
3848 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
3849 else
3850 pDbgc->fRegCtxGuest = fRegCtxGuest;
3851 break;
3852 }
3853
3854 case DBGFEVENT_STEPPED:
3855 case DBGFEVENT_STEPPED_HYPER:
3856 {
3857 pDbgc->fRegCtxGuest = pEvent->enmType == DBGFEVENT_STEPPED;
3858
3859 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Single step! (%s)\n", dbgcGetEventCtx(pEvent->enmCtx));
3860 if (VBOX_SUCCESS(rc))
3861 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
3862 break;
3863 }
3864
3865 case DBGFEVENT_ASSERTION_HYPER:
3866 {
3867 pDbgc->fRegCtxGuest = false;
3868
3869 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3870 "\ndbgf event: Hypervisor Assertion! (%s)\n"
3871 "%s"
3872 "%s"
3873 "\n",
3874 dbgcGetEventCtx(pEvent->enmCtx),
3875 pEvent->u.Assert.pszMsg1,
3876 pEvent->u.Assert.pszMsg2);
3877 if (VBOX_SUCCESS(rc))
3878 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
3879 break;
3880 }
3881
3882 case DBGFEVENT_DEV_STOP:
3883 {
3884 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3885 "\n"
3886 "dbgf event: DBGFSTOP (%s)\n"
3887 "File: %s\n"
3888 "Line: %d\n"
3889 "Function: %s\n",
3890 dbgcGetEventCtx(pEvent->enmCtx),
3891 pEvent->u.Src.pszFile,
3892 pEvent->u.Src.uLine,
3893 pEvent->u.Src.pszFunction);
3894 if (VBOX_SUCCESS(rc) && pEvent->u.Src.pszMessage && *pEvent->u.Src.pszMessage)
3895 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
3896 "Message: %s\n",
3897 pEvent->u.Src.pszMessage);
3898 if (VBOX_SUCCESS(rc))
3899 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
3900 break;
3901 }
3902
3903
3904 case DBGFEVENT_INVALID_COMMAND:
3905 {
3906 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Invalid command event!\n");
3907 fPrintPrompt = !pDbgc->fReady;
3908 break;
3909 }
3910
3911 case DBGFEVENT_TERMINATING:
3912 {
3913 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\nVM is terminating!\n");
3914 rc = VERR_GENERAL_FAILURE;
3915 break;
3916 }
3917
3918
3919 default:
3920 {
3921 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Unknown event %d!\n", pEvent->enmType);
3922 fPrintPrompt = !pDbgc->fReady;
3923 break;
3924 }
3925 }
3926
3927 /*
3928 * Prompt, anyone?
3929 */
3930 if (fPrintPrompt && VBOX_SUCCESS(rc))
3931 {
3932 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "VBoxDbg> ");
3933 }
3934
3935 return rc;
3936}
3937
3938
3939
3940
3941
3942/**
3943 * Make a console instance.
3944 *
3945 * This will not return until either an 'exit' command is issued or a error code
3946 * indicating connection loss is encountered.
3947 *
3948 * @returns VINF_SUCCESS if console termination caused by the 'exit' command.
3949 * @returns The VBox status code causing the console termination.
3950 *
3951 * @param pVM VM Handle.
3952 * @param pBack Pointer to the backend structure. This must contain
3953 * a full set of function pointers to service the console.
3954 * @param fFlags Reserved, must be zero.
3955 * @remark A forced termination of the console is easiest done by forcing the
3956 * callbacks to return fatal failures.
3957 */
3958DBGDECL(int) DBGCCreate(PVM pVM, PDBGCBACK pBack, unsigned fFlags)
3959{
3960 /*
3961 * Validate input.
3962 */
3963 AssertReturn(VALID_PTR(pVM), VERR_INVALID_PARAMETER);
3964 AssertReturn(VALID_PTR(pBack), VERR_INVALID_PARAMETER);
3965 AssertMsgReturn(!fFlags, ("%#x", fFlags), VERR_INVALID_PARAMETER);
3966
3967 /*
3968 * Allocate and initialize instance data
3969 */
3970 PDBGC pDbgc = (PDBGC)RTMemAllocZ(sizeof(*pDbgc));
3971 if (!pDbgc)
3972 return VERR_NO_MEMORY;
3973
3974 pDbgc->CmdHlp.pfnWrite = dbgcHlpWrite;
3975 pDbgc->CmdHlp.pfnPrintfV = dbgcHlpPrintfV;
3976 pDbgc->CmdHlp.pfnPrintf = dbgcHlpPrintf;
3977 pDbgc->CmdHlp.pfnVBoxErrorV = dbgcHlpVBoxErrorV;
3978 pDbgc->CmdHlp.pfnVBoxError = dbgcHlpVBoxError;
3979 pDbgc->CmdHlp.pfnMemRead = dbgcHlpMemRead;
3980 pDbgc->CmdHlp.pfnMemWrite = dbgcHlpMemWrite;
3981 pDbgc->CmdHlp.pfnEval = dbgcHlpEval;
3982 pDbgc->CmdHlp.pfnExec = dbgcHlpExec;
3983 pDbgc->CmdHlp.pfnVarToDbgfAddr = dbgcHlpVarToDbgfAddr;
3984 pDbgc->CmdHlp.pfnVarToBool = dbgcHlpVarToBool;
3985 pDbgc->pBack = pBack;
3986 pDbgc->pVM = NULL;
3987 pDbgc->pszEmulation = "CodeView/WinDbg";
3988 pDbgc->paEmulationCmds = &g_aCmdsCodeView[0];
3989 pDbgc->cEmulationCmds = g_cCmdsCodeView;
3990 //pDbgc->fLog = false;
3991 pDbgc->fRegCtxGuest = true;
3992 pDbgc->fRegTerse = true;
3993 //pDbgc->DisasmPos = {0};
3994 //pDbgc->SourcePos = {0};
3995 //pDbgc->DumpPos = {0};
3996 //pDbgc->cbDumpElement = 0;
3997 //pDbgc->cVars = 0;
3998 //pDbgc->paVars = NULL;
3999 //pDbgc->pFirstBp = NULL;
4000 //pDbgc->uInputZero = 0;
4001 //pDbgc->iRead = 0;
4002 //pDbgc->iWrite = 0;
4003 //pDbgc->cInputLines = 0;
4004 //pDbgc->fInputOverflow = false;
4005 pDbgc->fReady = true;
4006 pDbgc->pszScratch = &pDbgc->achScratch[0];
4007 //pDbgc->iArg = 0;
4008 //pDbgc->rcOutput = 0;
4009
4010 dbgcInitOpCharBitMap();
4011
4012 /*
4013 * Print welcome message.
4014 */
4015 int rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
4016 "Welcome to the VirtualBox Debugger!\n");
4017 if (VBOX_FAILURE(rc))
4018 goto l_failure;
4019
4020 /*
4021 * Attach to the VM.
4022 */
4023 rc = DBGFR3Attach(pVM);
4024 if (VBOX_FAILURE(rc))
4025 {
4026 rc = pDbgc->CmdHlp.pfnVBoxError(&pDbgc->CmdHlp, rc, "When trying to attach to VM %p\n", pDbgc->pVM);
4027 goto l_failure;
4028 }
4029 pDbgc->pVM = pVM;
4030
4031 /*
4032 * Print commandline and auto select result.
4033 */
4034 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
4035 "Current VM is %08x\n" /** @todo get and print the VM name! */
4036 "VBoxDbg> ",
4037 pDbgc->pVM);
4038 if (VBOX_FAILURE(rc))
4039 goto l_failure;
4040
4041 /*
4042 * Main Debugger Loop.
4043 *
4044 * This loop will either block on waiting for input or on waiting on
4045 * debug events. If we're forwarding the log we cannot wait for long
4046 * before we must flush the log.
4047 */
4048 for (rc = 0;;)
4049 {
4050 if (pDbgc->pVM && DBGFR3CanWait(pDbgc->pVM))
4051 {
4052 /*
4053 * Wait for a debug event.
4054 */
4055 PCDBGFEVENT pEvent;
4056 rc = DBGFR3EventWait(pDbgc->pVM, pDbgc->fLog ? 1 : 32, &pEvent);
4057 if (VBOX_SUCCESS(rc))
4058 {
4059 rc = dbgcProcessEvent(pDbgc, pEvent);
4060 if (VBOX_FAILURE(rc))
4061 break;
4062 }
4063 else if (rc != VERR_TIMEOUT)
4064 break;
4065
4066 /*
4067 * Check for input.
4068 */
4069 if (pBack->pfnInput(pDbgc->pBack, 0))
4070 {
4071 rc = dbgcProcessInput(pDbgc);
4072 if (VBOX_FAILURE(rc))
4073 break;
4074 }
4075 }
4076 else
4077 {
4078 /*
4079 * Wait for input. If Logging is enabled we'll only wait very briefly.
4080 */
4081 if (pBack->pfnInput(pDbgc->pBack, pDbgc->fLog ? 1 : 1000))
4082 {
4083 rc = dbgcProcessInput(pDbgc);
4084 if (VBOX_FAILURE(rc))
4085 break;
4086 }
4087 }
4088
4089 /*
4090 * Forward log output.
4091 */
4092 if (pDbgc->fLog)
4093 {
4094 rc = dbgcProcessLog(pDbgc);
4095 if (VBOX_FAILURE(rc))
4096 break;
4097 }
4098 }
4099
4100
4101l_failure:
4102 /*
4103 * Cleanup console debugger session.
4104 */
4105 /* Disable log hook. */
4106 if (pDbgc->fLog)
4107 {
4108
4109 }
4110
4111 /* Detach from the VM. */
4112 if (pDbgc->pVM)
4113 DBGFR3Detach(pDbgc->pVM);
4114
4115 /* finally, free the instance memory. */
4116 RTMemFree(pDbgc);
4117
4118 return rc;
4119}
4120
4121
4122
4123/**
4124 * Register one or more external commands.
4125 *
4126 * @returns VBox status.
4127 * @param paCommands Pointer to an array of command descriptors.
4128 * The commands must be unique. It's not possible
4129 * to register the same commands more than once.
4130 * @param cCommands Number of commands.
4131 */
4132DBGDECL(int) DBGCRegisterCommands(PCDBGCCMD paCommands, unsigned cCommands)
4133{
4134 /*
4135 * Lock the list.
4136 */
4137 DBGCEXTCMDS_LOCK_WR();
4138 PDBGCEXTCMDS pCur = g_pExtCmdsHead;
4139 while (pCur)
4140 {
4141 if (paCommands == pCur->paCmds)
4142 {
4143 DBGCEXTCMDS_UNLOCK_WR();
4144 AssertMsgFailed(("Attempt at re-registering %d command(s)!\n", cCommands));
4145 return VWRN_DBGC_ALREADY_REGISTERED;
4146 }
4147 pCur = pCur->pNext;
4148 }
4149
4150 /*
4151 * Allocate new chunk.
4152 */
4153 int rc = 0;
4154 pCur = (PDBGCEXTCMDS)RTMemAlloc(sizeof(*pCur));
4155 if (pCur)
4156 {
4157 pCur->cCmds = cCommands;
4158 pCur->paCmds = paCommands;
4159 pCur->pNext = g_pExtCmdsHead;
4160 g_pExtCmdsHead = pCur;
4161 }
4162 else
4163 rc = VERR_NO_MEMORY;
4164 DBGCEXTCMDS_UNLOCK_WR();
4165
4166 return rc;
4167}
4168
4169
4170/**
4171 * Deregister one or more external commands previously registered by
4172 * DBGCRegisterCommands().
4173 *
4174 * @returns VBox status.
4175 * @param paCommands Pointer to an array of command descriptors
4176 * as given to DBGCRegisterCommands().
4177 * @param cCommands Number of commands.
4178 */
4179DBGDECL(int) DBGCDeregisterCommands(PCDBGCCMD paCommands, unsigned cCommands)
4180{
4181 /*
4182 * Lock the list.
4183 */
4184 DBGCEXTCMDS_LOCK_WR();
4185 PDBGCEXTCMDS pPrev = NULL;
4186 PDBGCEXTCMDS pCur = g_pExtCmdsHead;
4187 while (pCur)
4188 {
4189 if (paCommands == pCur->paCmds)
4190 {
4191 if (pPrev)
4192 pPrev->pNext = pCur->pNext;
4193 else
4194 g_pExtCmdsHead = pCur->pNext;
4195 DBGCEXTCMDS_UNLOCK_WR();
4196
4197 RTMemFree(pCur);
4198 return VINF_SUCCESS;
4199 }
4200 pPrev = pCur;
4201 pCur = pCur->pNext;
4202 }
4203 DBGCEXTCMDS_UNLOCK_WR();
4204
4205 NOREF(cCommands);
4206 return VERR_DBGC_COMMANDS_NOT_REGISTERED;
4207}
4208
Note: See 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