VirtualBox

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

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