VirtualBox

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

Last change on this file since 83088 was 83088, checked in by vboxsync, 5 years ago

DBGC: Added multistep command.

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