VirtualBox

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

Last change on this file since 35521 was 35508, checked in by vboxsync, 14 years ago

VBoxManage: Added the getregisters and setregisters subcommands to debugvm. Document them and other recent debugvm additions.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.5 KB
Line 
1/* $Id: VBoxManageDebugVM.cpp 35508 2011-01-12 14:54:12Z vboxsync $ */
2/** @file
3 * VBoxManage - Implementation of the debugvm command.
4 */
5
6/*
7 * Copyright (C) 2010 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/EventQueue.h>
29
30#include <VBox/com/VirtualBox.h>
31
32#include <iprt/ctype.h>
33#include <VBox/err.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>
40#include <VBox/log.h>
41
42#include "VBoxManage.h"
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_ERROR2_RET(pDebugger, GetRegisters(idCpu, ComSafeArrayAsOutParam(aBstrNames), ComSafeArrayAsOutParam(aBstrValues)),
84 RTEXITCODE_FAILURE);
85 Assert(aBstrNames.size() == aBstrValues.size());
86
87 for (size_t i = 0; i < aBstrNames.size(); i++)
88 RTPrintf("%ls = %ls\n", aBstrNames[i], aBstrValues[i]);
89 }
90 else
91 {
92 com::Bstr bstrName = ValueUnion.psz;
93 com::Bstr bstrValue;
94 CHECK_ERROR2_RET(pDebugger, GetRegister(idCpu, bstrName.raw(), bstrValue.asOutParam()), RTEXITCODE_FAILURE);
95 RTPrintf("%s = %ls\n", ValueUnion.psz, bstrValue.raw());
96 }
97 cRegisters++;
98 break;
99
100 default:
101 return errorGetOpt(USAGE_DEBUGVM, rc, &ValueUnion);
102 }
103 }
104
105 if (!cRegisters)
106 return errorSyntax(USAGE_DEBUGVM, "The getregisters sub-command takes at least one register name");
107 return RTEXITCODE_SUCCESS;
108}
109
110/**
111 * Handles the info sub-command.
112 *
113 * @returns Suitable exit code.
114 * @param a The handler arguments.
115 * @param pDebugger Pointer to the debugger interface.
116 */
117static RTEXITCODE handleDebugVM_Info(HandlerArg *a, IMachineDebugger *pDebugger)
118{
119 if (a->argc < 3 || a->argc > 4)
120 return errorSyntax(USAGE_DEBUGVM, "The inject sub-command takes at one or two arguments");
121
122 com::Bstr bstrName(a->argv[2]);
123 com::Bstr bstrArgs(a->argv[3]);
124 com::Bstr bstrInfo;
125 CHECK_ERROR2_RET(pDebugger, Info(bstrName.raw(), bstrArgs.raw(), bstrInfo.asOutParam()), RTEXITCODE_FAILURE);
126 RTPrintf("%ls", bstrInfo.raw());
127 return RTEXITCODE_SUCCESS;
128}
129
130/**
131 * Handles the inject sub-command.
132 *
133 * @returns Suitable exit code.
134 * @param a The handler arguments.
135 * @param pDebugger Pointer to the debugger interface.
136 */
137static RTEXITCODE handleDebugVM_InjectNMI(HandlerArg *a, IMachineDebugger *pDebugger)
138{
139 if (a->argc != 2)
140 return errorSyntax(USAGE_DEBUGVM, "The inject sub-command does not take any arguments");
141 CHECK_ERROR2_RET(pDebugger, InjectNMI(), RTEXITCODE_FAILURE);
142 return RTEXITCODE_SUCCESS;
143}
144
145/**
146 * Handles the inject sub-command.
147 *
148 * @returns Suitable exit code.
149 * @param pArgs The handler arguments.
150 * @param pDebugger Pointer to the debugger interface.
151 */
152static RTEXITCODE handleDebugVM_DumpVMCore(HandlerArg *pArgs, IMachineDebugger *pDebugger)
153{
154 /*
155 * Parse arguments.
156 */
157 const char *pszFilename = NULL;
158 const char *pszCompression = NULL;
159
160 RTGETOPTSTATE GetState;
161 RTGETOPTUNION ValueUnion;
162 static const RTGETOPTDEF s_aOptions[] =
163 {
164 { "--filename", 'f', RTGETOPT_REQ_STRING },
165 { "--compression", 'c', RTGETOPT_REQ_STRING }
166 };
167 int rc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, 0 /*fFlags*/);
168 AssertRCReturn(rc, RTEXITCODE_FAILURE);
169
170 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
171 {
172 switch (rc)
173 {
174 case 'c':
175 if (pszCompression)
176 return errorSyntax(USAGE_DEBUGVM, "The --compression option has already been given");
177 pszCompression = ValueUnion.psz;
178 break;
179 case 'f':
180 if (pszFilename)
181 return errorSyntax(USAGE_DEBUGVM, "The --filename option has already been given");
182 pszFilename = ValueUnion.psz;
183 break;
184 default:
185 return errorGetOpt(USAGE_DEBUGVM, rc, &ValueUnion);
186 }
187 }
188
189 if (!pszFilename)
190 return errorSyntax(USAGE_DEBUGVM, "The --filename option is required");
191
192 /*
193 * Make the filename absolute before handing it on to the API.
194 */
195 char szAbsFilename[RTPATH_MAX];
196 rc = RTPathAbs(pszFilename, szAbsFilename, sizeof(szAbsFilename));
197 if (RT_FAILURE(rc))
198 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs failed on '%s': %Rrc", pszFilename, rc);
199
200 com::Bstr bstrFilename(szAbsFilename);
201 com::Bstr bstrCompression(pszCompression);
202 CHECK_ERROR2_RET(pDebugger, DumpGuestCore(bstrFilename.raw(), bstrCompression.raw()), RTEXITCODE_FAILURE);
203 return RTEXITCODE_SUCCESS;
204}
205
206/**
207 * Handles the os sub-command.
208 *
209 * @returns Suitable exit code.
210 * @param a The handler arguments.
211 * @param pDebugger Pointer to the debugger interface.
212 */
213static RTEXITCODE handleDebugVM_OSDetect(HandlerArg *a, IMachineDebugger *pDebugger)
214{
215 if (a->argc != 2)
216 return errorSyntax(USAGE_DEBUGVM, "The osdetect sub-command does not take any arguments");
217
218 com::Bstr bstrName;
219 CHECK_ERROR2_RET(pDebugger, DetectOS(bstrName.asOutParam()), RTEXITCODE_FAILURE);
220 RTPrintf("Detected: %ls\n", bstrName.raw());
221 return RTEXITCODE_SUCCESS;
222}
223
224/**
225 * Handles the os sub-command.
226 *
227 * @returns Suitable exit code.
228 * @param a The handler arguments.
229 * @param pDebugger Pointer to the debugger interface.
230 */
231static RTEXITCODE handleDebugVM_OSInfo(HandlerArg *a, IMachineDebugger *pDebugger)
232{
233 if (a->argc != 2)
234 return errorSyntax(USAGE_DEBUGVM, "The osinfo sub-command does not take any arguments");
235
236 com::Bstr bstrName;
237 CHECK_ERROR2_RET(pDebugger, COMGETTER(OSName)(bstrName.asOutParam()), RTEXITCODE_FAILURE);
238 com::Bstr bstrVersion;
239 CHECK_ERROR2_RET(pDebugger, COMGETTER(OSVersion)(bstrVersion.asOutParam()), RTEXITCODE_FAILURE);
240 RTPrintf("Name: %ls\n", bstrName.raw());
241 RTPrintf("Version: %ls\n", bstrVersion.raw());
242 return RTEXITCODE_SUCCESS;
243}
244
245/**
246 * Handles the setregisters sub-command.
247 *
248 * @returns Suitable exit code.
249 * @param pArgs The handler arguments.
250 * @param pDebugger Pointer to the debugger interface.
251 */
252static RTEXITCODE handleDebugVM_SetRegisters(HandlerArg *pArgs, IMachineDebugger *pDebugger)
253{
254 /*
255 * We take a list of register assignments, that is register=value.
256 */
257 ULONG idCpu = 0;
258 com::SafeArray<IN_BSTR> aBstrNames;
259 com::SafeArray<IN_BSTR> aBstrValues;
260
261 RTGETOPTSTATE GetState;
262 RTGETOPTUNION ValueUnion;
263 static const RTGETOPTDEF s_aOptions[] =
264 {
265 { "--cpu", 'c', RTGETOPT_REQ_UINT32 },
266 };
267 int rc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
268 AssertRCReturn(rc, RTEXITCODE_FAILURE);
269
270 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
271 {
272 switch (rc)
273 {
274 case 'c':
275 idCpu = ValueUnion.u32;
276 break;
277
278 case VINF_GETOPT_NOT_OPTION:
279 {
280 const char *pszEqual = strchr(ValueUnion.psz, '=');
281 if (!pszEqual)
282 return errorSyntax(USAGE_DEBUGVM, "setregisters expects input on the form 'register=value' got '%s'", ValueUnion.psz);
283 try
284 {
285 com::Bstr bstrName(ValueUnion.psz, pszEqual - ValueUnion.psz);
286 com::Bstr bstrValue(pszEqual + 1);
287 if ( !aBstrNames.push_back(bstrName.raw())
288 || !aBstrValues.push_back(bstrValue.raw()))
289 throw std::bad_alloc();
290 }
291 catch (std::bad_alloc)
292 {
293 RTMsgError("Out of memory\n");
294 return RTEXITCODE_FAILURE;
295 }
296 break;
297 }
298
299 default:
300 return errorGetOpt(USAGE_DEBUGVM, rc, &ValueUnion);
301 }
302 }
303
304 if (!aBstrNames.size())
305 return errorSyntax(USAGE_DEBUGVM, "The setregisters sub-command takes at least one register name");
306
307 /*
308 * If it is only one register, use the single register method just so
309 * we expose it and can test it from the command line.
310 */
311 if (aBstrNames.size() == 1)
312 {
313 CHECK_ERROR2_RET(pDebugger, SetRegister(idCpu, aBstrNames[0], aBstrValues[0]), RTEXITCODE_FAILURE);
314 RTPrintf("Successfully set %ls\n", aBstrNames[0]);
315 }
316 else
317 {
318 CHECK_ERROR2_RET(pDebugger, SetRegisters(idCpu, ComSafeArrayAsInParam(aBstrNames), ComSafeArrayAsInParam(aBstrValues)), RTEXITCODE_FAILURE);
319 RTPrintf("Successfully set %u registers\n", aBstrNames.size());
320 }
321
322 return RTEXITCODE_SUCCESS;
323}
324
325/**
326 * Handles the statistics sub-command.
327 *
328 * @returns Suitable exit code.
329 * @param pArgs The handler arguments.
330 * @param pDebugger Pointer to the debugger interface.
331 */
332static RTEXITCODE handleDebugVM_Statistics(HandlerArg *pArgs, IMachineDebugger *pDebugger)
333{
334 /*
335 * Parse arguments.
336 */
337 bool fWithDescriptions = false;
338 const char *pszPattern = NULL; /* all */
339 bool fReset = false;
340
341 RTGETOPTSTATE GetState;
342 RTGETOPTUNION ValueUnion;
343 static const RTGETOPTDEF s_aOptions[] =
344 {
345 { "--descriptions", 'd', RTGETOPT_REQ_NOTHING },
346 { "--pattern", 'p', RTGETOPT_REQ_STRING },
347 { "--reset", 'r', RTGETOPT_REQ_NOTHING },
348 };
349 int rc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, 0 /*fFlags*/);
350 AssertRCReturn(rc, RTEXITCODE_FAILURE);
351
352 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
353 {
354 switch (rc)
355 {
356 case 'd':
357 fWithDescriptions = true;
358 break;
359
360 case 'p':
361 if (pszPattern)
362 return errorSyntax(USAGE_DEBUGVM, "Multiple --pattern options are not permitted");
363 pszPattern = ValueUnion.psz;
364 break;
365
366 case 'r':
367 fReset = true;
368 break;
369
370 default:
371 return errorGetOpt(USAGE_DEBUGVM, rc, &ValueUnion);
372 }
373 }
374
375 if (fReset && fWithDescriptions)
376 return errorSyntax(USAGE_DEBUGVM, "The --reset and --descriptions options does not mix");
377
378 /*
379 * Execute the order.
380 */
381 com::Bstr bstrPattern(pszPattern);
382 if (fReset)
383 CHECK_ERROR2_RET(pDebugger, ResetStats(bstrPattern.raw()), RTEXITCODE_FAILURE);
384 else
385 {
386 com::Bstr bstrStats;
387 CHECK_ERROR2_RET(pDebugger, GetStats(bstrPattern.raw(), fWithDescriptions, bstrStats.asOutParam()),
388 RTEXITCODE_FAILURE);
389 /* if (fFormatted)
390 { big mess }
391 else
392 */
393 RTPrintf("%ls\n", bstrStats.raw());
394 }
395
396 return RTEXITCODE_SUCCESS;
397}
398
399int handleDebugVM(HandlerArg *pArgs)
400{
401 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
402
403 /*
404 * The first argument is the VM name or UUID. Open a session to it.
405 */
406 if (pArgs->argc < 2)
407 return errorSyntax(USAGE_DEBUGVM, "Too few parameters");
408 ComPtr<IMachine> ptrMachine;
409 CHECK_ERROR2_RET(pArgs->virtualBox, FindMachine(com::Bstr(pArgs->argv[0]).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
410 CHECK_ERROR2_RET(ptrMachine, LockMachine(pArgs->session, LockType_Shared), RTEXITCODE_FAILURE);
411
412 /*
413 * Get the associated console and machine debugger.
414 */
415 HRESULT rc;
416 ComPtr<IConsole> ptrConsole;
417 CHECK_ERROR(pArgs->session, COMGETTER(Console)(ptrConsole.asOutParam()));
418 if (SUCCEEDED(rc))
419 {
420 ComPtr<IMachineDebugger> ptrDebugger;
421 CHECK_ERROR(ptrConsole, COMGETTER(Debugger)(ptrDebugger.asOutParam()));
422 if (SUCCEEDED(rc))
423 {
424 /*
425 * String switch on the sub-command.
426 */
427 const char *pszSubCmd = pArgs->argv[1];
428 if (!strcmp(pszSubCmd, "dumpguestcore"))
429 rcExit = handleDebugVM_DumpVMCore(pArgs, ptrDebugger);
430 else if (!strcmp(pszSubCmd, "getregisters"))
431 rcExit = handleDebugVM_GetRegisters(pArgs, ptrDebugger);
432 else if (!strcmp(pszSubCmd, "info"))
433 rcExit = handleDebugVM_Info(pArgs, ptrDebugger);
434 else if (!strcmp(pszSubCmd, "injectnmi"))
435 rcExit = handleDebugVM_InjectNMI(pArgs, ptrDebugger);
436 else if (!strcmp(pszSubCmd, "osdetect"))
437 rcExit = handleDebugVM_OSDetect(pArgs, ptrDebugger);
438 else if (!strcmp(pszSubCmd, "osinfo"))
439 rcExit = handleDebugVM_OSInfo(pArgs, ptrDebugger);
440 else if (!strcmp(pszSubCmd, "setregisters"))
441 rcExit = handleDebugVM_SetRegisters(pArgs, ptrDebugger);
442 else if (!strcmp(pszSubCmd, "statistics"))
443 rcExit = handleDebugVM_Statistics(pArgs, ptrDebugger);
444 else
445 errorSyntax(USAGE_DEBUGVM, "Invalid parameter '%s'", pArgs->argv[1]);
446 }
447 }
448
449 pArgs->session->UnlockMachine();
450
451 return rcExit;
452}
453
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