VirtualBox

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

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

Implemented DBGFR3RegNmQueryAll and MachineDebugger::GetRegisters.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.8 KB
Line 
1/* $Id: VBoxManageDebugVM.cpp 35550 2011-01-13 18:08:54Z 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 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_ERROR2_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(USAGE_DEBUGVM, rc, &ValueUnion);
110 }
111 }
112
113 if (!cRegisters)
114 return errorSyntax(USAGE_DEBUGVM, "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 a The handler arguments.
123 * @param pDebugger Pointer to the debugger interface.
124 */
125static RTEXITCODE handleDebugVM_Info(HandlerArg *a, IMachineDebugger *pDebugger)
126{
127 if (a->argc < 3 || a->argc > 4)
128 return errorSyntax(USAGE_DEBUGVM, "The inject sub-command takes at one or two arguments");
129
130 com::Bstr bstrName(a->argv[2]);
131 com::Bstr bstrArgs(a->argv[3]);
132 com::Bstr bstrInfo;
133 CHECK_ERROR2_RET(pDebugger, Info(bstrName.raw(), bstrArgs.raw(), bstrInfo.asOutParam()), RTEXITCODE_FAILURE);
134 RTPrintf("%ls", bstrInfo.raw());
135 return RTEXITCODE_SUCCESS;
136}
137
138/**
139 * Handles the inject sub-command.
140 *
141 * @returns Suitable exit code.
142 * @param a The handler arguments.
143 * @param pDebugger Pointer to the debugger interface.
144 */
145static RTEXITCODE handleDebugVM_InjectNMI(HandlerArg *a, IMachineDebugger *pDebugger)
146{
147 if (a->argc != 2)
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;
151}
152
153/**
154 * Handles the inject sub-command.
155 *
156 * @returns Suitable exit code.
157 * @param pArgs The handler arguments.
158 * @param pDebugger Pointer to the debugger interface.
159 */
160static RTEXITCODE handleDebugVM_DumpVMCore(HandlerArg *pArgs, IMachineDebugger *pDebugger)
161{
162 /*
163 * Parse arguments.
164 */
165 const char *pszFilename = NULL;
166 const char *pszCompression = NULL;
167
168 RTGETOPTSTATE GetState;
169 RTGETOPTUNION ValueUnion;
170 static const RTGETOPTDEF s_aOptions[] =
171 {
172 { "--filename", 'f', RTGETOPT_REQ_STRING },
173 { "--compression", 'c', RTGETOPT_REQ_STRING }
174 };
175 int rc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, 0 /*fFlags*/);
176 AssertRCReturn(rc, RTEXITCODE_FAILURE);
177
178 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
179 {
180 switch (rc)
181 {
182 case 'c':
183 if (pszCompression)
184 return errorSyntax(USAGE_DEBUGVM, "The --compression option has already been given");
185 pszCompression = ValueUnion.psz;
186 break;
187 case 'f':
188 if (pszFilename)
189 return errorSyntax(USAGE_DEBUGVM, "The --filename option has already been given");
190 pszFilename = ValueUnion.psz;
191 break;
192 default:
193 return errorGetOpt(USAGE_DEBUGVM, rc, &ValueUnion);
194 }
195 }
196
197 if (!pszFilename)
198 return errorSyntax(USAGE_DEBUGVM, "The --filename option is required");
199
200 /*
201 * Make the filename absolute before handing it on to the API.
202 */
203 char szAbsFilename[RTPATH_MAX];
204 rc = RTPathAbs(pszFilename, szAbsFilename, sizeof(szAbsFilename));
205 if (RT_FAILURE(rc))
206 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs failed on '%s': %Rrc", pszFilename, rc);
207
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;
212}
213
214/**
215 * Handles the os sub-command.
216 *
217 * @returns Suitable exit code.
218 * @param a The handler arguments.
219 * @param pDebugger Pointer to the debugger interface.
220 */
221static RTEXITCODE handleDebugVM_OSDetect(HandlerArg *a, IMachineDebugger *pDebugger)
222{
223 if (a->argc != 2)
224 return errorSyntax(USAGE_DEBUGVM, "The osdetect sub-command does not take any arguments");
225
226 com::Bstr bstrName;
227 CHECK_ERROR2_RET(pDebugger, DetectOS(bstrName.asOutParam()), RTEXITCODE_FAILURE);
228 RTPrintf("Detected: %ls\n", bstrName.raw());
229 return RTEXITCODE_SUCCESS;
230}
231
232/**
233 * Handles the os sub-command.
234 *
235 * @returns Suitable exit code.
236 * @param a The handler arguments.
237 * @param pDebugger Pointer to the debugger interface.
238 */
239static RTEXITCODE handleDebugVM_OSInfo(HandlerArg *a, IMachineDebugger *pDebugger)
240{
241 if (a->argc != 2)
242 return errorSyntax(USAGE_DEBUGVM, "The osinfo sub-command does not take any arguments");
243
244 com::Bstr bstrName;
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;
251}
252
253/**
254 * Handles the setregisters sub-command.
255 *
256 * @returns Suitable exit code.
257 * @param pArgs The handler arguments.
258 * @param pDebugger Pointer to the debugger interface.
259 */
260static RTEXITCODE handleDebugVM_SetRegisters(HandlerArg *pArgs, IMachineDebugger *pDebugger)
261{
262 /*
263 * We take a list of register assignments, that is register=value.
264 */
265 ULONG idCpu = 0;
266 com::SafeArray<IN_BSTR> aBstrNames;
267 com::SafeArray<IN_BSTR> aBstrValues;
268
269 RTGETOPTSTATE GetState;
270 RTGETOPTUNION ValueUnion;
271 static const RTGETOPTDEF s_aOptions[] =
272 {
273 { "--cpu", 'c', RTGETOPT_REQ_UINT32 },
274 };
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);
277
278 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
279 {
280 switch (rc)
281 {
282 case 'c':
283 idCpu = ValueUnion.u32;
284 break;
285
286 case VINF_GETOPT_NOT_OPTION:
287 {
288 const char *pszEqual = strchr(ValueUnion.psz, '=');
289 if (!pszEqual)
290 return errorSyntax(USAGE_DEBUGVM, "setregisters expects input on the form 'register=value' got '%s'", ValueUnion.psz);
291 try
292 {
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();
298 }
299 catch (std::bad_alloc)
300 {
301 RTMsgError("Out of memory\n");
302 return RTEXITCODE_FAILURE;
303 }
304 break;
305 }
306
307 default:
308 return errorGetOpt(USAGE_DEBUGVM, rc, &ValueUnion);
309 }
310 }
311
312 if (!aBstrNames.size())
313 return errorSyntax(USAGE_DEBUGVM, "The setregisters sub-command takes at least one register name");
314
315 /*
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.
318 */
319 if (aBstrNames.size() == 1)
320 {
321 CHECK_ERROR2_RET(pDebugger, SetRegister(idCpu, aBstrNames[0], aBstrValues[0]), RTEXITCODE_FAILURE);
322 RTPrintf("Successfully set %ls\n", aBstrNames[0]);
323 }
324 else
325 {
326 CHECK_ERROR2_RET(pDebugger, SetRegisters(idCpu, ComSafeArrayAsInParam(aBstrNames), ComSafeArrayAsInParam(aBstrValues)), RTEXITCODE_FAILURE);
327 RTPrintf("Successfully set %u registers\n", aBstrNames.size());
328 }
329
330 return RTEXITCODE_SUCCESS;
331}
332
333/**
334 * Handles the statistics sub-command.
335 *
336 * @returns Suitable exit code.
337 * @param pArgs The handler arguments.
338 * @param pDebugger Pointer to the debugger interface.
339 */
340static RTEXITCODE handleDebugVM_Statistics(HandlerArg *pArgs, IMachineDebugger *pDebugger)
341{
342 /*
343 * Parse arguments.
344 */
345 bool fWithDescriptions = false;
346 const char *pszPattern = NULL; /* all */
347 bool fReset = false;
348
349 RTGETOPTSTATE GetState;
350 RTGETOPTUNION ValueUnion;
351 static const RTGETOPTDEF s_aOptions[] =
352 {
353 { "--descriptions", 'd', RTGETOPT_REQ_NOTHING },
354 { "--pattern", 'p', RTGETOPT_REQ_STRING },
355 { "--reset", 'r', RTGETOPT_REQ_NOTHING },
356 };
357 int rc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, 0 /*fFlags*/);
358 AssertRCReturn(rc, RTEXITCODE_FAILURE);
359
360 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
361 {
362 switch (rc)
363 {
364 case 'd':
365 fWithDescriptions = true;
366 break;
367
368 case 'p':
369 if (pszPattern)
370 return errorSyntax(USAGE_DEBUGVM, "Multiple --pattern options are not permitted");
371 pszPattern = ValueUnion.psz;
372 break;
373
374 case 'r':
375 fReset = true;
376 break;
377
378 default:
379 return errorGetOpt(USAGE_DEBUGVM, rc, &ValueUnion);
380 }
381 }
382
383 if (fReset && fWithDescriptions)
384 return errorSyntax(USAGE_DEBUGVM, "The --reset and --descriptions options does not mix");
385
386 /*
387 * Execute the order.
388 */
389 com::Bstr bstrPattern(pszPattern);
390 if (fReset)
391 CHECK_ERROR2_RET(pDebugger, ResetStats(bstrPattern.raw()), RTEXITCODE_FAILURE);
392 else
393 {
394 com::Bstr bstrStats;
395 CHECK_ERROR2_RET(pDebugger, GetStats(bstrPattern.raw(), fWithDescriptions, bstrStats.asOutParam()),
396 RTEXITCODE_FAILURE);
397 /* if (fFormatted)
398 { big mess }
399 else
400 */
401 RTPrintf("%ls\n", bstrStats.raw());
402 }
403
404 return RTEXITCODE_SUCCESS;
405}
406
407int handleDebugVM(HandlerArg *pArgs)
408{
409 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
410
411 /*
412 * The first argument is the VM name or UUID. Open a session to it.
413 */
414 if (pArgs->argc < 2)
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);
419
420 /*
421 * Get the associated console and machine debugger.
422 */
423 HRESULT rc;
424 ComPtr<IConsole> ptrConsole;
425 CHECK_ERROR(pArgs->session, COMGETTER(Console)(ptrConsole.asOutParam()));
426 if (SUCCEEDED(rc))
427 {
428 ComPtr<IMachineDebugger> ptrDebugger;
429 CHECK_ERROR(ptrConsole, COMGETTER(Debugger)(ptrDebugger.asOutParam()));
430 if (SUCCEEDED(rc))
431 {
432 /*
433 * String switch on the sub-command.
434 */
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);
452 else
453 errorSyntax(USAGE_DEBUGVM, "Invalid parameter '%s'", pArgs->argv[1]);
454 }
455 }
456
457 pArgs->session->UnlockMachine();
458
459 return rcExit;
460}
461
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