VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageDebugVM.cpp

Last change on this file was 98298, checked in by vboxsync, 16 months ago

VBoxManage: rc -> vrc/hrc. Make scm check. bugref:10223

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.9 KB
RevLine 
[34913]1/* $Id: VBoxManageDebugVM.cpp 98298 2023-01-25 02:23:33Z vboxsync $ */
2/** @file
3 * VBoxManage - Implementation of the debugvm command.
4 */
5
6/*
[98103]7 * Copyright (C) 2012-2023 Oracle and/or its affiliates.
[34913]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
[34913]26 */
27
28
[57358]29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
[34913]32#include <VBox/com/com.h>
33#include <VBox/com/string.h>
34#include <VBox/com/Guid.h>
35#include <VBox/com/array.h>
36#include <VBox/com/ErrorInfo.h>
37#include <VBox/com/errorprint.h>
38#include <VBox/com/VirtualBox.h>
39
[61937]40#include <VBox/types.h>
[34913]41#include <iprt/ctype.h>
42#include <iprt/getopt.h>
43#include <iprt/path.h>
44#include <iprt/param.h>
45#include <iprt/stream.h>
46#include <iprt/string.h>
47#include <iprt/uuid.h>
48#include <VBox/log.h>
49
50#include "VBoxManage.h"
51
[92372]52DECLARE_TRANSLATION_CONTEXT(DebugVM);
[34913]53
[92372]54
[34913]55/**
[35508]56 * Handles the getregisters sub-command.
57 *
58 * @returns Suitable exit code.
59 * @param pArgs The handler arguments.
60 * @param pDebugger Pointer to the debugger interface.
61 */
62static RTEXITCODE handleDebugVM_GetRegisters(HandlerArg *pArgs, IMachineDebugger *pDebugger)
63{
64 /*
65 * We take a list of register names (case insensitive). If 'all' is
66 * encountered we'll dump all registers.
67 */
68 ULONG idCpu = 0;
69 unsigned cRegisters = 0;
70
71 RTGETOPTSTATE GetState;
72 RTGETOPTUNION ValueUnion;
73 static const RTGETOPTDEF s_aOptions[] =
74 {
75 { "--cpu", 'c', RTGETOPT_REQ_UINT32 },
76 };
[98298]77 int vrc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
78 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
[35508]79
[98298]80 while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0)
[35508]81 {
[98298]82 switch (vrc)
[35508]83 {
84 case 'c':
85 idCpu = ValueUnion.u32;
86 break;
87
88 case VINF_GETOPT_NOT_OPTION:
89 if (!RTStrICmp(ValueUnion.psz, "all"))
90 {
91 com::SafeArray<BSTR> aBstrNames;
92 com::SafeArray<BSTR> aBstrValues;
[56118]93 CHECK_ERROR2I_RET(pDebugger, GetRegisters(idCpu, ComSafeArrayAsOutParam(aBstrNames),
94 ComSafeArrayAsOutParam(aBstrValues)),
95 RTEXITCODE_FAILURE);
[35508]96 Assert(aBstrNames.size() == aBstrValues.size());
97
[35550]98 size_t cchMaxName = 8;
[35508]99 for (size_t i = 0; i < aBstrNames.size(); i++)
[35550]100 {
101 size_t cchName = RTUtf16Len(aBstrNames[i]);
102 if (cchName > cchMaxName)
103 cchMaxName = cchName;
104 }
105
106 for (size_t i = 0; i < aBstrNames.size(); i++)
107 RTPrintf("%-*ls = %ls\n", cchMaxName, aBstrNames[i], aBstrValues[i]);
[35508]108 }
109 else
110 {
111 com::Bstr bstrName = ValueUnion.psz;
112 com::Bstr bstrValue;
[56118]113 CHECK_ERROR2I_RET(pDebugger, GetRegister(idCpu, bstrName.raw(), bstrValue.asOutParam()), RTEXITCODE_FAILURE);
[35508]114 RTPrintf("%s = %ls\n", ValueUnion.psz, bstrValue.raw());
115 }
116 cRegisters++;
117 break;
118
119 default:
[98298]120 return errorGetOpt(vrc, &ValueUnion);
[35508]121 }
122 }
123
124 if (!cRegisters)
[92372]125 return errorSyntax(DebugVM::tr("The getregisters sub-command takes at least one register name"));
[35508]126 return RTEXITCODE_SUCCESS;
127}
128
129/**
[35306]130 * Handles the info sub-command.
131 *
132 * @returns Suitable exit code.
[56466]133 * @param pArgs The handler arguments.
[35306]134 * @param pDebugger Pointer to the debugger interface.
135 */
[56466]136static RTEXITCODE handleDebugVM_Info(HandlerArg *pArgs, IMachineDebugger *pDebugger)
[35306]137{
[56466]138 /*
139 * Parse arguments.
140 */
141 const char *pszInfo = NULL;
142 const char *pszArgs = NULL;
143 RTGETOPTSTATE GetState;
144 RTGETOPTUNION ValueUnion;
[98298]145 int vrc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, NULL, 0, 2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
146 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
[35306]147
[98298]148 while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0)
[56466]149 {
[98298]150 switch (vrc)
[56466]151 {
152 case VINF_GETOPT_NOT_OPTION:
153 if (!pszInfo)
154 pszInfo = ValueUnion.psz;
155 else if (!pszArgs)
156 pszArgs = ValueUnion.psz;
157 else
158 return errorTooManyParameters(&pArgs->argv[GetState.iNext - 1]);
159 break;
160 default:
[98298]161 return errorGetOpt(vrc, &ValueUnion);
[56466]162 }
163 }
164
165 if (!pszInfo)
[92372]166 return errorSyntax(DebugVM::tr("Must specify info item to display"));
[56466]167
168 /*
169 * Do the work.
170 */
171 com::Bstr bstrName(pszInfo);
172 com::Bstr bstrArgs(pszArgs);
[35306]173 com::Bstr bstrInfo;
[56118]174 CHECK_ERROR2I_RET(pDebugger, Info(bstrName.raw(), bstrArgs.raw(), bstrInfo.asOutParam()), RTEXITCODE_FAILURE);
[35306]175 RTPrintf("%ls", bstrInfo.raw());
176 return RTEXITCODE_SUCCESS;
177}
178
179/**
[34913]180 * Handles the inject sub-command.
[34971]181 *
[34913]182 * @returns Suitable exit code.
183 * @param a The handler arguments.
184 * @param pDebugger Pointer to the debugger interface.
185 */
186static RTEXITCODE handleDebugVM_InjectNMI(HandlerArg *a, IMachineDebugger *pDebugger)
187{
188 if (a->argc != 2)
[56466]189 return errorTooManyParameters(&a->argv[1]);
[56118]190 CHECK_ERROR2I_RET(pDebugger, InjectNMI(), RTEXITCODE_FAILURE);
[34913]191 return RTEXITCODE_SUCCESS;
192}
193
194/**
[39650]195 * Handles the log sub-command.
196 *
197 * @returns Suitable exit code.
198 * @param pArgs The handler arguments.
199 * @param pDebugger Pointer to the debugger interface.
200 * @param pszSubCmd The sub command.
201 */
202static RTEXITCODE handleDebugVM_LogXXXX(HandlerArg *pArgs, IMachineDebugger *pDebugger, const char *pszSubCmd)
203{
204 /*
205 * Parse arguments.
206 */
207 bool fRelease = false;
208 com::Utf8Str strSettings;
209
210 RTGETOPTSTATE GetState;
211 RTGETOPTUNION ValueUnion;
[41754]212
[52927]213 /*
214 * NB: don't use short options to prevent log specifications like
215 * "-drv_foo" from being interpreted as options.
216 */
217# define DEBUGVM_LOG_DEBUG (VINF_GETOPT_NOT_OPTION + 'd')
218# define DEBUGVM_LOG_RELEASE (VINF_GETOPT_NOT_OPTION + 'r')
219
[39650]220 static const RTGETOPTDEF s_aOptions[] =
221 {
[52927]222 { "--debug", DEBUGVM_LOG_DEBUG, RTGETOPT_REQ_NOTHING },
223 { "--release", DEBUGVM_LOG_RELEASE, RTGETOPT_REQ_NOTHING }
[39650]224 };
[98298]225 /*
226 * Note: RTGETOPTINIT_FLAGS_NO_STD_OPTS is needed to not get into an infinite hang in the following
227 * while-loop when processing log groups starting with "h",
228 * e.g. "VBoxManage debugvm <VM Name> log --debug -hex".
229 */
230 int vrc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2,
[57991]231 RTGETOPTINIT_FLAGS_OPTS_FIRST | RTGETOPTINIT_FLAGS_NO_STD_OPTS);
[98298]232 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
[39650]233
[98298]234 while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0)
[39650]235 {
[98298]236 switch (vrc)
[39650]237 {
[52927]238 case DEBUGVM_LOG_RELEASE:
[39650]239 fRelease = true;
240 break;
241
[52927]242 case DEBUGVM_LOG_DEBUG:
[39650]243 fRelease = false;
244 break;
245
[41754]246 /* Because log strings can start with "-" (like "-all+dev_foo")
247 * we have to take everything we got as a setting and apply it.
248 * IPRT will take care of the validation afterwards. */
249 default:
[39650]250 if (strSettings.length() == 0)
251 strSettings = ValueUnion.psz;
252 else
253 {
254 strSettings.append(' ');
255 strSettings.append(ValueUnion.psz);
256 }
257 break;
258 }
259 }
260
261 if (fRelease)
262 {
263 com::Utf8Str strTmp(strSettings);
[58212]264 strSettings = "release:";
[39650]265 strSettings.append(strTmp);
266 }
267
268 com::Bstr bstrSettings(strSettings);
269 if (!strcmp(pszSubCmd, "log"))
[56118]270 CHECK_ERROR2I_RET(pDebugger, ModifyLogGroups(bstrSettings.raw()), RTEXITCODE_FAILURE);
[39650]271 else if (!strcmp(pszSubCmd, "logdest"))
[56118]272 CHECK_ERROR2I_RET(pDebugger, ModifyLogDestinations(bstrSettings.raw()), RTEXITCODE_FAILURE);
[39650]273 else if (!strcmp(pszSubCmd, "logflags"))
[56118]274 CHECK_ERROR2I_RET(pDebugger, ModifyLogFlags(bstrSettings.raw()), RTEXITCODE_FAILURE);
[39650]275 else
276 AssertFailedReturn(RTEXITCODE_FAILURE);
277
278 return RTEXITCODE_SUCCESS;
279}
280
281
282/**
[34913]283 * Handles the inject sub-command.
[34971]284 *
[34913]285 * @returns Suitable exit code.
286 * @param pArgs The handler arguments.
287 * @param pDebugger Pointer to the debugger interface.
288 */
289static RTEXITCODE handleDebugVM_DumpVMCore(HandlerArg *pArgs, IMachineDebugger *pDebugger)
290{
291 /*
292 * Parse arguments.
293 */
[35242]294 const char *pszFilename = NULL;
295 const char *pszCompression = NULL;
[34913]296
297 RTGETOPTSTATE GetState;
298 RTGETOPTUNION ValueUnion;
299 static const RTGETOPTDEF s_aOptions[] =
300 {
[35242]301 { "--filename", 'f', RTGETOPT_REQ_STRING },
302 { "--compression", 'c', RTGETOPT_REQ_STRING }
[34913]303 };
[98298]304 int vrc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, 0 /*fFlags*/);
305 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
[34913]306
[98298]307 while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0)
[34913]308 {
[98298]309 switch (vrc)
[34913]310 {
[35242]311 case 'c':
312 if (pszCompression)
[92372]313 return errorSyntax(DebugVM::tr("The --compression option has already been given"));
[35242]314 pszCompression = ValueUnion.psz;
315 break;
[34913]316 case 'f':
317 if (pszFilename)
[92372]318 return errorSyntax(DebugVM::tr("The --filename option has already been given"));
[34913]319 pszFilename = ValueUnion.psz;
320 break;
321 default:
[98298]322 return errorGetOpt(vrc, &ValueUnion);
[34913]323 }
324 }
325
326 if (!pszFilename)
[92372]327 return errorSyntax(DebugVM::tr("The --filename option is required"));
[34913]328
329 /*
330 * Make the filename absolute before handing it on to the API.
331 */
332 char szAbsFilename[RTPATH_MAX];
[98298]333 vrc = RTPathAbs(pszFilename, szAbsFilename, sizeof(szAbsFilename));
334 if (RT_FAILURE(vrc))
335 return RTMsgErrorExit(RTEXITCODE_FAILURE, DebugVM::tr("RTPathAbs failed on '%s': %Rrc"), pszFilename, vrc);
[34913]336
337 com::Bstr bstrFilename(szAbsFilename);
[35242]338 com::Bstr bstrCompression(pszCompression);
[56118]339 CHECK_ERROR2I_RET(pDebugger, DumpGuestCore(bstrFilename.raw(), bstrCompression.raw()), RTEXITCODE_FAILURE);
[34913]340 return RTEXITCODE_SUCCESS;
341}
342
[34971]343/**
[56090]344 * Handles the osdetect sub-command.
[35306]345 *
346 * @returns Suitable exit code.
347 * @param a The handler arguments.
348 * @param pDebugger Pointer to the debugger interface.
349 */
350static RTEXITCODE handleDebugVM_OSDetect(HandlerArg *a, IMachineDebugger *pDebugger)
351{
352 if (a->argc != 2)
[56466]353 return errorTooManyParameters(&a->argv[1]);
[35306]354
[55884]355 com::Bstr bstrIgnore;
356 com::Bstr bstrAll("all");
[56118]357 CHECK_ERROR2I_RET(pDebugger, LoadPlugIn(bstrAll.raw(), bstrIgnore.asOutParam()), RTEXITCODE_FAILURE);
[55884]358
[35306]359 com::Bstr bstrName;
[56118]360 CHECK_ERROR2I_RET(pDebugger, DetectOS(bstrName.asOutParam()), RTEXITCODE_FAILURE);
[92372]361 RTPrintf(DebugVM::tr("Detected: %ls\n"), bstrName.raw());
[35306]362 return RTEXITCODE_SUCCESS;
363}
364
365/**
[56090]366 * Handles the osinfo sub-command.
[35306]367 *
368 * @returns Suitable exit code.
369 * @param a The handler arguments.
370 * @param pDebugger Pointer to the debugger interface.
371 */
372static RTEXITCODE handleDebugVM_OSInfo(HandlerArg *a, IMachineDebugger *pDebugger)
373{
374 if (a->argc != 2)
[56466]375 return errorTooManyParameters(&a->argv[1]);
[35306]376
377 com::Bstr bstrName;
[56118]378 CHECK_ERROR2I_RET(pDebugger, COMGETTER(OSName)(bstrName.asOutParam()), RTEXITCODE_FAILURE);
[35306]379 com::Bstr bstrVersion;
[56118]380 CHECK_ERROR2I_RET(pDebugger, COMGETTER(OSVersion)(bstrVersion.asOutParam()), RTEXITCODE_FAILURE);
[92372]381 RTPrintf(DebugVM::tr("Name: %ls\n"), bstrName.raw());
382 RTPrintf(DebugVM::tr("Version: %ls\n"), bstrVersion.raw());
[35306]383 return RTEXITCODE_SUCCESS;
384}
385
386/**
[56090]387 * Handles the osdmsg sub-command.
388 *
389 * @returns Suitable exit code.
390 * @param pArgs The handler arguments.
391 * @param pDebugger Pointer to the debugger interface.
392 */
393static RTEXITCODE handleDebugVM_OSDmesg(HandlerArg *pArgs, IMachineDebugger *pDebugger)
394{
395 /*
396 * Parse argument.
397 */
398 uint32_t uMaxMessages = 0;
399 RTGETOPTSTATE GetState;
400 RTGETOPTUNION ValueUnion;
401 static const RTGETOPTDEF s_aOptions[] =
402 {
403 { "--lines", 'n', RTGETOPT_REQ_UINT32 },
404 };
[98298]405 int vrc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
406 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
407 while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0)
408 switch (vrc)
[56090]409 {
410 case 'n': uMaxMessages = ValueUnion.u32; break;
[98298]411 default: return errorGetOpt(vrc, &ValueUnion);
[56090]412 }
413
414 /*
415 * Do it.
416 */
417 com::Bstr bstrDmesg;
[56118]418 CHECK_ERROR2I_RET(pDebugger, QueryOSKernelLog(uMaxMessages, bstrDmesg.asOutParam()), RTEXITCODE_FAILURE);
[56090]419 RTPrintf("%ls\n", bstrDmesg.raw());
420 return RTEXITCODE_SUCCESS;
421}
422
423/**
[35508]424 * Handles the setregisters sub-command.
425 *
426 * @returns Suitable exit code.
427 * @param pArgs The handler arguments.
428 * @param pDebugger Pointer to the debugger interface.
429 */
430static RTEXITCODE handleDebugVM_SetRegisters(HandlerArg *pArgs, IMachineDebugger *pDebugger)
431{
432 /*
433 * We take a list of register assignments, that is register=value.
434 */
435 ULONG idCpu = 0;
436 com::SafeArray<IN_BSTR> aBstrNames;
437 com::SafeArray<IN_BSTR> aBstrValues;
438
439 RTGETOPTSTATE GetState;
440 RTGETOPTUNION ValueUnion;
441 static const RTGETOPTDEF s_aOptions[] =
442 {
443 { "--cpu", 'c', RTGETOPT_REQ_UINT32 },
444 };
[98298]445 int vrc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
446 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
[35508]447
[98298]448 while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0)
[35508]449 {
[98298]450 switch (vrc)
[35508]451 {
452 case 'c':
453 idCpu = ValueUnion.u32;
454 break;
455
456 case VINF_GETOPT_NOT_OPTION:
457 {
458 const char *pszEqual = strchr(ValueUnion.psz, '=');
459 if (!pszEqual)
[92372]460 return errorSyntax(DebugVM::tr("setregisters expects input on the form 'register=value' got '%s'"),
461 ValueUnion.psz);
[35508]462 try
463 {
464 com::Bstr bstrName(ValueUnion.psz, pszEqual - ValueUnion.psz);
465 com::Bstr bstrValue(pszEqual + 1);
466 if ( !aBstrNames.push_back(bstrName.raw())
467 || !aBstrValues.push_back(bstrValue.raw()))
468 throw std::bad_alloc();
469 }
[73506]470 catch (std::bad_alloc &)
[35508]471 {
[92372]472 RTMsgError(DebugVM::tr("Out of memory\n"));
[35508]473 return RTEXITCODE_FAILURE;
474 }
475 break;
476 }
477
478 default:
[98298]479 return errorGetOpt(vrc, &ValueUnion);
[35508]480 }
481 }
482
483 if (!aBstrNames.size())
[92372]484 return errorSyntax(DebugVM::tr("The setregisters sub-command takes at least one register name"));
[35508]485
486 /*
487 * If it is only one register, use the single register method just so
488 * we expose it and can test it from the command line.
489 */
490 if (aBstrNames.size() == 1)
491 {
[56118]492 CHECK_ERROR2I_RET(pDebugger, SetRegister(idCpu, aBstrNames[0], aBstrValues[0]), RTEXITCODE_FAILURE);
[92372]493 RTPrintf(DebugVM::tr("Successfully set %ls\n"), aBstrNames[0]);
[35508]494 }
495 else
496 {
[56118]497 CHECK_ERROR2I_RET(pDebugger, SetRegisters(idCpu, ComSafeArrayAsInParam(aBstrNames), ComSafeArrayAsInParam(aBstrValues)),
498 RTEXITCODE_FAILURE);
[92594]499 RTPrintf(DebugVM::tr("Successfully set %u registers\n", "", aBstrNames.size()), aBstrNames.size());
[35508]500 }
501
502 return RTEXITCODE_SUCCESS;
503}
504
[39675]505/** @name debugvm show flags
506 * @{ */
507#define DEBUGVM_SHOW_FLAGS_HUMAN_READABLE UINT32_C(0x00000000)
508#define DEBUGVM_SHOW_FLAGS_SH_EXPORT UINT32_C(0x00000001)
509#define DEBUGVM_SHOW_FLAGS_SH_EVAL UINT32_C(0x00000002)
510#define DEBUGVM_SHOW_FLAGS_CMD_SET UINT32_C(0x00000003)
511#define DEBUGVM_SHOW_FLAGS_FMT_MASK UINT32_C(0x00000003)
512/** @} */
513
[35508]514/**
[39675]515 * Prints a variable according to the @a fFlags.
516 *
517 * @param pszVar The variable name.
518 * @param pbstrValue The variable value.
519 * @param fFlags The debugvm show flags.
520 */
521static void handleDebugVM_Show_PrintVar(const char *pszVar, com::Bstr const *pbstrValue, uint32_t fFlags)
522{
523 switch (fFlags & DEBUGVM_SHOW_FLAGS_FMT_MASK)
524 {
525 case DEBUGVM_SHOW_FLAGS_HUMAN_READABLE: RTPrintf(" %27s=%ls\n", pszVar, pbstrValue->raw()); break;
[92372]526 case DEBUGVM_SHOW_FLAGS_SH_EXPORT: RTPrintf(DebugVM::tr("export %s='%ls'\n"), pszVar, pbstrValue->raw()); break;
[39675]527 case DEBUGVM_SHOW_FLAGS_SH_EVAL: RTPrintf("%s='%ls'\n", pszVar, pbstrValue->raw()); break;
[92372]528 case DEBUGVM_SHOW_FLAGS_CMD_SET: RTPrintf(DebugVM::tr("set %s=%ls\n"), pszVar, pbstrValue->raw()); break;
[39675]529 default: AssertFailed();
530 }
531}
532
533/**
534 * Handles logdbg-settings.
535 *
536 * @returns Exit code.
537 * @param pDebugger The debugger interface.
538 * @param fFlags The debugvm show flags.
539 */
540static RTEXITCODE handleDebugVM_Show_LogDbgSettings(IMachineDebugger *pDebugger, uint32_t fFlags)
541{
542 if ((fFlags & DEBUGVM_SHOW_FLAGS_FMT_MASK) == DEBUGVM_SHOW_FLAGS_HUMAN_READABLE)
[92372]543 RTPrintf(DebugVM::tr("Debug logger settings:\n"));
[39675]544
545 com::Bstr bstr;
[57993]546 CHECK_ERROR2I_RET(pDebugger, COMGETTER(LogDbgGroups)(bstr.asOutParam()), RTEXITCODE_FAILURE);
[39675]547 handleDebugVM_Show_PrintVar("VBOX_LOG", &bstr, fFlags);
548
[57993]549 CHECK_ERROR2I_RET(pDebugger, COMGETTER(LogDbgFlags)(bstr.asOutParam()), RTEXITCODE_FAILURE);
[39675]550 handleDebugVM_Show_PrintVar("VBOX_LOG_FLAGS", &bstr, fFlags);
551
[56118]552 CHECK_ERROR2I_RET(pDebugger, COMGETTER(LogDbgDestinations)(bstr.asOutParam()), RTEXITCODE_FAILURE);
[39675]553 handleDebugVM_Show_PrintVar("VBOX_LOG_DEST", &bstr, fFlags);
554 return RTEXITCODE_SUCCESS;
555}
556
557/**
558 * Handles logrel-settings.
559 *
560 * @returns Exit code.
561 * @param pDebugger The debugger interface.
562 * @param fFlags The debugvm show flags.
563 */
564static RTEXITCODE handleDebugVM_Show_LogRelSettings(IMachineDebugger *pDebugger, uint32_t fFlags)
565{
566 if ((fFlags & DEBUGVM_SHOW_FLAGS_FMT_MASK) == DEBUGVM_SHOW_FLAGS_HUMAN_READABLE)
[92372]567 RTPrintf(DebugVM::tr("Release logger settings:\n"));
[39675]568
569 com::Bstr bstr;
[57993]570 CHECK_ERROR2I_RET(pDebugger, COMGETTER(LogRelGroups)(bstr.asOutParam()), RTEXITCODE_FAILURE);
[39675]571 handleDebugVM_Show_PrintVar("VBOX_RELEASE_LOG", &bstr, fFlags);
572
[57993]573 CHECK_ERROR2I_RET(pDebugger, COMGETTER(LogRelFlags)(bstr.asOutParam()), RTEXITCODE_FAILURE);
[39675]574 handleDebugVM_Show_PrintVar("VBOX_RELEASE_LOG_FLAGS", &bstr, fFlags);
575
[56118]576 CHECK_ERROR2I_RET(pDebugger, COMGETTER(LogRelDestinations)(bstr.asOutParam()), RTEXITCODE_FAILURE);
[39675]577 handleDebugVM_Show_PrintVar("VBOX_RELEASE_LOG_DEST", &bstr, fFlags);
578 return RTEXITCODE_SUCCESS;
579}
580
581/**
[39668]582 * Handles the show sub-command.
583 *
584 * @returns Suitable exit code.
585 * @param pArgs The handler arguments.
586 * @param pDebugger Pointer to the debugger interface.
587 */
588static RTEXITCODE handleDebugVM_Show(HandlerArg *pArgs, IMachineDebugger *pDebugger)
589{
[39675]590 /*
591 * Parse arguments and what to show. Order dependent.
592 */
593 uint32_t fFlags = DEBUGVM_SHOW_FLAGS_HUMAN_READABLE;
[39668]594
[39675]595 RTGETOPTSTATE GetState;
596 RTGETOPTUNION ValueUnion;
597 static const RTGETOPTDEF s_aOptions[] =
[39668]598 {
[39675]599 { "--human-readable", 'H', RTGETOPT_REQ_NOTHING },
600 { "--sh-export", 'e', RTGETOPT_REQ_NOTHING },
601 { "--sh-eval", 'E', RTGETOPT_REQ_NOTHING },
602 { "--cmd-set", 's', RTGETOPT_REQ_NOTHING },
603 };
[98298]604 int vrc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, 0 /*fFlags*/);
605 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
[39675]606
[98298]607 while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0)
[39668]608 {
[98298]609 switch (vrc)
[39675]610 {
611 case 'H':
612 fFlags = (fFlags & ~DEBUGVM_SHOW_FLAGS_FMT_MASK) | DEBUGVM_SHOW_FLAGS_HUMAN_READABLE;
613 break;
614
615 case 'e':
616 fFlags = (fFlags & ~DEBUGVM_SHOW_FLAGS_FMT_MASK) | DEBUGVM_SHOW_FLAGS_SH_EXPORT;
617 break;
618
619 case 'E':
620 fFlags = (fFlags & ~DEBUGVM_SHOW_FLAGS_FMT_MASK) | DEBUGVM_SHOW_FLAGS_SH_EVAL;
621 break;
622
623 case 's':
624 fFlags = (fFlags & ~DEBUGVM_SHOW_FLAGS_FMT_MASK) | DEBUGVM_SHOW_FLAGS_CMD_SET;
625 break;
626
627 case VINF_GETOPT_NOT_OPTION:
628 {
629 RTEXITCODE rcExit;
630 if (!strcmp(ValueUnion.psz, "log-settings"))
631 {
632 rcExit = handleDebugVM_Show_LogDbgSettings(pDebugger, fFlags);
633 if (rcExit == RTEXITCODE_SUCCESS)
634 rcExit = handleDebugVM_Show_LogRelSettings(pDebugger, fFlags);
635 }
636 else if (!strcmp(ValueUnion.psz, "logdbg-settings"))
637 rcExit = handleDebugVM_Show_LogDbgSettings(pDebugger, fFlags);
638 else if (!strcmp(ValueUnion.psz, "logrel-settings"))
639 rcExit = handleDebugVM_Show_LogRelSettings(pDebugger, fFlags);
640 else
[92372]641 rcExit = errorSyntax(DebugVM::tr("The show sub-command has no idea what '%s' might be"), ValueUnion.psz);
[39675]642 if (rcExit != RTEXITCODE_SUCCESS)
643 return rcExit;
644 break;
645 }
646
647 default:
[98298]648 return errorGetOpt(vrc, &ValueUnion);
[39675]649 }
[39668]650 }
651 return RTEXITCODE_SUCCESS;
652}
653
654/**
[61937]655 * Handles the stack sub-command.
656 *
657 * @returns Suitable exit code.
658 * @param pArgs The handler arguments.
659 * @param pDebugger Pointer to the debugger interface.
660 */
661static RTEXITCODE handleDebugVM_Stack(HandlerArg *pArgs, IMachineDebugger *pDebugger)
662{
663 /*
664 * Parse arguments.
665 */
666 VMCPUID idCpu = VMCPUID_ALL;
667
668 RTGETOPTSTATE GetState;
669 RTGETOPTUNION ValueUnion;
670 static const RTGETOPTDEF s_aOptions[] =
671 {
672 { "--cpu", 'c', RTGETOPT_REQ_UINT32 },
673 };
[98298]674 int vrc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
675 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
[61937]676
[98298]677 while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0)
[61937]678 {
[98298]679 switch (vrc)
[61937]680 {
681 case 'c':
682 idCpu = ValueUnion.u32;
683 break;
684
685 default:
[98298]686 return errorGetOpt(vrc, &ValueUnion);
[61937]687 }
688 }
689
690 /*
691 * Dump stack.
692 */
693 com::Bstr bstrGuestStack;
694 if (idCpu != VMCPUID_ALL)
695 {
696 /* Single CPU */
697 CHECK_ERROR2I_RET(pDebugger, DumpGuestStack(idCpu, bstrGuestStack.asOutParam()), RTEXITCODE_FAILURE);
698 RTPrintf("%ls\n", bstrGuestStack.raw());
699 }
700 else
701 {
702 /* All CPUs. */
703 ComPtr<IMachine> ptrMachine;
704 CHECK_ERROR2I_RET(pArgs->session, COMGETTER(Machine)(ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
705 ULONG cCpus;
706 CHECK_ERROR2I_RET(ptrMachine, COMGETTER(CPUCount)(&cCpus), RTEXITCODE_FAILURE);
707
708 for (idCpu = 0; idCpu < (VMCPUID)cCpus; idCpu++)
709 {
710 CHECK_ERROR2I_RET(pDebugger, DumpGuestStack(idCpu, bstrGuestStack.asOutParam()), RTEXITCODE_FAILURE);
711 if (cCpus > 1)
712 {
713 if (idCpu > 0)
714 RTPrintf("\n");
[92372]715 RTPrintf(DebugVM::tr("====================== CPU #%u ======================\n"), idCpu);
[61937]716 }
717 RTPrintf("%ls\n", bstrGuestStack.raw());
718 }
719 }
720
721
722 return RTEXITCODE_SUCCESS;
723}
724
725/**
[34971]726 * Handles the statistics sub-command.
727 *
728 * @returns Suitable exit code.
729 * @param pArgs The handler arguments.
730 * @param pDebugger Pointer to the debugger interface.
731 */
732static RTEXITCODE handleDebugVM_Statistics(HandlerArg *pArgs, IMachineDebugger *pDebugger)
733{
734 /*
735 * Parse arguments.
736 */
737 bool fWithDescriptions = false;
738 const char *pszPattern = NULL; /* all */
739 bool fReset = false;
740
741 RTGETOPTSTATE GetState;
742 RTGETOPTUNION ValueUnion;
743 static const RTGETOPTDEF s_aOptions[] =
744 {
745 { "--descriptions", 'd', RTGETOPT_REQ_NOTHING },
746 { "--pattern", 'p', RTGETOPT_REQ_STRING },
747 { "--reset", 'r', RTGETOPT_REQ_NOTHING },
748 };
[98298]749 int vrc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, 0 /*fFlags*/);
750 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
[34971]751
[98298]752 while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0)
[34971]753 {
[98298]754 switch (vrc)
[34971]755 {
756 case 'd':
757 fWithDescriptions = true;
758 break;
759
760 case 'p':
761 if (pszPattern)
[92372]762 return errorSyntax(DebugVM::tr("Multiple --pattern options are not permitted"));
[34971]763 pszPattern = ValueUnion.psz;
764 break;
765
766 case 'r':
767 fReset = true;
768 break;
769
770 default:
[98298]771 return errorGetOpt(vrc, &ValueUnion);
[34971]772 }
773 }
774
775 if (fReset && fWithDescriptions)
[92372]776 return errorSyntax(DebugVM::tr("The --reset and --descriptions options does not mix"));
[34971]777
778 /*
779 * Execute the order.
780 */
781 com::Bstr bstrPattern(pszPattern);
782 if (fReset)
[56118]783 CHECK_ERROR2I_RET(pDebugger, ResetStats(bstrPattern.raw()), RTEXITCODE_FAILURE);
[34971]784 else
785 {
786 com::Bstr bstrStats;
[56118]787 CHECK_ERROR2I_RET(pDebugger, GetStats(bstrPattern.raw(), fWithDescriptions, bstrStats.asOutParam()),
788 RTEXITCODE_FAILURE);
[34971]789 /* if (fFormatted)
790 { big mess }
791 else
792 */
793 RTPrintf("%ls\n", bstrStats.raw());
794 }
795
796 return RTEXITCODE_SUCCESS;
797}
798
[89697]799/**
800 * Handles the guestsample sub-command.
801 *
802 * @returns Suitable exit code.
803 * @param pArgs The handler arguments.
804 * @param pDebugger Pointer to the debugger interface.
805 */
806static RTEXITCODE handleDebugVM_GuestSample(HandlerArg *pArgs, IMachineDebugger *pDebugger)
807{
808 /*
809 * Parse arguments.
810 */
811 const char *pszFilename = NULL;
812 uint32_t cSampleIntervalUs = 1000;
813 uint64_t cSampleTimeUs = 1000*1000;
814
815 RTGETOPTSTATE GetState;
816 RTGETOPTUNION ValueUnion;
817 static const RTGETOPTDEF s_aOptions[] =
818 {
819 { "--filename", 'f', RTGETOPT_REQ_STRING },
820 { "--sample-interval-us", 'i', RTGETOPT_REQ_UINT32 },
821 { "--sample-time-us", 't', RTGETOPT_REQ_UINT64 },
822 };
[98298]823 int vrc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, 0 /*fFlags*/);
824 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
[89697]825
[98298]826 while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0)
[89697]827 {
[98298]828 switch (vrc)
[89697]829 {
830 case 'f':
831 pszFilename = ValueUnion.psz;
832 break;
833 case 'i':
834 cSampleIntervalUs = ValueUnion.u32;
835 break;
836 case 't':
837 cSampleTimeUs = ValueUnion.u64;
838 break;
839
840 default:
[98298]841 return errorGetOpt(vrc, &ValueUnion);
[89697]842 }
843 }
844
845 if (!pszFilename)
[92372]846 return errorSyntax(DebugVM::tr("The --filename is missing"));
[89697]847
848 /*
849 * Execute the order.
850 */
851 ComPtr<IProgress> ptrProgress;
852 com::Bstr bstrFilename(pszFilename);
853 CHECK_ERROR2I_RET(pDebugger, TakeGuestSample(bstrFilename.raw(), cSampleIntervalUs, cSampleTimeUs, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
854 showProgress(ptrProgress);
855
856 return RTEXITCODE_SUCCESS;
857}
858
[56118]859RTEXITCODE handleDebugVM(HandlerArg *pArgs)
[34913]860{
861 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
862
863 /*
864 * The first argument is the VM name or UUID. Open a session to it.
865 */
866 if (pArgs->argc < 2)
[56466]867 return errorNoSubcommand();
[34913]868 ComPtr<IMachine> ptrMachine;
[56118]869 CHECK_ERROR2I_RET(pArgs->virtualBox, FindMachine(com::Bstr(pArgs->argv[0]).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
870 CHECK_ERROR2I_RET(ptrMachine, LockMachine(pArgs->session, LockType_Shared), RTEXITCODE_FAILURE);
[34913]871
872 /*
873 * Get the associated console and machine debugger.
874 */
[61937]875 HRESULT hrc;
[34913]876 ComPtr<IConsole> ptrConsole;
[61937]877 CHECK_ERROR2(hrc, pArgs->session, COMGETTER(Console)(ptrConsole.asOutParam()));
878 if (SUCCEEDED(hrc))
[34913]879 {
[56119]880 if (ptrConsole.isNotNull())
[34913]881 {
[56119]882 ComPtr<IMachineDebugger> ptrDebugger;
[61937]883 CHECK_ERROR2(hrc, ptrConsole, COMGETTER(Debugger)(ptrDebugger.asOutParam()));
884 if (SUCCEEDED(hrc))
[56119]885 {
886 /*
887 * String switch on the sub-command.
888 */
889 const char *pszSubCmd = pArgs->argv[1];
[56466]890 if (!strcmp(pszSubCmd, "dumpvmcore"))
891 {
892 setCurrentSubcommand(HELP_SCOPE_DEBUGVM_DUMPVMCORE);
[56119]893 rcExit = handleDebugVM_DumpVMCore(pArgs, ptrDebugger);
[56466]894 }
[56119]895 else if (!strcmp(pszSubCmd, "getregisters"))
[56466]896 {
897 setCurrentSubcommand(HELP_SCOPE_DEBUGVM_GETREGISTERS);
[56119]898 rcExit = handleDebugVM_GetRegisters(pArgs, ptrDebugger);
[56466]899 }
[56119]900 else if (!strcmp(pszSubCmd, "info"))
[56466]901 {
902 setCurrentSubcommand(HELP_SCOPE_DEBUGVM_INFO);
[56119]903 rcExit = handleDebugVM_Info(pArgs, ptrDebugger);
[56466]904 }
[56119]905 else if (!strcmp(pszSubCmd, "injectnmi"))
[56466]906 {
907 setCurrentSubcommand(HELP_SCOPE_DEBUGVM_INJECTNMI);
[56119]908 rcExit = handleDebugVM_InjectNMI(pArgs, ptrDebugger);
[56466]909 }
[56119]910 else if (!strcmp(pszSubCmd, "log"))
[56466]911 {
912 setCurrentSubcommand(HELP_SCOPE_DEBUGVM_LOG);
[56119]913 rcExit = handleDebugVM_LogXXXX(pArgs, ptrDebugger, pszSubCmd);
[56466]914 }
[56119]915 else if (!strcmp(pszSubCmd, "logdest"))
[56466]916 {
917 setCurrentSubcommand(HELP_SCOPE_DEBUGVM_LOGDEST);
[56119]918 rcExit = handleDebugVM_LogXXXX(pArgs, ptrDebugger, pszSubCmd);
[56466]919 }
[56119]920 else if (!strcmp(pszSubCmd, "logflags"))
[56466]921 {
922 setCurrentSubcommand(HELP_SCOPE_DEBUGVM_LOGFLAGS);
[56119]923 rcExit = handleDebugVM_LogXXXX(pArgs, ptrDebugger, pszSubCmd);
[56466]924 }
[56119]925 else if (!strcmp(pszSubCmd, "osdetect"))
[56466]926 {
927 setCurrentSubcommand(HELP_SCOPE_DEBUGVM_OSDETECT);
[56119]928 rcExit = handleDebugVM_OSDetect(pArgs, ptrDebugger);
[56466]929 }
[56119]930 else if (!strcmp(pszSubCmd, "osinfo"))
[56466]931 {
932 setCurrentSubcommand(HELP_SCOPE_DEBUGVM_OSINFO);
[56119]933 rcExit = handleDebugVM_OSInfo(pArgs, ptrDebugger);
[56466]934 }
[56119]935 else if (!strcmp(pszSubCmd, "osdmesg"))
[56466]936 {
937 setCurrentSubcommand(HELP_SCOPE_DEBUGVM_OSDMESG);
[56119]938 rcExit = handleDebugVM_OSDmesg(pArgs, ptrDebugger);
[56466]939 }
[56119]940 else if (!strcmp(pszSubCmd, "setregisters"))
[56466]941 {
942 setCurrentSubcommand(HELP_SCOPE_DEBUGVM_SETREGISTERS);
[56119]943 rcExit = handleDebugVM_SetRegisters(pArgs, ptrDebugger);
[56466]944 }
[56119]945 else if (!strcmp(pszSubCmd, "show"))
[56466]946 {
947 setCurrentSubcommand(HELP_SCOPE_DEBUGVM_SHOW);
[56119]948 rcExit = handleDebugVM_Show(pArgs, ptrDebugger);
[56466]949 }
[61937]950 else if (!strcmp(pszSubCmd, "stack"))
951 {
952 setCurrentSubcommand(HELP_SCOPE_DEBUGVM_STACK);
953 rcExit = handleDebugVM_Stack(pArgs, ptrDebugger);
954 }
[56119]955 else if (!strcmp(pszSubCmd, "statistics"))
[56466]956 {
957 setCurrentSubcommand(HELP_SCOPE_DEBUGVM_STATISTICS);
[56119]958 rcExit = handleDebugVM_Statistics(pArgs, ptrDebugger);
[56466]959 }
[89697]960 else if (!strcmp(pszSubCmd, "guestsample"))
961 {
962 setCurrentSubcommand(HELP_SCOPE_DEBUGVM_GUESTSAMPLE);
963 rcExit = handleDebugVM_GuestSample(pArgs, ptrDebugger);
964 }
[56119]965 else
[56466]966 errorUnknownSubcommand(pszSubCmd);
[56119]967 }
[34913]968 }
[56119]969 else
[92372]970 RTMsgError(DebugVM::tr("Machine '%s' is not currently running.\n"), pArgs->argv[0]);
[34913]971 }
972
973 pArgs->session->UnlockMachine();
974
975 return rcExit;
976}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use