VirtualBox

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

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

DBGF,DBGC: Added dmesg command and implemented it for linux guests.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 81.7 KB
Line 
1/* $Id: DBGCCommands.cpp 54218 2015-02-16 15:17:05Z 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 }
1064 else
1065 rc = DBGCCmdHlpFail(pCmdHlp, pCmd, "Error allocating %#zu bytes.\n", cbBuf);
1066 }
1067 else
1068 rc = DBGCCmdHlpFail(pCmdHlp, pCmd, "The dmesg interface isn't implemented by guest OS.\n");
1069 return rc;
1070}
1071
1072
1073/**
1074 * @interface_method_impl{FNDBCCMD, The 'cpu' command.}
1075 */
1076static DECLCALLBACK(int) dbgcCmdCpu(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1077{
1078 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1079
1080 /* check that the parser did what it's supposed to do. */
1081 if ( cArgs != 0
1082 && ( cArgs != 1
1083 || paArgs[0].enmType != DBGCVAR_TYPE_NUMBER))
1084 return DBGCCmdHlpPrintf(pCmdHlp, "parser error\n");
1085 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
1086
1087 int rc;
1088 if (!cArgs)
1089 rc = DBGCCmdHlpPrintf(pCmdHlp, "Current CPU ID: %u\n", pDbgc->idCpu);
1090 else
1091 {
1092 VMCPUID cCpus = DBGFR3CpuGetCount(pUVM);
1093 if (paArgs[0].u.u64Number >= cCpus)
1094 rc = DBGCCmdHlpPrintf(pCmdHlp, "error: idCpu %u is out of range! Highest ID is %u.\n",
1095 paArgs[0].u.u64Number, cCpus-1);
1096 else
1097 {
1098 rc = DBGCCmdHlpPrintf(pCmdHlp, "Changed CPU from %u to %u.\n",
1099 pDbgc->idCpu, (VMCPUID)paArgs[0].u.u64Number);
1100 pDbgc->idCpu = (VMCPUID)paArgs[0].u.u64Number;
1101 }
1102 }
1103 return rc;
1104}
1105
1106
1107/**
1108 * @interface_method_impl{FNDBCCMD, The 'info' command.}
1109 */
1110static DECLCALLBACK(int) dbgcCmdInfo(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1111{
1112 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1113
1114 /*
1115 * Validate input.
1116 */
1117 if ( cArgs < 1
1118 || cArgs > 2
1119 || paArgs[0].enmType != DBGCVAR_TYPE_STRING
1120 || paArgs[cArgs - 1].enmType != DBGCVAR_TYPE_STRING)
1121 return DBGCCmdHlpPrintf(pCmdHlp, "internal error: The parser doesn't do its job properly yet.. quote the string.\n");
1122 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
1123
1124 /*
1125 * Dump it.
1126 */
1127 int rc = DBGFR3InfoEx(pUVM, pDbgc->idCpu,
1128 paArgs[0].u.pszString,
1129 cArgs == 2 ? paArgs[1].u.pszString : NULL,
1130 DBGCCmdHlpGetDbgfOutputHlp(pCmdHlp));
1131 if (RT_FAILURE(rc))
1132 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3InfoEx()\n");
1133
1134 NOREF(pCmd);
1135 return 0;
1136}
1137
1138
1139/**
1140 * @interface_method_impl{FNDBCCMD, The 'log' command.}
1141 */
1142static DECLCALLBACK(int) dbgcCmdLog(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1143{
1144 int rc;
1145 if (cArgs == 0)
1146 {
1147 char szBuf[_64K];
1148 rc = RTLogGetGroupSettings(NULL, szBuf, sizeof(szBuf));
1149 if (RT_FAILURE(rc))
1150 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "RTLogGetDestinations(NULL,,%#zx)\n", sizeof(szBuf));
1151 DBGCCmdHlpPrintf(pCmdHlp, "VBOX_LOG=%s\n", szBuf);
1152 }
1153 else
1154 {
1155 rc = DBGFR3LogModifyGroups(pUVM, paArgs[0].u.pszString);
1156 if (RT_FAILURE(rc))
1157 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3LogModifyGroups(%p,'%s')\n", pUVM, paArgs[0].u.pszString);
1158 }
1159 NOREF(pCmd);
1160 return VINF_SUCCESS;
1161}
1162
1163
1164/**
1165 * @interface_method_impl{FNDBCCMD, The 'logdest' command.}
1166 */
1167static DECLCALLBACK(int) dbgcCmdLogDest(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1168{
1169 int rc;
1170 if (cArgs == 0)
1171 {
1172 char szBuf[_16K];
1173 rc = RTLogGetDestinations(NULL, szBuf, sizeof(szBuf));
1174 if (RT_FAILURE(rc))
1175 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "RTLogGetDestinations(NULL,,%#zx)\n", sizeof(szBuf));
1176 DBGCCmdHlpPrintf(pCmdHlp, "VBOX_LOG_DEST=%s\n", szBuf);
1177 }
1178 else
1179 {
1180 rc = DBGFR3LogModifyDestinations(pUVM, paArgs[0].u.pszString);
1181 if (RT_FAILURE(rc))
1182 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3LogModifyDestinations(%p,'%s')\n", pUVM, paArgs[0].u.pszString);
1183 }
1184 NOREF(pCmd);
1185 return VINF_SUCCESS;
1186}
1187
1188
1189/**
1190 * @interface_method_impl{FNDBCCMD, The 'logflags' command.}
1191 */
1192static DECLCALLBACK(int) dbgcCmdLogFlags(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1193{
1194 int rc;
1195 if (cArgs == 0)
1196 {
1197 char szBuf[_16K];
1198 rc = RTLogGetFlags(NULL, szBuf, sizeof(szBuf));
1199 if (RT_FAILURE(rc))
1200 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "RTLogGetFlags(NULL,,%#zx)\n", sizeof(szBuf));
1201 DBGCCmdHlpPrintf(pCmdHlp, "VBOX_LOG_FLAGS=%s\n", szBuf);
1202 }
1203 else
1204 {
1205 rc = DBGFR3LogModifyFlags(pUVM, paArgs[0].u.pszString);
1206 if (RT_FAILURE(rc))
1207 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3LogModifyFlags(%p,'%s')\n", pUVM, paArgs[0].u.pszString);
1208 }
1209
1210 NOREF(pCmd);
1211 return rc;
1212}
1213
1214
1215/**
1216 * @interface_method_impl{FNDBCCMD, The 'logflush' command.}
1217 */
1218static DECLCALLBACK(int) dbgcCmdLogFlush(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1219{
1220 RTLogFlush(NULL);
1221 PRTLOGGER pLogRel = RTLogRelDefaultInstance();
1222 if (pLogRel)
1223 RTLogFlush(pLogRel);
1224
1225 NOREF(pCmd); NOREF(cArgs);
1226 return VINF_SUCCESS;
1227}
1228
1229
1230/**
1231 * @interface_method_impl{FNDBCCMD, The 'format' command.}
1232 */
1233static DECLCALLBACK(int) dbgcCmdFormat(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1234{
1235 LogFlow(("dbgcCmdFormat\n"));
1236 static const char *apszRangeDesc[] =
1237 {
1238 "none", "bytes", "elements"
1239 };
1240 int rc;
1241
1242 for (unsigned iArg = 0; iArg < cArgs; iArg++)
1243 {
1244 switch (paArgs[iArg].enmType)
1245 {
1246 case DBGCVAR_TYPE_UNKNOWN:
1247 rc = DBGCCmdHlpPrintf(pCmdHlp,
1248 "Unknown variable type!\n");
1249 break;
1250 case DBGCVAR_TYPE_GC_FLAT:
1251 if (paArgs[iArg].enmRangeType != DBGCVAR_RANGE_NONE)
1252 rc = DBGCCmdHlpPrintf(pCmdHlp,
1253 "Guest flat address: %%%08x range %lld %s\n",
1254 paArgs[iArg].u.GCFlat,
1255 paArgs[iArg].u64Range,
1256 apszRangeDesc[paArgs[iArg].enmRangeType]);
1257 else
1258 rc = DBGCCmdHlpPrintf(pCmdHlp,
1259 "Guest flat address: %%%08x\n",
1260 paArgs[iArg].u.GCFlat);
1261 break;
1262 case DBGCVAR_TYPE_GC_FAR:
1263 if (paArgs[iArg].enmRangeType != DBGCVAR_RANGE_NONE)
1264 rc = DBGCCmdHlpPrintf(pCmdHlp,
1265 "Guest far address: %04x:%08x range %lld %s\n",
1266 paArgs[iArg].u.GCFar.sel,
1267 paArgs[iArg].u.GCFar.off,
1268 paArgs[iArg].u64Range,
1269 apszRangeDesc[paArgs[iArg].enmRangeType]);
1270 else
1271 rc = DBGCCmdHlpPrintf(pCmdHlp,
1272 "Guest far address: %04x:%08x\n",
1273 paArgs[iArg].u.GCFar.sel,
1274 paArgs[iArg].u.GCFar.off);
1275 break;
1276 case DBGCVAR_TYPE_GC_PHYS:
1277 if (paArgs[iArg].enmRangeType != DBGCVAR_RANGE_NONE)
1278 rc = DBGCCmdHlpPrintf(pCmdHlp,
1279 "Guest physical address: %%%%%08x range %lld %s\n",
1280 paArgs[iArg].u.GCPhys,
1281 paArgs[iArg].u64Range,
1282 apszRangeDesc[paArgs[iArg].enmRangeType]);
1283 else
1284 rc = DBGCCmdHlpPrintf(pCmdHlp,
1285 "Guest physical address: %%%%%08x\n",
1286 paArgs[iArg].u.GCPhys);
1287 break;
1288 case DBGCVAR_TYPE_HC_FLAT:
1289 if (paArgs[iArg].enmRangeType != DBGCVAR_RANGE_NONE)
1290 rc = DBGCCmdHlpPrintf(pCmdHlp,
1291 "Host flat address: %%%08x range %lld %s\n",
1292 paArgs[iArg].u.pvHCFlat,
1293 paArgs[iArg].u64Range,
1294 apszRangeDesc[paArgs[iArg].enmRangeType]);
1295 else
1296 rc = DBGCCmdHlpPrintf(pCmdHlp,
1297 "Host flat address: %%%08x\n",
1298 paArgs[iArg].u.pvHCFlat);
1299 break;
1300 case DBGCVAR_TYPE_HC_PHYS:
1301 if (paArgs[iArg].enmRangeType != DBGCVAR_RANGE_NONE)
1302 rc = DBGCCmdHlpPrintf(pCmdHlp,
1303 "Host physical address: %RHp range %lld %s\n",
1304 paArgs[iArg].u.HCPhys,
1305 paArgs[iArg].u64Range,
1306 apszRangeDesc[paArgs[iArg].enmRangeType]);
1307 else
1308 rc = DBGCCmdHlpPrintf(pCmdHlp,
1309 "Host physical address: %RHp\n",
1310 paArgs[iArg].u.HCPhys);
1311 break;
1312
1313 case DBGCVAR_TYPE_STRING:
1314 rc = DBGCCmdHlpPrintf(pCmdHlp,
1315 "String, %lld bytes long: %s\n",
1316 paArgs[iArg].u64Range,
1317 paArgs[iArg].u.pszString);
1318 break;
1319
1320 case DBGCVAR_TYPE_SYMBOL:
1321 rc = DBGCCmdHlpPrintf(pCmdHlp,
1322 "Symbol, %lld bytes long: %s\n",
1323 paArgs[iArg].u64Range,
1324 paArgs[iArg].u.pszString);
1325 break;
1326
1327 case DBGCVAR_TYPE_NUMBER:
1328 if (paArgs[iArg].enmRangeType != DBGCVAR_RANGE_NONE)
1329 rc = DBGCCmdHlpPrintf(pCmdHlp,
1330 "Number: hex %llx dec 0i%lld oct 0t%llo range %lld %s\n",
1331 paArgs[iArg].u.u64Number,
1332 paArgs[iArg].u.u64Number,
1333 paArgs[iArg].u.u64Number,
1334 paArgs[iArg].u64Range,
1335 apszRangeDesc[paArgs[iArg].enmRangeType]);
1336 else
1337 rc = DBGCCmdHlpPrintf(pCmdHlp,
1338 "Number: hex %llx dec 0i%lld oct 0t%llo\n",
1339 paArgs[iArg].u.u64Number,
1340 paArgs[iArg].u.u64Number,
1341 paArgs[iArg].u.u64Number);
1342 break;
1343
1344 default:
1345 rc = DBGCCmdHlpPrintf(pCmdHlp,
1346 "Invalid argument type %d\n",
1347 paArgs[iArg].enmType);
1348 break;
1349 }
1350 } /* arg loop */
1351
1352 NOREF(pCmd); NOREF(pUVM);
1353 return 0;
1354}
1355
1356
1357/**
1358 * @interface_method_impl{FNDBCCMD, The 'loadimage' command.}
1359 */
1360static DECLCALLBACK(int) dbgcCmdLoadImage(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1361{
1362 /*
1363 * Validate the parsing and make sense of the input.
1364 * This is a mess as usual because we don't trust the parser yet.
1365 */
1366 AssertReturn( cArgs >= 2
1367 && cArgs <= 3
1368 && paArgs[0].enmType == DBGCVAR_TYPE_STRING
1369 && DBGCVAR_ISPOINTER(paArgs[1].enmType),
1370 VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
1371
1372 const char *pszFilename = paArgs[0].u.pszString;
1373
1374 DBGFADDRESS ModAddress;
1375 int rc = pCmdHlp->pfnVarToDbgfAddr(pCmdHlp, &paArgs[1], &ModAddress);
1376 if (RT_FAILURE(rc))
1377 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "pfnVarToDbgfAddr: %Dv\n", &paArgs[1]);
1378
1379 const char *pszModName = NULL;
1380 if (cArgs >= 3)
1381 {
1382 AssertReturn(paArgs[2].enmType == DBGCVAR_TYPE_STRING, VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
1383 pszModName = paArgs[2].u.pszString;
1384 }
1385
1386 /*
1387 * Determine the desired image arch from the load command used.
1388 */
1389 RTLDRARCH enmArch = RTLDRARCH_WHATEVER;
1390 if (pCmd->pszCmd[sizeof("loadimage") - 1] == '3')
1391 enmArch = RTLDRARCH_X86_32;
1392 else if (pCmd->pszCmd[sizeof("loadimage") - 1] == '6')
1393 enmArch = RTLDRARCH_AMD64;
1394
1395 /*
1396 * Try create a module for it.
1397 */
1398 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1399 rc = DBGFR3AsLoadImage(pUVM, pDbgc->hDbgAs, pszFilename, pszModName, enmArch, &ModAddress, NIL_RTDBGSEGIDX, 0 /*fFlags*/);
1400 if (RT_FAILURE(rc))
1401 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3ModuleLoadImage(,,'%s','%s',%Dv,)\n",
1402 pszFilename, pszModName, &paArgs[1]);
1403
1404 return VINF_SUCCESS;
1405}
1406
1407
1408/**
1409 * @interface_method_impl{FNDBCCMD, The 'loadmap' command.}
1410 */
1411static DECLCALLBACK(int) dbgcCmdLoadMap(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1412{
1413 /*
1414 * Validate the parsing and make sense of the input.
1415 * This is a mess as usual because we don't trust the parser yet.
1416 */
1417 AssertReturn( cArgs >= 2
1418 && cArgs <= 5
1419 && paArgs[0].enmType == DBGCVAR_TYPE_STRING
1420 && DBGCVAR_ISPOINTER(paArgs[1].enmType),
1421 VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
1422
1423 const char *pszFilename = paArgs[0].u.pszString;
1424
1425 DBGFADDRESS ModAddress;
1426 int rc = pCmdHlp->pfnVarToDbgfAddr(pCmdHlp, &paArgs[1], &ModAddress);
1427 if (RT_FAILURE(rc))
1428 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "pfnVarToDbgfAddr: %Dv\n", &paArgs[1]);
1429
1430 const char *pszModName = NULL;
1431 if (cArgs >= 3)
1432 {
1433 AssertReturn(paArgs[2].enmType == DBGCVAR_TYPE_STRING, VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
1434 pszModName = paArgs[2].u.pszString;
1435 }
1436
1437 RTGCUINTPTR uSubtrahend = 0;
1438 if (cArgs >= 4)
1439 {
1440 AssertReturn(paArgs[3].enmType == DBGCVAR_TYPE_NUMBER, VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
1441 uSubtrahend = paArgs[3].u.u64Number;
1442 }
1443
1444 RTDBGSEGIDX iModSeg = NIL_RTDBGSEGIDX;
1445 if (cArgs >= 5)
1446 {
1447 AssertReturn(paArgs[4].enmType == DBGCVAR_TYPE_NUMBER, VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
1448 iModSeg = (RTDBGSEGIDX)paArgs[4].u.u64Number;
1449 if ( iModSeg != paArgs[4].u.u64Number
1450 || iModSeg > RTDBGSEGIDX_LAST)
1451 return DBGCCmdHlpPrintf(pCmdHlp, "Segment index out of range: %Dv; range={0..%#x}\n", &paArgs[1], RTDBGSEGIDX_LAST);
1452 }
1453
1454 /*
1455 * Try create a module for it.
1456 */
1457 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1458 rc = DBGFR3AsLoadMap(pUVM, pDbgc->hDbgAs, pszFilename, pszModName, &ModAddress, NIL_RTDBGSEGIDX, uSubtrahend, 0 /*fFlags*/);
1459 if (RT_FAILURE(rc))
1460 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3AsLoadMap(,,'%s','%s',%Dv,)\n",
1461 pszFilename, pszModName, &paArgs[1]);
1462
1463 NOREF(pCmd);
1464 return VINF_SUCCESS;
1465}
1466
1467
1468/**
1469 * @interface_method_impl{FNDBCCMD, The 'loadseg' command.}
1470 */
1471static DECLCALLBACK(int) dbgcCmdLoadSeg(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1472{
1473 /*
1474 * Validate the parsing and make sense of the input.
1475 * This is a mess as usual because we don't trust the parser yet.
1476 */
1477 AssertReturn( cArgs >= 3
1478 && cArgs <= 4
1479 && paArgs[0].enmType == DBGCVAR_TYPE_STRING
1480 && DBGCVAR_ISPOINTER(paArgs[1].enmType)
1481 && paArgs[2].enmType == DBGCVAR_TYPE_NUMBER,
1482 VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
1483
1484 const char *pszFilename = paArgs[0].u.pszString;
1485
1486 DBGFADDRESS ModAddress;
1487 int rc = pCmdHlp->pfnVarToDbgfAddr(pCmdHlp, &paArgs[1], &ModAddress);
1488 if (RT_FAILURE(rc))
1489 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "pfnVarToDbgfAddr: %Dv\n", &paArgs[1]);
1490
1491 RTDBGSEGIDX iModSeg = (RTDBGSEGIDX)paArgs[1].u.u64Number;
1492 if ( iModSeg != paArgs[2].u.u64Number
1493 || iModSeg > RTDBGSEGIDX_LAST)
1494 return DBGCCmdHlpPrintf(pCmdHlp, "Segment index out of range: %Dv; range={0..%#x}\n", &paArgs[1], RTDBGSEGIDX_LAST);
1495
1496 const char *pszModName = NULL;
1497 if (cArgs >= 4)
1498 {
1499 AssertReturn(paArgs[3].enmType == DBGCVAR_TYPE_STRING, VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
1500 pszModName = paArgs[3].u.pszString;
1501 }
1502
1503 /*
1504 * Call the debug info manager about this loading.
1505 */
1506 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1507 rc = DBGFR3AsLoadImage(pUVM, pDbgc->hDbgAs, pszFilename, pszModName, RTLDRARCH_WHATEVER, &ModAddress, iModSeg, 0 /*fFlags*/);
1508 if (RT_FAILURE(rc))
1509 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3ModuleLoadImage(,,'%s','%s',%Dv,)\n",
1510 pszFilename, pszModName, &paArgs[1]);
1511
1512 NOREF(pCmd);
1513 return VINF_SUCCESS;
1514}
1515
1516
1517/**
1518 * @interface_method_impl{FNDBCCMD, The 'unload' command.}
1519 */
1520static DECLCALLBACK(int) dbgcCmdUnload(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1521{
1522 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1523
1524 /*
1525 * Validate the parsing and make sense of the input.
1526 * This is a mess as usual because we don't trust the parser yet.
1527 */
1528 AssertReturn( cArgs >= 1
1529 && paArgs[0].enmType == DBGCVAR_TYPE_STRING,
1530 VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
1531 for (unsigned i = 0; i < cArgs; i++)
1532 {
1533 AssertReturn(paArgs[i].enmType == DBGCVAR_TYPE_STRING, VERR_DBGC_PARSE_INCORRECT_ARG_TYPE);
1534
1535 int rc = DBGFR3AsUnlinkModuleByName(pUVM, pDbgc->hDbgAs, paArgs[i].u.pszString);
1536 if (RT_FAILURE(rc))
1537 return DBGCCmdHlpVBoxError(pCmdHlp, rc, "DBGFR3AsUnlinkModuleByName(,,'%s')\n", paArgs[i].u.pszString);
1538 }
1539
1540 NOREF(pCmd);
1541 return VINF_SUCCESS;
1542}
1543
1544
1545/**
1546 * @interface_method_impl{FNDBCCMD, The 'set' command.}
1547 */
1548static DECLCALLBACK(int) dbgcCmdSet(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1549{
1550 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1551
1552 /* parse sanity check. */
1553 AssertMsg(paArgs[0].enmType == DBGCVAR_TYPE_STRING, ("expected string not %d as first arg!\n", paArgs[0].enmType));
1554 if (paArgs[0].enmType != DBGCVAR_TYPE_STRING)
1555 return VERR_DBGC_PARSE_INCORRECT_ARG_TYPE;
1556
1557
1558 /*
1559 * A variable must start with an alpha chars and only contain alpha numerical chars.
1560 */
1561 const char *pszVar = paArgs[0].u.pszString;
1562 if (!RT_C_IS_ALPHA(*pszVar) || *pszVar == '_')
1563 return DBGCCmdHlpPrintf(pCmdHlp,
1564 "syntax error: Invalid variable name '%s'. Variable names must match regex '[_a-zA-Z][_a-zA-Z0-9*'!",
1565 paArgs[0].u.pszString);
1566
1567 while (RT_C_IS_ALNUM(*pszVar) || *pszVar == '_')
1568 *pszVar++;
1569 if (*pszVar)
1570 return DBGCCmdHlpPrintf(pCmdHlp,
1571 "syntax error: Invalid variable name '%s'. Variable names must match regex '[_a-zA-Z][_a-zA-Z0-9*]'!",
1572 paArgs[0].u.pszString);
1573
1574
1575 /*
1576 * Calc variable size.
1577 */
1578 size_t cbVar = (size_t)paArgs[0].u64Range + sizeof(DBGCNAMEDVAR);
1579 if (paArgs[1].enmType == DBGCVAR_TYPE_STRING)
1580 cbVar += 1 + (size_t)paArgs[1].u64Range;
1581
1582 /*
1583 * Look for existing one.
1584 */
1585 pszVar = paArgs[0].u.pszString;
1586 for (unsigned iVar = 0; iVar < pDbgc->cVars; iVar++)
1587 {
1588 if (!strcmp(pszVar, pDbgc->papVars[iVar]->szName))
1589 {
1590 /*
1591 * Update existing variable.
1592 */
1593 void *pv = RTMemRealloc(pDbgc->papVars[iVar], cbVar);
1594 if (!pv)
1595 return VERR_DBGC_PARSE_NO_MEMORY;
1596 PDBGCNAMEDVAR pVar = pDbgc->papVars[iVar] = (PDBGCNAMEDVAR)pv;
1597
1598 pVar->Var = paArgs[1];
1599 memcpy(pVar->szName, paArgs[0].u.pszString, (size_t)paArgs[0].u64Range + 1);
1600 if (paArgs[1].enmType == DBGCVAR_TYPE_STRING)
1601 pVar->Var.u.pszString = (char *)memcpy(&pVar->szName[paArgs[0].u64Range + 1], paArgs[1].u.pszString, (size_t)paArgs[1].u64Range + 1);
1602 return 0;
1603 }
1604 }
1605
1606 /*
1607 * Allocate another.
1608 */
1609 PDBGCNAMEDVAR pVar = (PDBGCNAMEDVAR)RTMemAlloc(cbVar);
1610
1611 pVar->Var = paArgs[1];
1612 memcpy(pVar->szName, pszVar, (size_t)paArgs[0].u64Range + 1);
1613 if (paArgs[1].enmType == DBGCVAR_TYPE_STRING)
1614 pVar->Var.u.pszString = (char *)memcpy(&pVar->szName[paArgs[0].u64Range + 1], paArgs[1].u.pszString, (size_t)paArgs[1].u64Range + 1);
1615
1616 /* need to reallocate the pointer array too? */
1617 if (!(pDbgc->cVars % 0x20))
1618 {
1619 void *pv = RTMemRealloc(pDbgc->papVars, (pDbgc->cVars + 0x20) * sizeof(pDbgc->papVars[0]));
1620 if (!pv)
1621 {
1622 RTMemFree(pVar);
1623 return VERR_DBGC_PARSE_NO_MEMORY;
1624 }
1625 pDbgc->papVars = (PDBGCNAMEDVAR *)pv;
1626 }
1627 pDbgc->papVars[pDbgc->cVars++] = pVar;
1628
1629 NOREF(pCmd); NOREF(pUVM); NOREF(cArgs);
1630 return 0;
1631}
1632
1633
1634/**
1635 * @interface_method_impl{FNDBCCMD, The 'unset' command.}
1636 */
1637static DECLCALLBACK(int) dbgcCmdUnset(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1638{
1639 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1640 for (unsigned i = 0; i < cArgs; i++)
1641 AssertReturn(paArgs[i].enmType == DBGCVAR_TYPE_SYMBOL, VERR_DBGC_PARSE_BUG);
1642
1643 /*
1644 * Iterate the variables and unset them.
1645 */
1646 for (unsigned iArg = 0; iArg < cArgs; iArg++)
1647 {
1648 const char *pszVar = paArgs[iArg].u.pszString;
1649
1650 /*
1651 * Look up the variable.
1652 */
1653 for (unsigned iVar = 0; iVar < pDbgc->cVars; iVar++)
1654 {
1655 if (!strcmp(pszVar, pDbgc->papVars[iVar]->szName))
1656 {
1657 /*
1658 * Shuffle the array removing this entry.
1659 */
1660 void *pvFree = pDbgc->papVars[iVar];
1661 if (iVar + 1 < pDbgc->cVars)
1662 memmove(&pDbgc->papVars[iVar],
1663 &pDbgc->papVars[iVar + 1],
1664 (pDbgc->cVars - iVar - 1) * sizeof(pDbgc->papVars[0]));
1665 pDbgc->papVars[--pDbgc->cVars] = NULL;
1666
1667 RTMemFree(pvFree);
1668 }
1669 } /* lookup */
1670 } /* arg loop */
1671
1672 NOREF(pCmd); NOREF(pUVM);
1673 return 0;
1674}
1675
1676
1677/**
1678 * @interface_method_impl{FNDBCCMD, The 'loadvars' command.}
1679 */
1680static DECLCALLBACK(int) dbgcCmdLoadVars(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1681{
1682 /*
1683 * Don't trust the parser.
1684 */
1685 if ( cArgs != 1
1686 || paArgs[0].enmType != DBGCVAR_TYPE_STRING)
1687 {
1688 AssertMsgFailed(("Expected one string exactly!\n"));
1689 return VERR_DBGC_PARSE_INCORRECT_ARG_TYPE;
1690 }
1691
1692 /*
1693 * Iterate the variables and unset them.
1694 */
1695 FILE *pFile = fopen(paArgs[0].u.pszString, "r");
1696 if (pFile)
1697 {
1698 char szLine[4096];
1699 while (fgets(szLine, sizeof(szLine), pFile))
1700 {
1701 /* Strip it. */
1702 char *psz = szLine;
1703 while (RT_C_IS_BLANK(*psz))
1704 psz++;
1705 int i = (int)strlen(psz) - 1;
1706 while (i >= 0 && RT_C_IS_SPACE(psz[i]))
1707 psz[i--] ='\0';
1708 /* Execute it if not comment or empty line. */
1709 if ( *psz != '\0'
1710 && *psz != '#'
1711 && *psz != ';')
1712 {
1713 DBGCCmdHlpPrintf(pCmdHlp, "dbg: set %s", psz);
1714 pCmdHlp->pfnExec(pCmdHlp, "set %s", psz);
1715 }
1716 }
1717 fclose(pFile);
1718 }
1719 else
1720 return DBGCCmdHlpPrintf(pCmdHlp, "Failed to open file '%s'.\n", paArgs[0].u.pszString);
1721
1722 NOREF(pCmd); NOREF(pUVM);
1723 return 0;
1724}
1725
1726
1727/**
1728 * @interface_method_impl{FNDBCCMD, The 'showvars' command.}
1729 */
1730static DECLCALLBACK(int) dbgcCmdShowVars(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1731{
1732 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
1733
1734 for (unsigned iVar = 0; iVar < pDbgc->cVars; iVar++)
1735 {
1736 int rc = DBGCCmdHlpPrintf(pCmdHlp, "%-20s ", &pDbgc->papVars[iVar]->szName);
1737 if (!rc)
1738 rc = dbgcCmdFormat(pCmd, pCmdHlp, pUVM, &pDbgc->papVars[iVar]->Var, 1);
1739 if (rc)
1740 return rc;
1741 }
1742
1743 NOREF(paArgs); NOREF(cArgs);
1744 return 0;
1745}
1746
1747
1748/**
1749 * Extracts the plugin name from a plugin specifier that may or may not include
1750 * path and/or suffix.
1751 *
1752 * @returns VBox status code.
1753 *
1754 * @param pszDst Where to return the name. At least DBGCPLUGIN_MAX_NAME
1755 * worth of buffer space.
1756 * @param pszPlugIn The plugin specifier to parse.
1757 */
1758static int dbgcPlugInExtractName(char *pszDst, const char *pszPlugIn)
1759{
1760 /*
1761 * Parse out the name stopping at the extension.
1762 */
1763 const char *pszName = RTPathFilename(pszPlugIn);
1764 if (!pszName || !*pszName)
1765 return VERR_INVALID_NAME;
1766 if (!RTStrNICmp(pszName, DBGC_PLUG_IN_PREFIX, sizeof(DBGC_PLUG_IN_PREFIX) - 1))
1767 {
1768 pszName += sizeof(DBGC_PLUG_IN_PREFIX) - 1;
1769 if (!*pszName)
1770 return VERR_INVALID_NAME;
1771 }
1772
1773 int ch;
1774 size_t cchName = 0;
1775 while ( (ch = pszName[cchName]) != '\0'
1776 && ch != '.')
1777 {
1778 if ( !RT_C_IS_ALPHA(ch)
1779 && ( !RT_C_IS_DIGIT(ch)
1780 || cchName == 0))
1781 return VERR_INVALID_NAME;
1782 cchName++;
1783 }
1784
1785 if (cchName >= DBGCPLUGIN_MAX_NAME)
1786 return VERR_OUT_OF_RANGE;
1787
1788 /*
1789 * We're very picky about the extension if there is no path.
1790 */
1791 if ( ch == '.'
1792 && !RTPathHavePath(pszPlugIn)
1793 && RTStrICmp(&pszName[cchName], RTLdrGetSuff()))
1794 return VERR_INVALID_NAME;
1795
1796 /*
1797 * Copy it.
1798 */
1799 memcpy(pszDst, pszName, cchName);
1800 pszDst[cchName] = '\0';
1801 return VINF_SUCCESS;
1802}
1803
1804
1805/**
1806 * Locate a plug-in in list.
1807 *
1808 * @returns Pointer to the plug-in tracking structure.
1809 * @param pDbgc Pointer to the DBGC instance data.
1810 * @param pszName The name of the plug-in we're looking for.
1811 * @param ppPrev Where to optionally return the pointer to the
1812 * previous list member.
1813 */
1814static PDBGCPLUGIN dbgcPlugInLocate(PDBGC pDbgc, const char *pszName, PDBGCPLUGIN *ppPrev)
1815{
1816 PDBGCPLUGIN pPrev = NULL;
1817 PDBGCPLUGIN pCur = pDbgc->pPlugInHead;
1818 while (pCur)
1819 {
1820 if (!RTStrICmp(pCur->szName, pszName))
1821 {
1822 if (ppPrev)
1823 *ppPrev = pPrev;
1824 return pCur;
1825 }
1826
1827 /* advance */
1828 pPrev = pCur;
1829 pCur = pCur->pNext;
1830 }
1831 return NULL;
1832}
1833
1834
1835/**
1836 * Try load the specified plug-in module.
1837 *
1838 * @returns VINF_SUCCESS on success, path error or loader error on failure.
1839 *
1840 * @param pPlugIn The plugin tracing record.
1841 * @param pszModule Module name.
1842 */
1843static int dbgcPlugInTryLoad(PDBGCPLUGIN pPlugIn, const char *pszModule)
1844{
1845 /*
1846 * Load it and try resolve the entry point.
1847 */
1848 int rc = RTLdrLoad(pszModule, &pPlugIn->hLdrMod);
1849 if (RT_SUCCESS(rc))
1850 {
1851 rc = RTLdrGetSymbol(pPlugIn->hLdrMod, DBGC_PLUG_IN_ENTRYPOINT, (void **)&pPlugIn->pfnEntry);
1852 if (RT_SUCCESS(rc))
1853 return VINF_SUCCESS;
1854 LogRel(("DBGC: RTLdrGetSymbol('%s', '%s',) -> %Rrc\n", pszModule, DBGC_PLUG_IN_ENTRYPOINT, rc));
1855
1856 RTLdrClose(pPlugIn->hLdrMod);
1857 pPlugIn->hLdrMod = NIL_RTLDRMOD;
1858 }
1859 return rc;
1860}
1861
1862
1863/**
1864 * RTPathTraverseList callback.
1865 *
1866 * @returns See FNRTPATHTRAVERSER.
1867 *
1868 * @param pchPath See FNRTPATHTRAVERSER.
1869 * @param cchPath See FNRTPATHTRAVERSER.
1870 * @param pvUser1 The plug-in specifier.
1871 * @param pvUser2 The plug-in tracking record.
1872 */
1873static DECLCALLBACK(int) dbgcPlugInLoadCallback(const char *pchPath, size_t cchPath, void *pvUser1, void *pvUser2)
1874{
1875 PDBGCPLUGIN pPlugIn = (PDBGCPLUGIN)pvUser2;
1876 const char *pszPlugIn = (const char *)pvUser1;
1877
1878 /*
1879 * Join the path and the specified plug-in module name, first with the
1880 * prefix and then without it.
1881 */
1882 size_t cchModule = cchPath + 1 + strlen(pszPlugIn) + sizeof(DBGC_PLUG_IN_PREFIX) + 8;
1883 char *pszModule = (char *)alloca(cchModule);
1884 AssertReturn(pszModule, VERR_TRY_AGAIN);
1885 memcpy(pszModule, pchPath, cchPath);
1886 pszModule[cchPath] = '\0';
1887
1888 int rc = RTPathAppend(pszModule, cchModule, DBGC_PLUG_IN_PREFIX);
1889 AssertRCReturn(rc, VERR_TRY_AGAIN);
1890 strcat(pszModule, pszPlugIn);
1891 rc = dbgcPlugInTryLoad(pPlugIn, pszModule);
1892 if (RT_SUCCESS(rc))
1893 return VINF_SUCCESS;
1894
1895 pszModule[cchPath] = '\0';
1896 rc = RTPathAppend(pszModule, cchModule, pszPlugIn);
1897 AssertRCReturn(rc, VERR_TRY_AGAIN);
1898 rc = dbgcPlugInTryLoad(pPlugIn, pszModule);
1899 if (RT_SUCCESS(rc))
1900 return VINF_SUCCESS;
1901
1902 return VERR_TRY_AGAIN;
1903}
1904
1905
1906/**
1907 * Loads a plug-in.
1908 *
1909 * @returns VBox status code. If pCmd is specified, it's the return from
1910 * DBGCCmdHlpFail.
1911 * @param pDbgc The DBGC instance data.
1912 * @param pszName The plug-in name.
1913 * @param pszPlugIn The plug-in module name.
1914 * @param pCmd The command pointer if invoked by the user, NULL
1915 * if invoked from elsewhere.
1916 */
1917static int dbgcPlugInLoad(PDBGC pDbgc, const char *pszName, const char *pszPlugIn, PCDBGCCMD pCmd)
1918{
1919 PDBGCCMDHLP pCmdHlp = &pDbgc->CmdHlp;
1920
1921 /*
1922 * Try load it. If specified with a path, we're assuming the user
1923 * wants to load a plug-in from some specific location. Otherwise
1924 * search for it.
1925 */
1926 PDBGCPLUGIN pPlugIn = (PDBGCPLUGIN)RTMemAllocZ(sizeof(*pPlugIn));
1927 if (!pPlugIn)
1928 return pCmd
1929 ? DBGCCmdHlpFail(pCmdHlp, pCmd, "out of memory\n")
1930 : VERR_NO_MEMORY;
1931 strcpy(pPlugIn->szName, pszName);
1932
1933 int rc;
1934 if (RTPathHavePath(pszPlugIn))
1935 rc = dbgcPlugInTryLoad(pPlugIn, pszPlugIn);
1936 else
1937 {
1938 /* 1. The private architecture directory. */
1939 char szPath[4*_1K];
1940 rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
1941 if (RT_SUCCESS(rc))
1942 rc = RTPathTraverseList(szPath, '\0', dbgcPlugInLoadCallback, (void *)pszPlugIn, pPlugIn);
1943 if (RT_FAILURE(rc))
1944 {
1945 /* 2. The DBGC PLUGIN_PATH variable. */
1946 DBGCVAR PathVar;
1947 int rc2 = DBGCCmdHlpEval(pCmdHlp, &PathVar, "$PLUGIN_PATH");
1948 if ( RT_SUCCESS(rc2)
1949 && PathVar.enmType == DBGCVAR_TYPE_STRING)
1950 rc = RTPathTraverseList(PathVar.u.pszString, ';', dbgcPlugInLoadCallback, (void *)pszPlugIn, pPlugIn);
1951 if (RT_FAILURE_NP(rc))
1952 {
1953 /* 3. The DBGC_PLUGIN_PATH environment variable. */
1954 rc2 = RTEnvGetEx(RTENV_DEFAULT, "DBGC_PLUGIN_PATH", szPath, sizeof(szPath), NULL);
1955 if (RT_SUCCESS(rc2))
1956 rc = RTPathTraverseList(szPath, ';', dbgcPlugInLoadCallback, (void *)pszPlugIn, pPlugIn);
1957 }
1958 }
1959 }
1960 if (RT_FAILURE(rc))
1961 {
1962 RTMemFree(pPlugIn);
1963 return pCmd
1964 ? DBGCCmdHlpFail(pCmdHlp, pCmd, "could not find/load '%s'\n", pszPlugIn)
1965 : rc;
1966 }
1967
1968 /*
1969 * Try initialize it.
1970 */
1971 rc = pPlugIn->pfnEntry(DBGCPLUGINOP_INIT, pDbgc->pUVM, VBOX_VERSION);
1972 if (RT_FAILURE(rc))
1973 {
1974 RTLdrClose(pPlugIn->hLdrMod);
1975 RTMemFree(pPlugIn);
1976 return pCmd
1977 ? DBGCCmdHlpFail(pCmdHlp, pCmd, "initialization of plug-in '%s' failed with rc=%Rrc\n", pszPlugIn, rc)
1978 : rc;
1979 }
1980
1981 /*
1982 * Link it and we're good.
1983 */
1984 pPlugIn->pNext = pDbgc->pPlugInHead;
1985 pDbgc->pPlugInHead = pPlugIn;
1986 DBGCCmdHlpPrintf(pCmdHlp, "Loaded plug-in '%s'.\n", pPlugIn->szName);
1987 return VINF_SUCCESS;
1988}
1989
1990
1991
1992
1993/**
1994 * Automatically load plug-ins from the architecture private directory of
1995 * VirtualBox.
1996 *
1997 * This is called during console init.
1998 *
1999 * @param pDbgc The DBGC instance data.
2000 */
2001void dbgcPlugInAutoLoad(PDBGC pDbgc)
2002{
2003 /*
2004 * Open the architecture specific directory with a filter on our prefix
2005 * and names including a dot.
2006 */
2007 const char *pszSuff = RTLdrGetSuff();
2008 size_t cchSuff = strlen(pszSuff);
2009
2010 char szPath[RTPATH_MAX];
2011 int rc = RTPathAppPrivateArch(szPath, sizeof(szPath) - cchSuff);
2012 AssertRCReturnVoid(rc);
2013 size_t offDir = strlen(szPath);
2014
2015 rc = RTPathAppend(szPath, sizeof(szPath) - cchSuff, DBGC_PLUG_IN_PREFIX "*");
2016 AssertRCReturnVoid(rc);
2017 strcat(szPath, pszSuff);
2018
2019 PRTDIR pDir;
2020 rc = RTDirOpenFiltered(&pDir, szPath, RTDIRFILTER_WINNT, 0);
2021 if (RT_SUCCESS(rc))
2022 {
2023 /*
2024 * Now read it and try load each of the plug-in modules.
2025 */
2026 RTDIRENTRY DirEntry;
2027 while (RT_SUCCESS(RTDirRead(pDir, &DirEntry, NULL)))
2028 {
2029 szPath[offDir] = '\0';
2030 rc = RTPathAppend(szPath, sizeof(szPath), DirEntry.szName);
2031 if (RT_SUCCESS(rc))
2032 {
2033 char szName[DBGCPLUGIN_MAX_NAME];
2034 rc = dbgcPlugInExtractName(szName, DirEntry.szName);
2035 if (RT_SUCCESS(rc))
2036 dbgcPlugInLoad(pDbgc, szName, szPath, NULL /*pCmd*/);
2037 }
2038 }
2039
2040 RTDirClose(pDir);
2041 }
2042}
2043
2044
2045/**
2046 * @interface_method_impl{FNDBCCMD, The 'loadplugin' command.}
2047 */
2048static DECLCALLBACK(int) dbgcCmdLoadPlugIn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2049{
2050 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2051
2052 /*
2053 * Loop thru the plugin names.
2054 */
2055 for (unsigned i = 0; i < cArgs; i++)
2056 {
2057 const char *pszPlugIn = paArgs[i].u.pszString;
2058
2059 /* Extract the plug-in name. */
2060 char szName[DBGCPLUGIN_MAX_NAME];
2061 int rc = dbgcPlugInExtractName(szName, pszPlugIn);
2062 if (RT_FAILURE(rc))
2063 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Malformed plug-in name: '%s'\n", pszPlugIn);
2064
2065 /* Loaded? */
2066 PDBGCPLUGIN pPlugIn = dbgcPlugInLocate(pDbgc, szName, NULL);
2067 if (pPlugIn)
2068 return DBGCCmdHlpFail(pCmdHlp, pCmd, "'%s' is already loaded\n", szName);
2069
2070 /* Load it. */
2071 rc = dbgcPlugInLoad(pDbgc, szName, pszPlugIn, pCmd);
2072 if (RT_FAILURE(rc))
2073 return rc;
2074 }
2075
2076 return VINF_SUCCESS;
2077}
2078
2079
2080/**
2081 * Unload all plug-ins.
2082 *
2083 * @param pDbgc The DBGC instance data.
2084 */
2085void dbgcPlugInUnloadAll(PDBGC pDbgc)
2086{
2087 while (pDbgc->pPlugInHead)
2088 {
2089 PDBGCPLUGIN pPlugIn = pDbgc->pPlugInHead;
2090 pDbgc->pPlugInHead = pPlugIn->pNext;
2091
2092 if ( pDbgc->pVM /* prevents trouble during destruction. */
2093 && pDbgc->pVM->enmVMState < VMSTATE_DESTROYING)
2094 {
2095 pPlugIn->pfnEntry(DBGCPLUGINOP_TERM, pDbgc->pUVM, 0);
2096 RTLdrClose(pPlugIn->hLdrMod);
2097 }
2098 pPlugIn->hLdrMod = NIL_RTLDRMOD;
2099
2100 RTMemFree(pPlugIn);
2101 }
2102}
2103
2104
2105/**
2106 * @interface_method_impl{FNDBCCMD, The 'unload' command.}
2107 */
2108static DECLCALLBACK(int) dbgcCmdUnloadPlugIn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2109{
2110 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2111
2112 /*
2113 * Loop thru the plugin names.
2114 */
2115 for (unsigned i = 0; i < cArgs; i++)
2116 {
2117 const char *pszPlugIn = paArgs[i].u.pszString;
2118
2119 /* Extract the plug-in name. */
2120 char szName[DBGCPLUGIN_MAX_NAME];
2121 int rc = dbgcPlugInExtractName(szName, pszPlugIn);
2122 if (RT_FAILURE(rc))
2123 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Malformed plug-in name: '%s'\n", pszPlugIn);
2124
2125 /* Loaded? */
2126 PDBGCPLUGIN pPrevPlugIn;
2127 PDBGCPLUGIN pPlugIn = dbgcPlugInLocate(pDbgc, szName, &pPrevPlugIn);
2128 if (!pPlugIn)
2129 return DBGCCmdHlpFail(pCmdHlp, pCmd, "'%s' is not\n", szName);
2130
2131 /*
2132 * Terminate and unload it.
2133 */
2134 pPlugIn->pfnEntry(DBGCPLUGINOP_TERM, pDbgc->pUVM, 0);
2135 RTLdrClose(pPlugIn->hLdrMod);
2136 pPlugIn->hLdrMod = NIL_RTLDRMOD;
2137
2138 if (pPrevPlugIn)
2139 pPrevPlugIn->pNext = pPlugIn->pNext;
2140 else
2141 pDbgc->pPlugInHead = pPlugIn->pNext;
2142 RTMemFree(pPlugIn->pNext);
2143 DBGCCmdHlpPrintf(pCmdHlp, "Unloaded plug-in '%s'\n", szName);
2144 }
2145
2146 return VINF_SUCCESS;
2147}
2148
2149
2150/**
2151 * @interface_method_impl{FNDBCCMD, The 'showplugins' command.}
2152 */
2153static DECLCALLBACK(int) dbgcCmdShowPlugIns(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2154{
2155 PDBGC pDbgc = DBGC_CMDHLP2DBGC(pCmdHlp);
2156 PDBGCPLUGIN pPlugIn = pDbgc->pPlugInHead;
2157 if (!pPlugIn)
2158 return DBGCCmdHlpPrintf(pCmdHlp, "No plug-ins loaded\n");
2159
2160 DBGCCmdHlpPrintf(pCmdHlp, "Plug-ins: %s", pPlugIn->szName);
2161 for (;;)
2162 {
2163 pPlugIn = pPlugIn->pNext;
2164 if (!pPlugIn)
2165 break;
2166 DBGCCmdHlpPrintf(pCmdHlp, ", %s", pPlugIn->szName);
2167 }
2168 return DBGCCmdHlpPrintf(pCmdHlp, "\n");
2169}
2170
2171
2172
2173/**
2174 * @interface_method_impl{FNDBCCMD, The 'harakiri' command.}
2175 */
2176static DECLCALLBACK(int) dbgcCmdHarakiri(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2177{
2178 Log(("dbgcCmdHarakiri\n"));
2179 for (;;)
2180 exit(126);
2181 NOREF(pCmd); NOREF(pCmdHlp); NOREF(pUVM); NOREF(paArgs); NOREF(cArgs);
2182}
2183
2184
2185/**
2186 * @interface_method_impl{FNDBCCMD, The 'writecore' command.}
2187 */
2188static DECLCALLBACK(int) dbgcCmdWriteCore(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2189{
2190 Log(("dbgcCmdWriteCore\n"));
2191
2192 /*
2193 * Validate input, lots of paranoia here.
2194 */
2195 if ( cArgs != 1
2196 || paArgs[0].enmType != DBGCVAR_TYPE_STRING)
2197 {
2198 AssertMsgFailed(("Expected one string exactly!\n"));
2199 return VERR_DBGC_PARSE_INCORRECT_ARG_TYPE;
2200 }
2201
2202 const char *pszDumpPath = paArgs[0].u.pszString;
2203 if (!pszDumpPath)
2204 return DBGCCmdHlpFail(pCmdHlp, pCmd, "Missing file path.\n");
2205
2206 int rc = DBGFR3CoreWrite(pUVM, pszDumpPath, true /*fReplaceFile*/);
2207 if (RT_FAILURE(rc))
2208 return DBGCCmdHlpFail(pCmdHlp, pCmd, "DBGFR3WriteCore failed. rc=%Rrc\n", rc);
2209
2210 return VINF_SUCCESS;
2211}
2212
2213
2214
2215/**
2216 * @callback_method_impl{The randu32() function implementation.}
2217 */
2218static DECLCALLBACK(int) dbgcFuncRandU32(PCDBGCFUNC pFunc, PDBGCCMDHLP pCmdHlp, PVM pUVM, PCDBGCVAR paArgs, uint32_t cArgs,
2219 PDBGCVAR pResult)
2220{
2221 AssertReturn(cArgs == 0, VERR_DBGC_PARSE_BUG);
2222 uint32_t u32 = RTRandU32();
2223 DBGCVAR_INIT_NUMBER(pResult, u32);
2224 NOREF(pFunc); NOREF(pCmdHlp); NOREF(pUVM); NOREF(paArgs);
2225 return VINF_SUCCESS;
2226}
2227
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