VirtualBox

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

Last change on this file since 94521 was 93901, checked in by vboxsync, 2 years ago

VMM,Main,++: Removed VM_IS_RAW_MODE_ENABLED/VM_EXEC_ENGINE_RAW_MODE and added VM_IS_EXEC_ENGINE_IEM/VM_EXEC_ENGINE_IEM instead. In IMachineDebugger::getExecutionEngine VMExecutionEngine_RawMode was removed and VMExecutionEngine_Emulated added. Removed dead code and updated frontends accordingly. On darwin.arm64 HM now falls back on IEM execution since neither HM or NEM is availble there. bugref:9898

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 50.6 KB
Line 
1/* $Id: MachineDebuggerImpl.cpp 93901 2022-02-23 15:35:26Z vboxsync $ */
2/** @file
3 * VBox IMachineDebugger COM class implementation (VBoxC).
4 */
5
6/*
7 * Copyright (C) 2006-2022 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/vmmr3vtable.h>
34#include <VBox/vmm/em.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 mLogEnabledQueued = -1;
91 mVirtualTimeRateQueued = UINT32_MAX;
92 mFlushMode = false;
93
94 m_hSampleReport = NULL;
95
96 /* Confirm a successful initialization */
97 autoInitSpan.setSucceeded();
98
99 return S_OK;
100}
101
102/**
103 * Uninitializes the instance and sets the ready flag to FALSE.
104 * Called either from FinalRelease() or by the parent when it gets destroyed.
105 */
106void MachineDebugger::uninit()
107{
108 LogFlowThisFunc(("\n"));
109
110 /* Enclose the state transition Ready->InUninit->NotReady */
111 AutoUninitSpan autoUninitSpan(this);
112 if (autoUninitSpan.uninitDone())
113 return;
114
115 unconst(mParent) = NULL;
116 mFlushMode = false;
117}
118
119/**
120 * @callback_method_impl{FNDBGFPROGRESS}
121 */
122/*static*/ DECLCALLBACK(int) MachineDebugger::i_dbgfProgressCallback(void *pvUser, unsigned uPercentage)
123{
124 MachineDebugger *pThis = (MachineDebugger *)pvUser;
125
126 int vrc = pThis->m_Progress->i_iprtProgressCallback(uPercentage, static_cast<Progress *>(pThis->m_Progress));
127 if ( RT_SUCCESS(vrc)
128 && uPercentage == 100)
129 {
130 PCVMMR3VTABLE const pVMM = pThis->mParent->i_getVMMVTable();
131 AssertPtrReturn(pVMM, VERR_INTERNAL_ERROR_3);
132
133 vrc = pVMM->pfnDBGFR3SampleReportDumpToFile(pThis->m_hSampleReport, pThis->m_strFilename.c_str());
134 pVMM->pfnDBGFR3SampleReportRelease(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 ptrVM.vtable()->pfnEMR3QueryExecutionPolicy(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 = ptrVM.vtable()->pfnEMR3SetExecutionPolicy(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 execute-all-in-IEM setting.
258 *
259 * @returns COM status code
260 * @param aExecuteAllInIEM Address of result variable.
261 */
262HRESULT MachineDebugger::getExecuteAllInIEM(BOOL *aExecuteAllInIEM)
263{
264 return i_getEmExecPolicyProperty(EMEXECPOLICY_IEM_ALL, aExecuteAllInIEM);
265}
266
267/**
268 * Changes the execute-all-in-IEM setting.
269 *
270 * @returns COM status code
271 * @param aExecuteAllInIEM New setting.
272 */
273HRESULT MachineDebugger::setExecuteAllInIEM(BOOL aExecuteAllInIEM)
274{
275 LogFlowThisFunc(("enable=%d\n", aExecuteAllInIEM));
276 return i_setEmExecPolicyProperty(EMEXECPOLICY_IEM_ALL, aExecuteAllInIEM);
277}
278
279/**
280 * Returns the log enabled / disabled status.
281 *
282 * @returns COM status code
283 * @param aLogEnabled address of result variable
284 */
285HRESULT MachineDebugger::getLogEnabled(BOOL *aLogEnabled)
286{
287#ifdef LOG_ENABLED
288 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
289
290 const PRTLOGGER pLogInstance = RTLogDefaultInstance();
291 *aLogEnabled = pLogInstance && !(RTLogGetFlags(pLogInstance) & RTLOGFLAGS_DISABLED);
292#else
293 *aLogEnabled = false;
294#endif
295
296 return S_OK;
297}
298
299/**
300 * Enables or disables logging.
301 *
302 * @returns COM status code
303 * @param aLogEnabled The new code log state.
304 */
305HRESULT MachineDebugger::setLogEnabled(BOOL aLogEnabled)
306{
307 LogFlowThisFunc(("aLogEnabled=%d\n", aLogEnabled));
308
309 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
310
311 if (i_queueSettings())
312 {
313 // queue the request
314 mLogEnabledQueued = aLogEnabled;
315 return S_OK;
316 }
317
318 Console::SafeVMPtr ptrVM(mParent);
319 if (FAILED(ptrVM.rc())) return ptrVM.rc();
320
321#ifdef LOG_ENABLED
322 int vrc = ptrVM.vtable()->pfnDBGFR3LogModifyFlags(ptrVM.rawUVM(), aLogEnabled ? "enabled" : "disabled");
323 if (RT_FAILURE(vrc))
324 {
325 /** @todo handle error code. */
326 }
327#endif
328
329 return S_OK;
330}
331
332HRESULT MachineDebugger::i_logStringProps(PRTLOGGER pLogger, PFNLOGGETSTR pfnLogGetStr,
333 const char *pszLogGetStr, Utf8Str *pstrSettings)
334{
335 /* Make sure the VM is powered up. */
336 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
337 Console::SafeVMPtr ptrVM(mParent);
338 HRESULT hrc = ptrVM.rc();
339 if (FAILED(hrc))
340 return hrc;
341
342 /* Make sure we've got a logger. */
343 if (!pLogger)
344 {
345 *pstrSettings = "";
346 return S_OK;
347 }
348
349 /* Do the job. */
350 size_t cbBuf = _1K;
351 for (;;)
352 {
353 char *pszBuf = (char *)RTMemTmpAlloc(cbBuf);
354 AssertReturn(pszBuf, E_OUTOFMEMORY);
355 int vrc = pstrSettings->reserveNoThrow(cbBuf);
356 if (RT_SUCCESS(vrc))
357 {
358 vrc = pfnLogGetStr(pLogger, pstrSettings->mutableRaw(), cbBuf);
359 if (RT_SUCCESS(vrc))
360 {
361 pstrSettings->jolt();
362 return S_OK;
363 }
364 *pstrSettings = "";
365 AssertReturn(vrc == VERR_BUFFER_OVERFLOW,
366 setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("%s returned %Rrc"), pszLogGetStr, vrc));
367 }
368 else
369 return E_OUTOFMEMORY;
370
371 /* try again with a bigger buffer. */
372 cbBuf *= 2;
373 AssertReturn(cbBuf <= _256K, setError(E_FAIL, tr("%s returns too much data"), pszLogGetStr));
374 }
375}
376
377HRESULT MachineDebugger::getLogDbgFlags(com::Utf8Str &aLogDbgFlags)
378{
379 return i_logStringProps(RTLogGetDefaultInstance(), RTLogQueryFlags, "RTLogQueryFlags", &aLogDbgFlags);
380}
381
382HRESULT MachineDebugger::getLogDbgGroups(com::Utf8Str &aLogDbgGroups)
383{
384 return i_logStringProps(RTLogGetDefaultInstance(), RTLogQueryGroupSettings, "RTLogQueryGroupSettings", &aLogDbgGroups);
385}
386
387HRESULT MachineDebugger::getLogDbgDestinations(com::Utf8Str &aLogDbgDestinations)
388{
389 return i_logStringProps(RTLogGetDefaultInstance(), RTLogQueryDestinations, "RTLogQueryDestinations", &aLogDbgDestinations);
390}
391
392HRESULT MachineDebugger::getLogRelFlags(com::Utf8Str &aLogRelFlags)
393{
394 return i_logStringProps(RTLogRelGetDefaultInstance(), RTLogQueryFlags, "RTLogQueryFlags", &aLogRelFlags);
395}
396
397HRESULT MachineDebugger::getLogRelGroups(com::Utf8Str &aLogRelGroups)
398{
399 return i_logStringProps(RTLogRelGetDefaultInstance(), RTLogQueryGroupSettings, "RTLogQueryGroupSettings", &aLogRelGroups);
400}
401
402HRESULT MachineDebugger::getLogRelDestinations(com::Utf8Str &aLogRelDestinations)
403{
404 return i_logStringProps(RTLogRelGetDefaultInstance(), RTLogQueryDestinations, "RTLogQueryDestinations", &aLogRelDestinations);
405}
406
407/**
408 * Return the main execution engine of the VM.
409 *
410 * @returns COM status code
411 * @param apenmEngine Address of the result variable.
412 */
413HRESULT MachineDebugger::getExecutionEngine(VMExecutionEngine_T *apenmEngine)
414{
415 *apenmEngine = VMExecutionEngine_NotSet;
416
417 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
418 Console::SafeVMPtrQuiet ptrVM(mParent);
419 if (ptrVM.isOk())
420 {
421 uint8_t bEngine = UINT8_MAX;
422 int rc = ptrVM.vtable()->pfnEMR3QueryMainExecutionEngine(ptrVM.rawUVM(), &bEngine);
423 if (RT_SUCCESS(rc))
424 switch (bEngine)
425 {
426 case VM_EXEC_ENGINE_NOT_SET: *apenmEngine = VMExecutionEngine_NotSet; break;
427 case VM_EXEC_ENGINE_IEM: *apenmEngine = VMExecutionEngine_Emulated; break;
428 case VM_EXEC_ENGINE_HW_VIRT: *apenmEngine = VMExecutionEngine_HwVirt; break;
429 case VM_EXEC_ENGINE_NATIVE_API: *apenmEngine = VMExecutionEngine_NativeApi; break;
430 default: AssertMsgFailed(("bEngine=%d\n", bEngine));
431 }
432 }
433
434 return S_OK;
435}
436
437/**
438 * Returns the current nested paging flag.
439 *
440 * @returns COM status code
441 * @param aHWVirtExNestedPagingEnabled address of result variable
442 */
443HRESULT MachineDebugger::getHWVirtExNestedPagingEnabled(BOOL *aHWVirtExNestedPagingEnabled)
444{
445 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
446
447 Console::SafeVMPtrQuiet ptrVM(mParent);
448 if (ptrVM.isOk())
449 *aHWVirtExNestedPagingEnabled = ptrVM.vtable()->pfnHMR3IsNestedPagingActive(ptrVM.rawUVM());
450 else
451 *aHWVirtExNestedPagingEnabled = false;
452
453 return S_OK;
454}
455
456/**
457 * Returns the current VPID flag.
458 *
459 * @returns COM status code
460 * @param aHWVirtExVPIDEnabled address of result variable
461 */
462HRESULT MachineDebugger::getHWVirtExVPIDEnabled(BOOL *aHWVirtExVPIDEnabled)
463{
464 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
465
466 Console::SafeVMPtrQuiet ptrVM(mParent);
467 if (ptrVM.isOk())
468 *aHWVirtExVPIDEnabled = ptrVM.vtable()->pfnHMR3IsVpidActive(ptrVM.rawUVM());
469 else
470 *aHWVirtExVPIDEnabled = false;
471
472 return S_OK;
473}
474
475/**
476 * Returns the current unrestricted execution setting.
477 *
478 * @returns COM status code
479 * @param aHWVirtExUXEnabled address of result variable
480 */
481HRESULT MachineDebugger::getHWVirtExUXEnabled(BOOL *aHWVirtExUXEnabled)
482{
483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
484
485 Console::SafeVMPtrQuiet ptrVM(mParent);
486 if (ptrVM.isOk())
487 *aHWVirtExUXEnabled = ptrVM.vtable()->pfnHMR3IsUXActive(ptrVM.rawUVM());
488 else
489 *aHWVirtExUXEnabled = false;
490
491 return S_OK;
492}
493
494HRESULT MachineDebugger::getOSName(com::Utf8Str &aOSName)
495{
496 LogFlowThisFunc(("\n"));
497 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
498
499 Console::SafeVMPtr ptrVM(mParent);
500 HRESULT hrc = ptrVM.rc();
501 if (SUCCEEDED(hrc))
502 {
503 /*
504 * Do the job and try convert the name.
505 */
506 char szName[64];
507 int vrc = ptrVM.vtable()->pfnDBGFR3OSQueryNameAndVersion(ptrVM.rawUVM(), szName, sizeof(szName), NULL, 0);
508 if (RT_SUCCESS(vrc))
509 hrc = aOSName.assignEx(szName);
510 else
511 hrc = setErrorBoth(VBOX_E_VM_ERROR, vrc, tr("DBGFR3OSQueryNameAndVersion failed with %Rrc"), vrc);
512 }
513 return hrc;
514}
515
516HRESULT MachineDebugger::getOSVersion(com::Utf8Str &aOSVersion)
517{
518 LogFlowThisFunc(("\n"));
519 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
520
521 Console::SafeVMPtr ptrVM(mParent);
522 HRESULT hrc = ptrVM.rc();
523 if (SUCCEEDED(hrc))
524 {
525 /*
526 * Do the job and try convert the name.
527 */
528 char szVersion[256];
529 int vrc = ptrVM.vtable()->pfnDBGFR3OSQueryNameAndVersion(ptrVM.rawUVM(), NULL, 0, szVersion, sizeof(szVersion));
530 if (RT_SUCCESS(vrc))
531 hrc = aOSVersion.assignEx(szVersion);
532 else
533 hrc = setErrorBoth(VBOX_E_VM_ERROR, vrc, tr("DBGFR3OSQueryNameAndVersion failed with %Rrc"), vrc);
534 }
535 return hrc;
536}
537
538/**
539 * Returns the current PAE flag.
540 *
541 * @returns COM status code
542 * @param aPAEEnabled address of result variable.
543 */
544HRESULT MachineDebugger::getPAEEnabled(BOOL *aPAEEnabled)
545{
546 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
547
548 Console::SafeVMPtrQuiet ptrVM(mParent);
549 if (ptrVM.isOk())
550 {
551 uint32_t cr4;
552 int rc = ptrVM.vtable()->pfnDBGFR3RegCpuQueryU32(ptrVM.rawUVM(), 0 /*idCpu*/, DBGFREG_CR4, &cr4); AssertRC(rc);
553 *aPAEEnabled = RT_BOOL(cr4 & X86_CR4_PAE);
554 }
555 else
556 *aPAEEnabled = false;
557
558 return S_OK;
559}
560
561/**
562 * Returns the current virtual time rate.
563 *
564 * @returns COM status code.
565 * @param aVirtualTimeRate Where to store the rate.
566 */
567HRESULT MachineDebugger::getVirtualTimeRate(ULONG *aVirtualTimeRate)
568{
569 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
570
571 Console::SafeVMPtr ptrVM(mParent);
572 HRESULT hrc = ptrVM.rc();
573 if (SUCCEEDED(hrc))
574 *aVirtualTimeRate = ptrVM.vtable()->pfnTMR3GetWarpDrive(ptrVM.rawUVM());
575
576 return hrc;
577}
578
579/**
580 * Set the virtual time rate.
581 *
582 * @returns COM status code.
583 * @param aVirtualTimeRate The new rate.
584 */
585HRESULT MachineDebugger::setVirtualTimeRate(ULONG aVirtualTimeRate)
586{
587 HRESULT hrc = S_OK;
588
589 if (aVirtualTimeRate < 2 || aVirtualTimeRate > 20000)
590 return setError(E_INVALIDARG, tr("%u is out of range [2..20000]"), aVirtualTimeRate);
591
592 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
593 if (i_queueSettings())
594 mVirtualTimeRateQueued = aVirtualTimeRate;
595 else
596 {
597 Console::SafeVMPtr ptrVM(mParent);
598 hrc = ptrVM.rc();
599 if (SUCCEEDED(hrc))
600 {
601 int vrc = ptrVM.vtable()->pfnTMR3SetWarpDrive(ptrVM.rawUVM(), aVirtualTimeRate);
602 if (RT_FAILURE(vrc))
603 hrc = setErrorBoth(VBOX_E_VM_ERROR, vrc, tr("TMR3SetWarpDrive(, %u) failed with rc=%Rrc"), aVirtualTimeRate, vrc);
604 }
605 }
606
607 return hrc;
608}
609
610/**
611 * Get the VM uptime in milliseconds.
612 *
613 * @returns COM status code
614 * @param aUptime Where to store the uptime.
615 */
616HRESULT MachineDebugger::getUptime(LONG64 *aUptime)
617{
618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
619
620 Console::SafeVMPtr ptrVM(mParent);
621 HRESULT hrc = ptrVM.rc();
622 if (SUCCEEDED(hrc))
623 *aUptime = (int64_t)ptrVM.vtable()->pfnTMR3TimeVirtGetMilli(ptrVM.rawUVM());
624
625 return hrc;
626}
627
628// IMachineDebugger methods
629/////////////////////////////////////////////////////////////////////////////
630
631HRESULT MachineDebugger::dumpGuestCore(const com::Utf8Str &aFilename, const com::Utf8Str &aCompression)
632{
633 if (aCompression.length())
634 return setError(E_INVALIDARG, tr("The compression parameter must be empty"));
635
636 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
637 Console::SafeVMPtr ptrVM(mParent);
638 HRESULT hrc = ptrVM.rc();
639 if (SUCCEEDED(hrc))
640 {
641 int vrc = ptrVM.vtable()->pfnDBGFR3CoreWrite(ptrVM.rawUVM(), aFilename.c_str(), false /*fReplaceFile*/);
642 if (RT_SUCCESS(vrc))
643 hrc = S_OK;
644 else
645 hrc = setErrorBoth(E_FAIL, vrc, tr("DBGFR3CoreWrite failed with %Rrc"), vrc);
646 }
647
648 return hrc;
649}
650
651HRESULT MachineDebugger::dumpHostProcessCore(const com::Utf8Str &aFilename, const com::Utf8Str &aCompression)
652{
653 RT_NOREF(aFilename, aCompression);
654 ReturnComNotImplemented();
655}
656
657/**
658 * Debug info string buffer formatter.
659 */
660typedef struct MACHINEDEBUGGERINOFHLP
661{
662 /** The core info helper structure. */
663 DBGFINFOHLP Core;
664 /** Pointer to the buffer. */
665 char *pszBuf;
666 /** The size of the buffer. */
667 size_t cbBuf;
668 /** The offset into the buffer */
669 size_t offBuf;
670 /** Indicates an out-of-memory condition. */
671 bool fOutOfMemory;
672} MACHINEDEBUGGERINOFHLP;
673/** Pointer to a Debug info string buffer formatter. */
674typedef MACHINEDEBUGGERINOFHLP *PMACHINEDEBUGGERINOFHLP;
675
676
677/**
678 * @callback_method_impl{FNRTSTROUTPUT}
679 */
680static DECLCALLBACK(size_t) MachineDebuggerInfoOutput(void *pvArg, const char *pachChars, size_t cbChars)
681{
682 PMACHINEDEBUGGERINOFHLP pHlp = (PMACHINEDEBUGGERINOFHLP)pvArg;
683
684 /*
685 * Grow the buffer if required.
686 */
687 size_t const cbRequired = cbChars + pHlp->offBuf + 1;
688 if (cbRequired > pHlp->cbBuf)
689 {
690 if (RT_UNLIKELY(pHlp->fOutOfMemory))
691 return 0;
692
693 size_t cbBufNew = pHlp->cbBuf * 2;
694 if (cbRequired > cbBufNew)
695 cbBufNew = RT_ALIGN_Z(cbRequired, 256);
696 void *pvBufNew = RTMemRealloc(pHlp->pszBuf, cbBufNew);
697 if (RT_UNLIKELY(!pvBufNew))
698 {
699 pHlp->fOutOfMemory = true;
700 RTMemFree(pHlp->pszBuf);
701 pHlp->pszBuf = NULL;
702 pHlp->cbBuf = 0;
703 pHlp->offBuf = 0;
704 return 0;
705 }
706
707 pHlp->pszBuf = (char *)pvBufNew;
708 pHlp->cbBuf = cbBufNew;
709 }
710
711 /*
712 * Copy the bytes into the buffer and terminate it.
713 */
714 if (cbChars)
715 {
716 memcpy(&pHlp->pszBuf[pHlp->offBuf], pachChars, cbChars);
717 pHlp->offBuf += cbChars;
718 }
719 pHlp->pszBuf[pHlp->offBuf] = '\0';
720 Assert(pHlp->offBuf < pHlp->cbBuf);
721 return cbChars;
722}
723
724/**
725 * @interface_method_impl{DBGFINFOHLP,pfnPrintfV}
726 */
727static DECLCALLBACK(void) MachineDebuggerInfoPrintfV(PCDBGFINFOHLP pHlp, const char *pszFormat, va_list args)
728{
729 RTStrFormatV(MachineDebuggerInfoOutput, (void *)pHlp, NULL, NULL, pszFormat, args);
730}
731
732/**
733 * @interface_method_impl{DBGFINFOHLP,pfnPrintf}
734 */
735static DECLCALLBACK(void) MachineDebuggerInfoPrintf(PCDBGFINFOHLP pHlp, const char *pszFormat, ...)
736{
737 va_list va;
738 va_start(va, pszFormat);
739 MachineDebuggerInfoPrintfV(pHlp, pszFormat, va);
740 va_end(va);
741}
742
743/**
744 * Initializes the debug info string buffer formatter
745 *
746 * @param pHlp The help structure to init.
747 * @param pVMM The VMM vtable.
748 */
749static void MachineDebuggerInfoInit(PMACHINEDEBUGGERINOFHLP pHlp, PCVMMR3VTABLE pVMM)
750{
751 pHlp->Core.pfnPrintf = MachineDebuggerInfoPrintf;
752 pHlp->Core.pfnPrintfV = MachineDebuggerInfoPrintfV;
753 pHlp->Core.pfnGetOptError = pVMM->pfnDBGFR3InfoGenericGetOptError;
754 pHlp->pszBuf = NULL;
755 pHlp->cbBuf = 0;
756 pHlp->offBuf = 0;
757 pHlp->fOutOfMemory = false;
758}
759
760/**
761 * Deletes the debug info string buffer formatter.
762 * @param pHlp The helper structure to delete.
763 */
764static void MachineDebuggerInfoDelete(PMACHINEDEBUGGERINOFHLP pHlp)
765{
766 RTMemFree(pHlp->pszBuf);
767 pHlp->pszBuf = NULL;
768}
769
770HRESULT MachineDebugger::info(const com::Utf8Str &aName, const com::Utf8Str &aArgs, com::Utf8Str &aInfo)
771{
772 LogFlowThisFunc(("\n"));
773
774 /*
775 * Do the autocaller and lock bits.
776 */
777 AutoCaller autoCaller(this);
778 HRESULT hrc = autoCaller.rc();
779 if (SUCCEEDED(hrc))
780 {
781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
782 Console::SafeVMPtr ptrVM(mParent);
783 hrc = ptrVM.rc();
784 if (SUCCEEDED(hrc))
785 {
786 /*
787 * Create a helper and call DBGFR3Info.
788 */
789 MACHINEDEBUGGERINOFHLP Hlp;
790 MachineDebuggerInfoInit(&Hlp, ptrVM.vtable());
791 int vrc = ptrVM.vtable()->pfnDBGFR3Info(ptrVM.rawUVM(), aName.c_str(), aArgs.c_str(), &Hlp.Core);
792 if (RT_SUCCESS(vrc))
793 {
794 if (!Hlp.fOutOfMemory)
795 hrc = aInfo.assignEx(Hlp.pszBuf);
796 else
797 hrc = E_OUTOFMEMORY;
798 }
799 else
800 hrc = setErrorBoth(VBOX_E_VM_ERROR, vrc, tr("DBGFR3Info failed with %Rrc"), vrc);
801 MachineDebuggerInfoDelete(&Hlp);
802 }
803 }
804 return hrc;
805}
806
807HRESULT MachineDebugger::injectNMI()
808{
809 LogFlowThisFunc(("\n"));
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 = ptrVM.vtable()->pfnDBGFR3InjectNMI(ptrVM.rawUVM(), 0);
817 if (RT_SUCCESS(vrc))
818 hrc = S_OK;
819 else
820 hrc = setErrorBoth(E_FAIL, vrc, tr("DBGFR3InjectNMI failed with %Rrc"), vrc);
821 }
822 return hrc;
823}
824
825HRESULT MachineDebugger::modifyLogFlags(const com::Utf8Str &aSettings)
826{
827 LogFlowThisFunc(("aSettings=%s\n", aSettings.c_str()));
828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
829 Console::SafeVMPtr ptrVM(mParent);
830 HRESULT hrc = ptrVM.rc();
831 if (SUCCEEDED(hrc))
832 {
833 int vrc = ptrVM.vtable()->pfnDBGFR3LogModifyFlags(ptrVM.rawUVM(), aSettings.c_str());
834 if (RT_SUCCESS(vrc))
835 hrc = S_OK;
836 else
837 hrc = setErrorBoth(E_FAIL, vrc, tr("DBGFR3LogModifyFlags failed with %Rrc"), vrc);
838 }
839 return hrc;
840}
841
842HRESULT MachineDebugger::modifyLogGroups(const com::Utf8Str &aSettings)
843{
844 LogFlowThisFunc(("aSettings=%s\n", aSettings.c_str()));
845 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
846 Console::SafeVMPtr ptrVM(mParent);
847 HRESULT hrc = ptrVM.rc();
848 if (SUCCEEDED(hrc))
849 {
850 int vrc = ptrVM.vtable()->pfnDBGFR3LogModifyGroups(ptrVM.rawUVM(), aSettings.c_str());
851 if (RT_SUCCESS(vrc))
852 hrc = S_OK;
853 else
854 hrc = setErrorBoth(E_FAIL, vrc, tr("DBGFR3LogModifyGroups failed with %Rrc"), vrc);
855 }
856 return hrc;
857}
858
859HRESULT MachineDebugger::modifyLogDestinations(const com::Utf8Str &aSettings)
860{
861 LogFlowThisFunc(("aSettings=%s\n", aSettings.c_str()));
862 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
863 Console::SafeVMPtr ptrVM(mParent);
864 HRESULT hrc = ptrVM.rc();
865 if (SUCCEEDED(hrc))
866 {
867 int vrc = ptrVM.vtable()->pfnDBGFR3LogModifyDestinations(ptrVM.rawUVM(), aSettings.c_str());
868 if (RT_SUCCESS(vrc))
869 hrc = S_OK;
870 else
871 hrc = setErrorBoth(E_FAIL, vrc, tr("DBGFR3LogModifyDestinations failed with %Rrc"), vrc);
872 }
873 return hrc;
874}
875
876HRESULT MachineDebugger::readPhysicalMemory(LONG64 aAddress, ULONG aSize, std::vector<BYTE> &aBytes)
877{
878 RT_NOREF(aAddress, aSize, aBytes);
879 ReturnComNotImplemented();
880}
881
882HRESULT MachineDebugger::writePhysicalMemory(LONG64 aAddress, ULONG aSize, const std::vector<BYTE> &aBytes)
883{
884 RT_NOREF(aAddress, aSize, aBytes);
885 ReturnComNotImplemented();
886}
887
888HRESULT MachineDebugger::readVirtualMemory(ULONG aCpuId, LONG64 aAddress, ULONG aSize, std::vector<BYTE> &aBytes)
889{
890 RT_NOREF(aCpuId, aAddress, aSize, aBytes);
891 ReturnComNotImplemented();
892}
893
894HRESULT MachineDebugger::writeVirtualMemory(ULONG aCpuId, LONG64 aAddress, ULONG aSize, const std::vector<BYTE> &aBytes)
895{
896 RT_NOREF(aCpuId, aAddress, aSize, aBytes);
897 ReturnComNotImplemented();
898}
899
900HRESULT MachineDebugger::loadPlugIn(const com::Utf8Str &aName, com::Utf8Str &aPlugInName)
901{
902 /*
903 * Lock the debugger and get the VM pointer
904 */
905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
906 Console::SafeVMPtr ptrVM(mParent);
907 HRESULT hrc = ptrVM.rc();
908 if (SUCCEEDED(hrc))
909 {
910 /*
911 * Do the job and try convert the name.
912 */
913 if (aName.equals("all"))
914 {
915 ptrVM.vtable()->pfnDBGFR3PlugInLoadAll(ptrVM.rawUVM());
916 hrc = aPlugInName.assignEx("all");
917 }
918 else
919 {
920 RTERRINFOSTATIC ErrInfo;
921 char szName[80];
922 int vrc = ptrVM.vtable()->pfnDBGFR3PlugInLoad(ptrVM.rawUVM(), aName.c_str(), szName, sizeof(szName), RTErrInfoInitStatic(&ErrInfo));
923 if (RT_SUCCESS(vrc))
924 hrc = aPlugInName.assignEx(szName);
925 else
926 hrc = setErrorVrc(vrc, "%s", ErrInfo.szMsg);
927 }
928 }
929 return hrc;
930
931}
932
933HRESULT MachineDebugger::unloadPlugIn(const com::Utf8Str &aName)
934{
935 /*
936 * Lock the debugger and get the VM pointer
937 */
938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
939 Console::SafeVMPtr ptrVM(mParent);
940 HRESULT hrc = ptrVM.rc();
941 if (SUCCEEDED(hrc))
942 {
943 /*
944 * Do the job and try convert the name.
945 */
946 if (aName.equals("all"))
947 {
948 ptrVM.vtable()->pfnDBGFR3PlugInUnloadAll(ptrVM.rawUVM());
949 hrc = S_OK;
950 }
951 else
952 {
953 int vrc = ptrVM.vtable()->pfnDBGFR3PlugInUnload(ptrVM.rawUVM(), aName.c_str());
954 if (RT_SUCCESS(vrc))
955 hrc = S_OK;
956 else if (vrc == VERR_NOT_FOUND)
957 hrc = setErrorBoth(E_FAIL, vrc, tr("Plug-in '%s' was not found"), aName.c_str());
958 else
959 hrc = setErrorVrc(vrc, tr("Error unloading '%s': %Rrc"), aName.c_str(), vrc);
960 }
961 }
962 return hrc;
963
964}
965
966HRESULT MachineDebugger::detectOS(com::Utf8Str &aOs)
967{
968 LogFlowThisFunc(("\n"));
969
970 /*
971 * Lock the debugger and get the VM pointer
972 */
973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
974 Console::SafeVMPtr ptrVM(mParent);
975 HRESULT hrc = ptrVM.rc();
976 if (SUCCEEDED(hrc))
977 {
978 /*
979 * Do the job.
980 */
981 char szName[64];
982 int vrc = ptrVM.vtable()->pfnDBGFR3OSDetect(ptrVM.rawUVM(), szName, sizeof(szName));
983 if (RT_SUCCESS(vrc) && vrc != VINF_DBGF_OS_NOT_DETCTED)
984 hrc = aOs.assignEx(szName);
985 else
986 hrc = setErrorBoth(VBOX_E_VM_ERROR, vrc, tr("DBGFR3OSDetect failed with %Rrc"), vrc);
987 }
988 return hrc;
989}
990
991HRESULT MachineDebugger::queryOSKernelLog(ULONG aMaxMessages, com::Utf8Str &aDmesg)
992{
993 /*
994 * Lock the debugger and get the VM pointer
995 */
996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
997 Console::SafeVMPtr ptrVM(mParent);
998 HRESULT hrc = ptrVM.rc();
999 if (SUCCEEDED(hrc))
1000 {
1001 PDBGFOSIDMESG pDmesg = (PDBGFOSIDMESG)ptrVM.vtable()->pfnDBGFR3OSQueryInterface(ptrVM.rawUVM(), DBGFOSINTERFACE_DMESG);
1002 if (pDmesg)
1003 {
1004 size_t cbActual;
1005 size_t cbBuf = _512K;
1006 int vrc = aDmesg.reserveNoThrow(cbBuf);
1007 if (RT_SUCCESS(vrc))
1008 {
1009 uint32_t cMessages = aMaxMessages == 0 ? UINT32_MAX : aMaxMessages;
1010 vrc = pDmesg->pfnQueryKernelLog(pDmesg, ptrVM.rawUVM(), ptrVM.vtable(), 0 /*fFlags*/, cMessages,
1011 aDmesg.mutableRaw(), cbBuf, &cbActual);
1012
1013 uint32_t cTries = 10;
1014 while (vrc == VERR_BUFFER_OVERFLOW && cbBuf < 16*_1M && cTries-- > 0)
1015 {
1016 cbBuf = RT_ALIGN_Z(cbActual + _4K, _4K);
1017 vrc = aDmesg.reserveNoThrow(cbBuf);
1018 if (RT_SUCCESS(vrc))
1019 vrc = pDmesg->pfnQueryKernelLog(pDmesg, ptrVM.rawUVM(), ptrVM.vtable(), 0 /*fFlags*/, cMessages,
1020 aDmesg.mutableRaw(), cbBuf, &cbActual);
1021 }
1022 if (RT_SUCCESS(vrc))
1023 aDmesg.jolt();
1024 else if (vrc == VERR_BUFFER_OVERFLOW)
1025 hrc = setError(E_FAIL, tr("Too much log available, must use the maxMessages parameter to restrict."));
1026 else
1027 hrc = setErrorVrc(vrc);
1028 }
1029 else
1030 hrc = setErrorBoth(E_OUTOFMEMORY, vrc);
1031 }
1032 else
1033 hrc = setError(E_FAIL, tr("The dmesg interface isn't implemented by guest OS digger, or detectOS() has not been called."));
1034 }
1035 return hrc;
1036}
1037
1038HRESULT MachineDebugger::getRegister(ULONG aCpuId, const com::Utf8Str &aName, com::Utf8Str &aValue)
1039{
1040 /*
1041 * The prologue.
1042 */
1043 LogFlowThisFunc(("\n"));
1044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1045 Console::SafeVMPtr ptrVM(mParent);
1046 HRESULT hrc = ptrVM.rc();
1047 if (SUCCEEDED(hrc))
1048 {
1049 /*
1050 * Real work.
1051 */
1052 DBGFREGVAL Value;
1053 DBGFREGVALTYPE enmType;
1054 int vrc = ptrVM.vtable()->pfnDBGFR3RegNmQuery(ptrVM.rawUVM(), aCpuId, aName.c_str(), &Value, &enmType);
1055 if (RT_SUCCESS(vrc))
1056 {
1057 char szHex[160];
1058 ssize_t cch = ptrVM.vtable()->pfnDBGFR3RegFormatValue(szHex, sizeof(szHex), &Value, enmType, true /*fSpecial*/);
1059 if (cch > 0)
1060 hrc = aValue.assignEx(szHex);
1061 else
1062 hrc = E_UNEXPECTED;
1063 }
1064 else if (vrc == VERR_DBGF_REGISTER_NOT_FOUND)
1065 hrc = setErrorBoth(E_FAIL, vrc, tr("Register '%s' was not found"), aName.c_str());
1066 else if (vrc == VERR_INVALID_CPU_ID)
1067 hrc = setErrorBoth(E_FAIL, vrc, tr("Invalid CPU ID: %u"), aCpuId);
1068 else
1069 hrc = setErrorBoth(VBOX_E_VM_ERROR, vrc,
1070 tr("DBGFR3RegNmQuery failed with rc=%Rrc querying register '%s' with default cpu set to %u"),
1071 vrc, aName.c_str(), aCpuId);
1072 }
1073
1074 return hrc;
1075}
1076
1077HRESULT MachineDebugger::getRegisters(ULONG aCpuId, std::vector<com::Utf8Str> &aNames, std::vector<com::Utf8Str> &aValues)
1078{
1079 RT_NOREF(aCpuId); /** @todo fix missing aCpuId usage! */
1080
1081 /*
1082 * The prologue.
1083 */
1084 LogFlowThisFunc(("\n"));
1085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1086 Console::SafeVMPtr ptrVM(mParent);
1087 HRESULT hrc = ptrVM.rc();
1088 if (SUCCEEDED(hrc))
1089 {
1090 /*
1091 * Real work.
1092 */
1093 size_t cRegs;
1094 int vrc = ptrVM.vtable()->pfnDBGFR3RegNmQueryAllCount(ptrVM.rawUVM(), &cRegs);
1095 if (RT_SUCCESS(vrc))
1096 {
1097 PDBGFREGENTRYNM paRegs = (PDBGFREGENTRYNM)RTMemAllocZ(sizeof(paRegs[0]) * cRegs);
1098 if (paRegs)
1099 {
1100 vrc = ptrVM.vtable()->pfnDBGFR3RegNmQueryAll(ptrVM.rawUVM(), paRegs, cRegs);
1101 if (RT_SUCCESS(vrc))
1102 {
1103 try
1104 {
1105 aValues.resize(cRegs);
1106 aNames.resize(cRegs);
1107 for (uint32_t iReg = 0; iReg < cRegs; iReg++)
1108 {
1109 char szHex[160];
1110 szHex[159] = szHex[0] = '\0';
1111 ssize_t cch = ptrVM.vtable()->pfnDBGFR3RegFormatValue(szHex, sizeof(szHex), &paRegs[iReg].Val,
1112 paRegs[iReg].enmType, true /*fSpecial*/);
1113 Assert(cch > 0); NOREF(cch);
1114 aNames[iReg] = paRegs[iReg].pszName;
1115 aValues[iReg] = szHex;
1116 }
1117 }
1118 catch (std::bad_alloc &)
1119 {
1120 hrc = E_OUTOFMEMORY;
1121 }
1122 }
1123 else
1124 hrc = setErrorBoth(E_FAIL, vrc, tr("DBGFR3RegNmQueryAll failed with %Rrc"), vrc);
1125
1126 RTMemFree(paRegs);
1127 }
1128 else
1129 hrc = E_OUTOFMEMORY;
1130 }
1131 else
1132 hrc = setErrorBoth(E_FAIL, vrc, tr("DBGFR3RegNmQueryAllCount failed with %Rrc"), vrc);
1133 }
1134 return hrc;
1135}
1136
1137HRESULT MachineDebugger::setRegister(ULONG aCpuId, const com::Utf8Str &aName, const com::Utf8Str &aValue)
1138{
1139 RT_NOREF(aCpuId, aName, aValue);
1140 ReturnComNotImplemented();
1141}
1142
1143HRESULT MachineDebugger::setRegisters(ULONG aCpuId, const std::vector<com::Utf8Str> &aNames,
1144 const std::vector<com::Utf8Str> &aValues)
1145{
1146 RT_NOREF(aCpuId, aNames, aValues);
1147 ReturnComNotImplemented();
1148}
1149
1150HRESULT MachineDebugger::dumpGuestStack(ULONG aCpuId, com::Utf8Str &aStack)
1151{
1152 /*
1153 * The prologue.
1154 */
1155 LogFlowThisFunc(("\n"));
1156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1157 Console::SafeVMPtr ptrVM(mParent);
1158 HRESULT hrc = ptrVM.rc();
1159 if (SUCCEEDED(hrc))
1160 {
1161 /*
1162 * There is currently a problem with the windows diggers and SMP, where
1163 * guest driver memory is being read from CPU zero in order to ensure that
1164 * we've got a consisten virtual memory view. If one of the other CPUs
1165 * initiates a rendezvous while we're unwinding the stack and trying to
1166 * read guest driver memory, we will deadlock.
1167 *
1168 * So, check the VM state and maybe suspend the VM before we continue.
1169 */
1170 int vrc = VINF_SUCCESS;
1171 bool fPaused = false;
1172 if (aCpuId != 0)
1173 {
1174 VMSTATE enmVmState = ptrVM.vtable()->pfnVMR3GetStateU(ptrVM.rawUVM());
1175 if ( enmVmState == VMSTATE_RUNNING
1176 || enmVmState == VMSTATE_RUNNING_LS)
1177 {
1178 alock.release();
1179 vrc = ptrVM.vtable()->pfnVMR3Suspend(ptrVM.rawUVM(), VMSUSPENDREASON_USER);
1180 alock.acquire();
1181 fPaused = RT_SUCCESS(vrc);
1182 }
1183 }
1184 if (RT_SUCCESS(vrc))
1185 {
1186 PCDBGFSTACKFRAME pFirstFrame;
1187 vrc = ptrVM.vtable()->pfnDBGFR3StackWalkBegin(ptrVM.rawUVM(), aCpuId, DBGFCODETYPE_GUEST, &pFirstFrame);
1188 if (RT_SUCCESS(vrc))
1189 {
1190 /*
1191 * Print header.
1192 */
1193 try
1194 {
1195 uint32_t fBitFlags = 0;
1196 for (PCDBGFSTACKFRAME pFrame = pFirstFrame;
1197 pFrame;
1198 pFrame = ptrVM.vtable()->pfnDBGFR3StackWalkNext(pFrame))
1199 {
1200 uint32_t const fCurBitFlags = pFrame->fFlags & (DBGFSTACKFRAME_FLAGS_16BIT | DBGFSTACKFRAME_FLAGS_32BIT | DBGFSTACKFRAME_FLAGS_64BIT);
1201 if (fCurBitFlags & DBGFSTACKFRAME_FLAGS_16BIT)
1202 {
1203 if (fCurBitFlags != fBitFlags)
1204 aStack.append("SS:BP Ret SS:BP Ret CS:EIP Arg0 Arg1 Arg2 Arg3 CS:EIP / Symbol [line]\n");
1205 aStack.appendPrintf("%04RX16:%04RX16 %04RX16:%04RX16 %04RX32:%08RX32 %08RX32 %08RX32 %08RX32 %08RX32",
1206 pFrame->AddrFrame.Sel,
1207 (uint16_t)pFrame->AddrFrame.off,
1208 pFrame->AddrReturnFrame.Sel,
1209 (uint16_t)pFrame->AddrReturnFrame.off,
1210 (uint32_t)pFrame->AddrReturnPC.Sel,
1211 (uint32_t)pFrame->AddrReturnPC.off,
1212 pFrame->Args.au32[0],
1213 pFrame->Args.au32[1],
1214 pFrame->Args.au32[2],
1215 pFrame->Args.au32[3]);
1216 }
1217 else if (fCurBitFlags & DBGFSTACKFRAME_FLAGS_32BIT)
1218 {
1219 if (fCurBitFlags != fBitFlags)
1220 aStack.append("EBP Ret EBP Ret CS:EIP Arg0 Arg1 Arg2 Arg3 CS:EIP / Symbol [line]\n");
1221 aStack.appendPrintf("%08RX32 %08RX32 %04RX32:%08RX32 %08RX32 %08RX32 %08RX32 %08RX32",
1222 (uint32_t)pFrame->AddrFrame.off,
1223 (uint32_t)pFrame->AddrReturnFrame.off,
1224 (uint32_t)pFrame->AddrReturnPC.Sel,
1225 (uint32_t)pFrame->AddrReturnPC.off,
1226 pFrame->Args.au32[0],
1227 pFrame->Args.au32[1],
1228 pFrame->Args.au32[2],
1229 pFrame->Args.au32[3]);
1230 }
1231 else if (fCurBitFlags & DBGFSTACKFRAME_FLAGS_64BIT)
1232 {
1233 if (fCurBitFlags != fBitFlags)
1234 aStack.append("RBP Ret SS:RBP Ret RIP CS:RIP / Symbol [line]\n");
1235 aStack.appendPrintf("%016RX64 %04RX16:%016RX64 %016RX64",
1236 (uint64_t)pFrame->AddrFrame.off,
1237 pFrame->AddrReturnFrame.Sel,
1238 (uint64_t)pFrame->AddrReturnFrame.off,
1239 (uint64_t)pFrame->AddrReturnPC.off);
1240 }
1241
1242 if (!pFrame->pSymPC)
1243 aStack.appendPrintf(fCurBitFlags & DBGFSTACKFRAME_FLAGS_64BIT
1244 ? " %RTsel:%016RGv"
1245 : fCurBitFlags & DBGFSTACKFRAME_FLAGS_32BIT
1246 ? " %RTsel:%08RGv"
1247 : " %RTsel:%04RGv"
1248 , pFrame->AddrPC.Sel, pFrame->AddrPC.off);
1249 else
1250 {
1251 RTGCINTPTR offDisp = pFrame->AddrPC.FlatPtr - pFrame->pSymPC->Value; /** @todo this isn't 100% correct for segmented stuff. */
1252 if (offDisp > 0)
1253 aStack.appendPrintf(" %s+%llx", pFrame->pSymPC->szName, (int64_t)offDisp);
1254 else if (offDisp < 0)
1255 aStack.appendPrintf(" %s-%llx", pFrame->pSymPC->szName, -(int64_t)offDisp);
1256 else
1257 aStack.appendPrintf(" %s", pFrame->pSymPC->szName);
1258 }
1259 if (pFrame->pLinePC)
1260 aStack.appendPrintf(" [%s @ 0i%d]", pFrame->pLinePC->szFilename, pFrame->pLinePC->uLineNo);
1261 aStack.append("\n");
1262
1263 fBitFlags = fCurBitFlags;
1264 }
1265 }
1266 catch (std::bad_alloc &)
1267 {
1268 hrc = E_OUTOFMEMORY;
1269 }
1270
1271 ptrVM.vtable()->pfnDBGFR3StackWalkEnd(pFirstFrame);
1272 }
1273 else
1274 hrc = setErrorBoth(E_FAIL, vrc, tr("DBGFR3StackWalkBegin failed with %Rrc"), vrc);
1275
1276 /*
1277 * Resume the VM if we suspended it.
1278 */
1279 if (fPaused)
1280 {
1281 alock.release();
1282 ptrVM.vtable()->pfnVMR3Resume(ptrVM.rawUVM(), VMRESUMEREASON_USER);
1283 }
1284 }
1285 else
1286 hrc = setErrorBoth(E_FAIL, vrc, tr("Suspending the VM failed with %Rrc\n"), vrc);
1287 }
1288
1289 return hrc;
1290}
1291
1292/**
1293 * Resets VM statistics.
1294 *
1295 * @returns COM status code.
1296 * @param aPattern The selection pattern. A bit similar to filename globbing.
1297 */
1298HRESULT MachineDebugger::resetStats(const com::Utf8Str &aPattern)
1299{
1300 Console::SafeVMPtrQuiet ptrVM(mParent);
1301 if (!ptrVM.isOk())
1302 return setError(VBOX_E_INVALID_VM_STATE, tr("Machine is not running"));
1303
1304 ptrVM.vtable()->pfnSTAMR3Reset(ptrVM.rawUVM(), aPattern.c_str());
1305
1306 return S_OK;
1307}
1308
1309/**
1310 * Dumps VM statistics to the log.
1311 *
1312 * @returns COM status code.
1313 * @param aPattern The selection pattern. A bit similar to filename globbing.
1314 */
1315HRESULT MachineDebugger::dumpStats(const com::Utf8Str &aPattern)
1316{
1317 Console::SafeVMPtrQuiet ptrVM(mParent);
1318 if (!ptrVM.isOk())
1319 return setError(VBOX_E_INVALID_VM_STATE, tr("Machine is not running"));
1320
1321 ptrVM.vtable()->pfnSTAMR3Dump(ptrVM.rawUVM(), aPattern.c_str());
1322
1323 return S_OK;
1324}
1325
1326/**
1327 * Get the VM statistics in an XML format.
1328 *
1329 * @returns COM status code.
1330 * @param aPattern The selection pattern. A bit similar to filename globbing.
1331 * @param aWithDescriptions Whether to include the descriptions.
1332 * @param aStats The XML document containing the statistics.
1333 */
1334HRESULT MachineDebugger::getStats(const com::Utf8Str &aPattern, BOOL aWithDescriptions, com::Utf8Str &aStats)
1335{
1336 Console::SafeVMPtrQuiet ptrVM(mParent);
1337 if (!ptrVM.isOk())
1338 return setError(VBOX_E_INVALID_VM_STATE, tr("Machine is not running"));
1339
1340 char *pszSnapshot;
1341 int vrc = ptrVM.vtable()->pfnSTAMR3Snapshot(ptrVM.rawUVM(), aPattern.c_str(), &pszSnapshot, NULL, !!aWithDescriptions);
1342 if (RT_FAILURE(vrc))
1343 return vrc == VERR_NO_MEMORY ? E_OUTOFMEMORY : E_FAIL;
1344
1345 /** @todo this is horribly inefficient! And it's kinda difficult to tell whether it failed...
1346 * Must use UTF-8 or ASCII here and completely avoid these two extra copy operations.
1347 * Until that's done, this method is kind of useless for debugger statistics GUI because
1348 * of the amount statistics in a debug build. */
1349 HRESULT hrc = aStats.assignEx(pszSnapshot);
1350 ptrVM.vtable()->pfnSTAMR3SnapshotFree(ptrVM.rawUVM(), pszSnapshot);
1351
1352 return hrc;
1353}
1354
1355
1356/** Wrapper around TMR3GetCpuLoadPercents. */
1357HRESULT MachineDebugger::getCPULoad(ULONG aCpuId, ULONG *aPctExecuting, ULONG *aPctHalted, ULONG *aPctOther, LONG64 *aMsInterval)
1358{
1359 HRESULT hrc;
1360 Console::SafeVMPtrQuiet ptrVM(mParent);
1361 if (ptrVM.isOk())
1362 {
1363 uint8_t uPctExecuting = 0;
1364 uint8_t uPctHalted = 0;
1365 uint8_t uPctOther = 0;
1366 uint64_t msInterval = 0;
1367 int vrc = ptrVM.vtable()->pfnTMR3GetCpuLoadPercents(ptrVM.rawUVM(), aCpuId >= UINT32_MAX / 2 ? VMCPUID_ALL : aCpuId,
1368 &msInterval, &uPctExecuting, &uPctHalted, &uPctOther);
1369 if (RT_SUCCESS(vrc))
1370 {
1371 *aPctExecuting = uPctExecuting;
1372 *aPctHalted = uPctHalted;
1373 *aPctOther = uPctOther;
1374 *aMsInterval = msInterval;
1375 hrc = S_OK;
1376 }
1377 else
1378 hrc = setErrorVrc(vrc);
1379 }
1380 else
1381 hrc = setError(VBOX_E_INVALID_VM_STATE, tr("Machine is not running"));
1382 return hrc;
1383}
1384
1385
1386HRESULT MachineDebugger::takeGuestSample(const com::Utf8Str &aFilename, ULONG aUsInterval, LONG64 aUsSampleTime, ComPtr<IProgress> &pProgress)
1387{
1388 /*
1389 * The prologue.
1390 */
1391 LogFlowThisFunc(("\n"));
1392 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1393 Console::SafeVMPtr ptrVM(mParent);
1394 HRESULT hrc = ptrVM.rc();
1395 if (SUCCEEDED(hrc))
1396 {
1397 if (!m_hSampleReport)
1398 {
1399 m_strFilename = aFilename;
1400
1401 int vrc = ptrVM.vtable()->pfnDBGFR3SampleReportCreate(ptrVM.rawUVM(), aUsInterval,
1402 DBGF_SAMPLE_REPORT_F_STACK_REVERSE, &m_hSampleReport);
1403 if (RT_SUCCESS(vrc))
1404 {
1405 hrc = m_Progress.createObject();
1406 if (SUCCEEDED(hrc))
1407 {
1408 hrc = m_Progress->init(static_cast<IMachineDebugger*>(this),
1409 tr("Creating guest sample report..."),
1410 TRUE /* aCancelable */);
1411 if (SUCCEEDED(hrc))
1412 {
1413 vrc = ptrVM.vtable()->pfnDBGFR3SampleReportStart(m_hSampleReport, aUsSampleTime, i_dbgfProgressCallback,
1414 static_cast<MachineDebugger*>(this));
1415 if (RT_SUCCESS(vrc))
1416 hrc = m_Progress.queryInterfaceTo(pProgress.asOutParam());
1417 else
1418 hrc = setErrorVrc(vrc);
1419 }
1420 }
1421
1422 if (FAILED(hrc))
1423 {
1424 ptrVM.vtable()->pfnDBGFR3SampleReportRelease(m_hSampleReport);
1425 m_hSampleReport = NULL;
1426 }
1427 }
1428 else
1429 hrc = setErrorVrc(vrc);
1430 }
1431 else
1432 hrc = setError(VBOX_E_INVALID_VM_STATE, tr("A sample report is already in progress"));
1433 }
1434
1435 return hrc;
1436}
1437
1438/**
1439 * Hack for getting the user mode VM handle (UVM) and VMM function table.
1440 *
1441 * @returns COM status code
1442 * @param aMagicVersion The VMMR3VTABLE_MAGIC_VERSION value of the
1443 * caller so we can check that the function table
1444 * is compatible. (Otherwise, the caller can't
1445 * safely release the UVM reference.)
1446 * @param aUVM Where to store the vm handle. Since there is no
1447 * uintptr_t in COM, we're using the max integer. (No,
1448 * ULONG is not pointer sized!)
1449 * @param aVMMFunctionTable Where to store the vm handle.
1450 *
1451 * @remarks The returned handle must be passed to VMR3ReleaseUVM()!
1452 */
1453HRESULT MachineDebugger::getUVMAndVMMFunctionTable(LONG64 aMagicVersion, LONG64 *aVMMFunctionTable, LONG64 *aUVM)
1454{
1455 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1456
1457 /*
1458 * Make sure it is a local call.
1459 */
1460 RTTHREAD hThread = RTThreadSelf();
1461 if (hThread != NIL_RTTHREAD)
1462 {
1463 const char *pszName = RTThreadGetName(hThread);
1464 if ( !RTStrStartsWith(pszName, "ALIEN-") /* COM worker threads are aliens */
1465 && !RTStrStartsWith(pszName, "nspr-") /* XPCOM worker threads are nspr-X */ )
1466 {
1467 /*
1468 * Use safe VM pointer to get both the UVM and VMM function table.
1469 */
1470 Console::SafeVMPtr ptrVM(mParent);
1471 HRESULT hrc = ptrVM.rc();
1472 if (SUCCEEDED(hrc))
1473 {
1474 if (VMMR3VTABLE_IS_COMPATIBLE_EX(ptrVM.vtable()->uMagicVersion, (uint64_t)aMagicVersion))
1475 {
1476 ptrVM.vtable()->pfnVMR3RetainUVM(ptrVM.rawUVM());
1477 *aUVM = (intptr_t)ptrVM.rawUVM();
1478 *aVMMFunctionTable = (intptr_t)ptrVM.vtable();
1479 hrc = S_OK;
1480 }
1481 else
1482 hrc = setError(E_FAIL, tr("Incompatible VMM function table: %RX64 vs %RX64 (caller)"),
1483 ptrVM.vtable()->uMagicVersion, (uint64_t)aMagicVersion);
1484 }
1485 return hrc;
1486 }
1487 }
1488
1489 return setError(E_ACCESSDENIED, tr("The method getUVMAndVMMFunctionTable is only for local calls"));
1490}
1491
1492
1493
1494// public methods only for internal purposes
1495/////////////////////////////////////////////////////////////////////////////
1496
1497void MachineDebugger::i_flushQueuedSettings()
1498{
1499 mFlushMode = true;
1500 if (mSingleStepQueued != -1)
1501 {
1502 COMSETTER(SingleStep)(mSingleStepQueued);
1503 mSingleStepQueued = -1;
1504 }
1505 for (unsigned i = 0; i < EMEXECPOLICY_END; i++)
1506 if (maiQueuedEmExecPolicyParams[i] != UINT8_MAX)
1507 {
1508 i_setEmExecPolicyProperty((EMEXECPOLICY)i, RT_BOOL(maiQueuedEmExecPolicyParams[i]));
1509 maiQueuedEmExecPolicyParams[i] = UINT8_MAX;
1510 }
1511 if (mLogEnabledQueued != -1)
1512 {
1513 COMSETTER(LogEnabled)(mLogEnabledQueued);
1514 mLogEnabledQueued = -1;
1515 }
1516 if (mVirtualTimeRateQueued != UINT32_MAX)
1517 {
1518 COMSETTER(VirtualTimeRate)(mVirtualTimeRateQueued);
1519 mVirtualTimeRateQueued = UINT32_MAX;
1520 }
1521 mFlushMode = false;
1522}
1523
1524// private methods
1525/////////////////////////////////////////////////////////////////////////////
1526
1527bool MachineDebugger::i_queueSettings() const
1528{
1529 if (!mFlushMode)
1530 {
1531 // check if the machine is running
1532 MachineState_T machineState;
1533 mParent->COMGETTER(State)(&machineState);
1534 switch (machineState)
1535 {
1536 // queue the request
1537 default:
1538 return true;
1539
1540 case MachineState_Running:
1541 case MachineState_Paused:
1542 case MachineState_Stuck:
1543 case MachineState_LiveSnapshotting:
1544 case MachineState_Teleporting:
1545 break;
1546 }
1547 }
1548 return false;
1549}
1550/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use