VirtualBox

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

Last change on this file since 41583 was 41583, checked in by vboxsync, 13 years ago

DBGC: Help fixes.

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