VirtualBox

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

Last change on this file since 107505 was 107505, checked in by vboxsync, 3 weeks ago

src/VBox/Main/src-client/MachineDebuggerImpl.cpp: Fixed warning found by Parfait (uninitialized attributes). jiraref:VBP-1424

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