VirtualBox

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

Last change on this file since 97441 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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