VirtualBox

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

Last change on this file since 98273 was 98262, checked in by vboxsync, 23 months ago

Main: rc() -> hrc()/vrc(). bugref:10223

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