VirtualBox

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

Last change on this file since 74942 was 73506, checked in by vboxsync, 6 years ago

VBoxManage: GCC 8.2.0 fixes

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

© 2023 Oracle
ContactPrivacy policyTerms of Use