1
/* $Id: VBoxManageDebugVM.cpp 35550 2011-01-13 18:08:54Z vboxsync $ */
3
* VBoxManage - Implementation of the debugvm command.
7
* Copyright (C) 2010 Oracle Corporation
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.
19
/*******************************************************************************
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/EventQueue.h>
30
#include <VBox/com/VirtualBox.h>
32
#include <iprt/ctype.h>
34
#include <iprt/getopt.h>
35
#include <iprt/path.h>
36
#include <iprt/param.h>
37
#include <iprt/stream.h>
38
#include <iprt/string.h>
39
#include <iprt/uuid.h>
42
#include "VBoxManage.h"
46
* Handles the getregisters sub-command.
48
* @returns Suitable exit code.
49
* @param pArgs The handler arguments.
50
* @param pDebugger Pointer to the debugger interface.
52
static RTEXITCODE handleDebugVM_GetRegisters(HandlerArg *pArgs, IMachineDebugger *pDebugger)
55
* We take a list of register names (case insensitive). If 'all' is
56
* encountered we'll dump all registers.
59
unsigned cRegisters = 0;
61
RTGETOPTSTATE GetState;
62
RTGETOPTUNION ValueUnion;
63
static const RTGETOPTDEF s_aOptions[] =
65
{ "--cpu", 'c', RTGETOPT_REQ_UINT32 },
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);
70
while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
75
idCpu = ValueUnion.u32;
78
case VINF_GETOPT_NOT_OPTION:
79
if (!RTStrICmp(ValueUnion.psz, "all"))
81
com::SafeArray<BSTR> aBstrNames;
82
com::SafeArray<BSTR> aBstrValues;
83
CHECK_ERROR2_RET(pDebugger, GetRegisters(idCpu, ComSafeArrayAsOutParam(aBstrNames), ComSafeArrayAsOutParam(aBstrValues)),
85
Assert(aBstrNames.size() == aBstrValues.size());
87
size_t cchMaxName = 8;
88
for (size_t i = 0; i < aBstrNames.size(); i++)
90
size_t cchName = RTUtf16Len(aBstrNames[i]);
91
if (cchName > cchMaxName)
95
for (size_t i = 0; i < aBstrNames.size(); i++)
96
RTPrintf("%-*ls = %ls\n", cchMaxName, aBstrNames[i], aBstrValues[i]);
100
com::Bstr bstrName = ValueUnion.psz;
102
CHECK_ERROR2_RET(pDebugger, GetRegister(idCpu, bstrName.raw(), bstrValue.asOutParam()), RTEXITCODE_FAILURE);
103
RTPrintf("%s = %ls\n", ValueUnion.psz, bstrValue.raw());
109
return errorGetOpt(USAGE_DEBUGVM, rc, &ValueUnion);
114
return errorSyntax(USAGE_DEBUGVM, "The getregisters sub-command takes at least one register name");
115
return RTEXITCODE_SUCCESS;
119
* Handles the info sub-command.
121
* @returns Suitable exit code.
122
* @param a The handler arguments.
123
* @param pDebugger Pointer to the debugger interface.
125
static RTEXITCODE handleDebugVM_Info(HandlerArg *a, IMachineDebugger *pDebugger)
127
if (a->argc < 3 || a->argc > 4)
128
return errorSyntax(USAGE_DEBUGVM, "The inject sub-command takes at one or two arguments");
130
com::Bstr bstrName(a->argv[2]);
131
com::Bstr bstrArgs(a->argv[3]);
133
CHECK_ERROR2_RET(pDebugger, Info(bstrName.raw(), bstrArgs.raw(), bstrInfo.asOutParam()), RTEXITCODE_FAILURE);
134
RTPrintf("%ls", bstrInfo.raw());
135
return RTEXITCODE_SUCCESS;
139
* Handles the inject sub-command.
141
* @returns Suitable exit code.
142
* @param a The handler arguments.
143
* @param pDebugger Pointer to the debugger interface.
145
static RTEXITCODE handleDebugVM_InjectNMI(HandlerArg *a, IMachineDebugger *pDebugger)
148
return errorSyntax(USAGE_DEBUGVM, "The inject sub-command does not take any arguments");
149
CHECK_ERROR2_RET(pDebugger, InjectNMI(), RTEXITCODE_FAILURE);
150
return RTEXITCODE_SUCCESS;
154
* Handles the inject sub-command.
156
* @returns Suitable exit code.
157
* @param pArgs The handler arguments.
158
* @param pDebugger Pointer to the debugger interface.
160
static RTEXITCODE handleDebugVM_DumpVMCore(HandlerArg *pArgs, IMachineDebugger *pDebugger)
165
const char *pszFilename = NULL;
166
const char *pszCompression = NULL;
168
RTGETOPTSTATE GetState;
169
RTGETOPTUNION ValueUnion;
170
static const RTGETOPTDEF s_aOptions[] =
172
{ "--filename", 'f', RTGETOPT_REQ_STRING },
173
{ "--compression", 'c', RTGETOPT_REQ_STRING }
175
int rc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, 0 /*fFlags*/);
176
AssertRCReturn(rc, RTEXITCODE_FAILURE);
178
while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
184
return errorSyntax(USAGE_DEBUGVM, "The --compression option has already been given");
185
pszCompression = ValueUnion.psz;
189
return errorSyntax(USAGE_DEBUGVM, "The --filename option has already been given");
190
pszFilename = ValueUnion.psz;
193
return errorGetOpt(USAGE_DEBUGVM, rc, &ValueUnion);
198
return errorSyntax(USAGE_DEBUGVM, "The --filename option is required");
201
* Make the filename absolute before handing it on to the API.
203
char szAbsFilename[RTPATH_MAX];
204
rc = RTPathAbs(pszFilename, szAbsFilename, sizeof(szAbsFilename));
206
return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs failed on '%s': %Rrc", pszFilename, rc);
208
com::Bstr bstrFilename(szAbsFilename);
209
com::Bstr bstrCompression(pszCompression);
210
CHECK_ERROR2_RET(pDebugger, DumpGuestCore(bstrFilename.raw(), bstrCompression.raw()), RTEXITCODE_FAILURE);
211
return RTEXITCODE_SUCCESS;
215
* Handles the os sub-command.
217
* @returns Suitable exit code.
218
* @param a The handler arguments.
219
* @param pDebugger Pointer to the debugger interface.
221
static RTEXITCODE handleDebugVM_OSDetect(HandlerArg *a, IMachineDebugger *pDebugger)
224
return errorSyntax(USAGE_DEBUGVM, "The osdetect sub-command does not take any arguments");
227
CHECK_ERROR2_RET(pDebugger, DetectOS(bstrName.asOutParam()), RTEXITCODE_FAILURE);
228
RTPrintf("Detected: %ls\n", bstrName.raw());
229
return RTEXITCODE_SUCCESS;
233
* Handles the os sub-command.
235
* @returns Suitable exit code.
236
* @param a The handler arguments.
237
* @param pDebugger Pointer to the debugger interface.
239
static RTEXITCODE handleDebugVM_OSInfo(HandlerArg *a, IMachineDebugger *pDebugger)
242
return errorSyntax(USAGE_DEBUGVM, "The osinfo sub-command does not take any arguments");
245
CHECK_ERROR2_RET(pDebugger, COMGETTER(OSName)(bstrName.asOutParam()), RTEXITCODE_FAILURE);
246
com::Bstr bstrVersion;
247
CHECK_ERROR2_RET(pDebugger, COMGETTER(OSVersion)(bstrVersion.asOutParam()), RTEXITCODE_FAILURE);
248
RTPrintf("Name: %ls\n", bstrName.raw());
249
RTPrintf("Version: %ls\n", bstrVersion.raw());
250
return RTEXITCODE_SUCCESS;
254
* Handles the setregisters sub-command.
256
* @returns Suitable exit code.
257
* @param pArgs The handler arguments.
258
* @param pDebugger Pointer to the debugger interface.
260
static RTEXITCODE handleDebugVM_SetRegisters(HandlerArg *pArgs, IMachineDebugger *pDebugger)
263
* We take a list of register assignments, that is register=value.
266
com::SafeArray<IN_BSTR> aBstrNames;
267
com::SafeArray<IN_BSTR> aBstrValues;
269
RTGETOPTSTATE GetState;
270
RTGETOPTUNION ValueUnion;
271
static const RTGETOPTDEF s_aOptions[] =
273
{ "--cpu", 'c', RTGETOPT_REQ_UINT32 },
275
int rc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
276
AssertRCReturn(rc, RTEXITCODE_FAILURE);
278
while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
283
idCpu = ValueUnion.u32;
286
case VINF_GETOPT_NOT_OPTION:
288
const char *pszEqual = strchr(ValueUnion.psz, '=');
290
return errorSyntax(USAGE_DEBUGVM, "setregisters expects input on the form 'register=value' got '%s'", ValueUnion.psz);
293
com::Bstr bstrName(ValueUnion.psz, pszEqual - ValueUnion.psz);
294
com::Bstr bstrValue(pszEqual + 1);
295
if ( !aBstrNames.push_back(bstrName.raw())
296
|| !aBstrValues.push_back(bstrValue.raw()))
297
throw std::bad_alloc();
299
catch (std::bad_alloc)
301
RTMsgError("Out of memory\n");
302
return RTEXITCODE_FAILURE;
308
return errorGetOpt(USAGE_DEBUGVM, rc, &ValueUnion);
312
if (!aBstrNames.size())
313
return errorSyntax(USAGE_DEBUGVM, "The setregisters sub-command takes at least one register name");
316
* If it is only one register, use the single register method just so
317
* we expose it and can test it from the command line.
319
if (aBstrNames.size() == 1)
321
CHECK_ERROR2_RET(pDebugger, SetRegister(idCpu, aBstrNames[0], aBstrValues[0]), RTEXITCODE_FAILURE);
322
RTPrintf("Successfully set %ls\n", aBstrNames[0]);
326
CHECK_ERROR2_RET(pDebugger, SetRegisters(idCpu, ComSafeArrayAsInParam(aBstrNames), ComSafeArrayAsInParam(aBstrValues)), RTEXITCODE_FAILURE);
327
RTPrintf("Successfully set %u registers\n", aBstrNames.size());
330
return RTEXITCODE_SUCCESS;
334
* Handles the statistics sub-command.
336
* @returns Suitable exit code.
337
* @param pArgs The handler arguments.
338
* @param pDebugger Pointer to the debugger interface.
340
static RTEXITCODE handleDebugVM_Statistics(HandlerArg *pArgs, IMachineDebugger *pDebugger)
345
bool fWithDescriptions = false;
346
const char *pszPattern = NULL; /* all */
349
RTGETOPTSTATE GetState;
350
RTGETOPTUNION ValueUnion;
351
static const RTGETOPTDEF s_aOptions[] =
353
{ "--descriptions", 'd', RTGETOPT_REQ_NOTHING },
354
{ "--pattern", 'p', RTGETOPT_REQ_STRING },
355
{ "--reset", 'r', RTGETOPT_REQ_NOTHING },
357
int rc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, 0 /*fFlags*/);
358
AssertRCReturn(rc, RTEXITCODE_FAILURE);
360
while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
365
fWithDescriptions = true;
370
return errorSyntax(USAGE_DEBUGVM, "Multiple --pattern options are not permitted");
371
pszPattern = ValueUnion.psz;
379
return errorGetOpt(USAGE_DEBUGVM, rc, &ValueUnion);
383
if (fReset && fWithDescriptions)
384
return errorSyntax(USAGE_DEBUGVM, "The --reset and --descriptions options does not mix");
389
com::Bstr bstrPattern(pszPattern);
391
CHECK_ERROR2_RET(pDebugger, ResetStats(bstrPattern.raw()), RTEXITCODE_FAILURE);
395
CHECK_ERROR2_RET(pDebugger, GetStats(bstrPattern.raw(), fWithDescriptions, bstrStats.asOutParam()),
401
RTPrintf("%ls\n", bstrStats.raw());
404
return RTEXITCODE_SUCCESS;
407
int handleDebugVM(HandlerArg *pArgs)
409
RTEXITCODE rcExit = RTEXITCODE_FAILURE;
412
* The first argument is the VM name or UUID. Open a session to it.
415
return errorSyntax(USAGE_DEBUGVM, "Too few parameters");
416
ComPtr<IMachine> ptrMachine;
417
CHECK_ERROR2_RET(pArgs->virtualBox, FindMachine(com::Bstr(pArgs->argv[0]).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
418
CHECK_ERROR2_RET(ptrMachine, LockMachine(pArgs->session, LockType_Shared), RTEXITCODE_FAILURE);
421
* Get the associated console and machine debugger.
424
ComPtr<IConsole> ptrConsole;
425
CHECK_ERROR(pArgs->session, COMGETTER(Console)(ptrConsole.asOutParam()));
428
ComPtr<IMachineDebugger> ptrDebugger;
429
CHECK_ERROR(ptrConsole, COMGETTER(Debugger)(ptrDebugger.asOutParam()));
433
* String switch on the sub-command.
435
const char *pszSubCmd = pArgs->argv[1];
436
if (!strcmp(pszSubCmd, "dumpguestcore"))
437
rcExit = handleDebugVM_DumpVMCore(pArgs, ptrDebugger);
438
else if (!strcmp(pszSubCmd, "getregisters"))
439
rcExit = handleDebugVM_GetRegisters(pArgs, ptrDebugger);
440
else if (!strcmp(pszSubCmd, "info"))
441
rcExit = handleDebugVM_Info(pArgs, ptrDebugger);
442
else if (!strcmp(pszSubCmd, "injectnmi"))
443
rcExit = handleDebugVM_InjectNMI(pArgs, ptrDebugger);
444
else if (!strcmp(pszSubCmd, "osdetect"))
445
rcExit = handleDebugVM_OSDetect(pArgs, ptrDebugger);
446
else if (!strcmp(pszSubCmd, "osinfo"))
447
rcExit = handleDebugVM_OSInfo(pArgs, ptrDebugger);
448
else if (!strcmp(pszSubCmd, "setregisters"))
449
rcExit = handleDebugVM_SetRegisters(pArgs, ptrDebugger);
450
else if (!strcmp(pszSubCmd, "statistics"))
451
rcExit = handleDebugVM_Statistics(pArgs, ptrDebugger);
453
errorSyntax(USAGE_DEBUGVM, "Invalid parameter '%s'", pArgs->argv[1]);
457
pArgs->session->UnlockMachine();