VirtualBox

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

Last change on this file since 47229 was 46167, checked in by vboxsync, 12 years ago

Buried DBGFSym.cpp and with it loadsyms - rip.

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