VirtualBox

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

Last change on this file since 105301 was 103085, checked in by vboxsync, 10 months ago

Main,FE/VBoxManage,FE/VirtualBox,ValidationKit: Allow setting the primary VM execution engine to make it easier to force particular engine for testing, bugref:10583

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