VirtualBox

source: vbox/trunk/src/VBox/Debugger/DBGCCommands.cpp@ 51287

Last change on this file since 51287 was 48696, checked in by vboxsync, 11 years ago

DBGC: Added unload command as well as loadimage32 and loadimage64 for mac os x and efi debugging.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 79.0 KB
Line 
1/* $Id: DBGCCommands.cpp 48696 2013-09-26 00:22:48Z vboxsync $ */
2/** @file
3 * DBGC - Debugger Console, Native Commands.
4 */
5
6/*
7 * Copyright (C) 2006-2013 Oracle Corporation
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 (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_DBGC
22#include <VBox/dbg.h>
23#include <VBox/vmm/dbgf.h>
24#include <VBox/vmm/vm.h>
25#include <VBox/param.h>
26#include <VBox/err.h>
27#include <VBox/log.h>
28#include <VBox/version.h>
29
30#include <iprt/alloca.h>
31#include <iprt/assert.h>
32#include <iprt/ctype.h>
33#include <iprt/dir.h>
34#include <iprt/env.h>
35#include <iprt/ldr.h>
36#include <iprt/mem.h>
37#include <iprt/rand.h>
38#include <iprt/path.h>
39#include <iprt/string.h>
40
41#include <stdlib.h>
42#include <stdio.h>
43
44#include "DBGCInternal.h"
45
46
47/*******************************************************************************
48* Internal Functions *
49*******************************************************************************/
50static FNDBGCCMD dbgcCmdHelp;
51static FNDBGCCMD dbgcCmdQuit;
52static FNDBGCCMD dbgcCmdStop;
53static FNDBGCCMD dbgcCmdDetect;
54static FNDBGCCMD dbgcCmdCpu;
55static FNDBGCCMD dbgcCmdInfo;
56static FNDBGCCMD dbgcCmdLog;
57static FNDBGCCMD dbgcCmdLogDest;
58static FNDBGCCMD dbgcCmdLogFlags;
59static FNDBGCCMD dbgcCmdLogFlush;
60static FNDBGCCMD dbgcCmdFormat;
61static FNDBGCCMD dbgcCmdLoadImage;
62static FNDBGCCMD dbgcCmdLoadMap;
63static FNDBGCCMD dbgcCmdLoadSeg;
64static FNDBGCCMD dbgcCmdUnload;
65static FNDBGCCMD dbgcCmdSet;
66static FNDBGCCMD dbgcCmdUnset;
67static FNDBGCCMD dbgcCmdLoadVars;
68static FNDBGCCMD dbgcCmdShowVars;
69static FNDBGCCMD dbgcCmdLoadPlugIn;
70static FNDBGCCMD dbgcCmdUnloadPlugIn;
71static FNDBGCCMD dbgcCmdShowPlugIns;
72static FNDBGCCMD dbgcCmdHarakiri;
73static FNDBGCCMD dbgcCmdEcho;
74static FNDBGCCMD dbgcCmdRunScript;
75static FNDBGCCMD dbgcCmdWriteCore;
76
77
78/*******************************************************************************
79* Global Variables *
80*******************************************************************************/
81/** One argument of any kind. */
82static const DBGCVARDESC g_aArgAny[] =
83{
84 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
85 { 0, 1, DBGCVAR_CAT_ANY, 0, "var", "Any type of argument." },
86};
87
88/** Multiple string arguments (min 1). */
89static const DBGCVARDESC g_aArgMultiStr[] =
90{
91 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
92 { 1, ~0U, DBGCVAR_CAT_STRING, 0, "strings", "One or more strings." },
93};
94
95/** Filename string. */
96static const DBGCVARDESC g_aArgFilename[] =
97{
98 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
99 { 1, 1, DBGCVAR_CAT_STRING, 0, "path", "Filename string." },
100};
101
102
103/** 'cpu' arguments. */
104static const DBGCVARDESC g_aArgCpu[] =
105{
106 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
107 { 0, 1, DBGCVAR_CAT_NUMBER_NO_RANGE, 0, "idCpu", "CPU ID" },
108};
109
110
111/** 'help' arguments. */
112static const DBGCVARDESC g_aArgHelp[] =
113{
114 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
115 { 0, ~0U, DBGCVAR_CAT_STRING, 0, "cmd/op", "Zero or more command or operator names." },
116};
117
118
119/** 'info' arguments. */
120static const DBGCVARDESC g_aArgInfo[] =
121{
122 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
123 { 1, 1, DBGCVAR_CAT_STRING, 0, "info", "The name of the info to display." },
124 { 0, 1, DBGCVAR_CAT_STRING, 0, "args", "String arguments to the handler." },
125};
126
127
128/** loadimage arguments. */
129static const DBGCVARDESC g_aArgLoadImage[] =
130{
131 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
132 { 1, 1, DBGCVAR_CAT_STRING, 0, "filename", "Filename string." },
133 { 1, 1, DBGCVAR_CAT_POINTER, 0, "address", "The module address." },
134 { 0, 1, DBGCVAR_CAT_STRING, 0, "name", "The module name. (optional)" },
135};
136
137
138/** loadmap arguments. */
139static const DBGCVARDESC g_aArgLoadMap[] =
140{
141 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
142 { 1, 1, DBGCVAR_CAT_STRING, 0, "filename", "Filename string." },
143 { 1, 1, DBGCVAR_CAT_POINTER, DBGCVD_FLAGS_DEP_PREV, "address", "The module address." },
144 { 0, 1, DBGCVAR_CAT_STRING, DBGCVD_FLAGS_DEP_PREV, "name", "The module name. Empty string means default. (optional)" },
145 { 0, 1, DBGCVAR_CAT_NUMBER, DBGCVD_FLAGS_DEP_PREV, "subtrahend", "Value to subtract from the addresses in the map file to rebase it correctly to address. (optional)" },
146 { 0, 1, DBGCVAR_CAT_NUMBER, DBGCVD_FLAGS_DEP_PREV, "seg", "The module segment number (0-based). (optional)" },
147};
148
149
150/** loadseg arguments. */
151static const DBGCVARDESC g_aArgLoadSeg[] =
152{
153 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
154 { 1, 1, DBGCVAR_CAT_STRING, 0, "filename", "Filename string." },
155 { 1, 1, DBGCVAR_CAT_POINTER, 0, "address", "The module address." },
156 { 1, 1, DBGCVAR_CAT_NUMBER, 0, "seg", "The module segment number (0-based)." },
157 { 0, 1, DBGCVAR_CAT_STRING, DBGCVD_FLAGS_DEP_PREV, "name", "The module name. Empty string means default. (optional)" },
158};
159
160
161/** log arguments. */
162static const DBGCVARDESC g_aArgLog[] =
163{
164 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
165 { 0, 1, DBGCVAR_CAT_STRING, 0, "groups", "Group modifier string (quote it!)." }
166};
167
168
169/** logdest arguments. */
170static const DBGCVARDESC g_aArgLogDest[] =
171{
172 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
173 { 0, 1, DBGCVAR_CAT_STRING, 0, "dests", "Destination modifier string (quote it!)." }
174};
175
176
177/** logflags arguments. */
178static const DBGCVARDESC g_aArgLogFlags[] =
179{
180 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
181 { 0, 1, DBGCVAR_CAT_STRING, 0, "flags", "Flag modifier string (quote it!)." }
182};
183
184
185/** loadplugin, unloadplugin. */
186static const DBGCVARDESC g_aArgPlugIn[] =
187{
188 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
189 { 1, ~0U, DBGCVAR_CAT_STRING, 0, "plugin", "Plug-in name or filename." },
190};
191
192
193/** 'set' arguments */
194static const DBGCVARDESC g_aArgSet[] =
195{
196 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
197 { 1, 1, DBGCVAR_CAT_SYMBOL, 0, "var", "Variable name." },
198 { 1, 1, DBGCVAR_CAT_ANY, 0, "value", "Value to assign to the variable." },
199};
200
201/** loadplugin, unloadplugin. */
202static const DBGCVARDESC g_aArgUnload[] =
203{
204 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
205 { 1, ~0U, DBGCVAR_CAT_STRING, 0, "modname", "Unloads all mappings of the given modules in the active address space." },
206};
207
208/** 'unset' arguments */
209static const DBGCVARDESC g_aArgUnset[] =
210{
211 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
212 { 1, ~0U, DBGCVAR_CAT_SYMBOL, 0, "vars", "One or more variable names." },
213};
214
215/** writecore arguments. */
216static const DBGCVARDESC g_aArgWriteCore[] =
217{
218 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
219 { 1, 1, DBGCVAR_CAT_STRING, 0, "path", "Filename string." },
220};
221
222
223
224/** Command descriptors for the basic commands. */
225const DBGCCMD g_aDbgcCmds[] =
226{
227 /* pszCmd, cArgsMin, cArgsMax, paArgDescs, cArgDescs, fFlags, pfnHandler pszSyntax, ....pszDescription */
228 { "bye", 0, 0, NULL, 0, 0, dbgcCmdQuit, "", "Exits the debugger." },
229 { "cpu", 0, 1, &g_aArgCpu[0], RT_ELEMENTS(g_aArgCpu), 0, dbgcCmdCpu, "[idCpu]", "If no argument, display the current CPU, else change to the specified CPU." },
230 { "echo", 1, ~0U, &g_aArgMultiStr[0], RT_ELEMENTS(g_aArgMultiStr), 0, dbgcCmdEcho, "<str1> [str2..[strN]]", "Displays the strings separated by one blank space and the last one followed by a newline." },
231 { "exit", 0, 0, NULL, 0, 0, dbgcCmdQuit, "", "Exits the debugger." },
232 { "format", 1, 1, &g_aArgAny[0], RT_ELEMENTS(g_aArgAny), 0, dbgcCmdFormat, "", "Evaluates an expression and formats it." },
233 { "detect", 0, 0, NULL, 0, 0, dbgcCmdDetect, "", "Detects or re-detects the guest os and starts the OS specific digger." },
234 { "harakiri", 0, 0, NULL, 0, 0, dbgcCmdHarakiri, "", "Kills debugger process." },
235 { "help", 0, ~0U, &g_aArgHelp[0], RT_ELEMENTS(g_aArgHelp), 0, dbgcCmdHelp, "[cmd/op [..]]", "Display help. For help about info items try 'info help'." },
236 { "info", 1, 2, &g_aArgInfo[0], RT_ELEMENTS(g_aArgInfo), 0, dbgcCmdInfo, "<info> [args]", "Display info register in the DBGF. For a list of info items try 'info help'." },
237 { "loadimage", 2, 3, &g_aArgLoadImage[0], RT_ELEMENTS(g_aArgLoadImage), 0, dbgcCmdLoadImage, "<filename> <address> [name]",
238 "Loads the symbols of an executable image at the specified address. "
239 /*"Optionally giving the module a name other than the file name stem."*/ }, /** @todo implement line breaks */
240 { "loadimage32",2, 3, &g_aArgLoadImage[0], RT_ELEMENTS(g_aArgLoadImage), 0, dbgcCmdLoadImage, "<filename> <address> [name]", "loadimage variant for selecting 32-bit images (mach-o)." },
241 { "loadimage64",2, 3, &g_aArgLoadImage[0], RT_ELEMENTS(g_aArgLoadImage), 0, dbgcCmdLoadImage, "<filename> <address> [name]", "loadimage variant for selecting 64-bit images (mach-o)." },
242 { "loadmap", 2, 5, &g_aArgLoadMap[0], RT_ELEMENTS(g_aArgLoadMap), 0, dbgcCmdLoadMap, "<filename> <address> [name] [subtrahend] [seg]",
243 "Loads the symbols from a map file, usually at a specified address. "
244 /*"Optionally giving the module a name other than the file name stem "
245 "and a subtrahend to subtract from the addresses."*/ },
246 { "loadplugin", 1, 1, &g_aArgPlugIn[0], RT_ELEMENTS(g_aArgPlugIn), 0, dbgcCmdLoadPlugIn,"<plugin1> [plugin2..N]", "Loads one or more plugins" },
247 { "loadseg", 3, 4, &g_aArgLoadSeg[0], RT_ELEMENTS(g_aArgLoadSeg), 0, dbgcCmdLoadSeg, "<filename> <address> <seg> [name]",
248 "Loads the symbols of a segment in the executable image at the specified address. "
249 /*"Optionally giving the module a name other than the file name stem."*/ },
250 { "loadvars", 1, 1, &g_aArgFilename[0], RT_ELEMENTS(g_aArgFilename), 0, dbgcCmdLoadVars, "<filename>", "Load variables from file. One per line, same as the args to the set command." },
251 { "log", 0, 1, &g_aArgLog[0], RT_ELEMENTS(g_aArgLog), 0, dbgcCmdLog, "[group string]", "Displays or modifies the logging group settings (VBOX_LOG)" },
252 { "logdest", 0, 1, &g_aArgLogDest[0], RT_ELEMENTS(g_aArgLogDest), 0, dbgcCmdLogDest, "[dest string]", "Displays or modifies the logging destination (VBOX_LOG_DEST)." },
253 { "logflags", 0, 1, &g_aArgLogFlags[0], RT_ELEMENTS(g_aArgLogFlags), 0, dbgcCmdLogFlags, "[flags string]", "Displays or modifies the logging flags (VBOX_LOG_FLAGS)." },
254 { "logflush", 0, 0, NULL, 0, 0, dbgcCmdLogFlush, "", "Flushes the log buffers." },
255 { "quit", 0, 0, NULL, 0, 0, dbgcCmdQuit, "", "Exits the debugger." },
256 { "runscript", 1, 1, &g_aArgFilename[0], RT_ELEMENTS(g_aArgFilename), 0, dbgcCmdRunScript, "<filename>", "Runs the command listed in the script. Lines starting with '#' "
257 "(after removing blanks) are comment. blank lines are ignored. Stops on failure." },
258 { "set", 2, 2, &g_aArgSet[0], RT_ELEMENTS(g_aArgSet), 0, dbgcCmdSet, "<var> <value>", "Sets a global variable." },
259 { "showplugins",0, 0, NULL, 0, 0, dbgcCmdShowPlugIns,"", "List loaded plugins." },
260 { "showvars", 0, 0, NULL, 0, 0, dbgcCmdShowVars, "", "List all the defined variables." },
261 { "stop", 0, 0, NULL, 0, 0, dbgcCmdStop, "", "Stop execution." },
262 { "unload", 1, ~0U, &g_aArgUnload[0], RT_ELEMENTS(g_aArgUnload), 0, dbgcCmdUnload, "<modname1> [modname2..N]", "Unloads one or more modules in the current address space." },
263 { "unloadplugin", 1, ~0U, &g_aArgPlugIn[0], RT_ELEMENTS(g_aArgPlugIn), 0, dbgcCmdUnloadPlugIn, "<plugin1> [plugin2..N]", "Unloads one or more plugins." },
264 { "unset", 1, ~0U, &g_aArgUnset[0], RT_ELEMENTS(g_aArgUnset), 0, dbgcCmdUnset, "<var1> [var1..[varN]]", "Unsets (delete) one or more global variables." },
265 { "writecore", 1, 1, &g_aArgWriteCore[0], RT_ELEMENTS(g_aArgWriteCore), 0, dbgcCmdWriteCore, "<filename>", "Write core to file." },
266};
267/** The number of native commands. */
268const uint32_t g_cDbgcCmds = RT_ELEMENTS(g_aDbgcCmds);
269/** Pointer to head of the list of external commands. */
270static PDBGCEXTCMDS g_pExtCmdsHead;
271
272
273
274
275/**
276 * Finds a routine.
277 *
278 * @returns Pointer to the command descriptor.
279 * If the request was for an external command, the caller is responsible for
280 * unlocking the external command list.
281 * @returns NULL if not found.
282 * @param pDbgc The debug console instance.
283 * @param pachName Pointer to the routine string (not terminated).
284 * @param cchName Length of the routine name.
285 * @param fExternal Whether or not the routine is external.
286 */
287PCDBGCCMD dbgcCommandLookup(PDBGC pDbgc, const char *pachName, size_t cchName, bool fExternal)
288{
289 if (!fExternal)
290 {
291 /* emulation first, so commands can be overloaded (info ++). */
292 PCDBGCCMD pCmd = pDbgc->paEmulationCmds;
293 unsigned cLeft = pDbgc->cEmulationCmds;
294 while (cLeft-- > 0)
295 {
296 if ( !strncmp(pachName, pCmd->pszCmd, cchName)
297 && !pCmd->pszCmd[cchName])
298 return pCmd;
299 pCmd++;
300 }
301
302 for (unsigned iCmd = 0; iCmd < RT_ELEMENTS(g_aDbgcCmds); iCmd++)
303 {
304 if ( !strncmp(pachName, g_aDbgcCmds[iCmd].pszCmd, cchName)
305 && !g_aDbgcCmds[iCmd].pszCmd[cchName])
306 return &g_aDbgcCmds[iCmd];
307 }
308 }
309 else
310 {
311 DBGCEXTLISTS_LOCK_RD();
312 for (PDBGCEXTCMDS pExtCmds = g_pExtCmdsHead; pExtCmds; pExtCmds = pExtCmds->pNext)
313 {
314 for (unsigned iCmd = 0; iCmd < pExtCmds->cCmds; iCmd++)
315 {
316 if ( !strncmp(pachName, pExtCmds->paCmds[iCmd].pszCmd, cchName)
317 && !pExtCmds->paCmds[iCmd].pszCmd[cchName])
318 return &pExtCmds->paCmds[iCmd];
319 }
320 }
321 DBGCEXTLISTS_UNLOCK_RD();
322 }
323
324 return NULL;
325}
326
327
328/**
329 * Register one or more external commands.
330 *
331 * @returns VBox status.
332 * @param paCommands Pointer to an array of command descriptors.
333 * The commands must be unique. It's not possible
334 * to register the same commands more than once.
335 * @param cCommands Number of commands.
336 */
337DBGDECL(int) DBGCRegisterCommands(PCDBGCCMD paCommands, unsigned cCommands)
338{
339 /*
340 * Lock the list.
341 */
342 DBGCEXTLISTS_LOCK_WR();
343 PDBGCEXTCMDS pCur = g_pExtCmdsHead;
344 while (pCur)
345 {
346 if (paCommands == pCur->paCmds)
347 {
348 DBGCEXTLISTS_UNLOCK_WR();
349 AssertMsgFailed(("Attempt at re-registering %d command(s)!\n", cCommands));
350 return VWRN_DBGC_ALREADY_REGISTERED;
351 }
352 pCur = pCur->pNext;
353 }
354
355 /*
356 * Allocate new chunk.
357 */
358 int rc = 0;
359 pCur = (PDBGCEXTCMDS)RTMemAlloc(sizeof(*pCur));
360 if (pCur)
361 {
362 pCur->cCmds = cCommands;
363 pCur->paCmds = paCommands;
364 pCur->pNext = g_pExtCmdsHead;
365 g_pExtCmdsHead = pCur;
366 }
367 else
368 rc = VERR_NO_MEMORY;
369 DBGCEXTLISTS_UNLOCK_WR();
370
371 return rc;
372}
373
374
375/**
376 * Deregister one or more external commands previously registered by
377 * DBGCRegisterCommands().
378 *
379 * @returns VBox status.
380 * @param paCommands Pointer to an array of command descriptors
381 * as given to DBGCRegisterCommands().
382 * @param cCommands Number of commands.
383 */
384DBGDECL(int) DBGCDeregisterCommands(PCDBGCCMD paCommands, unsigned cCommands)
385{
386 /*
387 * Lock the list.
388 */
389 DBGCEXTLISTS_LOCK_WR();
390 PDBGCEXTCMDS pPrev = NULL;
391 PDBGCEXTCMDS pCur = g_pExtCmdsHead;
392 while (pCur)
393 {
394 if (paCommands == pCur->paCmds)
395 {
396 if (pPrev)
397 pPrev->pNext = pCur->pNext;
398 else
399 g_pExtCmdsHead = pCur->pNext;
400 DBGCEXTLISTS_UNLOCK_WR();
401
402 RTMemFree(pCur);
403 return VINF_SUCCESS;
404 }
405 pPrev = pCur;
406 pCur = pCur->pNext;
407 }
408 DBGCEXTLISTS_UNLOCK_WR();
409
410 NOREF(cCommands);
411 return VERR_DBGC_COMMANDS_NOT_REGISTERED;
412}
413
414
415/**
416 * Outputs a command or function summary line.
417 *
418 * @returns Output status code
419 * @param pCmdHlp The command helpers.
420 * @param pszName The name of the function or command.
421 * @param fExternal Whether it's external.
422 * @param pszSyntax The syntax.
423 * @param pszDescription The description.
424 */
425static int dbgcCmdHelpCmdOrFunc(PDBGCCMDHLP pCmdHlp, const char *pszName, bool fExternal,
426 const char *pszSyntax, const char *pszDescription)
427{
428 /*
429 * Aiming for "%-11s %-30s %s". Need to adjust when any of the two
430 * columns are two wide as well as break the last column up if its
431 * too wide.
432 */
433 size_t const cchMaxWidth = 100;
434 size_t const cchCol1 = 11;
435 size_t const cchCol2 = 30;
436 size_t const cchCol3 = cchMaxWidth - cchCol1 - cchCol2 - 2;
437
438 size_t const cchName = strlen(pszName) + fExternal;
439 size_t const cchSyntax = strlen(pszSyntax);
440 size_t cchDesc = strlen(pszDescription);
441
442 /* Can we do it the simple + fast way? */
443 if ( cchName <= cchCol1
444 && cchSyntax <= cchCol2
445 && cchDesc <= cchCol3)
446 return DBGCCmdHlpPrintf(pCmdHlp,
447 !fExternal ? "%-*s %-*s %s\n" : ".%-*s %-*s %s\n",
448 cchCol1, pszName,
449 cchCol2, pszSyntax,
450 pszDescription);
451
452 /* Column 1. */
453 size_t off = 0;
454 DBGCCmdHlpPrintf(pCmdHlp, !fExternal ? "%s" : ".%s", pszName);
455 off += cchName;
456 ssize_t cchPadding = cchCol1 - off;
457 if (cchPadding <= 0)
458 cchPadding = 0;
459
460 /* Column 2. */
461 DBGCCmdHlpPrintf(pCmdHlp, "%*s %s", cchPadding, "", pszSyntax);
462 off += cchPadding + 1 + cchSyntax;
463 cchPadding = cchCol1 + 1 + cchCol2 - off;
464 if (cchPadding <= 0)
465 cchPadding = 0;
466 off += cchPadding;
467
468 /* Column 3. */
469 for (;;)
470 {
471 ssize_t cchCurWidth = cchMaxWidth - off - 1;
472 if (cchCurWidth != (ssize_t)cchCol3)
473 DBGCCmdHlpPrintf(pCmdHlp, "\n");
474 else if ((ssize_t)cchDesc <= cchCurWidth)
475 return DBGCCmdHlpPrintf(pCmdHlp, "%*s %s\n", cchPadding, "", pszDescription);
476 else
477 {
478 /* Split on preceeding blank. */
479 const char *pszEnd = &pszDescription[cchCurWidth];
480 if (!RT_C_IS_BLANK(*pszEnd))
481 while (pszEnd != pszDescription && !RT_C_IS_BLANK(pszEnd[-1]))
482 pszEnd--;
483 const char *pszNext = pszEnd;
484
485 while (pszEnd != pszDescription && RT_C_IS_BLANK(pszEnd[-1]))
486 pszEnd--;
487 if (pszEnd == pszDescription)
488 {
489 while (*pszEnd && !RT_C_IS_BLANK(*pszEnd))
490 pszEnd++;
491 pszNext = pszEnd;
492 }
493
494 while (RT_C_IS_BLANK(*pszNext))
495 pszNext++;
496
497 /* Output it and advance to the next line. */
498 if (!*pszNext)
499 return DBGCCmdHlpPrintf(pCmdHlp, "%*s %.*s\n", cchPadding, "", pszEnd - pszDescription, pszDescription);
500 DBGCCmdHlpPrintf(pCmdHlp, "%*s %.*s\n", cchPadding, "", pszEnd - pszDescription, pszDescription);
501
502 /* next */
503 cchDesc -= pszNext - pszDescription;
504 pszDescription = pszNext;
505 }
506 off = cchCol1 + 1 + cchCol2;
507 cchPadding = off;
508 }
509}
510
511
512/**
513 * Prints full command help.
514 */
515static void dbgcCmdHelpCmdOrFuncFull(PDBGCCMDHLP pCmdHlp, const char *pszName, bool fExternal,
516 const char *pszSyntax, const char *pszDescription,
517 uint32_t cArgsMin, uint32_t cArgsMax,
518 PCDBGCVARDESC paArgDescs, uint32_t cArgDescs, uint32_t *pcHits)
519{
520 if (*pcHits)
521 DBGCCmdHlpPrintf(pCmdHlp, "\n");
522 *pcHits += 1;
523
524 /* the command */
525 dbgcCmdHelpCmdOrFunc(pCmdHlp, pszName, fExternal, pszSyntax, pszDescription);
526#if 1
527 char szTmp[80];
528 if (!cArgsMin && cArgsMin == cArgsMax)
529 RTStrPrintf(szTmp, sizeof(szTmp), "<no args>");
530 else if (cArgsMin == cArgsMax)
531 RTStrPrintf(szTmp, sizeof(szTmp), " <%u args>", cArgsMin);
532 else if (cArgsMax == ~0U)
533 RTStrPrintf(szTmp, sizeof(szTmp), " <%u+ args>", cArgsMin);
534 else
535 RTStrPrintf(szTmp, sizeof(szTmp), " <%u to %u args>", cArgsMin, cArgsMax);
536 dbgcCmdHelpCmdOrFunc(pCmdHlp, "", false, szTmp, "");
537#endif
538
539 /* argument descriptions. */
540 for (uint32_t i = 0; i < cArgDescs; i++)
541 {
542 DBGCCmdHlpPrintf(pCmdHlp, " %-12s %s", paArgDescs[i].pszName, paArgDescs[i].pszDescription);
543 if (!paArgDescs[i].cTimesMin)
544 {
545 if (paArgDescs[i].cTimesMax == ~0U)
546 DBGCCmdHlpPrintf(pCmdHlp, " <optional+>\n");
547 else
548 DBGCCmdHlpPrintf(pCmdHlp, " <optional-%u>\n", paArgDescs[i].cTimesMax);
549 }
550 else
551 {
552 if (paArgDescs[i].cTimesMax == ~0U)
553 DBGCCmdHlpPrintf(pCmdHlp, " <%u+>\n", paArgDescs[i].cTimesMin);
554 else
555 DBGCCmdHlpPrintf(pCmdHlp, " <%u-%u>\n", paArgDescs[i].cTimesMin, paArgDescs[i].cTimesMax);
556 }
557 }
558}
559
560
561
562/**
563 * Prints full command help.
564 */
565static void dbgcPrintHelpCmd(PDBGCCMDHLP pCmdHlp, PCDBGCCMD pCmd, bool fExternal, uint32_t *pcHits)
566{
567 dbgcCmdHelpCmdOrFuncFull(pCmdHlp, pCmd->pszCmd, fExternal, pCmd->pszSyntax, pCmd->pszDescription,
568 pCmd->cArgsMin, pCmd->cArgsMax, pCmd->paArgDescs, pCmd->cArgDescs, pcHits);
569}
570
571
572/**
573 * Prints full function help.
574 */
575static void dbgcPrintHelpFunction(PDBGCCMDHLP pCmdHlp, PCDBGCFUNC pFunc, bool fExternal, uint32_t *pcHits)
576{
577 dbgcCmdHelpCmdOrFuncFull(pCmdHlp, pFunc->pszFuncNm, fExternal, pFunc->pszSyntax, pFunc->pszDescription,
578 pFunc->cArgsMin, pFunc->cArgsMax, pFunc->paArgDescs, pFunc->cArgDescs, pcHits);
579}
580
581
582static void dbgcCmdHelpCommandsWorker(PDBGC pDbgc, PDBGCCMDHLP pCmdHlp, PCDBGCCMD paCmds, uint32_t cCmds, bool fExternal,
583 const char *pszDescFmt, ...)
584{
585 if (pszDescFmt)
586 {
587 va_list va;
588 va_start(va, pszDescFmt);
589 pCmdHlp->pfnPrintfV(pCmdHlp, NULL, pszDescFmt, va);
590 va_end(va);
591 }
592
593 for (uint32_t i = 0; i < cCmds; i++)
594 dbgcCmdHelpCmdOrFunc(pCmdHlp, paCmds[i].pszCmd, fExternal, paCmds[i].pszSyntax, paCmds[i].pszDescription);
595}
596
597
598static void dbgcCmdHelpCommands(PDBGC pDbgc, PDBGCCMDHLP pCmdHlp, uint32_t *pcHits)
599{
600 if (*pcHits)
601 DBGCCmdHlpPrintf(pCmdHlp, "\n");
602 *pcHits += 1;
603
604 dbgcCmdHelpCommandsWorker(pDbgc, pCmdHlp, pDbgc->paEmulationCmds, pDbgc->cEmulationCmds, false,
605 "Commands for %s emulation:\n", pDbgc->pszEmulation);
606 dbgcCmdHelpCommandsWorker(pDbgc, pCmdHlp, g_aDbgcCmds, RT_ELEMENTS(g_aDbgcCmds), false,
607 "\nCommon Commands:\n");
608
609 DBGCEXTLISTS_LOCK_RD();
610 const char *pszDesc = "\nExternal Commands:\n";
611 for (PDBGCEXTCMDS pExtCmd = g_pExtCmdsHead; pExtCmd; pExtCmd = pExtCmd->pNext)
612 {
613 dbgcCmdHelpCommandsWorker(pDbgc, pCmdHlp, pExtCmd->paCmds, pExtCmd->cCmds, false, pszDesc);
614 pszDesc = NULL;
615 }
616 DBGCEXTLISTS_UNLOCK_RD();
617}
618
619
620static void dbgcCmdHelpFunctionsWorker(PDBGC pDbgc, PDBGCCMDHLP pCmdHlp, PCDBGCFUNC paFuncs, size_t cFuncs, bool fExternal,
621 const char *pszDescFmt, ...)
622{
623 if (pszDescFmt)
624 {
625 va_list va;
626 va_start(va, pszDescFmt);
627 DBGCCmdHlpPrintf(pCmdHlp, pszDescFmt, va);
628 va_end(va);
629 }
630
631 for (uint32_t i = 0; i < cFuncs; i++)
632 dbgcCmdHelpCmdOrFunc(pCmdHlp, paFuncs[i].pszFuncNm, fExternal, paFuncs[i].pszSyntax, paFuncs[i].pszDescription);
633}
634
635
636static void dbgcCmdHelpFunctions(PDBGC pDbgc, PDBGCCMDHLP pCmdHlp, uint32_t *pcHits)
637{
638 if (*pcHits)
639 DBGCCmdHlpPrintf(pCmdHlp, "\n");
640 *pcHits += 1;
641
642 dbgcCmdHelpFunctionsWorker(pDbgc, pCmdHlp, pDbgc->paEmulationFuncs, pDbgc->cEmulationFuncs, false,
643 "Functions for %s emulation:\n", pDbgc->pszEmulation);
644 dbgcCmdHelpFunctionsWorker(pDbgc, pCmdHlp, g_aDbgcFuncs, g_cDbgcFuncs, false,
645 "\nCommon Functions:\n");
646#if 0
647 DBGCEXTLISTS_LOCK_RD();
648 const char *pszDesc = "\nExternal Functions:\n";
649 for (PDBGCEXTFUNCS pExtFunc = g_pExtFuncsHead; pExtFunc; pExtFunc = pExtFunc->pNext)
650 {
651 dbgcCmdHelpFunctionsWorker(pDbgc, pCmdHlp, pExtFunc->paFuncs, pExtFunc->cFuncs, false,
652 pszDesc);
653 pszDesc = NULL;
654 }
655 DBGCEXTLISTS_UNLOCK_RD();
656#endif
657}
658
659
660static void dbgcCmdHelpOperators(PDBGC pDbgc, PDBGCCMDHLP pCmdHlp, uint32_t *pcHits)
661{
662 DBGCCmdHlpPrintf(pCmdHlp, !*pcHits ? "Operators:\n" : "\nOperators:\n");
663 *pcHits += 1;
664
665 unsigned iPrecedence = 0;
666 unsigned cLeft = g_cDbgcOps;
667 while (cLeft > 0)
668 {
669 for (unsigned i = 0; i < g_cDbgcOps; i++)
670 if (g_aDbgcOps[i].iPrecedence == iPrecedence)
671 {
672 dbgcCmdHelpCmdOrFunc(pCmdHlp, g_aDbgcOps[i].szName, false,
673 g_aDbgcOps[i].fBinary ? "Binary" : "Unary ",
674 g_aDbgcOps[i].pszDescription);
675 cLeft--;
676 }
677 iPrecedence++;
678 }
679}
680
681
682static void dbgcCmdHelpAll(PDBGC pDbgc, PDBGCCMDHLP pCmdHlp, uint32_t *pcHits)
683{
684 *pcHits += 1;
685 DBGCCmdHlpPrintf(pCmdHlp,
686 "\n"
687 "VirtualBox Debugger Help\n"
688 "------------------------\n"
689 "\n");
690 dbgcCmdHelpCommands(pDbgc, pCmdHlp, pcHits);
691 DBGCCmdHlpPrintf(pCmdHlp, "\n");
692 dbgcCmdHelpFunctions(pDbgc, pCmdHlp, pcHits);
693 DBGCCmdHlpPrintf(pCmdHlp, "\n");
694 dbgcCmdHelpOperators(pDbgc, pCmdHlp, pcHits);
695}
696
697
698static void dbgcCmdHelpSummary(PDBGC pDbgc, PDBGCCMDHLP pCmdHlp, uint32_t *pcHits)
699{
700 *pcHits += 1;
701 DBGCCmdHlpPrintf(pCmdHlp,
702 "\n"
703 "VirtualBox Debugger Help Summary\n"
704 "--------------------------------\n"
705 "\n"
706 "help commands Show help on all commands.\n"
707 "help functions Show help on all functions.\n"
708 "help operators Show help on all operators.\n"
709 "help all All the above.\n"
710 "help <cmd-pattern> [...]\n"
711 " Show details help on individual commands, simple\n"
712 " patterns can be used to match several commands.\n"
713 "help [summary] Displays this message.\n"
714 );
715}
716
717
718/**
719 * @interface_method_impl{FNDBCCMD, The 'help' command.}
720 */
721static DECLCALLBACK(int) dbgcCmdHelp(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
722{
723 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
724 int rc = VINF_SUCCESS;
725 uint32_t cHits = 0;
726
727 if (!cArgs)
728 /*
729 * No arguments, show summary.
730 */
731 dbgcCmdHelpSummary(pDbgc, pCmdHlp, &cHits);
732 else
733 {
734 /*
735 * Search for the arguments (strings).
736 */
737 DBGCEXTCMDS aFixedCmds[] =
738 {
739 { pDbgc->cEmulationCmds, pDbgc->paEmulationCmds, NULL },
740 { g_cDbgcCmds, g_aDbgcCmds, NULL },
741 };
742 DBGCEXTFUNCS aFixedFuncs[] =
743 {
744 { pDbgc->cEmulationFuncs, pDbgc->paEmulationFuncs, NULL },
745 { g_cDbgcFuncs, g_aDbgcFuncs, NULL },
746 };
747
748 for (unsigned iArg = 0; iArg < cArgs; iArg++)
749 {
750 AssertReturn(paArgs[iArg].enmType == DBGCVAR_TYPE_STRING, VERR_DBGC_PARSE_BUG);
751 const char *pszPattern = paArgs[iArg].u.pszString;
752
753 /* aliases */
754 if ( !strcmp(pszPattern, "commands")
755 || !strcmp(pszPattern, "cmds") )
756 dbgcCmdHelpCommands(pDbgc, pCmdHlp, &cHits);
757 else if ( !strcmp(pszPattern, "functions")
758 || !strcmp(pszPattern, "funcs") )
759 dbgcCmdHelpFunctions(pDbgc, pCmdHlp, &cHits);
760 else if ( !strcmp(pszPattern, "operators")
761 || !strcmp(pszPattern, "ops") )
762 dbgcCmdHelpOperators(pDbgc, pCmdHlp, &cHits);
763 else if (!strcmp(pszPattern, "all"))
764 dbgcCmdHelpAll(pDbgc, pCmdHlp, &cHits);
765 else if (!strcmp(pszPattern, "summary"))
766 dbgcCmdHelpSummary(pDbgc, pCmdHlp, &cHits);
767 /* Individual commands. */
768 else
769 {
770 uint32_t const cPrevHits = cHits;
771
772 /* lookup in the emulation command list first */
773 for (unsigned j = 0; j < RT_ELEMENTS(aFixedCmds); j++)
774 for (unsigned i = 0; i < aFixedCmds[j].cCmds; i++)
775 if (RTStrSimplePatternMatch(pszPattern, aFixedCmds[j].paCmds[i].pszCmd))
776 dbgcPrintHelpCmd(pCmdHlp, &aFixedCmds[j].paCmds[i], false, &cHits);
777 for (unsigned j = 0; j < RT_ELEMENTS(aFixedFuncs); j++)
778 for (unsigned i = 0; i < aFixedFuncs[j].cFuncs; i++)
779 if (RTStrSimplePatternMatch(pszPattern, aFixedFuncs[j].paFuncs[i].pszFuncNm))
780 dbgcPrintHelpFunction(pCmdHlp, &aFixedFuncs[j].paFuncs[i], false, &cHits);
781
782 /* external commands */
783 if ( g_pExtCmdsHead
784 && ( *pszPattern == '.'
785 || *pszPattern == '?'
786 || *pszPattern == '*'))
787 {
788 DBGCEXTLISTS_LOCK_RD();
789 const char *pszPattern2 = pszPattern + (*pszPattern == '.' || *pszPattern == '?');
790 for (PDBGCEXTCMDS pExtCmd = g_pExtCmdsHead; pExtCmd; pExtCmd = pExtCmd->pNext)
791 for (unsigned i = 0; i < pExtCmd->cCmds; i++)
792 if (RTStrSimplePatternMatch(pszPattern2, pExtCmd->paCmds[i].pszCmd))
793 dbgcPrintHelpCmd(pCmdHlp, &pExtCmd->paCmds[i], true, &cHits);
794#if 0
795 for (PDBGCEXTFUNCS pExtFunc = g_pExtFuncsHead; pExtFunc; pExtFunc = pExtFunc->pNext)
796 for (unsigned i = 0; i < pExtFunc->cFuncs; i++)
797 if (RTStrSimplePatternMatch(pszPattern2, pExtFunc->paFuncs[i].pszFuncNm))
798 dbgcPrintHelpFunction(pCmdHlp, &pExtFunc->paFuncs[i], true, &cHits);
799#endif
800 DBGCEXTLISTS_UNLOCK_RD();
801 }
802
803 /* operators */
804 if (cHits == cPrevHits && strlen(paArgs[iArg].u.pszString) < sizeof(g_aDbgcOps[0].szName))
805 for (unsigned i = 0; i < g_cDbgcOps && RT_SUCCESS(rc); i++)
806 if (RTStrSimplePatternMatch(pszPattern, g_aDbgcOps[i].szName))
807 {
808 if (cHits++)
809 DBGCCmdHlpPrintf(pCmdHlp, "\n");
810 dbgcCmdHelpCmdOrFunc(pCmdHlp, g_aDbgcOps[i].szName, false,
811 g_aDbgcOps[i].fBinary ? "Binary" : "Unary ",
812 g_aDbgcOps[i].pszDescription);
813 }
814
815 /* found? */
816 if (cHits == cPrevHits)
817 {
818 DBGCCmdHlpPrintf(pCmdHlp, "error: '%s' was not found!\n",
819 paArgs[iArg].u.pszString);
820 rc = VERR_DBGC_COMMAND_FAILED;
821 }
822 }
823 } /* foreach argument */
824 }
825
826 NOREF(pCmd);
827 NOREF(pUVM);
828 return rc;
829}
830
831
832/**
833 * @interface_method_impl{FNDBCCMD, The 'quit', 'exit' and 'bye' commands. }
834 */
835static DECLCALLBACK(int) dbgcCmdQuit(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
836{
837 DBGCCmdHlpPrintf(pCmdHlp, "Quitting console...\n");
838 NOREF(pCmd);
839 NOREF(pUVM);
840 NOREF(paArgs);
841 NOREF(cArgs);
842 return VERR_DBGC_QUIT;
843}
844
845
846/**
847 * @interface_method_impl{FNDBCCMD, The 'stop' command.}
848 */
849static DECLCALLBACK(int) dbgcCmdStop(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
850{
851 /*
852 * Check if the VM is halted or not before trying to halt it.
853 */
854 int rc;
855 if (DBGFR3IsHalted(pUVM))
856 rc = DBGCCmdHlpPrintf(pCmdHlp, "warning: The VM is already halted...\n");
857 else
858 {
859 rc = DBGFR3Halt(pUVM);
860 if (RT_SUCCESS(rc))
861 rc = VWRN_DBGC_CMD_PENDING;
862 else
863 rc = DBGCCmdHlpVBoxError(pCmdHlp, rc, "Executing DBGFR3Halt().");
864 }
865
866 NOREF(pCmd); NOREF(paArgs); NOREF(cArgs);
867 return rc;
868}
869
870
871/**
872 * @interface_method_impl{FNDBCCMD, The 'echo' command.}
873 */
874static DECLCALLBACK(int) dbgcCmdEcho(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
875{
876 /*
877 * Loop thru the arguments and print them with one space between.
878 */
879 int rc = 0;
880 for (unsigned i = 0; i < cArgs; i++)
881 {
882 AssertReturn(paArgs[i].enmType == DBGCVAR_TYPE_STRING, VERR_DBGC_PARSE_BUG);
883 rc = DBGCCmdHlpPrintf(pCmdHlp, i ? " %s" : "%s", paArgs[i].u.pszString);
884 if (RT_FAILURE(rc))
885 return rc;
886 }
887 NOREF(pCmd); NOREF(pUVM);
888 return DBGCCmdHlpPrintf(pCmdHlp, "\n");
889}
890
891
892/**
893 * @interface_method_impl{FNDBCCMD, The 'runscript' command.}
894 */
895static DECLCALLBACK(int) dbgcCmdRunScript(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
896{
897 /* check that the parser did what it's supposed to do. */
898 if ( cArgs != 1
899 || paArgs[0].enmType != DBGCVAR_TYPE_STRING)
900 return DBGCCmdHlpPrintf(pCmdHlp, "parser error\n");
901
902 /** @todo Load the script here, but someone else should do the actual
903 * evaluation and execution of it. */
904
905 /*
906 * Try open the script.
907 */
908 const char *pszFilename = paArgs[0].u.pszString;
909 FILE *pFile = fopen(pszFilename, "r");
910 if (!pFile)
911 return DBGCCmdHlpPrintf(pCmdHlp, "Failed to open '%s'.\n", pszFilename);
912
913 /*
914 * Execute it line by line.
915 */
916 int rc = 0;
917 unsigned iLine = 0;
918 char szLine[8192];
919 while (fgets(szLine, sizeof(szLine), pFile))
920 {
921 /* check that the line isn't too long. */
922 char *pszEnd = strchr(szLine, '\0');
923 if (pszEnd == &szLine[sizeof(szLine) - 1])
924 {
925 rc = DBGCCmdHlpPrintf(pCmdHlp, "runscript error: Line #%u is too long\n", iLine);
926 break;
927 }
928 iLine++;
929
930 /* strip leading blanks and check for comment / blank line. */
931 char *psz = RTStrStripL(szLine);
932 if ( *psz == '\0'
933 || *psz == '\n'
934 || *psz == '#')
935 continue;
936
937 /* strip trailing blanks and check for empty line (\r case). */
938 while ( pszEnd > psz
939 && RT_C_IS_SPACE(pszEnd[-1])) /* RT_C_IS_SPACE includes \n and \r normally. */
940 *--pszEnd = '\0';
941
942 /** @todo check for Control-C / Cancel at this point... */
943
944 /*
945 * Execute the command.
946 *
947 * This is a bit wasteful with scratch space btw., can fix it later.
948 * The whole return code crap should be fixed too, so that it's possible
949 * to know whether a command succeeded (RT_SUCCESS()) or failed, and
950 * more importantly why it failed.
951 */
952 rc = pCmdHlp->pfnExec(pCmdHlp, "%s", psz);
953 if (RT_FAILURE(rc))
954 {
955 if (rc == VERR_BUFFER_OVERFLOW)
956 rc = DBGCCmdHlpPrintf(pCmdHlp, "runscript error: Line #%u is too long (exec overflowed)\n", iLine);
957 break;
958 }
959 if (rc == VWRN_DBGC_CMD_PENDING)
960 {
961 rc = DBGCCmdHlpPrintf(pCmdHlp, "runscript error: VWRN_DBGC_CMD_PENDING on line #%u, script terminated\n", iLine);
962 break;
963 }
964 }
965
966 fclose(pFile);
967
968 NOREF(pCmd); NOREF(pUVM);
969 return rc;
970}
971
972
973/**
974 * @interface_method_impl{FNDBCCMD, The 'detect' command.}
975 */
976static DECLCALLBACK(int) dbgcCmdDetect(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
977{
978 /* check that the parser did what it's supposed to do. */
979 if (cArgs != 0)
980 return DBGCCmdHlpPrintf(pCmdHlp, "parser error\n");
981
982 /*
983 * Perform the detection.
984 */
985 char szName[64];
986 int rc = DBGFR3OSDetect(pUVM, szName, sizeof(szName));
987 if (RT_FAILURE(rc))
988 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "Executing DBGFR3OSDetect().\n");
989 if (rc == VINF_SUCCESS)
990 {
991 rc = DBGCCmdHlpPrintf(pCmdHlp, "Guest OS: %s\n", szName);
992 char szVersion[512];
993 int rc2 = DBGFR3OSQueryNameAndVersion(pUVM, NULL, 0, szVersion, sizeof(szVersion));
994 if (RT_SUCCESS(rc2))
995 rc = DBGCCmdHlpPrintf(pCmdHlp, "Version : %s\n", szVersion);
996 }
997 else
998 rc = DBGCCmdHlpPrintf(pCmdHlp, "Unable to figure out which guest OS it is, sorry.\n");
999 NOREF(pCmd); NOREF(paArgs);
1000 return rc;
1001}
1002
1003
1004/**
1005 * @interface_method_impl{FNDBCCMD, The 'cpu' command.}
1006 */
1007static DECLCALLBACK(int) dbgcCmdCpu(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1008{
1009 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1010
1011 /* check that the parser did what it's supposed to do. */
1012 if ( cArgs != 0
1013 && ( cArgs != 1
1014 || paArgs[0].enmType != DBGCVAR_TYPE_NUMBER))
1015 return DBGCCmdHlpPrintf(pCmdHlp, "parser error\n");
1016 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
1017
1018 int rc;
1019 if (!cArgs)
1020 rc = DBGCCmdHlpPrintf(pCmdHlp, "Current CPU ID: %u\n", pDbgc->idCpu);
1021 else
1022 {
1023 VMCPUID cCpus = DBGFR3CpuGetCount(pUVM);
1024 if (paArgs[0].u.u64Number >= cCpus)
1025 rc = DBGCCmdHlpPrintf(pCmdHlp, "error: idCpu %u is out of range! Highest ID is %u.\n",
1026 paArgs[0].u.u64Number, cCpus);
1027 else
1028 {
1029 rc = DBGCCmdHlpPrintf(pCmdHlp, "Changed CPU from %u to %u.\n",
1030 pDbgc->idCpu, (VMCPUID)paArgs[0].u.u64Number);
1031 pDbgc->idCpu = (VMCPUID)paArgs[0].u.u64Number;
1032 }
1033 }
1034 return rc;
1035}
1036
1037
1038/**
1039 * @interface_method_impl{FNDBCCMD, The 'info' command.}
1040 */
1041static DECLCALLBACK(int) dbgcCmdInfo(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1042{
1043 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1044
1045 /*
1046 * Validate input.
1047 */
1048 if ( cArgs < 1
1049 || cArgs > 2
1050 || paArgs[0].enmType != DBGCVAR_TYPE_STRING
1051 || paArgs[cArgs - 1].enmType != DBGCVAR_TYPE_STRING)
1052 return DBGCCmdHlpPrintf(pCmdHlp, "internal error: The parser doesn't do its job properly yet.. quote the string.\n");
1053 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
1054
1055 /*
1056 * Dump it.
1057 */
1058 int rc = DBGFR3InfoEx(pUVM, pDbgc->idCpu,
1059 paArgs[0].u.pszString,
1060 cArgs == 2 ? paArgs[1].u.pszString : NULL,
1061 DBGCCmdHlpGetDbgfOutputHlp(pCmdHlp));
1062 if (RT_FAILURE(rc))
1063 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3InfoEx()\n");
1064
1065 NOREF(pCmd);
1066 return 0;
1067}
1068
1069
1070/**
1071 * @interface_method_impl{FNDBCCMD, The 'log' command.}
1072 */
1073static DECLCALLBACK(int) dbgcCmdLog(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1074{
1075 int rc;
1076 if (cArgs == 0)
1077 {
1078 char szBuf[_64K];
1079 rc = RTLogGetGroupSettings(NULL, szBuf, sizeof(szBuf));
1080 if (RT_FAILURE(rc))
1081 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "RTLogGetDestinations(NULL,,%#zx)\n", sizeof(szBuf));
1082 DBGCCmdHlpPrintf(pCmdHlp, "VBOX_LOG=%s\n", szBuf);
1083 }
1084 else
1085 {
1086 rc = DBGFR3LogModifyGroups(pUVM, paArgs[0].u.pszString);
1087 if (RT_FAILURE(rc))
1088 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3LogModifyGroups(%p,'%s')\n", pUVM, paArgs[0].u.pszString);
1089 }
1090 NOREF(pCmd);
1091 return VINF_SUCCESS;
1092}
1093
1094
1095/**
1096 * @interface_method_impl{FNDBCCMD, The 'logdest' command.}
1097 */
1098static DECLCALLBACK(int) dbgcCmdLogDest(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1099{
1100 int rc;
1101 if (cArgs == 0)
1102 {
1103 char szBuf[_16K];
1104 rc = RTLogGetDestinations(NULL, szBuf, sizeof(szBuf));
1105 if (RT_FAILURE(rc))
1106 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "RTLogGetDestinations(NULL,,%#zx)\n", sizeof(szBuf));
1107 DBGCCmdHlpPrintf(pCmdHlp, "VBOX_LOG_DEST=%s\n", szBuf);
1108 }
1109 else
1110 {
1111 rc = DBGFR3LogModifyDestinations(pUVM, paArgs[0].u.pszString);
1112 if (RT_FAILURE(rc))
1113 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3LogModifyDestinations(%p,'%s')\n", pUVM, paArgs[0].u.pszString);
1114 }
1115 NOREF(pCmd);
1116 return VINF_SUCCESS;
1117}
1118
1119
1120/**
1121 * @interface_method_impl{FNDBCCMD, The 'logflags' command.}
1122 */
1123static DECLCALLBACK(int) dbgcCmdLogFlags(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1124{
1125 int rc;
1126 if (cArgs == 0)
1127 {
1128 char szBuf[_16K];
1129 rc = RTLogGetFlags(NULL, szBuf, sizeof(szBuf));
1130 if (RT_FAILURE(rc))
1131 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "RTLogGetFlags(NULL,,%#zx)\n", sizeof(szBuf));
1132 DBGCCmdHlpPrintf(pCmdHlp, "VBOX_LOG_FLAGS=%s\n", szBuf);
1133 }
1134 else
1135 {
1136 rc = DBGFR3LogModifyFlags(pUVM, paArgs[0].u.pszString);
1137 if (RT_FAILURE(rc))
1138 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3LogModifyFlags(%p,'%s')\n", pUVM, paArgs[0].u.pszString);
1139 }
1140
1141 NOREF(pCmd);
1142 return rc;
1143}
1144
1145
1146/**
1147 * @interface_method_impl{FNDBCCMD, The 'logflush' command.}
1148 */
1149static DECLCALLBACK(int) dbgcCmdLogFlush(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1150{
1151 RTLogFlush(NULL);
1152 PRTLOGGER pLogRel = RTLogRelDefaultInstance();
1153 if (pLogRel)
1154 RTLogFlush(pLogRel);
1155
1156 NOREF(pCmd); NOREF(cArgs);
1157 return VINF_SUCCESS;
1158}
1159
1160
1161/**
1162 * @interface_method_impl{FNDBCCMD, The 'format' command.}
1163 */
1164static DECLCALLBACK(int) dbgcCmdFormat(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1165{
1166 LogFlow(("dbgcCmdFormat\n"));
1167 static const char *apszRangeDesc[] =
1168 {
1169 "none", "bytes", "elements"
1170 };
1171 int rc;
1172
1173 for (unsigned iArg = 0; iArg < cArgs; iArg++)
1174 {
1175 switch (paArgs[iArg].enmType)
1176 {
1177 case DBGCVAR_TYPE_UNKNOWN:
1178 rc = DBGCCmdHlpPrintf(pCmdHlp,
1179 "Unknown variable type!\n");
1180 break;
1181 case DBGCVAR_TYPE_GC_FLAT:
1182 if (paArgs[iArg].enmRangeType != DBGCVAR_RANGE_NONE)
1183 rc = DBGCCmdHlpPrintf(pCmdHlp,
1184 "Guest flat address: %%%08x range %lld %s\n",
1185 paArgs[iArg].u.GCFlat,
1186 paArgs[iArg].u64Range,
1187 apszRangeDesc[paArgs[iArg].enmRangeType]);
1188 else
1189 rc = DBGCCmdHlpPrintf(pCmdHlp,
1190 "Guest flat address: %%%08x\n",
1191 paArgs[iArg].u.GCFlat);
1192 break;
1193 case DBGCVAR_TYPE_GC_FAR:
1194 if (paArgs[iArg].enmRangeType != DBGCVAR_RANGE_NONE)
1195 rc = DBGCCmdHlpPrintf(pCmdHlp,
1196 "Guest far address: %04x:%08x range %lld %s\n",
1197 paArgs[iArg].u.GCFar.sel,
1198 paArgs[iArg].u.GCFar.off,
1199 paArgs[iArg].u64Range,
1200 apszRangeDesc[paArgs[iArg].enmRangeType]);
1201 else
1202 rc = DBGCCmdHlpPrintf(pCmdHlp,
1203 "Guest far address: %04x:%08x\n",
1204 paArgs[iArg].u.GCFar.sel,
1205 paArgs[iArg].u.GCFar.off);
1206 break;
1207 case DBGCVAR_TYPE_GC_PHYS:
1208 if (paArgs[iArg].enmRangeType != DBGCVAR_RANGE_NONE)
1209 rc = DBGCCmdHlpPrintf(pCmdHlp,
1210 "Guest physical address: %%%%%08x range %lld %s\n",
1211 paArgs[iArg].u.GCPhys,
1212 paArgs[iArg].u64Range,
1213 apszRangeDesc[paArgs[iArg].enmRangeType]);
1214 else
1215 rc = DBGCCmdHlpPrintf(pCmdHlp,
1216 "Guest physical address: %%%%%08x\n",
1217 paArgs[iArg].u.GCPhys);
1218 break;
1219 case DBGCVAR_TYPE_HC_FLAT:
1220 if (paArgs[iArg].enmRangeType != DBGCVAR_RANGE_NONE)
1221 rc = DBGCCmdHlpPrintf(pCmdHlp,
1222 "Host flat address: %%%08x range %lld %s\n",
1223 paArgs[iArg].u.pvHCFlat,
1224 paArgs[iArg].u64Range,
1225 apszRangeDesc[paArgs[iArg].enmRangeType]);
1226 else
1227 rc = DBGCCmdHlpPrintf(pCmdHlp,
1228 "Host flat address: %%%08x\n",
1229 paArgs[iArg].u.pvHCFlat);
1230 break;
1231 case DBGCVAR_TYPE_HC_PHYS:
1232 if (paArgs[iArg].enmRangeType != DBGCVAR_RANGE_NONE)
1233 rc = DBGCCmdHlpPrintf(pCmdHlp,
1234 "Host physical address: %RHp range %lld %s\n",
1235 paArgs[iArg].u.HCPhys,
1236 paArgs[iArg].u64Range,
1237 apszRangeDesc[paArgs[iArg].enmRangeType]);
1238 else
1239 rc = DBGCCmdHlpPrintf(pCmdHlp,
1240 "Host physical address: %RHp\n",
1241 paArgs[iArg].u.HCPhys);
1242 break;
1243
1244 case DBGCVAR_TYPE_STRING:
1245 rc = DBGCCmdHlpPrintf(pCmdHlp,
1246 "String, %lld bytes long: %s\n",
1247 paArgs[iArg].u64Range,
1248 paArgs[iArg].u.pszString);
1249 break;
1250
1251 case DBGCVAR_TYPE_SYMBOL:
1252 rc = DBGCCmdHlpPrintf(pCmdHlp,
1253 "Symbol, %lld bytes long: %s\n",
1254 paArgs[iArg].u64Range,
1255 paArgs[iArg].u.pszString);
1256 break;
1257
1258 case DBGCVAR_TYPE_NUMBER:
1259 if (paArgs[iArg].enmRangeType != DBGCVAR_RANGE_NONE)
1260 rc = DBGCCmdHlpPrintf(pCmdHlp,
1261 "Number: hex %llx dec 0i%lld oct 0t%llo range %lld %s\n",
1262 paArgs[iArg].u.u64Number,
1263 paArgs[iArg].u.u64Number,
1264 paArgs[iArg].u.u64Number,
1265 paArgs[iArg].u64Range,
1266 apszRangeDesc[paArgs[iArg].enmRangeType]);
1267 else
1268 rc = DBGCCmdHlpPrintf(pCmdHlp,
1269 "Number: hex %llx dec 0i%lld oct 0t%llo\n",
1270 paArgs[iArg].u.u64Number,
1271 paArgs[iArg].u.u64Number,
1272 paArgs[iArg].u.u64Number);
1273 break;
1274
1275 default:
1276 rc = DBGCCmdHlpPrintf(pCmdHlp,
1277 "Invalid argument type %d\n",
1278 paArgs[iArg].enmType);
1279 break;
1280 }
1281 } /* arg loop */
1282
1283 NOREF(pCmd); NOREF(pUVM);
1284 return 0;
1285}
1286
1287
1288/**
1289 * @interface_method_impl{FNDBCCMD, The 'loadimage' command.}
1290 */
1291static DECLCALLBACK(int) dbgcCmdLoadImage(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1292{
1293 /*
1294 * Validate the parsing and make sense of the input.
1295 * This is a mess as usual because we don't trust the parser yet.
1296 */
1297 AssertReturn( cArgs >= 2
1298 && cArgs <= 3
1299 && paArgs[0].enmType == DBGCVAR_TYPE_STRING
1300 && DBGCVAR_ISPOINTER(paArgs[1].enmType),
1301 VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
1302
1303 const char *pszFilename = paArgs[0].u.pszString;
1304
1305 DBGFADDRESS ModAddress;
1306 int rc = pCmdHlp->pfnVarToDbgfAddr(pCmdHlp, &paArgs[1], &ModAddress);
1307 if (RT_FAILURE(rc))
1308 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "pfnVarToDbgfAddr: %Dv\n", &paArgs[1]);
1309
1310 const char *pszModName = NULL;
1311 if (cArgs >= 3)
1312 {
1313 AssertReturn(paArgs[2].enmType == DBGCVAR_TYPE_STRING, VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
1314 pszModName = paArgs[2].u.pszString;
1315 }
1316
1317 /*
1318 * Determine the desired image arch from the load command used.
1319 */
1320 RTLDRARCH enmArch = RTLDRARCH_WHATEVER;
1321 if (pCmd->pszCmd[sizeof("loadimage") - 1] == '3')
1322 enmArch = RTLDRARCH_X86_32;
1323 else if (pCmd->pszCmd[sizeof("loadimage") - 1] == '6')
1324 enmArch = RTLDRARCH_AMD64;
1325
1326 /*
1327 * Try create a module for it.
1328 */
1329 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1330 rc = DBGFR3AsLoadImage(pUVM, pDbgc->hDbgAs, pszFilename, pszModName, enmArch, &ModAddress, NIL_RTDBGSEGIDX, 0 /*fFlags*/);
1331 if (RT_FAILURE(rc))
1332 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3ModuleLoadImage(,,'%s','%s',%Dv,)\n",
1333 pszFilename, pszModName, &paArgs[1]);
1334
1335 return VINF_SUCCESS;
1336}
1337
1338
1339/**
1340 * @interface_method_impl{FNDBCCMD, The 'loadmap' command.}
1341 */
1342static DECLCALLBACK(int) dbgcCmdLoadMap(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1343{
1344 /*
1345 * Validate the parsing and make sense of the input.
1346 * This is a mess as usual because we don't trust the parser yet.
1347 */
1348 AssertReturn( cArgs >= 2
1349 && cArgs <= 5
1350 && paArgs[0].enmType == DBGCVAR_TYPE_STRING
1351 && DBGCVAR_ISPOINTER(paArgs[1].enmType),
1352 VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
1353
1354 const char *pszFilename = paArgs[0].u.pszString;
1355
1356 DBGFADDRESS ModAddress;
1357 int rc = pCmdHlp->pfnVarToDbgfAddr(pCmdHlp, &paArgs[1], &ModAddress);
1358 if (RT_FAILURE(rc))
1359 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "pfnVarToDbgfAddr: %Dv\n", &paArgs[1]);
1360
1361 const char *pszModName = NULL;
1362 if (cArgs >= 3)
1363 {
1364 AssertReturn(paArgs[2].enmType == DBGCVAR_TYPE_STRING, VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
1365 pszModName = paArgs[2].u.pszString;
1366 }
1367
1368 RTGCUINTPTR uSubtrahend = 0;
1369 if (cArgs >= 4)
1370 {
1371 AssertReturn(paArgs[3].enmType == DBGCVAR_TYPE_NUMBER, VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
1372 uSubtrahend = paArgs[3].u.u64Number;
1373 }
1374
1375 RTDBGSEGIDX iModSeg = NIL_RTDBGSEGIDX;
1376 if (cArgs >= 5)
1377 {
1378 AssertReturn(paArgs[4].enmType == DBGCVAR_TYPE_NUMBER, VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
1379 iModSeg = (RTDBGSEGIDX)paArgs[4].u.u64Number;
1380 if ( iModSeg != paArgs[4].u.u64Number
1381 || iModSeg > RTDBGSEGIDX_LAST)
1382 return DBGCCmdHlpPrintf(pCmdHlp, "Segment index out of range: %Dv; range={0..%#x}\n", &paArgs[1], RTDBGSEGIDX_LAST);
1383 }
1384
1385 /*
1386 * Try create a module for it.
1387 */
1388 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1389 rc = DBGFR3AsLoadMap(pUVM, pDbgc->hDbgAs, pszFilename, pszModName, &ModAddress, NIL_RTDBGSEGIDX, uSubtrahend, 0 /*fFlags*/);
1390 if (RT_FAILURE(rc))
1391 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3AsLoadMap(,,'%s','%s',%Dv,)\n",
1392 pszFilename, pszModName, &paArgs[1]);
1393
1394 NOREF(pCmd);
1395 return VINF_SUCCESS;
1396}
1397
1398
1399/**
1400 * @interface_method_impl{FNDBCCMD, The 'loadseg' command.}
1401 */
1402static DECLCALLBACK(int) dbgcCmdLoadSeg(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1403{
1404 /*
1405 * Validate the parsing and make sense of the input.
1406 * This is a mess as usual because we don't trust the parser yet.
1407 */
1408 AssertReturn( cArgs >= 3
1409 && cArgs <= 4
1410 && paArgs[0].enmType == DBGCVAR_TYPE_STRING
1411 && DBGCVAR_ISPOINTER(paArgs[1].enmType)
1412 && paArgs[2].enmType == DBGCVAR_TYPE_NUMBER,
1413 VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
1414
1415 const char *pszFilename = paArgs[0].u.pszString;
1416
1417 DBGFADDRESS ModAddress;
1418 int rc = pCmdHlp->pfnVarToDbgfAddr(pCmdHlp, &paArgs[1], &ModAddress);
1419 if (RT_FAILURE(rc))
1420 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "pfnVarToDbgfAddr: %Dv\n", &paArgs[1]);
1421
1422 RTDBGSEGIDX iModSeg = (RTDBGSEGIDX)paArgs[1].u.u64Number;
1423 if ( iModSeg != paArgs[2].u.u64Number
1424 || iModSeg > RTDBGSEGIDX_LAST)
1425 return DBGCCmdHlpPrintf(pCmdHlp, "Segment index out of range: %Dv; range={0..%#x}\n", &paArgs[1], RTDBGSEGIDX_LAST);
1426
1427 const char *pszModName = NULL;
1428 if (cArgs >= 4)
1429 {
1430 AssertReturn(paArgs[3].enmType == DBGCVAR_TYPE_STRING, VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
1431 pszModName = paArgs[3].u.pszString;
1432 }
1433
1434 /*
1435 * Call the debug info manager about this loading.
1436 */
1437 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1438 rc = DBGFR3AsLoadImage(pUVM, pDbgc->hDbgAs, pszFilename, pszModName, RTLDRARCH_WHATEVER, &ModAddress, iModSeg, 0 /*fFlags*/);
1439 if (RT_FAILURE(rc))
1440 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3ModuleLoadImage(,,'%s','%s',%Dv,)\n",
1441 pszFilename, pszModName, &paArgs[1]);
1442
1443 NOREF(pCmd);
1444 return VINF_SUCCESS;
1445}
1446
1447
1448/**
1449 * @interface_method_impl{FNDBCCMD, The 'unload' command.}
1450 */
1451static DECLCALLBACK(int) dbgcCmdUnload(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1452{
1453 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1454
1455 /*
1456 * Validate the parsing and make sense of the input.
1457 * This is a mess as usual because we don't trust the parser yet.
1458 */
1459 AssertReturn( cArgs >= 1
1460 && paArgs[0].enmType == DBGCVAR_TYPE_STRING,
1461 VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
1462 for (unsigned i = 0; i < cArgs; i++)
1463 {
1464 AssertReturn(paArgs[i].enmType == DBGCVAR_TYPE_STRING, VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
1465
1466 int rc = DBGFR3AsUnlinkModuleByName(pUVM, pDbgc->hDbgAs, paArgs[i].u.pszString);
1467 if (RT_FAILURE(rc))
1468 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3AsUnlinkModuleByName(,,'%s')\n", paArgs[i].u.pszString);
1469 }
1470
1471 NOREF(pCmd);
1472 return VINF_SUCCESS;
1473}
1474
1475
1476/**
1477 * @interface_method_impl{FNDBCCMD, The 'set' command.}
1478 */
1479static DECLCALLBACK(int) dbgcCmdSet(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1480{
1481 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1482
1483 /* parse sanity check. */
1484 AssertMsg(paArgs[0].enmType == DBGCVAR_TYPE_STRING, ("expected string not %d as first arg!\n", paArgs[0].enmType));
1485 if (paArgs[0].enmType != DBGCVAR_TYPE_STRING)
1486 return VERR_DBGC_PARSE_INCORRECT_ARG_TYPE;
1487
1488
1489 /*
1490 * A variable must start with an alpha chars and only contain alpha numerical chars.
1491 */
1492 const char *pszVar = paArgs[0].u.pszString;
1493 if (!RT_C_IS_ALPHA(*pszVar) || *pszVar == '_')
1494 return DBGCCmdHlpPrintf(pCmdHlp,
1495 "syntax error: Invalid variable name '%s'. Variable names must match regex '[_a-zA-Z][_a-zA-Z0-9*'!",
1496 paArgs[0].u.pszString);
1497
1498 while (RT_C_IS_ALNUM(*pszVar) || *pszVar == '_')
1499 *pszVar++;
1500 if (*pszVar)
1501 return DBGCCmdHlpPrintf(pCmdHlp,
1502 "syntax error: Invalid variable name '%s'. Variable names must match regex '[_a-zA-Z][_a-zA-Z0-9*]'!",
1503 paArgs[0].u.pszString);
1504
1505
1506 /*
1507 * Calc variable size.
1508 */
1509 size_t cbVar = (size_t)paArgs[0].u64Range + sizeof(DBGCNAMEDVAR);
1510 if (paArgs[1].enmType == DBGCVAR_TYPE_STRING)
1511 cbVar += 1 + (size_t)paArgs[1].u64Range;
1512
1513 /*
1514 * Look for existing one.
1515 */
1516 pszVar = paArgs[0].u.pszString;
1517 for (unsigned iVar = 0; iVar < pDbgc->cVars; iVar++)
1518 {
1519 if (!strcmp(pszVar, pDbgc->papVars[iVar]->szName))
1520 {
1521 /*
1522 * Update existing variable.
1523 */
1524 void *pv = RTMemRealloc(pDbgc->papVars[iVar], cbVar);
1525 if (!pv)
1526 return VERR_DBGC_PARSE_NO_MEMORY;
1527 PDBGCNAMEDVAR pVar = pDbgc->papVars[iVar] = (PDBGCNAMEDVAR)pv;
1528
1529 pVar->Var = paArgs[1];
1530 memcpy(pVar->szName, paArgs[0].u.pszString, (size_t)paArgs[0].u64Range + 1);
1531 if (paArgs[1].enmType == DBGCVAR_TYPE_STRING)
1532 pVar->Var.u.pszString = (char *)memcpy(&pVar->szName[paArgs[0].u64Range + 1], paArgs[1].u.pszString, (size_t)paArgs[1].u64Range + 1);
1533 return 0;
1534 }
1535 }
1536
1537 /*
1538 * Allocate another.
1539 */
1540 PDBGCNAMEDVAR pVar = (PDBGCNAMEDVAR)RTMemAlloc(cbVar);
1541
1542 pVar->Var = paArgs[1];
1543 memcpy(pVar->szName, pszVar, (size_t)paArgs[0].u64Range + 1);
1544 if (paArgs[1].enmType == DBGCVAR_TYPE_STRING)
1545 pVar->Var.u.pszString = (char *)memcpy(&pVar->szName[paArgs[0].u64Range + 1], paArgs[1].u.pszString, (size_t)paArgs[1].u64Range + 1);
1546
1547 /* need to reallocate the pointer array too? */
1548 if (!(pDbgc->cVars % 0x20))
1549 {
1550 void *pv = RTMemRealloc(pDbgc->papVars, (pDbgc->cVars + 0x20) * sizeof(pDbgc->papVars[0]));
1551 if (!pv)
1552 {
1553 RTMemFree(pVar);
1554 return VERR_DBGC_PARSE_NO_MEMORY;
1555 }
1556 pDbgc->papVars = (PDBGCNAMEDVAR *)pv;
1557 }
1558 pDbgc->papVars[pDbgc->cVars++] = pVar;
1559
1560 NOREF(pCmd); NOREF(pUVM); NOREF(cArgs);
1561 return 0;
1562}
1563
1564
1565/**
1566 * @interface_method_impl{FNDBCCMD, The 'unset' command.}
1567 */
1568static DECLCALLBACK(int) dbgcCmdUnset(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1569{
1570 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1571 for (unsigned i = 0; i < cArgs; i++)
1572 AssertReturn(paArgs[i].enmType == DBGCVAR_TYPE_SYMBOL, VERR_DBGC_PARSE_BUG);
1573
1574 /*
1575 * Iterate the variables and unset them.
1576 */
1577 for (unsigned iArg = 0; iArg < cArgs; iArg++)
1578 {
1579 const char *pszVar = paArgs[iArg].u.pszString;
1580
1581 /*
1582 * Look up the variable.
1583 */
1584 for (unsigned iVar = 0; iVar < pDbgc->cVars; iVar++)
1585 {
1586 if (!strcmp(pszVar, pDbgc->papVars[iVar]->szName))
1587 {
1588 /*
1589 * Shuffle the array removing this entry.
1590 */
1591 void *pvFree = pDbgc->papVars[iVar];
1592 if (iVar + 1 < pDbgc->cVars)
1593 memmove(&pDbgc->papVars[iVar],
1594 &pDbgc->papVars[iVar + 1],
1595 (pDbgc->cVars - iVar - 1) * sizeof(pDbgc->papVars[0]));
1596 pDbgc->papVars[--pDbgc->cVars] = NULL;
1597
1598 RTMemFree(pvFree);
1599 }
1600 } /* lookup */
1601 } /* arg loop */
1602
1603 NOREF(pCmd); NOREF(pUVM);
1604 return 0;
1605}
1606
1607
1608/**
1609 * @interface_method_impl{FNDBCCMD, The 'loadvars' command.}
1610 */
1611static DECLCALLBACK(int) dbgcCmdLoadVars(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1612{
1613 /*
1614 * Don't trust the parser.
1615 */
1616 if ( cArgs != 1
1617 || paArgs[0].enmType != DBGCVAR_TYPE_STRING)
1618 {
1619 AssertMsgFailed(("Expected one string exactly!\n"));
1620 return VERR_DBGC_PARSE_INCORRECT_ARG_TYPE;
1621 }
1622
1623 /*
1624 * Iterate the variables and unset them.
1625 */
1626 FILE *pFile = fopen(paArgs[0].u.pszString, "r");
1627 if (pFile)
1628 {
1629 char szLine[4096];
1630 while (fgets(szLine, sizeof(szLine), pFile))
1631 {
1632 /* Strip it. */
1633 char *psz = szLine;
1634 while (RT_C_IS_BLANK(*psz))
1635 psz++;
1636 int i = (int)strlen(psz) - 1;
1637 while (i >= 0 && RT_C_IS_SPACE(psz[i]))
1638 psz[i--] ='\0';
1639 /* Execute it if not comment or empty line. */
1640 if ( *psz != '\0'
1641 && *psz != '#'
1642 && *psz != ';')
1643 {
1644 DBGCCmdHlpPrintf(pCmdHlp, "dbg: set %s", psz);
1645 pCmdHlp->pfnExec(pCmdHlp, "set %s", psz);
1646 }
1647 }
1648 fclose(pFile);
1649 }
1650 else
1651 return DBGCCmdHlpPrintf(pCmdHlp, "Failed to open file '%s'.\n", paArgs[0].u.pszString);
1652
1653 NOREF(pCmd); NOREF(pUVM);
1654 return 0;
1655}
1656
1657
1658/**
1659 * @interface_method_impl{FNDBCCMD, The 'showvars' command.}
1660 */
1661static DECLCALLBACK(int) dbgcCmdShowVars(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1662{
1663 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1664
1665 for (unsigned iVar = 0; iVar < pDbgc->cVars; iVar++)
1666 {
1667 int rc = DBGCCmdHlpPrintf(pCmdHlp, "%-20s ", &pDbgc->papVars[iVar]->szName);
1668 if (!rc)
1669 rc = dbgcCmdFormat(pCmd, pCmdHlp, pUVM, &pDbgc->papVars[iVar]->Var, 1);
1670 if (rc)
1671 return rc;
1672 }
1673
1674 NOREF(paArgs); NOREF(cArgs);
1675 return 0;
1676}
1677
1678
1679/**
1680 * Extracts the plugin name from a plugin specifier that may or may not include
1681 * path and/or suffix.
1682 *
1683 * @returns VBox status code.
1684 *
1685 * @param pszDst Where to return the name. At least DBGCPLUGIN_MAX_NAME
1686 * worth of buffer space.
1687 * @param pszPlugIn The plugin specifier to parse.
1688 */
1689static int dbgcPlugInExtractName(char *pszDst, const char *pszPlugIn)
1690{
1691 /*
1692 * Parse out the name stopping at the extension.
1693 */
1694 const char *pszName = RTPathFilename(pszPlugIn);
1695 if (!pszName || !*pszName)
1696 return VERR_INVALID_NAME;
1697 if (!RTStrNICmp(pszName, DBGC_PLUG_IN_PREFIX, sizeof(DBGC_PLUG_IN_PREFIX) - 1))
1698 {
1699 pszName += sizeof(DBGC_PLUG_IN_PREFIX) - 1;
1700 if (!*pszName)
1701 return VERR_INVALID_NAME;
1702 }
1703
1704 int ch;
1705 size_t cchName = 0;
1706 while ( (ch = pszName[cchName]) != '\0'
1707 && ch != '.')
1708 {
1709 if ( !RT_C_IS_ALPHA(ch)
1710 && ( !RT_C_IS_DIGIT(ch)
1711 || cchName == 0))
1712 return VERR_INVALID_NAME;
1713 cchName++;
1714 }
1715
1716 if (cchName >= DBGCPLUGIN_MAX_NAME)
1717 return VERR_OUT_OF_RANGE;
1718
1719 /*
1720 * We're very picky about the extension if there is no path.
1721 */
1722 if ( ch == '.'
1723 && !RTPathHavePath(pszPlugIn)
1724 && RTStrICmp(&pszName[cchName], RTLdrGetSuff()))
1725 return VERR_INVALID_NAME;
1726
1727 /*
1728 * Copy it.
1729 */
1730 memcpy(pszDst, pszName, cchName);
1731 pszDst[cchName] = '\0';
1732 return VINF_SUCCESS;
1733}
1734
1735
1736/**
1737 * Locate a plug-in in list.
1738 *
1739 * @returns Pointer to the plug-in tracking structure.
1740 * @param pDbgc Pointer to the DBGC instance data.
1741 * @param pszName The name of the plug-in we're looking for.
1742 * @param ppPrev Where to optionally return the pointer to the
1743 * previous list member.
1744 */
1745static PDBGCPLUGIN dbgcPlugInLocate(PDBGC pDbgc, const char *pszName, PDBGCPLUGIN *ppPrev)
1746{
1747 PDBGCPLUGIN pPrev = NULL;
1748 PDBGCPLUGIN pCur = pDbgc->pPlugInHead;
1749 while (pCur)
1750 {
1751 if (!RTStrICmp(pCur->szName, pszName))
1752 {
1753 if (ppPrev)
1754 *ppPrev = pPrev;
1755 return pCur;
1756 }
1757
1758 /* advance */
1759 pPrev = pCur;
1760 pCur = pCur->pNext;
1761 }
1762 return NULL;
1763}
1764
1765
1766/**
1767 * Try load the specified plug-in module.
1768 *
1769 * @returns VINF_SUCCESS on success, path error or loader error on failure.
1770 *
1771 * @param pPlugIn The plugin tracing record.
1772 * @param pszModule Module name.
1773 */
1774static int dbgcPlugInTryLoad(PDBGCPLUGIN pPlugIn, const char *pszModule)
1775{
1776 /*
1777 * Load it and try resolve the entry point.
1778 */
1779 int rc = RTLdrLoad(pszModule, &pPlugIn->hLdrMod);
1780 if (RT_SUCCESS(rc))
1781 {
1782 rc = RTLdrGetSymbol(pPlugIn->hLdrMod, DBGC_PLUG_IN_ENTRYPOINT, (void **)&pPlugIn->pfnEntry);
1783 if (RT_SUCCESS(rc))
1784 return VINF_SUCCESS;
1785 LogRel(("DBGC: RTLdrGetSymbol('%s', '%s',) -> %Rrc\n", pszModule, DBGC_PLUG_IN_ENTRYPOINT, rc));
1786
1787 RTLdrClose(pPlugIn->hLdrMod);
1788 pPlugIn->hLdrMod = NIL_RTLDRMOD;
1789 }
1790 return rc;
1791}
1792
1793
1794/**
1795 * RTPathTraverseList callback.
1796 *
1797 * @returns See FNRTPATHTRAVERSER.
1798 *
1799 * @param pchPath See FNRTPATHTRAVERSER.
1800 * @param cchPath See FNRTPATHTRAVERSER.
1801 * @param pvUser1 The plug-in specifier.
1802 * @param pvUser2 The plug-in tracking record.
1803 */
1804static DECLCALLBACK(int) dbgcPlugInLoadCallback(const char *pchPath, size_t cchPath, void *pvUser1, void *pvUser2)
1805{
1806 PDBGCPLUGIN pPlugIn = (PDBGCPLUGIN)pvUser2;
1807 const char *pszPlugIn = (const char *)pvUser1;
1808
1809 /*
1810 * Join the path and the specified plug-in module name, first with the
1811 * prefix and then without it.
1812 */
1813 size_t cchModule = cchPath + 1 + strlen(pszPlugIn) + sizeof(DBGC_PLUG_IN_PREFIX) + 8;
1814 char *pszModule = (char *)alloca(cchModule);
1815 AssertReturn(pszModule, VERR_TRY_AGAIN);
1816 memcpy(pszModule, pchPath, cchPath);
1817 pszModule[cchPath] = '\0';
1818
1819 int rc = RTPathAppend(pszModule, cchModule, DBGC_PLUG_IN_PREFIX);
1820 AssertRCReturn(rc, VERR_TRY_AGAIN);
1821 strcat(pszModule, pszPlugIn);
1822 rc = dbgcPlugInTryLoad(pPlugIn, pszModule);
1823 if (RT_SUCCESS(rc))
1824 return VINF_SUCCESS;
1825
1826 pszModule[cchPath] = '\0';
1827 rc = RTPathAppend(pszModule, cchModule, pszPlugIn);
1828 AssertRCReturn(rc, VERR_TRY_AGAIN);
1829 rc = dbgcPlugInTryLoad(pPlugIn, pszModule);
1830 if (RT_SUCCESS(rc))
1831 return VINF_SUCCESS;
1832
1833 return VERR_TRY_AGAIN;
1834}
1835
1836
1837/**
1838 * Loads a plug-in.
1839 *
1840 * @returns VBox status code. If pCmd is specified, it's the return from
1841 * DBGCCmdHlpFail.
1842 * @param pDbgc The DBGC instance data.
1843 * @param pszName The plug-in name.
1844 * @param pszPlugIn The plug-in module name.
1845 * @param pCmd The command pointer if invoked by the user, NULL
1846 * if invoked from elsewhere.
1847 */
1848static int dbgcPlugInLoad(PDBGC pDbgc, const char *pszName, const char *pszPlugIn, PCDBGCCMD pCmd)
1849{
1850 PDBGCCMDHLP pCmdHlp = &pDbgc->CmdHlp;
1851
1852 /*
1853 * Try load it. If specified with a path, we're assuming the user
1854 * wants to load a plug-in from some specific location. Otherwise
1855 * search for it.
1856 */
1857 PDBGCPLUGIN pPlugIn = (PDBGCPLUGIN)RTMemAllocZ(sizeof(*pPlugIn));
1858 if (!pPlugIn)
1859 return pCmd
1860 ? DBGCCmdHlpFail(pCmdHlp, pCmd, "out of memory\n")
1861 : VERR_NO_MEMORY;
1862 strcpy(pPlugIn->szName, pszName);
1863
1864 int rc;
1865 if (RTPathHavePath(pszPlugIn))
1866 rc = dbgcPlugInTryLoad(pPlugIn, pszPlugIn);
1867 else
1868 {
1869 /* 1. The private architecture directory. */
1870 char szPath[4*_1K];
1871 rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
1872 if (RT_SUCCESS(rc))
1873 rc = RTPathTraverseList(szPath, '\0', dbgcPlugInLoadCallback, (void *)pszPlugIn, pPlugIn);
1874 if (RT_FAILURE(rc))
1875 {
1876 /* 2. The DBGC PLUGIN_PATH variable. */
1877 DBGCVAR PathVar;
1878 int rc2 = DBGCCmdHlpEval(pCmdHlp, &PathVar, "$PLUGIN_PATH");
1879 if ( RT_SUCCESS(rc2)
1880 && PathVar.enmType == DBGCVAR_TYPE_STRING)
1881 rc = RTPathTraverseList(PathVar.u.pszString, ';', dbgcPlugInLoadCallback, (void *)pszPlugIn, pPlugIn);
1882 if (RT_FAILURE_NP(rc))
1883 {
1884 /* 3. The DBGC_PLUGIN_PATH environment variable. */
1885 rc2 = RTEnvGetEx(RTENV_DEFAULT, "DBGC_PLUGIN_PATH", szPath, sizeof(szPath), NULL);
1886 if (RT_SUCCESS(rc2))
1887 rc = RTPathTraverseList(szPath, ';', dbgcPlugInLoadCallback, (void *)pszPlugIn, pPlugIn);
1888 }
1889 }
1890 }
1891 if (RT_FAILURE(rc))
1892 {
1893 RTMemFree(pPlugIn);
1894 return pCmd
1895 ? DBGCCmdHlpFail(pCmdHlp, pCmd, "could not find/load '%s'\n", pszPlugIn)
1896 : rc;
1897 }
1898
1899 /*
1900 * Try initialize it.
1901 */
1902 rc = pPlugIn->pfnEntry(DBGCPLUGINOP_INIT, pDbgc->pUVM, VBOX_VERSION);
1903 if (RT_FAILURE(rc))
1904 {
1905 RTLdrClose(pPlugIn->hLdrMod);
1906 RTMemFree(pPlugIn);
1907 return pCmd
1908 ? DBGCCmdHlpFail(pCmdHlp, pCmd, "initialization of plug-in '%s' failed with rc=%Rrc\n", pszPlugIn, rc)
1909 : rc;
1910 }
1911
1912 /*
1913 * Link it and we're good.
1914 */
1915 pPlugIn->pNext = pDbgc->pPlugInHead;
1916 pDbgc->pPlugInHead = pPlugIn;
1917 DBGCCmdHlpPrintf(pCmdHlp, "Loaded plug-in '%s'.\n", pPlugIn->szName);
1918 return VINF_SUCCESS;
1919}
1920
1921
1922
1923
1924/**
1925 * Automatically load plug-ins from the architecture private directory of
1926 * VirtualBox.
1927 *
1928 * This is called during console init.
1929 *
1930 * @param pDbgc The DBGC instance data.
1931 */
1932void dbgcPlugInAutoLoad(PDBGC pDbgc)
1933{
1934 /*
1935 * Open the architecture specific directory with a filter on our prefix
1936 * and names including a dot.
1937 */
1938 const char *pszSuff = RTLdrGetSuff();
1939 size_t cchSuff = strlen(pszSuff);
1940
1941 char szPath[RTPATH_MAX];
1942 int rc = RTPathAppPrivateArch(szPath, sizeof(szPath) - cchSuff);
1943 AssertRCReturnVoid(rc);
1944 size_t offDir = strlen(szPath);
1945
1946 rc = RTPathAppend(szPath, sizeof(szPath) - cchSuff, DBGC_PLUG_IN_PREFIX "*");
1947 AssertRCReturnVoid(rc);
1948 strcat(szPath, pszSuff);
1949
1950 PRTDIR pDir;
1951 rc = RTDirOpenFiltered(&pDir, szPath, RTDIRFILTER_WINNT, 0);
1952 if (RT_SUCCESS(rc))
1953 {
1954 /*
1955 * Now read it and try load each of the plug-in modules.
1956 */
1957 RTDIRENTRY DirEntry;
1958 while (RT_SUCCESS(RTDirRead(pDir, &DirEntry, NULL)))
1959 {
1960 szPath[offDir] = '\0';
1961 rc = RTPathAppend(szPath, sizeof(szPath), DirEntry.szName);
1962 if (RT_SUCCESS(rc))
1963 {
1964 char szName[DBGCPLUGIN_MAX_NAME];
1965 rc = dbgcPlugInExtractName(szName, DirEntry.szName);
1966 if (RT_SUCCESS(rc))
1967 dbgcPlugInLoad(pDbgc, szName, szPath, NULL /*pCmd*/);
1968 }
1969 }
1970
1971 RTDirClose(pDir);
1972 }
1973}
1974
1975
1976/**
1977 * @interface_method_impl{FNDBCCMD, The 'loadplugin' command.}
1978 */
1979static DECLCALLBACK(int) dbgcCmdLoadPlugIn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1980{
1981 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1982
1983 /*
1984 * Loop thru the plugin names.
1985 */
1986 for (unsigned i = 0; i < cArgs; i++)
1987 {
1988 const char *pszPlugIn = paArgs[i].u.pszString;
1989
1990 /* Extract the plug-in name. */
1991 char szName[DBGCPLUGIN_MAX_NAME];
1992 int rc = dbgcPlugInExtractName(szName, pszPlugIn);
1993 if (RT_FAILURE(rc))
1994 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Malformed plug-in name: '%s'\n", pszPlugIn);
1995
1996 /* Loaded? */
1997 PDBGCPLUGIN pPlugIn = dbgcPlugInLocate(pDbgc, szName, NULL);
1998 if (pPlugIn)
1999 return DBGCCmdHlpFail(pCmdHlp, pCmd, "'%s' is already loaded\n", szName);
2000
2001 /* Load it. */
2002 rc = dbgcPlugInLoad(pDbgc, szName, pszPlugIn, pCmd);
2003 if (RT_FAILURE(rc))
2004 return rc;
2005 }
2006
2007 return VINF_SUCCESS;
2008}
2009
2010
2011/**
2012 * Unload all plug-ins.
2013 *
2014 * @param pDbgc The DBGC instance data.
2015 */
2016void dbgcPlugInUnloadAll(PDBGC pDbgc)
2017{
2018 while (pDbgc->pPlugInHead)
2019 {
2020 PDBGCPLUGIN pPlugIn = pDbgc->pPlugInHead;
2021 pDbgc->pPlugInHead = pPlugIn->pNext;
2022
2023 if ( pDbgc->pVM /* prevents trouble during destruction. */
2024 && pDbgc->pVM->enmVMState < VMSTATE_DESTROYING)
2025 {
2026 pPlugIn->pfnEntry(DBGCPLUGINOP_TERM, pDbgc->pUVM, 0);
2027 RTLdrClose(pPlugIn->hLdrMod);
2028 }
2029 pPlugIn->hLdrMod = NIL_RTLDRMOD;
2030
2031 RTMemFree(pPlugIn);
2032 }
2033}
2034
2035
2036/**
2037 * @interface_method_impl{FNDBCCMD, The 'unload' command.}
2038 */
2039static DECLCALLBACK(int) dbgcCmdUnloadPlugIn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2040{
2041 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2042
2043 /*
2044 * Loop thru the plugin names.
2045 */
2046 for (unsigned i = 0; i < cArgs; i++)
2047 {
2048 const char *pszPlugIn = paArgs[i].u.pszString;
2049
2050 /* Extract the plug-in name. */
2051 char szName[DBGCPLUGIN_MAX_NAME];
2052 int rc = dbgcPlugInExtractName(szName, pszPlugIn);
2053 if (RT_FAILURE(rc))
2054 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Malformed plug-in name: '%s'\n", pszPlugIn);
2055
2056 /* Loaded? */
2057 PDBGCPLUGIN pPrevPlugIn;
2058 PDBGCPLUGIN pPlugIn = dbgcPlugInLocate(pDbgc, szName, &pPrevPlugIn);
2059 if (!pPlugIn)
2060 return DBGCCmdHlpFail(pCmdHlp, pCmd, "'%s' is not\n", szName);
2061
2062 /*
2063 * Terminate and unload it.
2064 */
2065 pPlugIn->pfnEntry(DBGCPLUGINOP_TERM, pDbgc->pUVM, 0);
2066 RTLdrClose(pPlugIn->hLdrMod);
2067 pPlugIn->hLdrMod = NIL_RTLDRMOD;
2068
2069 if (pPrevPlugIn)
2070 pPrevPlugIn->pNext = pPlugIn->pNext;
2071 else
2072 pDbgc->pPlugInHead = pPlugIn->pNext;
2073 RTMemFree(pPlugIn->pNext);
2074 DBGCCmdHlpPrintf(pCmdHlp, "Unloaded plug-in '%s'\n", szName);
2075 }
2076
2077 return VINF_SUCCESS;
2078}
2079
2080
2081/**
2082 * @interface_method_impl{FNDBCCMD, The 'showplugins' command.}
2083 */
2084static DECLCALLBACK(int) dbgcCmdShowPlugIns(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2085{
2086 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2087 PDBGCPLUGIN pPlugIn = pDbgc->pPlugInHead;
2088 if (!pPlugIn)
2089 return DBGCCmdHlpPrintf(pCmdHlp, "No plug-ins loaded\n");
2090
2091 DBGCCmdHlpPrintf(pCmdHlp, "Plug-ins: %s", pPlugIn->szName);
2092 for (;;)
2093 {
2094 pPlugIn = pPlugIn->pNext;
2095 if (!pPlugIn)
2096 break;
2097 DBGCCmdHlpPrintf(pCmdHlp, ", %s", pPlugIn->szName);
2098 }
2099 return DBGCCmdHlpPrintf(pCmdHlp, "\n");
2100}
2101
2102
2103
2104/**
2105 * @interface_method_impl{FNDBCCMD, The 'harakiri' command.}
2106 */
2107static DECLCALLBACK(int) dbgcCmdHarakiri(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2108{
2109 Log(("dbgcCmdHarakiri\n"));
2110 for (;;)
2111 exit(126);
2112 NOREF(pCmd); NOREF(pCmdHlp); NOREF(pUVM); NOREF(paArgs); NOREF(cArgs);
2113}
2114
2115
2116/**
2117 * @interface_method_impl{FNDBCCMD, The 'writecore' command.}
2118 */
2119static DECLCALLBACK(int) dbgcCmdWriteCore(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2120{
2121 Log(("dbgcCmdWriteCore\n"));
2122
2123 /*
2124 * Validate input, lots of paranoia here.
2125 */
2126 if ( cArgs != 1
2127 || paArgs[0].enmType != DBGCVAR_TYPE_STRING)
2128 {
2129 AssertMsgFailed(("Expected one string exactly!\n"));
2130 return VERR_DBGC_PARSE_INCORRECT_ARG_TYPE;
2131 }
2132
2133 const char *pszDumpPath = paArgs[0].u.pszString;
2134 if (!pszDumpPath)
2135 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Missing file path.\n");
2136
2137 int rc = DBGFR3CoreWrite(pUVM, pszDumpPath, true /*fReplaceFile*/);
2138 if (RT_FAILURE(rc))
2139 return DBGCCmdHlpFail(pCmdHlp, pCmd, "DBGFR3WriteCore failed. rc=%Rrc\n", rc);
2140
2141 return VINF_SUCCESS;
2142}
2143
2144
2145
2146/**
2147 * @callback_method_impl{The randu32() function implementation.}
2148 */
2149static DECLCALLBACK(int) dbgcFuncRandU32(PCDBGCFUNC pFunc, PDBGCCMDHLP pCmdHlp, PVM pUVM, PCDBGCVAR paArgs, uint32_t cArgs,
2150 PDBGCVAR pResult)
2151{
2152 AssertReturn(cArgs == 0, VERR_DBGC_PARSE_BUG);
2153 uint32_t u32 = RTRandU32();
2154 DBGCVAR_INIT_NUMBER(pResult, u32);
2155 NOREF(pFunc); NOREF(pCmdHlp); NOREF(pUVM); NOREF(paArgs);
2156 return VINF_SUCCESS;
2157}
2158
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