VirtualBox

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

Last change on this file since 90441 was 89696, checked in by vboxsync, 3 years ago

Main/MachineDebugger: Implement API for taking a guest sample, bugref:10025

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