VirtualBox

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

Last change on this file since 102092 was 102092, checked in by vboxsync, 6 months ago

VMM/DBGF,DBGC,Main: Added DBGFR3RegNmQueryEx and fixed some issues with DBGFR3RegNmQueryAll that lead to assertions in Main and empty entries in VBoxManage output. Extended the 'r' and 'rg' debugger commands to make use of the two APIs, the first by appending '.' to a register (e.g. r @cr0.) and the latter by using 'all' as the register name.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 51.4 KB
Line 
1/* $Id: MachineDebuggerImpl.cpp 102092 2023-11-14 23:53:15Z 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 vrc=%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 vrc=%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 uint32_t iDst = 0;
1118 for (uint32_t iSrc = 0; iSrc < cRegs; iSrc++)
1119 if (paRegs[iSrc].pszName) /* skip padding entries */
1120 {
1121 char szHex[160];
1122 szHex[159] = szHex[0] = '\0';
1123 ssize_t cch = ptrVM.vtable()->pfnDBGFR3RegFormatValue(szHex, sizeof(szHex), &paRegs[iSrc].Val,
1124 paRegs[iSrc].enmType, true /*fSpecial*/);
1125 Assert(cch > 0); NOREF(cch);
1126 aNames[iDst] = paRegs[iSrc].pszName;
1127 aValues[iDst] = szHex;
1128 iDst++;
1129 }
1130
1131 /* If we skipped padding entries, resize the return arrays to the actual return size. */
1132 if (iDst < cRegs)
1133 {
1134 aValues.resize(iDst);
1135 aNames.resize(iDst);
1136 }
1137 }
1138 catch (std::bad_alloc &)
1139 {
1140 hrc = E_OUTOFMEMORY;
1141 }
1142 }
1143 else
1144 hrc = setErrorBoth(E_FAIL, vrc, tr("DBGFR3RegNmQueryAll failed with %Rrc"), vrc);
1145
1146 RTMemFree(paRegs);
1147 }
1148 else
1149 hrc = E_OUTOFMEMORY;
1150 }
1151 else
1152 hrc = setErrorBoth(E_FAIL, vrc, tr("DBGFR3RegNmQueryAllCount failed with %Rrc"), vrc);
1153 }
1154 return hrc;
1155}
1156
1157HRESULT MachineDebugger::setRegister(ULONG aCpuId, const com::Utf8Str &aName, const com::Utf8Str &aValue)
1158{
1159 RT_NOREF(aCpuId, aName, aValue);
1160 ReturnComNotImplemented();
1161}
1162
1163HRESULT MachineDebugger::setRegisters(ULONG aCpuId, const std::vector<com::Utf8Str> &aNames,
1164 const std::vector<com::Utf8Str> &aValues)
1165{
1166 RT_NOREF(aCpuId, aNames, aValues);
1167 ReturnComNotImplemented();
1168}
1169
1170HRESULT MachineDebugger::dumpGuestStack(ULONG aCpuId, com::Utf8Str &aStack)
1171{
1172 /*
1173 * The prologue.
1174 */
1175 LogFlowThisFunc(("\n"));
1176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1177 Console::SafeVMPtr ptrVM(mParent);
1178 HRESULT hrc = ptrVM.hrc();
1179 if (SUCCEEDED(hrc))
1180 {
1181 /*
1182 * There is currently a problem with the windows diggers and SMP, where
1183 * guest driver memory is being read from CPU zero in order to ensure that
1184 * we've got a consisten virtual memory view. If one of the other CPUs
1185 * initiates a rendezvous while we're unwinding the stack and trying to
1186 * read guest driver memory, we will deadlock.
1187 *
1188 * So, check the VM state and maybe suspend the VM before we continue.
1189 */
1190 int vrc = VINF_SUCCESS;
1191 bool fPaused = false;
1192 if (aCpuId != 0)
1193 {
1194 VMSTATE enmVmState = ptrVM.vtable()->pfnVMR3GetStateU(ptrVM.rawUVM());
1195 if ( enmVmState == VMSTATE_RUNNING
1196 || enmVmState == VMSTATE_RUNNING_LS)
1197 {
1198 alock.release();
1199 vrc = ptrVM.vtable()->pfnVMR3Suspend(ptrVM.rawUVM(), VMSUSPENDREASON_USER);
1200 alock.acquire();
1201 fPaused = RT_SUCCESS(vrc);
1202 }
1203 }
1204 if (RT_SUCCESS(vrc))
1205 {
1206 PCDBGFSTACKFRAME pFirstFrame;
1207 vrc = ptrVM.vtable()->pfnDBGFR3StackWalkBegin(ptrVM.rawUVM(), aCpuId, DBGFCODETYPE_GUEST, &pFirstFrame);
1208 if (RT_SUCCESS(vrc))
1209 {
1210 /*
1211 * Print header.
1212 */
1213 try
1214 {
1215 uint32_t fBitFlags = 0;
1216 for (PCDBGFSTACKFRAME pFrame = pFirstFrame;
1217 pFrame;
1218 pFrame = ptrVM.vtable()->pfnDBGFR3StackWalkNext(pFrame))
1219 {
1220 uint32_t const fCurBitFlags = pFrame->fFlags & (DBGFSTACKFRAME_FLAGS_16BIT | DBGFSTACKFRAME_FLAGS_32BIT | DBGFSTACKFRAME_FLAGS_64BIT);
1221 if (fCurBitFlags & DBGFSTACKFRAME_FLAGS_16BIT)
1222 {
1223 if (fCurBitFlags != fBitFlags)
1224 aStack.append("SS:BP Ret SS:BP Ret CS:EIP Arg0 Arg1 Arg2 Arg3 CS:EIP / Symbol [line]\n");
1225 aStack.appendPrintf("%04RX16:%04RX16 %04RX16:%04RX16 %04RX32:%08RX32 %08RX32 %08RX32 %08RX32 %08RX32",
1226 pFrame->AddrFrame.Sel,
1227 (uint16_t)pFrame->AddrFrame.off,
1228 pFrame->AddrReturnFrame.Sel,
1229 (uint16_t)pFrame->AddrReturnFrame.off,
1230 (uint32_t)pFrame->AddrReturnPC.Sel,
1231 (uint32_t)pFrame->AddrReturnPC.off,
1232 pFrame->Args.au32[0],
1233 pFrame->Args.au32[1],
1234 pFrame->Args.au32[2],
1235 pFrame->Args.au32[3]);
1236 }
1237 else if (fCurBitFlags & DBGFSTACKFRAME_FLAGS_32BIT)
1238 {
1239 if (fCurBitFlags != fBitFlags)
1240 aStack.append("EBP Ret EBP Ret CS:EIP Arg0 Arg1 Arg2 Arg3 CS:EIP / Symbol [line]\n");
1241 aStack.appendPrintf("%08RX32 %08RX32 %04RX32:%08RX32 %08RX32 %08RX32 %08RX32 %08RX32",
1242 (uint32_t)pFrame->AddrFrame.off,
1243 (uint32_t)pFrame->AddrReturnFrame.off,
1244 (uint32_t)pFrame->AddrReturnPC.Sel,
1245 (uint32_t)pFrame->AddrReturnPC.off,
1246 pFrame->Args.au32[0],
1247 pFrame->Args.au32[1],
1248 pFrame->Args.au32[2],
1249 pFrame->Args.au32[3]);
1250 }
1251 else if (fCurBitFlags & DBGFSTACKFRAME_FLAGS_64BIT)
1252 {
1253 if (fCurBitFlags != fBitFlags)
1254 aStack.append("RBP Ret SS:RBP Ret RIP CS:RIP / Symbol [line]\n");
1255 aStack.appendPrintf("%016RX64 %04RX16:%016RX64 %016RX64",
1256 (uint64_t)pFrame->AddrFrame.off,
1257 pFrame->AddrReturnFrame.Sel,
1258 (uint64_t)pFrame->AddrReturnFrame.off,
1259 (uint64_t)pFrame->AddrReturnPC.off);
1260 }
1261
1262 if (!pFrame->pSymPC)
1263 aStack.appendPrintf(fCurBitFlags & DBGFSTACKFRAME_FLAGS_64BIT
1264 ? " %RTsel:%016RGv"
1265 : fCurBitFlags & DBGFSTACKFRAME_FLAGS_32BIT
1266 ? " %RTsel:%08RGv"
1267 : " %RTsel:%04RGv"
1268 , pFrame->AddrPC.Sel, pFrame->AddrPC.off);
1269 else
1270 {
1271 RTGCINTPTR offDisp = pFrame->AddrPC.FlatPtr - pFrame->pSymPC->Value; /** @todo this isn't 100% correct for segmented stuff. */
1272 if (offDisp > 0)
1273 aStack.appendPrintf(" %s+%llx", pFrame->pSymPC->szName, (int64_t)offDisp);
1274 else if (offDisp < 0)
1275 aStack.appendPrintf(" %s-%llx", pFrame->pSymPC->szName, -(int64_t)offDisp);
1276 else
1277 aStack.appendPrintf(" %s", pFrame->pSymPC->szName);
1278 }
1279 if (pFrame->pLinePC)
1280 aStack.appendPrintf(" [%s @ 0i%d]", pFrame->pLinePC->szFilename, pFrame->pLinePC->uLineNo);
1281 aStack.append("\n");
1282
1283 fBitFlags = fCurBitFlags;
1284 }
1285 }
1286 catch (std::bad_alloc &)
1287 {
1288 hrc = E_OUTOFMEMORY;
1289 }
1290
1291 ptrVM.vtable()->pfnDBGFR3StackWalkEnd(pFirstFrame);
1292 }
1293 else
1294 hrc = setErrorBoth(E_FAIL, vrc, tr("DBGFR3StackWalkBegin failed with %Rrc"), vrc);
1295
1296 /*
1297 * Resume the VM if we suspended it.
1298 */
1299 if (fPaused)
1300 {
1301 alock.release();
1302 ptrVM.vtable()->pfnVMR3Resume(ptrVM.rawUVM(), VMRESUMEREASON_USER);
1303 }
1304 }
1305 else
1306 hrc = setErrorBoth(E_FAIL, vrc, tr("Suspending the VM failed with %Rrc\n"), vrc);
1307 }
1308
1309 return hrc;
1310}
1311
1312/**
1313 * Resets VM statistics.
1314 *
1315 * @returns COM status code.
1316 * @param aPattern The selection pattern. A bit similar to filename globbing.
1317 */
1318HRESULT MachineDebugger::resetStats(const com::Utf8Str &aPattern)
1319{
1320 Console::SafeVMPtrQuiet ptrVM(mParent);
1321 if (!ptrVM.isOk())
1322 return setError(VBOX_E_INVALID_VM_STATE, tr("Machine is not running"));
1323
1324 ptrVM.vtable()->pfnSTAMR3Reset(ptrVM.rawUVM(), aPattern.c_str());
1325
1326 return S_OK;
1327}
1328
1329/**
1330 * Dumps VM statistics to the log.
1331 *
1332 * @returns COM status code.
1333 * @param aPattern The selection pattern. A bit similar to filename globbing.
1334 */
1335HRESULT MachineDebugger::dumpStats(const com::Utf8Str &aPattern)
1336{
1337 Console::SafeVMPtrQuiet ptrVM(mParent);
1338 if (!ptrVM.isOk())
1339 return setError(VBOX_E_INVALID_VM_STATE, tr("Machine is not running"));
1340
1341 ptrVM.vtable()->pfnSTAMR3Dump(ptrVM.rawUVM(), aPattern.c_str());
1342
1343 return S_OK;
1344}
1345
1346/**
1347 * Get the VM statistics in an XML format.
1348 *
1349 * @returns COM status code.
1350 * @param aPattern The selection pattern. A bit similar to filename globbing.
1351 * @param aWithDescriptions Whether to include the descriptions.
1352 * @param aStats The XML document containing the statistics.
1353 */
1354HRESULT MachineDebugger::getStats(const com::Utf8Str &aPattern, BOOL aWithDescriptions, com::Utf8Str &aStats)
1355{
1356 Console::SafeVMPtrQuiet ptrVM(mParent);
1357 if (!ptrVM.isOk())
1358 return setError(VBOX_E_INVALID_VM_STATE, tr("Machine is not running"));
1359
1360 char *pszSnapshot;
1361 int vrc = ptrVM.vtable()->pfnSTAMR3Snapshot(ptrVM.rawUVM(), aPattern.c_str(), &pszSnapshot, NULL, !!aWithDescriptions);
1362 if (RT_FAILURE(vrc))
1363 return vrc == VERR_NO_MEMORY ? E_OUTOFMEMORY : E_FAIL;
1364
1365 /** @todo this is horribly inefficient! And it's kinda difficult to tell whether it failed...
1366 * Must use UTF-8 or ASCII here and completely avoid these two extra copy operations.
1367 * Until that's done, this method is kind of useless for debugger statistics GUI because
1368 * of the amount statistics in a debug build. */
1369 HRESULT hrc = aStats.assignEx(pszSnapshot);
1370 ptrVM.vtable()->pfnSTAMR3SnapshotFree(ptrVM.rawUVM(), pszSnapshot);
1371
1372 return hrc;
1373}
1374
1375
1376/** Wrapper around TMR3GetCpuLoadPercents. */
1377HRESULT MachineDebugger::getCPULoad(ULONG aCpuId, ULONG *aPctExecuting, ULONG *aPctHalted, ULONG *aPctOther, LONG64 *aMsInterval)
1378{
1379 HRESULT hrc;
1380 Console::SafeVMPtrQuiet ptrVM(mParent);
1381 if (ptrVM.isOk())
1382 {
1383 uint8_t uPctExecuting = 0;
1384 uint8_t uPctHalted = 0;
1385 uint8_t uPctOther = 0;
1386 uint64_t msInterval = 0;
1387 int vrc = ptrVM.vtable()->pfnTMR3GetCpuLoadPercents(ptrVM.rawUVM(), aCpuId >= UINT32_MAX / 2 ? VMCPUID_ALL : aCpuId,
1388 &msInterval, &uPctExecuting, &uPctHalted, &uPctOther);
1389 if (RT_SUCCESS(vrc))
1390 {
1391 *aPctExecuting = uPctExecuting;
1392 *aPctHalted = uPctHalted;
1393 *aPctOther = uPctOther;
1394 *aMsInterval = msInterval;
1395 hrc = S_OK;
1396 }
1397 else
1398 hrc = setErrorVrc(vrc);
1399 }
1400 else
1401 hrc = setError(VBOX_E_INVALID_VM_STATE, tr("Machine is not running"));
1402 return hrc;
1403}
1404
1405
1406HRESULT MachineDebugger::takeGuestSample(const com::Utf8Str &aFilename, ULONG aUsInterval, LONG64 aUsSampleTime, ComPtr<IProgress> &pProgress)
1407{
1408 /*
1409 * The prologue.
1410 */
1411 LogFlowThisFunc(("\n"));
1412 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1413 Console::SafeVMPtr ptrVM(mParent);
1414 HRESULT hrc = ptrVM.hrc();
1415 if (SUCCEEDED(hrc))
1416 {
1417 if (!m_hSampleReport)
1418 {
1419 m_strFilename = aFilename;
1420
1421 int vrc = ptrVM.vtable()->pfnDBGFR3SampleReportCreate(ptrVM.rawUVM(), aUsInterval,
1422 DBGF_SAMPLE_REPORT_F_STACK_REVERSE, &m_hSampleReport);
1423 if (RT_SUCCESS(vrc))
1424 {
1425 hrc = m_Progress.createObject();
1426 if (SUCCEEDED(hrc))
1427 {
1428 hrc = m_Progress->init(static_cast<IMachineDebugger*>(this),
1429 tr("Creating guest sample report..."),
1430 TRUE /* aCancelable */);
1431 if (SUCCEEDED(hrc))
1432 {
1433 vrc = ptrVM.vtable()->pfnDBGFR3SampleReportStart(m_hSampleReport, aUsSampleTime, i_dbgfProgressCallback,
1434 static_cast<MachineDebugger*>(this));
1435 if (RT_SUCCESS(vrc))
1436 hrc = m_Progress.queryInterfaceTo(pProgress.asOutParam());
1437 else
1438 hrc = setErrorVrc(vrc);
1439 }
1440 }
1441
1442 if (FAILED(hrc))
1443 {
1444 ptrVM.vtable()->pfnDBGFR3SampleReportRelease(m_hSampleReport);
1445 m_hSampleReport = NULL;
1446 }
1447 }
1448 else
1449 hrc = setErrorVrc(vrc);
1450 }
1451 else
1452 hrc = setError(VBOX_E_INVALID_VM_STATE, tr("A sample report is already in progress"));
1453 }
1454
1455 return hrc;
1456}
1457
1458/**
1459 * Hack for getting the user mode VM handle (UVM) and VMM function table.
1460 *
1461 * @returns COM status code
1462 * @param aMagicVersion The VMMR3VTABLE_MAGIC_VERSION value of the
1463 * caller so we can check that the function table
1464 * is compatible. (Otherwise, the caller can't
1465 * safely release the UVM reference.)
1466 * @param aUVM Where to store the vm handle. Since there is no
1467 * uintptr_t in COM, we're using the max integer. (No,
1468 * ULONG is not pointer sized!)
1469 * @param aVMMFunctionTable Where to store the vm handle.
1470 *
1471 * @remarks The returned handle must be passed to VMR3ReleaseUVM()!
1472 */
1473HRESULT MachineDebugger::getUVMAndVMMFunctionTable(LONG64 aMagicVersion, LONG64 *aVMMFunctionTable, LONG64 *aUVM)
1474{
1475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1476
1477 /*
1478 * Make sure it is a local call.
1479 */
1480 RTTHREAD hThread = RTThreadSelf();
1481 if (hThread != NIL_RTTHREAD)
1482 {
1483 const char *pszName = RTThreadGetName(hThread);
1484 if ( !RTStrStartsWith(pszName, "ALIEN-") /* COM worker threads are aliens */
1485 && !RTStrStartsWith(pszName, "nspr-") /* XPCOM worker threads are nspr-X */ )
1486 {
1487 /*
1488 * Use safe VM pointer to get both the UVM and VMM function table.
1489 */
1490 Console::SafeVMPtr ptrVM(mParent);
1491 HRESULT hrc = ptrVM.hrc();
1492 if (SUCCEEDED(hrc))
1493 {
1494 if (VMMR3VTABLE_IS_COMPATIBLE_EX(ptrVM.vtable()->uMagicVersion, (uint64_t)aMagicVersion))
1495 {
1496 ptrVM.vtable()->pfnVMR3RetainUVM(ptrVM.rawUVM());
1497 *aUVM = (intptr_t)ptrVM.rawUVM();
1498 *aVMMFunctionTable = (intptr_t)ptrVM.vtable();
1499 hrc = S_OK;
1500 }
1501 else
1502 hrc = setError(E_FAIL, tr("Incompatible VMM function table: %RX64 vs %RX64 (caller)"),
1503 ptrVM.vtable()->uMagicVersion, (uint64_t)aMagicVersion);
1504 }
1505 return hrc;
1506 }
1507 }
1508
1509 return setError(E_ACCESSDENIED, tr("The method getUVMAndVMMFunctionTable is only for local calls"));
1510}
1511
1512
1513
1514// public methods only for internal purposes
1515/////////////////////////////////////////////////////////////////////////////
1516
1517void MachineDebugger::i_flushQueuedSettings()
1518{
1519 mFlushMode = true;
1520 if (mSingleStepQueued != -1)
1521 {
1522 COMSETTER(SingleStep)(mSingleStepQueued);
1523 mSingleStepQueued = -1;
1524 }
1525 for (unsigned i = 0; i < EMEXECPOLICY_END; i++)
1526 if (maiQueuedEmExecPolicyParams[i] != UINT8_MAX)
1527 {
1528 i_setEmExecPolicyProperty((EMEXECPOLICY)i, RT_BOOL(maiQueuedEmExecPolicyParams[i]));
1529 maiQueuedEmExecPolicyParams[i] = UINT8_MAX;
1530 }
1531 if (mLogEnabledQueued != -1)
1532 {
1533 COMSETTER(LogEnabled)(mLogEnabledQueued);
1534 mLogEnabledQueued = -1;
1535 }
1536 if (mVirtualTimeRateQueued != UINT32_MAX)
1537 {
1538 COMSETTER(VirtualTimeRate)(mVirtualTimeRateQueued);
1539 mVirtualTimeRateQueued = UINT32_MAX;
1540 }
1541 mFlushMode = false;
1542}
1543
1544// private methods
1545/////////////////////////////////////////////////////////////////////////////
1546
1547bool MachineDebugger::i_queueSettings() const
1548{
1549 if (!mFlushMode)
1550 {
1551 // check if the machine is running
1552 MachineState_T machineState;
1553 mParent->COMGETTER(State)(&machineState);
1554 switch (machineState)
1555 {
1556 // queue the request
1557 default:
1558 return true;
1559
1560 case MachineState_Running:
1561 case MachineState_Paused:
1562 case MachineState_Stuck:
1563 case MachineState_LiveSnapshotting:
1564 case MachineState_Teleporting:
1565 break;
1566 }
1567 }
1568 return false;
1569}
1570/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use