VirtualBox

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

Last change on this file since 106401 was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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