VirtualBox

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

Last change on this file since 107772 was 107611, checked in by vboxsync, 6 weeks ago

Main/src-client/MachineDebuggerImpl.cpp: Missing member initializer, bugref:3409

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