VirtualBox

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

Last change on this file since 54220 was 54220, checked in by vboxsync, 10 years ago

oops

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