VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/MachineDebuggerImpl.cpp@ 73097

Last change on this file since 73097 was 73003, checked in by vboxsync, 6 years ago

Main: Use setErrorBoth when we've got a VBox status code handy. (The COM status codes aren't too specfic and this may help us decode error messages and provide an alternative to strstr for API clients. setErrorBoth isn't new, btw.)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 51.4 KB
Line 
1/* $Id: MachineDebuggerImpl.cpp 73003 2018-07-09 11:09:32Z vboxsync $ */
2/** @file
3 * VBox IMachineDebugger COM class implementation (VBoxC).
4 */
5
6/*
7 * Copyright (C) 2006-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#define LOG_GROUP LOG_GROUP_MAIN_MACHINEDEBUGGER
23#include "LoggingNew.h"
24
25#include "MachineDebuggerImpl.h"
26
27#include "Global.h"
28#include "ConsoleImpl.h"
29
30#include "AutoCaller.h"
31
32#include <VBox/vmm/em.h>
33#include <VBox/vmm/patm.h>
34#include <VBox/vmm/csam.h>
35#include <VBox/vmm/uvm.h>
36#include <VBox/vmm/tm.h>
37#include <VBox/vmm/hm.h>
38#include <VBox/err.h>
39#include <iprt/cpp/utils.h>
40
41
42// constructor / destructor
43/////////////////////////////////////////////////////////////////////////////
44
45MachineDebugger::MachineDebugger()
46 : mParent(NULL)
47{
48}
49
50MachineDebugger::~MachineDebugger()
51{
52}
53
54HRESULT MachineDebugger::FinalConstruct()
55{
56 unconst(mParent) = NULL;
57 return BaseFinalConstruct();
58}
59
60void MachineDebugger::FinalRelease()
61{
62 uninit();
63 BaseFinalRelease();
64}
65
66// public initializer/uninitializer for internal purposes only
67/////////////////////////////////////////////////////////////////////////////
68
69/**
70 * Initializes the machine debugger object.
71 *
72 * @returns COM result indicator
73 * @param aParent handle of our parent object
74 */
75HRESULT MachineDebugger::init(Console *aParent)
76{
77 LogFlowThisFunc(("aParent=%p\n", aParent));
78
79 ComAssertRet(aParent, E_INVALIDARG);
80
81 /* Enclose the state transition NotReady->InInit->Ready */
82 AutoInitSpan autoInitSpan(this);
83 AssertReturn(autoInitSpan.isOk(), E_FAIL);
84
85 unconst(mParent) = aParent;
86
87 for (unsigned i = 0; i < RT_ELEMENTS(maiQueuedEmExecPolicyParams); i++)
88 maiQueuedEmExecPolicyParams[i] = UINT8_MAX;
89 mSingleStepQueued = -1;
90 mRecompileUserQueued = -1;
91 mRecompileSupervisorQueued = -1;
92 mPatmEnabledQueued = -1;
93 mCsamEnabledQueued = -1;
94 mLogEnabledQueued = -1;
95 mVirtualTimeRateQueued = UINT32_MAX;
96 mFlushMode = false;
97
98 /* Confirm a successful initialization */
99 autoInitSpan.setSucceeded();
100
101 return S_OK;
102}
103
104/**
105 * Uninitializes the instance and sets the ready flag to FALSE.
106 * Called either from FinalRelease() or by the parent when it gets destroyed.
107 */
108void MachineDebugger::uninit()
109{
110 LogFlowThisFunc(("\n"));
111
112 /* Enclose the state transition Ready->InUninit->NotReady */
113 AutoUninitSpan autoUninitSpan(this);
114 if (autoUninitSpan.uninitDone())
115 return;
116
117 unconst(mParent) = NULL;
118 mFlushMode = false;
119}
120
121// IMachineDebugger properties
122/////////////////////////////////////////////////////////////////////////////
123
124/**
125 * Returns the current singlestepping flag.
126 *
127 * @returns COM status code
128 * @param aSingleStep Where to store the result.
129 */
130HRESULT MachineDebugger::getSingleStep(BOOL *aSingleStep)
131{
132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
133 Console::SafeVMPtr ptrVM(mParent);
134 HRESULT hrc = ptrVM.rc();
135 if (SUCCEEDED(hrc))
136 {
137 RT_NOREF(aSingleStep); /** @todo */
138 ReturnComNotImplemented();
139 }
140 return hrc;
141}
142
143/**
144 * Sets the singlestepping flag.
145 *
146 * @returns COM status code
147 * @param aSingleStep The new state.
148 */
149HRESULT MachineDebugger::setSingleStep(BOOL aSingleStep)
150{
151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
152 Console::SafeVMPtr ptrVM(mParent);
153 HRESULT hrc = ptrVM.rc();
154 if (SUCCEEDED(hrc))
155 {
156 NOREF(aSingleStep); /** @todo */
157 ReturnComNotImplemented();
158 }
159 return hrc;
160}
161
162/**
163 * Internal worker for getting an EM executable policy setting.
164 *
165 * @returns COM status code.
166 * @param enmPolicy Which EM policy.
167 * @param pfEnforced Where to return the policy setting.
168 */
169HRESULT MachineDebugger::i_getEmExecPolicyProperty(EMEXECPOLICY enmPolicy, BOOL *pfEnforced)
170{
171 CheckComArgOutPointerValid(pfEnforced);
172
173 AutoCaller autoCaller(this);
174 HRESULT hrc = autoCaller.rc();
175 if (SUCCEEDED(hrc))
176 {
177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
178 if (i_queueSettings())
179 *pfEnforced = maiQueuedEmExecPolicyParams[enmPolicy] == 1;
180 else
181 {
182 bool fEnforced = false;
183 Console::SafeVMPtrQuiet ptrVM(mParent);
184 hrc = ptrVM.rc();
185 if (SUCCEEDED(hrc))
186 EMR3QueryExecutionPolicy(ptrVM.rawUVM(), enmPolicy, &fEnforced);
187 *pfEnforced = fEnforced;
188 }
189 }
190 return hrc;
191}
192
193/**
194 * Internal worker for setting an EM executable policy.
195 *
196 * @returns COM status code.
197 * @param enmPolicy Which policy to change.
198 * @param fEnforce Whether to enforce the policy or not.
199 */
200HRESULT MachineDebugger::i_setEmExecPolicyProperty(EMEXECPOLICY enmPolicy, BOOL fEnforce)
201{
202 AutoCaller autoCaller(this);
203 HRESULT hrc = autoCaller.rc();
204 if (SUCCEEDED(hrc))
205 {
206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
207 if (i_queueSettings())
208 maiQueuedEmExecPolicyParams[enmPolicy] = fEnforce ? 1 : 0;
209 else
210 {
211 Console::SafeVMPtrQuiet ptrVM(mParent);
212 hrc = ptrVM.rc();
213 if (SUCCEEDED(hrc))
214 {
215 int vrc = EMR3SetExecutionPolicy(ptrVM.rawUVM(), enmPolicy, fEnforce != FALSE);
216 if (RT_FAILURE(vrc))
217 hrc = setErrorBoth(VBOX_E_VM_ERROR, vrc, tr("EMR3SetExecutionPolicy failed with %Rrc"), vrc);
218 }
219 }
220 }
221 return hrc;
222}
223
224/**
225 * Returns the current recompile user mode code flag.
226 *
227 * @returns COM status code
228 * @param aRecompileUser address of result variable
229 */
230HRESULT MachineDebugger::getRecompileUser(BOOL *aRecompileUser)
231{
232 return i_getEmExecPolicyProperty(EMEXECPOLICY_RECOMPILE_RING3, aRecompileUser);
233}
234
235/**
236 * Sets the recompile user mode code flag.
237 *
238 * @returns COM status
239 * @param aRecompileUser new user mode code recompile flag.
240 */
241HRESULT MachineDebugger::setRecompileUser(BOOL aRecompileUser)
242{
243 LogFlowThisFunc(("enable=%d\n", aRecompileUser));
244 return i_setEmExecPolicyProperty(EMEXECPOLICY_RECOMPILE_RING3, aRecompileUser);
245}
246
247/**
248 * Returns the current recompile supervisor code flag.
249 *
250 * @returns COM status code
251 * @param aRecompileSupervisor address of result variable
252 */
253HRESULT MachineDebugger::getRecompileSupervisor(BOOL *aRecompileSupervisor)
254{
255 return i_getEmExecPolicyProperty(EMEXECPOLICY_RECOMPILE_RING0, aRecompileSupervisor);
256}
257
258/**
259 * Sets the new recompile supervisor code flag.
260 *
261 * @returns COM status code
262 * @param aRecompileSupervisor new recompile supervisor code flag
263 */
264HRESULT MachineDebugger::setRecompileSupervisor(BOOL aRecompileSupervisor)
265{
266 LogFlowThisFunc(("enable=%d\n", aRecompileSupervisor));
267 return i_setEmExecPolicyProperty(EMEXECPOLICY_RECOMPILE_RING0, aRecompileSupervisor);
268}
269
270/**
271 * Returns the current execute-all-in-IEM setting.
272 *
273 * @returns COM status code
274 * @param aExecuteAllInIEM Address of result variable.
275 */
276HRESULT MachineDebugger::getExecuteAllInIEM(BOOL *aExecuteAllInIEM)
277{
278 return i_getEmExecPolicyProperty(EMEXECPOLICY_IEM_ALL, aExecuteAllInIEM);
279}
280
281/**
282 * Changes the execute-all-in-IEM setting.
283 *
284 * @returns COM status code
285 * @param aExecuteAllInIEM New setting.
286 */
287HRESULT MachineDebugger::setExecuteAllInIEM(BOOL aExecuteAllInIEM)
288{
289 LogFlowThisFunc(("enable=%d\n", aExecuteAllInIEM));
290 return i_setEmExecPolicyProperty(EMEXECPOLICY_IEM_ALL, aExecuteAllInIEM);
291}
292
293/**
294 * Returns the current patch manager enabled flag.
295 *
296 * @returns COM status code
297 * @param aPATMEnabled address of result variable
298 */
299HRESULT MachineDebugger::getPATMEnabled(BOOL *aPATMEnabled)
300{
301#ifdef VBOX_WITH_RAW_MODE
302 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
303
304 Console::SafeVMPtrQuiet ptrVM(mParent);
305 if (ptrVM.isOk())
306 *aPATMEnabled = PATMR3IsEnabled(ptrVM.rawUVM());
307 else
308#endif
309 *aPATMEnabled = false;
310
311 return S_OK;
312}
313
314/**
315 * Set the new patch manager enabled flag.
316 *
317 * @returns COM status code
318 * @param aPATMEnabled new patch manager enabled flag
319 */
320HRESULT MachineDebugger::setPATMEnabled(BOOL aPATMEnabled)
321{
322 LogFlowThisFunc(("enable=%d\n", aPATMEnabled));
323
324#ifdef VBOX_WITH_RAW_MODE
325 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
326
327 if (i_queueSettings())
328 {
329 // queue the request
330 mPatmEnabledQueued = aPATMEnabled;
331 return S_OK;
332 }
333
334 Console::SafeVMPtr ptrVM(mParent);
335 if (FAILED(ptrVM.rc()))
336 return ptrVM.rc();
337
338 int vrc = PATMR3AllowPatching(ptrVM.rawUVM(), RT_BOOL(aPATMEnabled));
339 if (RT_FAILURE(vrc))
340 return setErrorBoth(VBOX_E_VM_ERROR, vrc, tr("PATMR3AllowPatching returned %Rrc"), vrc);
341
342#else /* !VBOX_WITH_RAW_MODE */
343 if (aPATMEnabled)
344 return setErrorBoth(VBOX_E_VM_ERROR, VERR_RAW_MODE_NOT_SUPPORTED, tr("PATM not present"), VERR_NOT_SUPPORTED);
345#endif /* !VBOX_WITH_RAW_MODE */
346 return S_OK;
347}
348
349/**
350 * Returns the current code scanner enabled flag.
351 *
352 * @returns COM status code
353 * @param aCSAMEnabled address of result variable
354 */
355HRESULT MachineDebugger::getCSAMEnabled(BOOL *aCSAMEnabled)
356{
357#ifdef VBOX_WITH_RAW_MODE
358 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
359
360 Console::SafeVMPtrQuiet ptrVM(mParent);
361
362 if (ptrVM.isOk())
363 *aCSAMEnabled = CSAMR3IsEnabled(ptrVM.rawUVM());
364 else
365#endif /* VBOX_WITH_RAW_MODE */
366 *aCSAMEnabled = false;
367
368 return S_OK;
369}
370
371/**
372 * Sets the new code scanner enabled flag.
373 *
374 * @returns COM status code
375 * @param aCSAMEnabled new code scanner enabled flag
376 */
377HRESULT MachineDebugger::setCSAMEnabled(BOOL aCSAMEnabled)
378{
379 LogFlowThisFunc(("enable=%d\n", aCSAMEnabled));
380
381#ifdef VBOX_WITH_RAW_MODE
382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
383
384 if (i_queueSettings())
385 {
386 // queue the request
387 mCsamEnabledQueued = aCSAMEnabled;
388 return S_OK;
389 }
390
391 Console::SafeVMPtr ptrVM(mParent);
392 if (FAILED(ptrVM.rc()))
393 return ptrVM.rc();
394
395 int vrc = CSAMR3SetScanningEnabled(ptrVM.rawUVM(), aCSAMEnabled != FALSE);
396 if (RT_FAILURE(vrc))
397 return setErrorBoth(VBOX_E_VM_ERROR, vrc, tr("CSAMR3SetScanningEnabled returned %Rrc"), vrc);
398
399#else /* !VBOX_WITH_RAW_MODE */
400 if (aCSAMEnabled)
401 return setErrorBoth(VBOX_E_VM_ERROR, VERR_RAW_MODE_NOT_SUPPORTED, tr("CASM not present"));
402#endif /* !VBOX_WITH_RAW_MODE */
403 return S_OK;
404}
405
406/**
407 * Returns the log enabled / disabled status.
408 *
409 * @returns COM status code
410 * @param aLogEnabled address of result variable
411 */
412HRESULT MachineDebugger::getLogEnabled(BOOL *aLogEnabled)
413{
414#ifdef LOG_ENABLED
415 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
416
417 const PRTLOGGER pLogInstance = RTLogDefaultInstance();
418 *aLogEnabled = pLogInstance && !(pLogInstance->fFlags & RTLOGFLAGS_DISABLED);
419#else
420 *aLogEnabled = false;
421#endif
422
423 return S_OK;
424}
425
426/**
427 * Enables or disables logging.
428 *
429 * @returns COM status code
430 * @param aLogEnabled The new code log state.
431 */
432HRESULT MachineDebugger::setLogEnabled(BOOL aLogEnabled)
433{
434 LogFlowThisFunc(("aLogEnabled=%d\n", aLogEnabled));
435
436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
437
438 if (i_queueSettings())
439 {
440 // queue the request
441 mLogEnabledQueued = aLogEnabled;
442 return S_OK;
443 }
444
445 Console::SafeVMPtr ptrVM(mParent);
446 if (FAILED(ptrVM.rc())) return ptrVM.rc();
447
448#ifdef LOG_ENABLED
449 int vrc = DBGFR3LogModifyFlags(ptrVM.rawUVM(), aLogEnabled ? "enabled" : "disabled");
450 if (RT_FAILURE(vrc))
451 {
452 /** @todo handle error code. */
453 }
454#endif
455
456 return S_OK;
457}
458
459HRESULT MachineDebugger::i_logStringProps(PRTLOGGER pLogger, PFNLOGGETSTR pfnLogGetStr,
460 const char *pszLogGetStr, Utf8Str *pstrSettings)
461{
462 /* Make sure the VM is powered up. */
463 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
464 Console::SafeVMPtr ptrVM(mParent);
465 HRESULT hrc = ptrVM.rc();
466 if (FAILED(hrc))
467 return hrc;
468
469 /* Make sure we've got a logger. */
470 if (!pLogger)
471 {
472 *pstrSettings = "";
473 return S_OK;
474 }
475
476 /* Do the job. */
477 size_t cbBuf = _1K;
478 for (;;)
479 {
480 char *pszBuf = (char *)RTMemTmpAlloc(cbBuf);
481 AssertReturn(pszBuf, E_OUTOFMEMORY);
482 int vrc = pstrSettings->reserveNoThrow(cbBuf);
483 if (RT_SUCCESS(vrc))
484 {
485 vrc = pfnLogGetStr(pLogger, pstrSettings->mutableRaw(), cbBuf);
486 if (RT_SUCCESS(vrc))
487 {
488 pstrSettings->jolt();
489 return S_OK;
490 }
491 *pstrSettings = "";
492 AssertReturn(vrc == VERR_BUFFER_OVERFLOW,
493 setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("%s returned %Rrc"), pszLogGetStr, vrc));
494 }
495 else
496 return E_OUTOFMEMORY;
497
498 /* try again with a bigger buffer. */
499 cbBuf *= 2;
500 AssertReturn(cbBuf <= _256K, setError(E_FAIL, tr("%s returns too much data"), pszLogGetStr));
501 }
502}
503
504HRESULT MachineDebugger::getLogDbgFlags(com::Utf8Str &aLogDbgFlags)
505{
506 return i_logStringProps(RTLogGetDefaultInstance(), RTLogGetFlags, "RTGetFlags", &aLogDbgFlags);
507}
508
509HRESULT MachineDebugger::getLogDbgGroups(com::Utf8Str &aLogDbgGroups)
510{
511 return i_logStringProps(RTLogGetDefaultInstance(), RTLogGetGroupSettings, "RTLogGetGroupSettings", &aLogDbgGroups);
512}
513
514HRESULT MachineDebugger::getLogDbgDestinations(com::Utf8Str &aLogDbgDestinations)
515{
516 return i_logStringProps(RTLogGetDefaultInstance(), RTLogGetDestinations, "RTLogGetDestinations", &aLogDbgDestinations);
517}
518
519HRESULT MachineDebugger::getLogRelFlags(com::Utf8Str &aLogRelFlags)
520{
521 return i_logStringProps(RTLogRelGetDefaultInstance(), RTLogGetFlags, "RTGetFlags", &aLogRelFlags);
522}
523
524HRESULT MachineDebugger::getLogRelGroups(com::Utf8Str &aLogRelGroups)
525{
526 return i_logStringProps(RTLogRelGetDefaultInstance(), RTLogGetGroupSettings, "RTLogGetGroupSettings", &aLogRelGroups);
527}
528
529HRESULT MachineDebugger::getLogRelDestinations(com::Utf8Str &aLogRelDestinations)
530{
531 return i_logStringProps(RTLogRelGetDefaultInstance(), RTLogGetDestinations, "RTLogGetDestinations", &aLogRelDestinations);
532}
533
534/**
535 * Return the main execution engine of the VM.
536 *
537 * @returns COM status code
538 * @param apenmEngine Address of the result variable.
539 */
540HRESULT MachineDebugger::getExecutionEngine(VMExecutionEngine_T *apenmEngine)
541{
542 *apenmEngine = VMExecutionEngine_NotSet;
543
544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
545 Console::SafeVMPtrQuiet ptrVM(mParent);
546 if (ptrVM.isOk())
547 {
548 uint8_t bEngine = UINT8_MAX;
549 int rc = EMR3QueryMainExecutionEngine(ptrVM.rawUVM(), &bEngine);
550 if (RT_SUCCESS(rc))
551 switch (bEngine)
552 {
553 case VM_EXEC_ENGINE_NOT_SET: *apenmEngine = VMExecutionEngine_NotSet; break;
554 case VM_EXEC_ENGINE_RAW_MODE: *apenmEngine = VMExecutionEngine_RawMode; break;
555 case VM_EXEC_ENGINE_HW_VIRT: *apenmEngine = VMExecutionEngine_HwVirt; break;
556 case VM_EXEC_ENGINE_NATIVE_API: *apenmEngine = VMExecutionEngine_NativeApi; break;
557 default: AssertMsgFailed(("bEngine=%d\n", bEngine));
558 }
559 }
560
561 return S_OK;
562}
563
564/**
565 * Returns the current hardware virtualization flag.
566 *
567 * @returns COM status code
568 * @param aHWVirtExEnabled address of result variable
569 */
570HRESULT MachineDebugger::getHWVirtExEnabled(BOOL *aHWVirtExEnabled)
571{
572 *aHWVirtExEnabled = false;
573
574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
575 Console::SafeVMPtrQuiet ptrVM(mParent);
576 if (ptrVM.isOk())
577 {
578 uint8_t bEngine = UINT8_MAX;
579 int rc = EMR3QueryMainExecutionEngine(ptrVM.rawUVM(), &bEngine);
580 *aHWVirtExEnabled = RT_SUCCESS(rc) && bEngine == VM_EXEC_ENGINE_HW_VIRT;
581 }
582
583 return S_OK;
584}
585
586/**
587 * Returns the current nested paging flag.
588 *
589 * @returns COM status code
590 * @param aHWVirtExNestedPagingEnabled address of result variable
591 */
592HRESULT MachineDebugger::getHWVirtExNestedPagingEnabled(BOOL *aHWVirtExNestedPagingEnabled)
593{
594 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
595
596 Console::SafeVMPtrQuiet ptrVM(mParent);
597
598 if (ptrVM.isOk())
599 *aHWVirtExNestedPagingEnabled = HMR3IsNestedPagingActive(ptrVM.rawUVM());
600 else
601 *aHWVirtExNestedPagingEnabled = false;
602
603 return S_OK;
604}
605
606/**
607 * Returns the current VPID flag.
608 *
609 * @returns COM status code
610 * @param aHWVirtExVPIDEnabled address of result variable
611 */
612HRESULT MachineDebugger::getHWVirtExVPIDEnabled(BOOL *aHWVirtExVPIDEnabled)
613{
614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
615
616 Console::SafeVMPtrQuiet ptrVM(mParent);
617
618 if (ptrVM.isOk())
619 *aHWVirtExVPIDEnabled = HMR3IsVpidActive(ptrVM.rawUVM());
620 else
621 *aHWVirtExVPIDEnabled = false;
622
623 return S_OK;
624}
625
626/**
627 * Returns the current unrestricted execution setting.
628 *
629 * @returns COM status code
630 * @param aHWVirtExUXEnabled address of result variable
631 */
632HRESULT MachineDebugger::getHWVirtExUXEnabled(BOOL *aHWVirtExUXEnabled)
633{
634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
635
636 Console::SafeVMPtrQuiet ptrVM(mParent);
637
638 if (ptrVM.isOk())
639 *aHWVirtExUXEnabled = HMR3IsUXActive(ptrVM.rawUVM());
640 else
641 *aHWVirtExUXEnabled = false;
642
643 return S_OK;
644}
645
646HRESULT MachineDebugger::getOSName(com::Utf8Str &aOSName)
647{
648 LogFlowThisFunc(("\n"));
649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
650 Console::SafeVMPtr ptrVM(mParent);
651 HRESULT hrc = ptrVM.rc();
652 if (SUCCEEDED(hrc))
653 {
654 /*
655 * Do the job and try convert the name.
656 */
657 char szName[64];
658 int vrc = DBGFR3OSQueryNameAndVersion(ptrVM.rawUVM(), szName, sizeof(szName), NULL, 0);
659 if (RT_SUCCESS(vrc))
660 {
661 try
662 {
663 Bstr bstrName(szName);
664 aOSName = Utf8Str(bstrName);
665 }
666 catch (std::bad_alloc)
667 {
668 hrc = E_OUTOFMEMORY;
669 }
670 }
671 else
672 hrc = setErrorBoth(VBOX_E_VM_ERROR, vrc, tr("DBGFR3OSQueryNameAndVersion failed with %Rrc"), vrc);
673 }
674 return hrc;
675}
676
677HRESULT MachineDebugger::getOSVersion(com::Utf8Str &aOSVersion)
678{
679 LogFlowThisFunc(("\n"));
680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
681 Console::SafeVMPtr ptrVM(mParent);
682 HRESULT hrc = ptrVM.rc();
683 if (SUCCEEDED(hrc))
684 {
685 /*
686 * Do the job and try convert the name.
687 */
688 char szVersion[256];
689 int vrc = DBGFR3OSQueryNameAndVersion(ptrVM.rawUVM(), NULL, 0, szVersion, sizeof(szVersion));
690 if (RT_SUCCESS(vrc))
691 {
692 try
693 {
694 Bstr bstrVersion(szVersion);
695 aOSVersion = Utf8Str(bstrVersion);
696 }
697 catch (std::bad_alloc)
698 {
699 hrc = E_OUTOFMEMORY;
700 }
701 }
702 else
703 hrc = setErrorBoth(VBOX_E_VM_ERROR, vrc, tr("DBGFR3OSQueryNameAndVersion failed with %Rrc"), vrc);
704 }
705 return hrc;
706}
707
708/**
709 * Returns the current PAE flag.
710 *
711 * @returns COM status code
712 * @param aPAEEnabled address of result variable.
713 */
714HRESULT MachineDebugger::getPAEEnabled(BOOL *aPAEEnabled)
715{
716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
717
718 Console::SafeVMPtrQuiet ptrVM(mParent);
719
720 if (ptrVM.isOk())
721 {
722 uint32_t cr4;
723 int rc = DBGFR3RegCpuQueryU32(ptrVM.rawUVM(), 0 /*idCpu*/, DBGFREG_CR4, &cr4); AssertRC(rc);
724 *aPAEEnabled = RT_BOOL(cr4 & X86_CR4_PAE);
725 }
726 else
727 *aPAEEnabled = false;
728
729 return S_OK;
730}
731
732/**
733 * Returns the current virtual time rate.
734 *
735 * @returns COM status code.
736 * @param aVirtualTimeRate Where to store the rate.
737 */
738HRESULT MachineDebugger::getVirtualTimeRate(ULONG *aVirtualTimeRate)
739{
740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
741
742 Console::SafeVMPtr ptrVM(mParent);
743 HRESULT hrc = ptrVM.rc();
744 if (SUCCEEDED(hrc))
745 *aVirtualTimeRate = TMR3GetWarpDrive(ptrVM.rawUVM());
746
747 return hrc;
748}
749
750/**
751 * Set the virtual time rate.
752 *
753 * @returns COM status code.
754 * @param aVirtualTimeRate The new rate.
755 */
756HRESULT MachineDebugger::setVirtualTimeRate(ULONG aVirtualTimeRate)
757{
758 HRESULT hrc = S_OK;
759
760 if (aVirtualTimeRate < 2 || aVirtualTimeRate > 20000)
761 return setError(E_INVALIDARG, tr("%u is out of range [2..20000]"), aVirtualTimeRate);
762
763 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
764 if (i_queueSettings())
765 mVirtualTimeRateQueued = aVirtualTimeRate;
766 else
767 {
768 Console::SafeVMPtr ptrVM(mParent);
769 hrc = ptrVM.rc();
770 if (SUCCEEDED(hrc))
771 {
772 int vrc = TMR3SetWarpDrive(ptrVM.rawUVM(), aVirtualTimeRate);
773 if (RT_FAILURE(vrc))
774 hrc = setErrorBoth(VBOX_E_VM_ERROR, vrc, tr("TMR3SetWarpDrive(, %u) failed with rc=%Rrc"), aVirtualTimeRate, vrc);
775 }
776 }
777
778 return hrc;
779}
780
781/**
782 * Hack for getting the user mode VM handle (UVM).
783 *
784 * This is only temporary (promise) while prototyping the debugger.
785 *
786 * @returns COM status code
787 * @param aVM Where to store the vm handle. Since there is no
788 * uintptr_t in COM, we're using the max integer.
789 * (No, ULONG is not pointer sized!)
790 * @remarks The returned handle must be passed to VMR3ReleaseUVM()!
791 * @remarks Prior to 4.3 this returned PVM.
792 */
793HRESULT MachineDebugger::getVM(LONG64 *aVM)
794{
795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
796
797 Console::SafeVMPtr ptrVM(mParent);
798 HRESULT hrc = ptrVM.rc();
799 if (SUCCEEDED(hrc))
800 {
801 VMR3RetainUVM(ptrVM.rawUVM());
802 *aVM = (intptr_t)ptrVM.rawUVM();
803 }
804
805 /*
806 * Note! ptrVM protection provided by SafeVMPtr is no long effective
807 * after we return from this method.
808 */
809 return hrc;
810}
811
812/**
813 * Get the VM uptime in milliseconds.
814 *
815 * @returns COM status code
816 * @param aUptime Where to store the uptime.
817 */
818HRESULT MachineDebugger::getUptime(LONG64 *aUptime)
819{
820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
821
822 Console::SafeVMPtr ptrVM(mParent);
823 HRESULT hrc = ptrVM.rc();
824 if (SUCCEEDED(hrc))
825 *aUptime = (int64_t)TMR3TimeVirtGetMilli(ptrVM.rawUVM());
826
827 return hrc;
828}
829
830// IMachineDebugger methods
831/////////////////////////////////////////////////////////////////////////////
832
833HRESULT MachineDebugger::dumpGuestCore(const com::Utf8Str &aFilename, const com::Utf8Str &aCompression)
834{
835 if (aCompression.length())
836 return setError(E_INVALIDARG, tr("The compression parameter must be empty"));
837
838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
839 Console::SafeVMPtr ptrVM(mParent);
840 HRESULT hrc = ptrVM.rc();
841 if (SUCCEEDED(hrc))
842 {
843 int vrc = DBGFR3CoreWrite(ptrVM.rawUVM(), aFilename.c_str(), false /*fReplaceFile*/);
844 if (RT_SUCCESS(vrc))
845 hrc = S_OK;
846 else
847 hrc = setErrorBoth(E_FAIL, vrc, tr("DBGFR3CoreWrite failed with %Rrc"), vrc);
848 }
849
850 return hrc;
851}
852
853HRESULT MachineDebugger::dumpHostProcessCore(const com::Utf8Str &aFilename, const com::Utf8Str &aCompression)
854{
855 RT_NOREF(aFilename, aCompression);
856 ReturnComNotImplemented();
857}
858
859/**
860 * Debug info string buffer formatter.
861 */
862typedef struct MACHINEDEBUGGERINOFHLP
863{
864 /** The core info helper structure. */
865 DBGFINFOHLP Core;
866 /** Pointer to the buffer. */
867 char *pszBuf;
868 /** The size of the buffer. */
869 size_t cbBuf;
870 /** The offset into the buffer */
871 size_t offBuf;
872 /** Indicates an out-of-memory condition. */
873 bool fOutOfMemory;
874} MACHINEDEBUGGERINOFHLP;
875/** Pointer to a Debug info string buffer formatter. */
876typedef MACHINEDEBUGGERINOFHLP *PMACHINEDEBUGGERINOFHLP;
877
878
879/**
880 * @callback_method_impl{FNRTSTROUTPUT}
881 */
882static DECLCALLBACK(size_t) MachineDebuggerInfoOutput(void *pvArg, const char *pachChars, size_t cbChars)
883{
884 PMACHINEDEBUGGERINOFHLP pHlp = (PMACHINEDEBUGGERINOFHLP)pvArg;
885
886 /*
887 * Grow the buffer if required.
888 */
889 size_t const cbRequired = cbChars + pHlp->offBuf + 1;
890 if (cbRequired > pHlp->cbBuf)
891 {
892 if (RT_UNLIKELY(pHlp->fOutOfMemory))
893 return 0;
894
895 size_t cbBufNew = pHlp->cbBuf * 2;
896 if (cbRequired > cbBufNew)
897 cbBufNew = RT_ALIGN_Z(cbRequired, 256);
898 void *pvBufNew = RTMemRealloc(pHlp->pszBuf, cbBufNew);
899 if (RT_UNLIKELY(!pvBufNew))
900 {
901 pHlp->fOutOfMemory = true;
902 RTMemFree(pHlp->pszBuf);
903 pHlp->pszBuf = NULL;
904 pHlp->cbBuf = 0;
905 pHlp->offBuf = 0;
906 return 0;
907 }
908
909 pHlp->pszBuf = (char *)pvBufNew;
910 pHlp->cbBuf = cbBufNew;
911 }
912
913 /*
914 * Copy the bytes into the buffer and terminate it.
915 */
916 if (cbChars)
917 {
918 memcpy(&pHlp->pszBuf[pHlp->offBuf], pachChars, cbChars);
919 pHlp->offBuf += cbChars;
920 }
921 pHlp->pszBuf[pHlp->offBuf] = '\0';
922 Assert(pHlp->offBuf < pHlp->cbBuf);
923 return cbChars;
924}
925
926/**
927 * @interface_method_impl{DBGFINFOHLP,pfnPrintfV}
928 */
929static DECLCALLBACK(void) MachineDebuggerInfoPrintfV(PCDBGFINFOHLP pHlp, const char *pszFormat, va_list args)
930{
931 RTStrFormatV(MachineDebuggerInfoOutput, (void *)pHlp, NULL, NULL, pszFormat, args);
932}
933
934/**
935 * @interface_method_impl{DBGFINFOHLP,pfnPrintf}
936 */
937static DECLCALLBACK(void) MachineDebuggerInfoPrintf(PCDBGFINFOHLP pHlp, const char *pszFormat, ...)
938{
939 va_list va;
940 va_start(va, pszFormat);
941 MachineDebuggerInfoPrintfV(pHlp, pszFormat, va);
942 va_end(va);
943}
944
945/**
946 * Initializes the debug info string buffer formatter
947 *
948 * @param pHlp The help structure to init.
949 */
950static void MachineDebuggerInfoInit(PMACHINEDEBUGGERINOFHLP pHlp)
951{
952 pHlp->Core.pfnPrintf = MachineDebuggerInfoPrintf;
953 pHlp->Core.pfnPrintfV = MachineDebuggerInfoPrintfV;
954 pHlp->pszBuf = NULL;
955 pHlp->cbBuf = 0;
956 pHlp->offBuf = 0;
957 pHlp->fOutOfMemory = false;
958}
959
960/**
961 * Deletes the debug info string buffer formatter.
962 * @param pHlp The helper structure to delete.
963 */
964static void MachineDebuggerInfoDelete(PMACHINEDEBUGGERINOFHLP pHlp)
965{
966 RTMemFree(pHlp->pszBuf);
967 pHlp->pszBuf = NULL;
968}
969
970HRESULT MachineDebugger::info(const com::Utf8Str &aName, const com::Utf8Str &aArgs, com::Utf8Str &aInfo)
971{
972 LogFlowThisFunc(("\n"));
973
974 /*
975 * Do the autocaller and lock bits.
976 */
977 AutoCaller autoCaller(this);
978 HRESULT hrc = autoCaller.rc();
979 if (SUCCEEDED(hrc))
980 {
981 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
982 Console::SafeVMPtr ptrVM(mParent);
983 hrc = ptrVM.rc();
984 if (SUCCEEDED(hrc))
985 {
986 /*
987 * Create a helper and call DBGFR3Info.
988 */
989 MACHINEDEBUGGERINOFHLP Hlp;
990 MachineDebuggerInfoInit(&Hlp);
991 int vrc = DBGFR3Info(ptrVM.rawUVM(), aName.c_str(), aArgs.c_str(), &Hlp.Core);
992 if (RT_SUCCESS(vrc))
993 {
994 if (!Hlp.fOutOfMemory)
995 {
996 /*
997 * Convert the info string, watching out for allocation errors.
998 */
999 try
1000 {
1001 Bstr bstrInfo(Hlp.pszBuf);
1002 aInfo = bstrInfo;
1003 }
1004 catch (std::bad_alloc)
1005 {
1006 hrc = E_OUTOFMEMORY;
1007 }
1008 }
1009 else
1010 hrc = E_OUTOFMEMORY;
1011 }
1012 else
1013 hrc = setErrorBoth(VBOX_E_VM_ERROR, vrc, tr("DBGFR3Info failed with %Rrc"), vrc);
1014 MachineDebuggerInfoDelete(&Hlp);
1015 }
1016 }
1017 return hrc;
1018}
1019
1020HRESULT MachineDebugger::injectNMI()
1021{
1022 LogFlowThisFunc(("\n"));
1023
1024 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1025 Console::SafeVMPtr ptrVM(mParent);
1026 HRESULT hrc = ptrVM.rc();
1027 if (SUCCEEDED(hrc))
1028 {
1029 int vrc = DBGFR3InjectNMI(ptrVM.rawUVM(), 0);
1030 if (RT_SUCCESS(vrc))
1031 hrc = S_OK;
1032 else
1033 hrc = setErrorBoth(E_FAIL, vrc, tr("DBGFR3InjectNMI failed with %Rrc"), vrc);
1034 }
1035 return hrc;
1036}
1037
1038HRESULT MachineDebugger::modifyLogFlags(const com::Utf8Str &aSettings)
1039{
1040 LogFlowThisFunc(("aSettings=%s\n", aSettings.c_str()));
1041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1042 Console::SafeVMPtr ptrVM(mParent);
1043 HRESULT hrc = ptrVM.rc();
1044 if (SUCCEEDED(hrc))
1045 {
1046 int vrc = DBGFR3LogModifyFlags(ptrVM.rawUVM(), aSettings.c_str());
1047 if (RT_SUCCESS(vrc))
1048 hrc = S_OK;
1049 else
1050 hrc = setErrorBoth(E_FAIL, vrc, tr("DBGFR3LogModifyFlags failed with %Rrc"), vrc);
1051 }
1052 return hrc;
1053}
1054
1055HRESULT MachineDebugger::modifyLogGroups(const com::Utf8Str &aSettings)
1056{
1057 LogFlowThisFunc(("aSettings=%s\n", aSettings.c_str()));
1058 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1059 Console::SafeVMPtr ptrVM(mParent);
1060 HRESULT hrc = ptrVM.rc();
1061 if (SUCCEEDED(hrc))
1062 {
1063 int vrc = DBGFR3LogModifyGroups(ptrVM.rawUVM(), aSettings.c_str());
1064 if (RT_SUCCESS(vrc))
1065 hrc = S_OK;
1066 else
1067 hrc = setErrorBoth(E_FAIL, vrc, tr("DBGFR3LogModifyGroups failed with %Rrc"), vrc);
1068 }
1069 return hrc;
1070}
1071
1072HRESULT MachineDebugger::modifyLogDestinations(const com::Utf8Str &aSettings)
1073{
1074 LogFlowThisFunc(("aSettings=%s\n", aSettings.c_str()));
1075 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1076 Console::SafeVMPtr ptrVM(mParent);
1077 HRESULT hrc = ptrVM.rc();
1078 if (SUCCEEDED(hrc))
1079 {
1080 int vrc = DBGFR3LogModifyDestinations(ptrVM.rawUVM(), aSettings.c_str());
1081 if (RT_SUCCESS(vrc))
1082 hrc = S_OK;
1083 else
1084 hrc = setErrorBoth(E_FAIL, vrc, tr("DBGFR3LogModifyDestinations failed with %Rrc"), vrc);
1085 }
1086 return hrc;
1087}
1088
1089HRESULT MachineDebugger::readPhysicalMemory(LONG64 aAddress, ULONG aSize, std::vector<BYTE> &aBytes)
1090{
1091 RT_NOREF(aAddress, aSize, aBytes);
1092 ReturnComNotImplemented();
1093}
1094
1095HRESULT MachineDebugger::writePhysicalMemory(LONG64 aAddress, ULONG aSize, const std::vector<BYTE> &aBytes)
1096{
1097 RT_NOREF(aAddress, aSize, aBytes);
1098 ReturnComNotImplemented();
1099}
1100
1101HRESULT MachineDebugger::readVirtualMemory(ULONG aCpuId, LONG64 aAddress, ULONG aSize, std::vector<BYTE> &aBytes)
1102{
1103 RT_NOREF(aCpuId, aAddress, aSize, aBytes);
1104 ReturnComNotImplemented();
1105}
1106
1107HRESULT MachineDebugger::writeVirtualMemory(ULONG aCpuId, LONG64 aAddress, ULONG aSize, const std::vector<BYTE> &aBytes)
1108{
1109 RT_NOREF(aCpuId, aAddress, aSize, aBytes);
1110 ReturnComNotImplemented();
1111}
1112
1113HRESULT MachineDebugger::loadPlugIn(const com::Utf8Str &aName, com::Utf8Str &aPlugInName)
1114{
1115 /*
1116 * Lock the debugger and get the VM pointer
1117 */
1118 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1119 Console::SafeVMPtr ptrVM(mParent);
1120 HRESULT hrc = ptrVM.rc();
1121 if (SUCCEEDED(hrc))
1122 {
1123 /*
1124 * Do the job and try convert the name.
1125 */
1126 if (aName.equals("all"))
1127 {
1128 DBGFR3PlugInLoadAll(ptrVM.rawUVM());
1129 try
1130 {
1131 aPlugInName = "all";
1132 hrc = S_OK;
1133 }
1134 catch (std::bad_alloc)
1135 {
1136 hrc = E_OUTOFMEMORY;
1137 }
1138 }
1139 else
1140 {
1141 RTERRINFOSTATIC ErrInfo;
1142 char szName[80];
1143 int vrc = DBGFR3PlugInLoad(ptrVM.rawUVM(), aName.c_str(), szName, sizeof(szName), RTErrInfoInitStatic(&ErrInfo));
1144 if (RT_SUCCESS(vrc))
1145 {
1146 try
1147 {
1148 aPlugInName = szName;
1149 hrc = S_OK;
1150 }
1151 catch (std::bad_alloc)
1152 {
1153 hrc = E_OUTOFMEMORY;
1154 }
1155 }
1156 else
1157 hrc = setErrorVrc(vrc, "%s", ErrInfo.szMsg);
1158 }
1159 }
1160 return hrc;
1161
1162}
1163
1164HRESULT MachineDebugger::unloadPlugIn(const com::Utf8Str &aName)
1165{
1166 /*
1167 * Lock the debugger and get the VM pointer
1168 */
1169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1170 Console::SafeVMPtr ptrVM(mParent);
1171 HRESULT hrc = ptrVM.rc();
1172 if (SUCCEEDED(hrc))
1173 {
1174 /*
1175 * Do the job and try convert the name.
1176 */
1177 if (aName.equals("all"))
1178 {
1179 DBGFR3PlugInUnloadAll(ptrVM.rawUVM());
1180 hrc = S_OK;
1181 }
1182 else
1183 {
1184 int vrc = DBGFR3PlugInUnload(ptrVM.rawUVM(), aName.c_str());
1185 if (RT_SUCCESS(vrc))
1186 hrc = S_OK;
1187 else if (vrc == VERR_NOT_FOUND)
1188 hrc = setErrorBoth(E_FAIL, vrc, "Plug-in '%s' was not found", aName.c_str());
1189 else
1190 hrc = setErrorVrc(vrc, "Error unloading '%s': %Rrc", aName.c_str(), vrc);
1191 }
1192 }
1193 return hrc;
1194
1195}
1196
1197HRESULT MachineDebugger::detectOS(com::Utf8Str &aOs)
1198{
1199 LogFlowThisFunc(("\n"));
1200
1201 /*
1202 * Lock the debugger and get the VM pointer
1203 */
1204 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1205 Console::SafeVMPtr ptrVM(mParent);
1206 HRESULT hrc = ptrVM.rc();
1207 if (SUCCEEDED(hrc))
1208 {
1209 /*
1210 * Do the job.
1211 */
1212 char szName[64];
1213 int vrc = DBGFR3OSDetect(ptrVM.rawUVM(), szName, sizeof(szName));
1214 if (RT_SUCCESS(vrc) && vrc != VINF_DBGF_OS_NOT_DETCTED)
1215 {
1216 try
1217 {
1218 aOs = szName;
1219 }
1220 catch (std::bad_alloc)
1221 {
1222 hrc = E_OUTOFMEMORY;
1223 }
1224 }
1225 else
1226 hrc = setErrorBoth(VBOX_E_VM_ERROR, vrc, tr("DBGFR3OSDetect failed with %Rrc"), vrc);
1227 }
1228 return hrc;
1229}
1230
1231HRESULT MachineDebugger::queryOSKernelLog(ULONG aMaxMessages, com::Utf8Str &aDmesg)
1232{
1233 /*
1234 * Lock the debugger and get the VM pointer
1235 */
1236 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1237 Console::SafeVMPtr ptrVM(mParent);
1238 HRESULT hrc = ptrVM.rc();
1239 if (SUCCEEDED(hrc))
1240 {
1241 PDBGFOSIDMESG pDmesg = (PDBGFOSIDMESG)DBGFR3OSQueryInterface(ptrVM.rawUVM(), DBGFOSINTERFACE_DMESG);
1242 if (pDmesg)
1243 {
1244 size_t cbActual;
1245 size_t cbBuf = _512K;
1246 int vrc = aDmesg.reserveNoThrow(cbBuf);
1247 if (RT_SUCCESS(vrc))
1248 {
1249 uint32_t cMessages = aMaxMessages == 0 ? UINT32_MAX : aMaxMessages;
1250 vrc = pDmesg->pfnQueryKernelLog(pDmesg, ptrVM.rawUVM(), 0 /*fFlags*/, cMessages,
1251 aDmesg.mutableRaw(), cbBuf, &cbActual);
1252
1253 uint32_t cTries = 10;
1254 while (vrc == VERR_BUFFER_OVERFLOW && cbBuf < 16*_1M && cTries-- > 0)
1255 {
1256 cbBuf = RT_ALIGN_Z(cbActual + _4K, _4K);
1257 vrc = aDmesg.reserveNoThrow(cbBuf);
1258 if (RT_SUCCESS(vrc))
1259 vrc = pDmesg->pfnQueryKernelLog(pDmesg, ptrVM.rawUVM(), 0 /*fFlags*/, cMessages,
1260 aDmesg.mutableRaw(), cbBuf, &cbActual);
1261 }
1262 if (RT_SUCCESS(vrc))
1263 aDmesg.jolt();
1264 else if (vrc == VERR_BUFFER_OVERFLOW)
1265 hrc = setError(E_FAIL, "Too much log available, must use the maxMessages parameter to restrict.");
1266 else
1267 hrc = setErrorVrc(vrc);
1268 }
1269 else
1270 hrc = setErrorBoth(E_OUTOFMEMORY, vrc);
1271 }
1272 else
1273 hrc = setError(E_FAIL, "The dmesg interface isn't implemented by guest OS digger, or detectOS() has not been called.");
1274 }
1275 return hrc;
1276}
1277
1278/**
1279 * Formats a register value.
1280 *
1281 * This is used by both register getter methods.
1282 *
1283 * @returns
1284 * @param a_pbstr The output Bstr variable.
1285 * @param a_pValue The value to format.
1286 * @param a_enmType The type of the value.
1287 */
1288DECLINLINE(HRESULT) formatRegisterValue(Bstr *a_pbstr, PCDBGFREGVAL a_pValue, DBGFREGVALTYPE a_enmType)
1289{
1290 char szHex[160];
1291 ssize_t cch = DBGFR3RegFormatValue(szHex, sizeof(szHex), a_pValue, a_enmType, true /*fSpecial*/);
1292 if (RT_UNLIKELY(cch <= 0))
1293 return E_UNEXPECTED;
1294 *a_pbstr = szHex;
1295 return S_OK;
1296}
1297
1298HRESULT MachineDebugger::getRegister(ULONG aCpuId, const com::Utf8Str &aName, com::Utf8Str &aValue)
1299{
1300 /*
1301 * The prologue.
1302 */
1303 LogFlowThisFunc(("\n"));
1304 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1305 Console::SafeVMPtr ptrVM(mParent);
1306 HRESULT hrc = ptrVM.rc();
1307 if (SUCCEEDED(hrc))
1308 {
1309 /*
1310 * Real work.
1311 */
1312 DBGFREGVAL Value;
1313 DBGFREGVALTYPE enmType;
1314 int vrc = DBGFR3RegNmQuery(ptrVM.rawUVM(), aCpuId, aName.c_str(), &Value, &enmType);
1315 if (RT_SUCCESS(vrc))
1316 {
1317 try
1318 {
1319 Bstr bstrValue;
1320 hrc = formatRegisterValue(&bstrValue, &Value, enmType);
1321 if (SUCCEEDED(hrc))
1322 aValue = Utf8Str(bstrValue);
1323 }
1324 catch (std::bad_alloc)
1325 {
1326 hrc = E_OUTOFMEMORY;
1327 }
1328 }
1329 else if (vrc == VERR_DBGF_REGISTER_NOT_FOUND)
1330 hrc = setErrorBoth(E_FAIL, vrc, tr("Register '%s' was not found"), aName.c_str());
1331 else if (vrc == VERR_INVALID_CPU_ID)
1332 hrc = setErrorBoth(E_FAIL, vrc, tr("Invalid CPU ID: %u"), aCpuId);
1333 else
1334 hrc = setErrorBoth(VBOX_E_VM_ERROR, vrc,
1335 tr("DBGFR3RegNmQuery failed with rc=%Rrc querying register '%s' with default cpu set to %u"),
1336 vrc, aName.c_str(), aCpuId);
1337 }
1338
1339 return hrc;
1340}
1341
1342HRESULT MachineDebugger::getRegisters(ULONG aCpuId, std::vector<com::Utf8Str> &aNames, std::vector<com::Utf8Str> &aValues)
1343{
1344 RT_NOREF(aCpuId); /** @todo fix missing aCpuId usage! */
1345
1346 /*
1347 * The prologue.
1348 */
1349 LogFlowThisFunc(("\n"));
1350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1351 Console::SafeVMPtr ptrVM(mParent);
1352 HRESULT hrc = ptrVM.rc();
1353 if (SUCCEEDED(hrc))
1354 {
1355 /*
1356 * Real work.
1357 */
1358 size_t cRegs;
1359 int vrc = DBGFR3RegNmQueryAllCount(ptrVM.rawUVM(), &cRegs);
1360 if (RT_SUCCESS(vrc))
1361 {
1362 PDBGFREGENTRYNM paRegs = (PDBGFREGENTRYNM)RTMemAllocZ(sizeof(paRegs[0]) * cRegs);
1363 if (paRegs)
1364 {
1365 vrc = DBGFR3RegNmQueryAll(ptrVM.rawUVM(), paRegs, cRegs);
1366 if (RT_SUCCESS(vrc))
1367 {
1368 try
1369 {
1370 aValues.resize(cRegs);
1371 aNames.resize(cRegs);
1372 for (uint32_t iReg = 0; iReg < cRegs; iReg++)
1373 {
1374 char szHex[160];
1375 szHex[159] = szHex[0] = '\0';
1376 ssize_t cch = DBGFR3RegFormatValue(szHex, sizeof(szHex), &paRegs[iReg].Val,
1377 paRegs[iReg].enmType, true /*fSpecial*/);
1378 Assert(cch > 0); NOREF(cch);
1379 aNames[iReg] = Utf8Str(paRegs[iReg].pszName);
1380 aValues[iReg] = Utf8Str(szHex);
1381 }
1382 }
1383 catch (std::bad_alloc)
1384 {
1385 hrc = E_OUTOFMEMORY;
1386 }
1387 }
1388 else
1389 hrc = setErrorBoth(E_FAIL, vrc, tr("DBGFR3RegNmQueryAll failed with %Rrc"), vrc);
1390
1391 RTMemFree(paRegs);
1392 }
1393 else
1394 hrc = E_OUTOFMEMORY;
1395 }
1396 else
1397 hrc = setErrorBoth(E_FAIL, vrc, tr("DBGFR3RegNmQueryAllCount failed with %Rrc"), vrc);
1398 }
1399 return hrc;
1400}
1401
1402HRESULT MachineDebugger::setRegister(ULONG aCpuId, const com::Utf8Str &aName, const com::Utf8Str &aValue)
1403{
1404 RT_NOREF(aCpuId, aName, aValue);
1405 ReturnComNotImplemented();
1406}
1407
1408HRESULT MachineDebugger::setRegisters(ULONG aCpuId, const std::vector<com::Utf8Str> &aNames,
1409 const std::vector<com::Utf8Str> &aValues)
1410{
1411 RT_NOREF(aCpuId, aNames, aValues);
1412 ReturnComNotImplemented();
1413}
1414
1415HRESULT MachineDebugger::dumpGuestStack(ULONG aCpuId, com::Utf8Str &aStack)
1416{
1417 /*
1418 * The prologue.
1419 */
1420 LogFlowThisFunc(("\n"));
1421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1422 Console::SafeVMPtr ptrVM(mParent);
1423 HRESULT hrc = ptrVM.rc();
1424 if (SUCCEEDED(hrc))
1425 {
1426 /*
1427 * There is currently a problem with the windows diggers and SMP, where
1428 * guest driver memory is being read from CPU zero in order to ensure that
1429 * we've got a consisten virtual memory view. If one of the other CPUs
1430 * initiates a rendezvous while we're unwinding the stack and trying to
1431 * read guest driver memory, we will deadlock.
1432 *
1433 * So, check the VM state and maybe suspend the VM before we continue.
1434 */
1435 int vrc = VINF_SUCCESS;
1436 bool fPaused = false;
1437 if (aCpuId != 0)
1438 {
1439 VMSTATE enmVmState = VMR3GetStateU(ptrVM.rawUVM());
1440 if ( enmVmState == VMSTATE_RUNNING
1441 || enmVmState == VMSTATE_RUNNING_LS
1442 || enmVmState == VMSTATE_RUNNING_FT)
1443 {
1444 alock.release();
1445 vrc = VMR3Suspend(ptrVM.rawUVM(), VMSUSPENDREASON_USER);
1446 alock.acquire();
1447 fPaused = RT_SUCCESS(vrc);
1448 }
1449 }
1450 if (RT_SUCCESS(vrc))
1451 {
1452 PCDBGFSTACKFRAME pFirstFrame;
1453 vrc = DBGFR3StackWalkBegin(ptrVM.rawUVM(), aCpuId, DBGFCODETYPE_GUEST, &pFirstFrame);
1454 if (RT_SUCCESS(vrc))
1455 {
1456 /*
1457 * Print header.
1458 */
1459 try
1460 {
1461 uint32_t fBitFlags = 0;
1462 for (PCDBGFSTACKFRAME pFrame = pFirstFrame;
1463 pFrame;
1464 pFrame = DBGFR3StackWalkNext(pFrame))
1465 {
1466 uint32_t const fCurBitFlags = pFrame->fFlags & (DBGFSTACKFRAME_FLAGS_16BIT | DBGFSTACKFRAME_FLAGS_32BIT | DBGFSTACKFRAME_FLAGS_64BIT);
1467 if (fCurBitFlags & DBGFSTACKFRAME_FLAGS_16BIT)
1468 {
1469 if (fCurBitFlags != fBitFlags)
1470 aStack.append("SS:BP Ret SS:BP Ret CS:EIP Arg0 Arg1 Arg2 Arg3 CS:EIP / Symbol [line]\n");
1471 aStack.append(Utf8StrFmt("%04RX16:%04RX16 %04RX16:%04RX16 %04RX32:%08RX32 %08RX32 %08RX32 %08RX32 %08RX32",
1472 pFrame->AddrFrame.Sel,
1473 (uint16_t)pFrame->AddrFrame.off,
1474 pFrame->AddrReturnFrame.Sel,
1475 (uint16_t)pFrame->AddrReturnFrame.off,
1476 (uint32_t)pFrame->AddrReturnPC.Sel,
1477 (uint32_t)pFrame->AddrReturnPC.off,
1478 pFrame->Args.au32[0],
1479 pFrame->Args.au32[1],
1480 pFrame->Args.au32[2],
1481 pFrame->Args.au32[3]));
1482 }
1483 else if (fCurBitFlags & DBGFSTACKFRAME_FLAGS_32BIT)
1484 {
1485 if (fCurBitFlags != fBitFlags)
1486 aStack.append("EBP Ret EBP Ret CS:EIP Arg0 Arg1 Arg2 Arg3 CS:EIP / Symbol [line]\n");
1487 aStack.append(Utf8StrFmt("%08RX32 %08RX32 %04RX32:%08RX32 %08RX32 %08RX32 %08RX32 %08RX32",
1488 (uint32_t)pFrame->AddrFrame.off,
1489 (uint32_t)pFrame->AddrReturnFrame.off,
1490 (uint32_t)pFrame->AddrReturnPC.Sel,
1491 (uint32_t)pFrame->AddrReturnPC.off,
1492 pFrame->Args.au32[0],
1493 pFrame->Args.au32[1],
1494 pFrame->Args.au32[2],
1495 pFrame->Args.au32[3]));
1496 }
1497 else if (fCurBitFlags & DBGFSTACKFRAME_FLAGS_64BIT)
1498 {
1499 if (fCurBitFlags != fBitFlags)
1500 aStack.append("RBP Ret SS:RBP Ret RIP CS:RIP / Symbol [line]\n");
1501 aStack.append(Utf8StrFmt("%016RX64 %04RX16:%016RX64 %016RX64",
1502 (uint64_t)pFrame->AddrFrame.off,
1503 pFrame->AddrReturnFrame.Sel,
1504 (uint64_t)pFrame->AddrReturnFrame.off,
1505 (uint64_t)pFrame->AddrReturnPC.off));
1506 }
1507
1508 if (!pFrame->pSymPC)
1509 aStack.append(Utf8StrFmt(fCurBitFlags & DBGFSTACKFRAME_FLAGS_64BIT
1510 ? " %RTsel:%016RGv"
1511 : fCurBitFlags & DBGFSTACKFRAME_FLAGS_32BIT
1512 ? " %RTsel:%08RGv"
1513 : " %RTsel:%04RGv"
1514 , pFrame->AddrPC.Sel, pFrame->AddrPC.off));
1515 else
1516 {
1517 RTGCINTPTR offDisp = pFrame->AddrPC.FlatPtr - pFrame->pSymPC->Value; /** @todo this isn't 100% correct for segmented stuff. */
1518 if (offDisp > 0)
1519 aStack.append(Utf8StrFmt(" %s+%llx", pFrame->pSymPC->szName, (int64_t)offDisp));
1520 else if (offDisp < 0)
1521 aStack.append(Utf8StrFmt(" %s-%llx", pFrame->pSymPC->szName, -(int64_t)offDisp));
1522 else
1523 aStack.append(Utf8StrFmt(" %s", pFrame->pSymPC->szName));
1524 }
1525 if (pFrame->pLinePC)
1526 aStack.append(Utf8StrFmt(" [%s @ 0i%d]", pFrame->pLinePC->szFilename, pFrame->pLinePC->uLineNo));
1527 aStack.append(Utf8StrFmt("\n"));
1528
1529 fBitFlags = fCurBitFlags;
1530 }
1531 }
1532 catch (std::bad_alloc)
1533 {
1534 hrc = E_OUTOFMEMORY;
1535 }
1536
1537 DBGFR3StackWalkEnd(pFirstFrame);
1538 }
1539 else
1540 hrc = setErrorBoth(E_FAIL, vrc, tr("DBGFR3StackWalkBegin failed with %Rrc"), vrc);
1541
1542 /*
1543 * Resume the VM if we suspended it.
1544 */
1545 if (fPaused)
1546 {
1547 alock.release();
1548 VMR3Resume(ptrVM.rawUVM(), VMRESUMEREASON_USER);
1549 }
1550 }
1551 else
1552 hrc = setErrorBoth(E_FAIL, vrc, tr("Suspending the VM failed with %Rrc\n"), vrc);
1553 }
1554
1555 return hrc;
1556}
1557
1558/**
1559 * Resets VM statistics.
1560 *
1561 * @returns COM status code.
1562 * @param aPattern The selection pattern. A bit similar to filename globbing.
1563 */
1564HRESULT MachineDebugger::resetStats(const com::Utf8Str &aPattern)
1565{
1566 Console::SafeVMPtrQuiet ptrVM(mParent);
1567
1568 if (!ptrVM.isOk())
1569 return setError(VBOX_E_INVALID_VM_STATE, "Machine is not running");
1570
1571 STAMR3Reset(ptrVM.rawUVM(), aPattern.c_str());
1572
1573 return S_OK;
1574}
1575
1576/**
1577 * Dumps VM statistics to the log.
1578 *
1579 * @returns COM status code.
1580 * @param aPattern The selection pattern. A bit similar to filename globbing.
1581 */
1582HRESULT MachineDebugger::dumpStats(const com::Utf8Str &aPattern)
1583{
1584 Console::SafeVMPtrQuiet ptrVM(mParent);
1585
1586 if (!ptrVM.isOk())
1587 return setError(VBOX_E_INVALID_VM_STATE, "Machine is not running");
1588
1589 STAMR3Dump(ptrVM.rawUVM(), aPattern.c_str());
1590
1591 return S_OK;
1592}
1593
1594/**
1595 * Get the VM statistics in an XML format.
1596 *
1597 * @returns COM status code.
1598 * @param aPattern The selection pattern. A bit similar to filename globbing.
1599 * @param aWithDescriptions Whether to include the descriptions.
1600 * @param aStats The XML document containing the statistics.
1601 */
1602HRESULT MachineDebugger::getStats(const com::Utf8Str &aPattern, BOOL aWithDescriptions, com::Utf8Str &aStats)
1603{
1604 Console::SafeVMPtrQuiet ptrVM(mParent);
1605
1606 if (!ptrVM.isOk())
1607 return setError(VBOX_E_INVALID_VM_STATE, "Machine is not running");
1608
1609 char *pszSnapshot;
1610 int vrc = STAMR3Snapshot(ptrVM.rawUVM(), aPattern.c_str(), &pszSnapshot, NULL,
1611 !!aWithDescriptions);
1612 if (RT_FAILURE(vrc))
1613 return vrc == VERR_NO_MEMORY ? E_OUTOFMEMORY : E_FAIL;
1614
1615 /** @todo this is horribly inefficient! And it's kinda difficult to tell whether it failed...
1616 * Must use UTF-8 or ASCII here and completely avoid these two extra copy operations.
1617 * Until that's done, this method is kind of useless for debugger statistics GUI because
1618 * of the amount statistics in a debug build. */
1619 aStats = Utf8Str(pszSnapshot);
1620 STAMR3SnapshotFree(ptrVM.rawUVM(), pszSnapshot);
1621
1622 return S_OK;
1623}
1624
1625
1626// public methods only for internal purposes
1627/////////////////////////////////////////////////////////////////////////////
1628
1629void MachineDebugger::i_flushQueuedSettings()
1630{
1631 mFlushMode = true;
1632 if (mSingleStepQueued != -1)
1633 {
1634 COMSETTER(SingleStep)(mSingleStepQueued);
1635 mSingleStepQueued = -1;
1636 }
1637 for (unsigned i = 0; i < EMEXECPOLICY_END; i++)
1638 if (maiQueuedEmExecPolicyParams[i] != UINT8_MAX)
1639 {
1640 i_setEmExecPolicyProperty((EMEXECPOLICY)i, RT_BOOL(maiQueuedEmExecPolicyParams[i]));
1641 maiQueuedEmExecPolicyParams[i] = UINT8_MAX;
1642 }
1643 if (mPatmEnabledQueued != -1)
1644 {
1645 COMSETTER(PATMEnabled)(mPatmEnabledQueued);
1646 mPatmEnabledQueued = -1;
1647 }
1648 if (mCsamEnabledQueued != -1)
1649 {
1650 COMSETTER(CSAMEnabled)(mCsamEnabledQueued);
1651 mCsamEnabledQueued = -1;
1652 }
1653 if (mLogEnabledQueued != -1)
1654 {
1655 COMSETTER(LogEnabled)(mLogEnabledQueued);
1656 mLogEnabledQueued = -1;
1657 }
1658 if (mVirtualTimeRateQueued != UINT32_MAX)
1659 {
1660 COMSETTER(VirtualTimeRate)(mVirtualTimeRateQueued);
1661 mVirtualTimeRateQueued = UINT32_MAX;
1662 }
1663 mFlushMode = false;
1664}
1665
1666// private methods
1667/////////////////////////////////////////////////////////////////////////////
1668
1669bool MachineDebugger::i_queueSettings() const
1670{
1671 if (!mFlushMode)
1672 {
1673 // check if the machine is running
1674 MachineState_T machineState;
1675 mParent->COMGETTER(State)(&machineState);
1676 switch (machineState)
1677 {
1678 // queue the request
1679 default:
1680 return true;
1681
1682 case MachineState_Running:
1683 case MachineState_Paused:
1684 case MachineState_Stuck:
1685 case MachineState_LiveSnapshotting:
1686 case MachineState_Teleporting:
1687 break;
1688 }
1689 }
1690 return false;
1691}
1692/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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