VirtualBox

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

Last change on this file since 94234 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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