VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/STAM.cpp@ 45026

Last change on this file since 45026 was 44399, checked in by vboxsync, 12 years ago

DBGF,DBGC,++: PVM -> PUVM. Some refactoring and cleanup as well.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 76.1 KB
Line 
1/* $Id: STAM.cpp 44399 2013-01-27 21:12:53Z vboxsync $ */
2/** @file
3 * STAM - The Statistics Manager.
4 */
5
6/*
7 * Copyright (C) 2006-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/** @page pg_stam STAM - The Statistics Manager
19 *
20 * The purpose for the statistics manager is to present the rest of the system
21 * with a somewhat uniform way of accessing VMM statistics. STAM sports a
22 * couple of different APIs for accessing them: STAMR3EnumU, STAMR3SnapshotU,
23 * STAMR3DumpU, STAMR3DumpToReleaseLogU and the debugger. Main is exposing the
24 * XML based one, STAMR3SnapshotU.
25 *
26 * The rest of the VMM together with the devices and drivers registers their
27 * statistics with STAM giving them a name. The name is hierarchical, the
28 * components separated by slashes ('/') and must start with a slash.
29 *
30 * Each item registered with STAM - also, half incorrectly, called a sample -
31 * has a type, unit, visibility, data pointer and description associated with it
32 * in addition to the name (described above). The type tells STAM what kind of
33 * structure the pointer is pointing to. The visibility allows unused
34 * statistics from cluttering the output or showing up in the GUI. All the bits
35 * together makes STAM able to present the items in a sensible way to the user.
36 * Some types also allows STAM to reset the data, which is very convenient when
37 * digging into specific operations and such.
38 *
39 * PS. The VirtualBox Debugger GUI has a viewer for inspecting the statistics
40 * STAM provides. You will also find statistics in the release and debug logs.
41 * And as mentioned in the introduction, the debugger console features a couple
42 * of command: .stats and .statsreset.
43 *
44 * @see grp_stam
45 */
46
47/*******************************************************************************
48* Header Files *
49*******************************************************************************/
50#define LOG_GROUP LOG_GROUP_STAM
51#include <VBox/vmm/stam.h>
52#include "STAMInternal.h"
53#include <VBox/vmm/vm.h>
54#include <VBox/vmm/uvm.h>
55#include <VBox/err.h>
56#include <VBox/dbg.h>
57#include <VBox/log.h>
58
59#include <iprt/assert.h>
60#include <iprt/asm.h>
61#include <iprt/alloc.h>
62#include <iprt/stream.h>
63#include <iprt/string.h>
64
65
66/*******************************************************************************
67* Structures and Typedefs *
68*******************************************************************************/
69/**
70 * Argument structure for stamR3PrintOne().
71 */
72typedef struct STAMR3PRINTONEARGS
73{
74 PUVM pUVM;
75 void *pvArg;
76 DECLCALLBACKMEMBER(void, pfnPrintf)(struct STAMR3PRINTONEARGS *pvArg, const char *pszFormat, ...);
77} STAMR3PRINTONEARGS, *PSTAMR3PRINTONEARGS;
78
79
80/**
81 * Argument structure to stamR3EnumOne().
82 */
83typedef struct STAMR3ENUMONEARGS
84{
85 PVM pVM;
86 PFNSTAMR3ENUM pfnEnum;
87 void *pvUser;
88} STAMR3ENUMONEARGS, *PSTAMR3ENUMONEARGS;
89
90
91/**
92 * The snapshot status structure.
93 * Argument package passed to stamR3SnapshotOne, stamR3SnapshotPrintf and stamR3SnapshotOutput.
94 */
95typedef struct STAMR3SNAPSHOTONE
96{
97 /** Pointer to the buffer start. */
98 char *pszStart;
99 /** Pointer to the buffer end. */
100 char *pszEnd;
101 /** Pointer to the current buffer position. */
102 char *psz;
103 /** Pointer to the VM. */
104 PVM pVM;
105 /** The number of bytes allocated. */
106 size_t cbAllocated;
107 /** The status code. */
108 int rc;
109 /** Whether to include the description strings. */
110 bool fWithDesc;
111} STAMR3SNAPSHOTONE, *PSTAMR3SNAPSHOTONE;
112
113
114/**
115 * Init record for a ring-0 statistic sample.
116 */
117typedef struct STAMR0SAMPLE
118{
119 /** The GVMMSTATS structure offset of the variable. */
120 unsigned offVar;
121 /** The type. */
122 STAMTYPE enmType;
123 /** The unit. */
124 STAMUNIT enmUnit;
125 /** The name. */
126 const char *pszName;
127 /** The description. */
128 const char *pszDesc;
129} STAMR0SAMPLE;
130
131
132/*******************************************************************************
133* Internal Functions *
134*******************************************************************************/
135static int stamR3RegisterU(PUVM pUVM, void *pvSample, PFNSTAMR3CALLBACKRESET pfnReset, PFNSTAMR3CALLBACKPRINT pfnPrint,
136 STAMTYPE enmType, STAMVISIBILITY enmVisibility, const char *pszName, STAMUNIT enmUnit, const char *pszDesc);
137static int stamR3ResetOne(PSTAMDESC pDesc, void *pvArg);
138static DECLCALLBACK(void) stamR3EnumLogPrintf(PSTAMR3PRINTONEARGS pvArg, const char *pszFormat, ...);
139static DECLCALLBACK(void) stamR3EnumRelLogPrintf(PSTAMR3PRINTONEARGS pvArg, const char *pszFormat, ...);
140static DECLCALLBACK(void) stamR3EnumPrintf(PSTAMR3PRINTONEARGS pvArg, const char *pszFormat, ...);
141static int stamR3SnapshotOne(PSTAMDESC pDesc, void *pvArg);
142static int stamR3SnapshotPrintf(PSTAMR3SNAPSHOTONE pThis, const char *pszFormat, ...);
143static int stamR3PrintOne(PSTAMDESC pDesc, void *pvArg);
144static int stamR3EnumOne(PSTAMDESC pDesc, void *pvArg);
145static bool stamR3MultiMatch(const char * const *papszExpressions, unsigned cExpressions, unsigned *piExpression, const char *pszName);
146static char ** stamR3SplitPattern(const char *pszPat, unsigned *pcExpressions, char **ppszCopy);
147static int stamR3EnumU(PUVM pUVM, const char *pszPat, bool fUpdateRing0, int (pfnCallback)(PSTAMDESC pDesc, void *pvArg), void *pvArg);
148static void stamR3Ring0StatsRegisterU(PUVM pUVM);
149static void stamR3Ring0StatsUpdateU(PUVM pUVM, const char *pszPat);
150static void stamR3Ring0StatsUpdateMultiU(PUVM pUVM, const char * const *papszExpressions, unsigned cExpressions);
151
152#ifdef VBOX_WITH_DEBUGGER
153static FNDBGCCMD stamR3CmdStats;
154static DECLCALLBACK(void) stamR3EnumDbgfPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...);
155static FNDBGCCMD stamR3CmdStatsReset;
156#endif
157
158
159/*******************************************************************************
160* Global Variables *
161*******************************************************************************/
162#ifdef VBOX_WITH_DEBUGGER
163/** Pattern argument. */
164static const DBGCVARDESC g_aArgPat[] =
165{
166 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
167 { 0, 1, DBGCVAR_CAT_STRING, 0, "pattern", "Which samples the command shall be applied to. Use '*' as wildcard. Use ';' to separate expression." }
168};
169
170/** Command descriptors. */
171static const DBGCCMD g_aCmds[] =
172{
173 /* pszCmd, cArgsMin, cArgsMax, paArgDesc, cArgDescs, fFlags, pfnHandler pszSyntax, ....pszDescription */
174 { "stats", 0, 1, &g_aArgPat[0], RT_ELEMENTS(g_aArgPat), 0, stamR3CmdStats, "[pattern]", "Display statistics." },
175 { "statsreset", 0, 1, &g_aArgPat[0], RT_ELEMENTS(g_aArgPat), 0, stamR3CmdStatsReset,"[pattern]", "Resets statistics." }
176};
177#endif
178
179
180/**
181 * The GVMM mapping records - sans the host cpus.
182 */
183static const STAMR0SAMPLE g_aGVMMStats[] =
184{
185 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cHaltCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/HaltCalls", "The number of calls to GVMMR0SchedHalt." },
186 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cHaltBlocking), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/HaltBlocking", "The number of times we did go to sleep in GVMMR0SchedHalt." },
187 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cHaltTimeouts), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/HaltTimeouts", "The number of times we timed out in GVMMR0SchedHalt." },
188 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cHaltNotBlocking), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/HaltNotBlocking", "The number of times we didn't go to sleep in GVMMR0SchedHalt." },
189 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cHaltWakeUps), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/HaltWakeUps", "The number of wake ups done during GVMMR0SchedHalt." },
190 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cWakeUpCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/WakeUpCalls", "The number of calls to GVMMR0WakeUp." },
191 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cWakeUpNotHalted), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/WakeUpNotHalted", "The number of times the EMT thread wasn't actually halted when GVMMR0WakeUp was called." },
192 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cWakeUpWakeUps), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/WakeUpWakeUps", "The number of wake ups done during GVMMR0WakeUp (not counting the explicit one)." },
193 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cPokeCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/PokeCalls", "The number of calls to GVMMR0Poke." },
194 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cPokeNotBusy), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/PokeNotBusy", "The number of times the EMT thread wasn't actually busy when GVMMR0Poke was called." },
195 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cPollCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/PollCalls", "The number of calls to GVMMR0SchedPoll." },
196 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cPollHalts), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/PollHalts", "The number of times the EMT has halted in a GVMMR0SchedPoll call." },
197 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cPollWakeUps), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/PollWakeUps", "The number of wake ups done during GVMMR0SchedPoll." },
198
199 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cHaltCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/HaltCalls", "The number of calls to GVMMR0SchedHalt." },
200 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cHaltBlocking), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/HaltBlocking", "The number of times we did go to sleep in GVMMR0SchedHalt." },
201 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cHaltTimeouts), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/HaltTimeouts", "The number of times we timed out in GVMMR0SchedHalt." },
202 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cHaltNotBlocking), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/HaltNotBlocking", "The number of times we didn't go to sleep in GVMMR0SchedHalt." },
203 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cHaltWakeUps), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/HaltWakeUps", "The number of wake ups done during GVMMR0SchedHalt." },
204 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cWakeUpCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/WakeUpCalls", "The number of calls to GVMMR0WakeUp." },
205 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cWakeUpNotHalted), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/WakeUpNotHalted", "The number of times the EMT thread wasn't actually halted when GVMMR0WakeUp was called." },
206 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cWakeUpWakeUps), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/WakeUpWakeUps", "The number of wake ups done during GVMMR0WakeUp (not counting the explicit one)." },
207 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cPokeCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/PokeCalls", "The number of calls to GVMMR0Poke." },
208 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cPokeNotBusy), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/PokeNotBusy", "The number of times the EMT thread wasn't actually busy when GVMMR0Poke was called." },
209 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cPollCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/PollCalls", "The number of calls to GVMMR0SchedPoll." },
210 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cPollHalts), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/PollHalts", "The number of times the EMT has halted in a GVMMR0SchedPoll call." },
211 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cPollWakeUps), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/PollWakeUps", "The number of wake ups done during GVMMR0SchedPoll." },
212
213 { RT_UOFFSETOF(GVMMSTATS, cVMs), STAMTYPE_U32, STAMUNIT_CALLS, "/GVMM/VMs", "The number of VMs accessible to the caller." },
214 { RT_UOFFSETOF(GVMMSTATS, cEMTs), STAMTYPE_U32, STAMUNIT_CALLS, "/GVMM/EMTs", "The number of emulation threads." },
215 { RT_UOFFSETOF(GVMMSTATS, cHostCpus), STAMTYPE_U32, STAMUNIT_CALLS, "/GVMM/HostCPUs", "The number of host CPUs." },
216};
217
218
219/**
220 * The GMM mapping records.
221 */
222static const STAMR0SAMPLE g_aGMMStats[] =
223{
224 { RT_UOFFSETOF(GMMSTATS, cMaxPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/cMaxPages", "The maximum number of pages GMM is allowed to allocate." },
225 { RT_UOFFSETOF(GMMSTATS, cReservedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/cReservedPages", "The number of pages that has been reserved." },
226 { RT_UOFFSETOF(GMMSTATS, cOverCommittedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/cOverCommittedPages", "The number of pages that we have over-committed in reservations." },
227 { RT_UOFFSETOF(GMMSTATS, cAllocatedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/cAllocatedPages", "The number of actually allocated (committed if you like) pages." },
228 { RT_UOFFSETOF(GMMSTATS, cSharedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/cSharedPages", "The number of pages that are shared. A subset of cAllocatedPages." },
229 { RT_UOFFSETOF(GMMSTATS, cDuplicatePages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/cDuplicatePages", "The number of pages that are actually shared between VMs." },
230 { RT_UOFFSETOF(GMMSTATS, cLeftBehindSharedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/cLeftBehindSharedPages", "The number of pages that are shared that has been left behind by VMs not doing proper cleanups." },
231 { RT_UOFFSETOF(GMMSTATS, cBalloonedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/cBalloonedPages", "The number of current ballooned pages." },
232 { RT_UOFFSETOF(GMMSTATS, cChunks), STAMTYPE_U32, STAMUNIT_COUNT, "/GMM/cChunks", "The number of allocation chunks." },
233 { RT_UOFFSETOF(GMMSTATS, cFreedChunks), STAMTYPE_U32, STAMUNIT_COUNT, "/GMM/cFreedChunks", "The number of freed chunks ever." },
234 { RT_UOFFSETOF(GMMSTATS, cShareableModules), STAMTYPE_U32, STAMUNIT_COUNT, "/GMM/cShareableModules", "The number of shareable modules." },
235 { RT_UOFFSETOF(GMMSTATS, VMStats.Reserved.cBasePages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/VM/Reserved/cBasePages", "The amount of base memory (RAM, ROM, ++) reserved by the VM." },
236 { RT_UOFFSETOF(GMMSTATS, VMStats.Reserved.cShadowPages), STAMTYPE_U32, STAMUNIT_PAGES, "/GMM/VM/Reserved/cShadowPages", "The amount of memory reserved for shadow/nested page tables." },
237 { RT_UOFFSETOF(GMMSTATS, VMStats.Reserved.cFixedPages), STAMTYPE_U32, STAMUNIT_PAGES, "/GMM/VM/Reserved/cFixedPages", "The amount of memory reserved for fixed allocations like MMIO2 and the hyper heap." },
238 { RT_UOFFSETOF(GMMSTATS, VMStats.Allocated.cBasePages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/VM/Allocated/cBasePages", "The amount of base memory (RAM, ROM, ++) allocated by the VM." },
239 { RT_UOFFSETOF(GMMSTATS, VMStats.Allocated.cShadowPages), STAMTYPE_U32, STAMUNIT_PAGES, "/GMM/VM/Allocated/cShadowPages", "The amount of memory allocated for shadow/nested page tables." },
240 { RT_UOFFSETOF(GMMSTATS, VMStats.Allocated.cFixedPages), STAMTYPE_U32, STAMUNIT_PAGES, "/GMM/VM/Allocated/cFixedPages", "The amount of memory allocated for fixed allocations like MMIO2 and the hyper heap." },
241 { RT_UOFFSETOF(GMMSTATS, VMStats.cPrivatePages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/VM/cPrivatePages", "The current number of private pages." },
242 { RT_UOFFSETOF(GMMSTATS, VMStats.cSharedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/VM/cSharedPages", "The current number of shared pages." },
243 { RT_UOFFSETOF(GMMSTATS, VMStats.cBalloonedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/VM/cBalloonedPages", "The current number of ballooned pages." },
244 { RT_UOFFSETOF(GMMSTATS, VMStats.cMaxBalloonedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/VM/cMaxBalloonedPages", "The max number of pages that can be ballooned." },
245 { RT_UOFFSETOF(GMMSTATS, VMStats.cReqBalloonedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/VM/cReqBalloonedPages", "The number of pages we've currently requested the guest to give us." },
246 { RT_UOFFSETOF(GMMSTATS, VMStats.cReqActuallyBalloonedPages),STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/VM/cReqActuallyBalloonedPages","The number of pages the guest has given us in response to the request." },
247 { RT_UOFFSETOF(GMMSTATS, VMStats.cReqDeflatePages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/VM/cReqDeflatePages", "The number of pages we've currently requested the guest to take back." },
248 { RT_UOFFSETOF(GMMSTATS, VMStats.cShareableModules), STAMTYPE_U32, STAMUNIT_COUNT, "/GMM/VM/cShareableModules", "The number of shareable modules traced by the VM." },
249 { RT_UOFFSETOF(GMMSTATS, VMStats.enmPolicy), STAMTYPE_U32, STAMUNIT_NONE, "/GMM/VM/enmPolicy", "The current over-commit policy." },
250 { RT_UOFFSETOF(GMMSTATS, VMStats.enmPriority), STAMTYPE_U32, STAMUNIT_NONE, "/GMM/VM/enmPriority", "The VM priority for arbitrating VMs in low and out of memory situation." },
251 { RT_UOFFSETOF(GMMSTATS, VMStats.fBallooningEnabled), STAMTYPE_BOOL, STAMUNIT_NONE, "/GMM/VM/fBallooningEnabled", "Whether ballooning is enabled or not." },
252 { RT_UOFFSETOF(GMMSTATS, VMStats.fBallooningEnabled), STAMTYPE_BOOL, STAMUNIT_NONE, "/GMM/VM/fSharedPagingEnabled", "Whether shared paging is enabled or not." },
253 { RT_UOFFSETOF(GMMSTATS, VMStats.fBallooningEnabled), STAMTYPE_BOOL, STAMUNIT_NONE, "/GMM/VM/fMayAllocate", "Whether the VM is allowed to allocate memory or not." },
254};
255
256
257/**
258 * Initializes the STAM.
259 *
260 * @returns VBox status code.
261 * @param pVM Pointer to the VM.
262 */
263VMMR3DECL(int) STAMR3InitUVM(PUVM pUVM)
264{
265 LogFlow(("STAMR3Init\n"));
266
267 /*
268 * Assert alignment and sizes.
269 */
270 AssertCompile(sizeof(pUVM->stam.s) <= sizeof(pUVM->stam.padding));
271 AssertRelease(sizeof(pUVM->stam.s) <= sizeof(pUVM->stam.padding));
272
273 /*
274 * Setup any fixed pointers and offsets.
275 */
276 int rc = RTSemRWCreate(&pUVM->stam.s.RWSem);
277 AssertRCReturn(rc, rc);
278
279 /*
280 * Register the ring-0 statistics (GVMM/GMM).
281 */
282 stamR3Ring0StatsRegisterU(pUVM);
283
284#ifdef VBOX_WITH_DEBUGGER
285 /*
286 * Register debugger commands.
287 */
288 static bool fRegisteredCmds = false;
289 if (!fRegisteredCmds)
290 {
291 rc = DBGCRegisterCommands(&g_aCmds[0], RT_ELEMENTS(g_aCmds));
292 if (RT_SUCCESS(rc))
293 fRegisteredCmds = true;
294 }
295#endif
296
297 return VINF_SUCCESS;
298}
299
300
301/**
302 * Terminates the STAM.
303 *
304 * @param pUVM Pointer to the user mode VM structure.
305 */
306VMMR3DECL(void) STAMR3TermUVM(PUVM pUVM)
307{
308 /*
309 * Free used memory and the RWLock.
310 */
311 PSTAMDESC pCur = pUVM->stam.s.pHead;
312 while (pCur)
313 {
314 void *pvFree = pCur;
315 pCur = pCur->pNext;
316 RTMemFree(pvFree);
317 }
318 pUVM->stam.s.pHead = NULL;
319
320 Assert(pUVM->stam.s.RWSem != NIL_RTSEMRW);
321 RTSemRWDestroy(pUVM->stam.s.RWSem);
322 pUVM->stam.s.RWSem = NIL_RTSEMRW;
323}
324
325
326/**
327 * Registers a sample with the statistics manager.
328 *
329 * Statistics are maintained on a per VM basis and is normally registered
330 * during the VM init stage, but there is nothing preventing you from
331 * register them at runtime.
332 *
333 * Use STAMR3Deregister() to deregister statistics at runtime, however do
334 * not bother calling at termination time.
335 *
336 * It is not possible to register the same sample twice.
337 *
338 * @returns VBox status.
339 * @param pUVM Pointer to the user mode VM structure.
340 * @param pvSample Pointer to the sample.
341 * @param enmType Sample type. This indicates what pvSample is pointing at.
342 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
343 * @param pszName Sample name. The name is on this form "/<component>/<sample>".
344 * Further nesting is possible.
345 * @param enmUnit Sample unit.
346 * @param pszDesc Sample description.
347 */
348VMMR3DECL(int) STAMR3RegisterU(PUVM pUVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, const char *pszName, STAMUNIT enmUnit, const char *pszDesc)
349{
350 AssertReturn(enmType != STAMTYPE_CALLBACK, VERR_INVALID_PARAMETER);
351 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
352 return stamR3RegisterU(pUVM, pvSample, NULL, NULL, enmType, enmVisibility, pszName, enmUnit, pszDesc);
353}
354
355
356/**
357 * Registers a sample with the statistics manager.
358 *
359 * Statistics are maintained on a per VM basis and is normally registered
360 * during the VM init stage, but there is nothing preventing you from
361 * register them at runtime.
362 *
363 * Use STAMR3Deregister() to deregister statistics at runtime, however do
364 * not bother calling at termination time.
365 *
366 * It is not possible to register the same sample twice.
367 *
368 * @returns VBox status.
369 * @param pVM Pointer to the VM.
370 * @param pvSample Pointer to the sample.
371 * @param enmType Sample type. This indicates what pvSample is pointing at.
372 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
373 * @param pszName Sample name. The name is on this form "/<component>/<sample>".
374 * Further nesting is possible.
375 * @param enmUnit Sample unit.
376 * @param pszDesc Sample description.
377 */
378VMMR3DECL(int) STAMR3Register(PVM pVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, const char *pszName, STAMUNIT enmUnit, const char *pszDesc)
379{
380 AssertReturn(enmType != STAMTYPE_CALLBACK, VERR_INVALID_PARAMETER);
381 return stamR3RegisterU(pVM->pUVM, pvSample, NULL, NULL, enmType, enmVisibility, pszName, enmUnit, pszDesc);
382}
383
384
385/**
386 * Same as STAMR3RegisterU except that the name is specified in a
387 * RTStrPrintf like fashion.
388 *
389 * @returns VBox status.
390 * @param pUVM Pointer to the user mode VM structure.
391 * @param pvSample Pointer to the sample.
392 * @param enmType Sample type. This indicates what pvSample is pointing at.
393 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
394 * @param enmUnit Sample unit.
395 * @param pszDesc Sample description.
396 * @param pszName The sample name format string.
397 * @param ... Arguments to the format string.
398 */
399VMMR3DECL(int) STAMR3RegisterFU(PUVM pUVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
400 const char *pszDesc, const char *pszName, ...)
401{
402 va_list args;
403 va_start(args, pszName);
404 int rc = STAMR3RegisterVU(pUVM, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName, args);
405 va_end(args);
406 return rc;
407}
408
409
410/**
411 * Same as STAMR3Register except that the name is specified in a
412 * RTStrPrintf like fashion.
413 *
414 * @returns VBox status.
415 * @param pVM Pointer to the VM.
416 * @param pvSample Pointer to the sample.
417 * @param enmType Sample type. This indicates what pvSample is pointing at.
418 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
419 * @param enmUnit Sample unit.
420 * @param pszDesc Sample description.
421 * @param pszName The sample name format string.
422 * @param ... Arguments to the format string.
423 */
424VMMR3DECL(int) STAMR3RegisterF(PVM pVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
425 const char *pszDesc, const char *pszName, ...)
426{
427 va_list args;
428 va_start(args, pszName);
429 int rc = STAMR3RegisterVU(pVM->pUVM, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName, args);
430 va_end(args);
431 return rc;
432}
433
434
435/**
436 * Same as STAMR3Register except that the name is specified in a
437 * RTStrPrintfV like fashion.
438 *
439 * @returns VBox status.
440 * @param pVM Pointer to the VM.
441 * @param pvSample Pointer to the sample.
442 * @param enmType Sample type. This indicates what pvSample is pointing at.
443 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
444 * @param enmUnit Sample unit.
445 * @param pszDesc Sample description.
446 * @param pszName The sample name format string.
447 * @param args Arguments to the format string.
448 */
449VMMR3DECL(int) STAMR3RegisterVU(PUVM pUVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
450 const char *pszDesc, const char *pszName, va_list args)
451{
452 AssertReturn(enmType != STAMTYPE_CALLBACK, VERR_INVALID_PARAMETER);
453
454 char *pszFormattedName;
455 RTStrAPrintfV(&pszFormattedName, pszName, args);
456 if (!pszFormattedName)
457 return VERR_NO_MEMORY;
458
459 int rc = STAMR3RegisterU(pUVM, pvSample, enmType, enmVisibility, pszFormattedName, enmUnit, pszDesc);
460 RTStrFree(pszFormattedName);
461 return rc;
462}
463
464
465/**
466 * Same as STAMR3Register except that the name is specified in a
467 * RTStrPrintfV like fashion.
468 *
469 * @returns VBox status.
470 * @param pVM Pointer to the VM.
471 * @param pvSample Pointer to the sample.
472 * @param enmType Sample type. This indicates what pvSample is pointing at.
473 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
474 * @param enmUnit Sample unit.
475 * @param pszDesc Sample description.
476 * @param pszName The sample name format string.
477 * @param args Arguments to the format string.
478 */
479VMMR3DECL(int) STAMR3RegisterV(PVM pVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
480 const char *pszDesc, const char *pszName, va_list args)
481{
482 return STAMR3RegisterVU(pVM->pUVM, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName, args);
483}
484
485
486/**
487 * Similar to STAMR3Register except for the two callbacks, the implied type (STAMTYPE_CALLBACK),
488 * and name given in an RTStrPrintf like fashion.
489 *
490 * @returns VBox status.
491 * @param pVM Pointer to the VM.
492 * @param pvSample Pointer to the sample.
493 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
494 * @param enmUnit Sample unit.
495 * @param pfnReset Callback for resetting the sample. NULL should be used if the sample can't be reset.
496 * @param pfnPrint Print the sample.
497 * @param pszDesc Sample description.
498 * @param pszName The sample name format string.
499 * @param ... Arguments to the format string.
500 * @remark There is currently no device or driver variant of this API. Add one if it should become necessary!
501 */
502VMMR3DECL(int) STAMR3RegisterCallback(PVM pVM, void *pvSample, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
503 PFNSTAMR3CALLBACKRESET pfnReset, PFNSTAMR3CALLBACKPRINT pfnPrint,
504 const char *pszDesc, const char *pszName, ...)
505{
506 va_list args;
507 va_start(args, pszName);
508 int rc = STAMR3RegisterCallbackV(pVM, pvSample, enmVisibility, enmUnit, pfnReset, pfnPrint, pszDesc, pszName, args);
509 va_end(args);
510 return rc;
511}
512
513
514/**
515 * Same as STAMR3RegisterCallback() except for the ellipsis which is a va_list here.
516 *
517 * @returns VBox status.
518 * @param pVM Pointer to the VM.
519 * @param pvSample Pointer to the sample.
520 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
521 * @param enmUnit Sample unit.
522 * @param pfnReset Callback for resetting the sample. NULL should be used if the sample can't be reset.
523 * @param pfnPrint Print the sample.
524 * @param pszDesc Sample description.
525 * @param pszName The sample name format string.
526 * @param args Arguments to the format string.
527 * @remark There is currently no device or driver variant of this API. Add one if it should become necessary!
528 */
529VMMR3DECL(int) STAMR3RegisterCallbackV(PVM pVM, void *pvSample, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
530 PFNSTAMR3CALLBACKRESET pfnReset, PFNSTAMR3CALLBACKPRINT pfnPrint,
531 const char *pszDesc, const char *pszName, va_list args)
532{
533 char *pszFormattedName;
534 RTStrAPrintfV(&pszFormattedName, pszName, args);
535 if (!pszFormattedName)
536 return VERR_NO_MEMORY;
537
538 int rc = stamR3RegisterU(pVM->pUVM, pvSample, pfnReset, pfnPrint, STAMTYPE_CALLBACK, enmVisibility, pszFormattedName, enmUnit, pszDesc);
539 RTStrFree(pszFormattedName);
540 return rc;
541}
542
543
544#ifdef VBOX_STRICT
545/**
546 * Divide the strings into sub-strings using '/' as delimiter
547 * and then compare them in strcmp fashion.
548 *
549 * @returns Difference.
550 * @retval 0 if equal.
551 * @retval < 0 if psz1 is less than psz2.
552 * @retval > 0 if psz1 greater than psz2.
553 *
554 * @param psz1 The first string.
555 * @param psz2 The second string.
556 */
557static int stamR3SlashCompare(const char *psz1, const char *psz2)
558{
559 for (;;)
560 {
561 unsigned int ch1 = *psz1++;
562 unsigned int ch2 = *psz2++;
563 if (ch1 != ch2)
564 {
565 /* slash is end-of-sub-string, so it trumps everything but '\0'. */
566 if (ch1 == '/')
567 return ch2 ? -1 : 1;
568 if (ch2 == '/')
569 return ch1 ? 1 : -1;
570 return ch1 - ch2;
571 }
572
573 /* done? */
574 if (ch1 == '\0')
575 return 0;
576 }
577}
578#endif /* VBOX_STRICT */
579
580
581/**
582 * Internal worker for the different register calls.
583 *
584 * @returns VBox status.
585 * @param pUVM Pointer to the user mode VM structure.
586 * @param pvSample Pointer to the sample.
587 * @param pfnReset Callback for resetting the sample. NULL should be used if the sample can't be reset.
588 * @param pfnPrint Print the sample.
589 * @param enmType Sample type. This indicates what pvSample is pointing at.
590 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
591 * @param enmUnit Sample unit.
592 * @param pszDesc Sample description.
593 * @param pszName The sample name format string.
594 * @param args Arguments to the format string.
595 * @remark There is currently no device or driver variant of this API. Add one if it should become necessary!
596 */
597static int stamR3RegisterU(PUVM pUVM, void *pvSample, PFNSTAMR3CALLBACKRESET pfnReset, PFNSTAMR3CALLBACKPRINT pfnPrint,
598 STAMTYPE enmType, STAMVISIBILITY enmVisibility, const char *pszName, STAMUNIT enmUnit, const char *pszDesc)
599{
600 STAM_LOCK_WR(pUVM);
601
602 /*
603 * Check if exists.
604 */
605 PSTAMDESC pPrev = NULL;
606 PSTAMDESC pCur = pUVM->stam.s.pHead;
607 while (pCur)
608 {
609 int iDiff = strcmp(pCur->pszName, pszName);
610 /* passed it */
611 if (iDiff > 0)
612 break;
613 /* found it. */
614 if (!iDiff)
615 {
616 STAM_UNLOCK_WR(pUVM);
617 AssertMsgFailed(("Duplicate sample name: %s\n", pszName));
618 return VERR_ALREADY_EXISTS;
619 }
620
621 /* next */
622 pPrev = pCur;
623 pCur = pCur->pNext;
624 }
625
626 /*
627 * Check that the name doesn't screw up sorting order when taking
628 * slashes into account. The QT4 GUI makes some assumptions.
629 * Problematic chars are: !"#$%&'()*+,-.
630 */
631 Assert(pszName[0] == '/');
632 if (pPrev)
633 Assert(stamR3SlashCompare(pPrev->pszName, pszName) < 0);
634 if (pCur)
635 Assert(stamR3SlashCompare(pCur->pszName, pszName) > 0);
636
637#ifdef VBOX_STRICT
638 /*
639 * Check alignment requirements.
640 */
641 switch (enmType)
642 {
643 /* 8 byte / 64-bit */
644 case STAMTYPE_U64:
645 case STAMTYPE_U64_RESET:
646 case STAMTYPE_X64:
647 case STAMTYPE_X64_RESET:
648 case STAMTYPE_COUNTER:
649 case STAMTYPE_PROFILE:
650 case STAMTYPE_PROFILE_ADV:
651 AssertMsg(!((uintptr_t)pvSample & 7), ("%p - %s\n", pvSample, pszName));
652 break;
653
654 /* 4 byte / 32-bit */
655 case STAMTYPE_RATIO_U32:
656 case STAMTYPE_RATIO_U32_RESET:
657 case STAMTYPE_U32:
658 case STAMTYPE_U32_RESET:
659 case STAMTYPE_X32:
660 case STAMTYPE_X32_RESET:
661 AssertMsg(!((uintptr_t)pvSample & 3), ("%p - %s\n", pvSample, pszName));
662 break;
663
664 /* 2 byte / 32-bit */
665 case STAMTYPE_U16:
666 case STAMTYPE_U16_RESET:
667 case STAMTYPE_X16:
668 case STAMTYPE_X16_RESET:
669 AssertMsg(!((uintptr_t)pvSample & 1), ("%p - %s\n", pvSample, pszName));
670 break;
671
672 /* 1 byte / 8-bit / unaligned */
673 case STAMTYPE_U8:
674 case STAMTYPE_U8_RESET:
675 case STAMTYPE_X8:
676 case STAMTYPE_X8_RESET:
677 case STAMTYPE_BOOL:
678 case STAMTYPE_BOOL_RESET:
679 case STAMTYPE_CALLBACK:
680 break;
681
682 default:
683 AssertMsgFailed(("%d\n", enmType));
684 break;
685 }
686#endif /* VBOX_STRICT */
687
688 /*
689 * Create a new node and insert it at the current location.
690 */
691 int rc;
692 size_t cchName = strlen(pszName) + 1;
693 size_t cchDesc = pszDesc ? strlen(pszDesc) + 1 : 0;
694 PSTAMDESC pNew = (PSTAMDESC)RTMemAlloc(sizeof(*pNew) + cchName + cchDesc);
695 if (pNew)
696 {
697 pNew->pszName = (char *)memcpy((char *)(pNew + 1), pszName, cchName);
698 pNew->enmType = enmType;
699 pNew->enmVisibility = enmVisibility;
700 if (enmType != STAMTYPE_CALLBACK)
701 pNew->u.pv = pvSample;
702 else
703 {
704 pNew->u.Callback.pvSample = pvSample;
705 pNew->u.Callback.pfnReset = pfnReset;
706 pNew->u.Callback.pfnPrint = pfnPrint;
707 }
708 pNew->enmUnit = enmUnit;
709 pNew->pszDesc = NULL;
710 if (pszDesc)
711 pNew->pszDesc = (char *)memcpy((char *)(pNew + 1) + cchName, pszDesc, cchDesc);
712
713 pNew->pNext = pCur;
714 if (pPrev)
715 pPrev->pNext = pNew;
716 else
717 pUVM->stam.s.pHead = pNew;
718
719 stamR3ResetOne(pNew, pUVM->pVM);
720 rc = VINF_SUCCESS;
721 }
722 else
723 rc = VERR_NO_MEMORY;
724
725 STAM_UNLOCK_WR(pUVM);
726 return rc;
727}
728
729
730/**
731 * Deregisters a sample previously registered by STAR3Register().
732 *
733 * This is intended used for devices which can be unplugged and for
734 * temporary samples.
735 *
736 * @returns VBox status.
737 * @param pUVM Pointer to the user mode VM structure.
738 * @param pvSample Pointer to the sample registered with STAMR3Register().
739 */
740VMMR3DECL(int) STAMR3DeregisterU(PUVM pUVM, void *pvSample)
741{
742 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
743
744 STAM_LOCK_WR(pUVM);
745
746 /*
747 * Search for it.
748 */
749 int rc = VERR_INVALID_HANDLE;
750 PSTAMDESC pPrev = NULL;
751 PSTAMDESC pCur = pUVM->stam.s.pHead;
752 while (pCur)
753 {
754 if (pCur->u.pv == pvSample)
755 {
756 void *pvFree = pCur;
757 pCur = pCur->pNext;
758 if (pPrev)
759 pPrev->pNext = pCur;
760 else
761 pUVM->stam.s.pHead = pCur;
762
763 RTMemFree(pvFree);
764 rc = VINF_SUCCESS;
765 continue;
766 }
767
768 /* next */
769 pPrev = pCur;
770 pCur = pCur->pNext;
771 }
772
773 STAM_UNLOCK_WR(pUVM);
774 return rc;
775}
776
777
778/**
779 * Deregisters a sample previously registered by STAR3Register().
780 *
781 * This is intended used for devices which can be unplugged and for
782 * temporary samples.
783 *
784 * @returns VBox status.
785 * @param pVM Pointer to the VM.
786 * @param pvSample Pointer to the sample registered with STAMR3Register().
787 */
788VMMR3DECL(int) STAMR3Deregister(PVM pVM, void *pvSample)
789{
790 return STAMR3DeregisterU(pVM->pUVM, pvSample);
791}
792
793
794/**
795 * Resets statistics for the specified VM.
796 * It's possible to select a subset of the samples.
797 *
798 * @returns VBox status. (Basically, it cannot fail.)
799 * @param pUVM The user mode VM handle.
800 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
801 * If NULL all samples are reset.
802 * @remarks Don't confuse this with the other 'XYZR3Reset' methods, it's not called at VM reset.
803 */
804VMMR3DECL(int) STAMR3Reset(PUVM pUVM, const char *pszPat)
805{
806 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
807 VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
808
809 int rc = VINF_SUCCESS;
810
811 /* ring-0 */
812 GVMMRESETSTATISTICSSREQ GVMMReq;
813 GMMRESETSTATISTICSSREQ GMMReq;
814 bool fGVMMMatched = !pszPat || !*pszPat;
815 bool fGMMMatched = fGVMMMatched;
816 if (fGVMMMatched)
817 {
818 memset(&GVMMReq.Stats, 0xff, sizeof(GVMMReq.Stats));
819 memset(&GMMReq.Stats, 0xff, sizeof(GMMReq.Stats));
820 }
821 else
822 {
823 char *pszCopy;
824 unsigned cExpressions;
825 char **papszExpressions = stamR3SplitPattern(pszPat, &cExpressions, &pszCopy);
826 if (!papszExpressions)
827 return VERR_NO_MEMORY;
828
829 /* GVMM */
830 RT_ZERO(GVMMReq.Stats);
831 for (unsigned i = 0; i < RT_ELEMENTS(g_aGVMMStats); i++)
832 if (stamR3MultiMatch(papszExpressions, cExpressions, NULL, g_aGVMMStats[i].pszName))
833 {
834 *((uint8_t *)&GVMMReq.Stats + g_aGVMMStats[i].offVar) = 0xff;
835 fGVMMMatched = true;
836 }
837 if (!fGVMMMatched)
838 {
839 /** @todo match cpu leaves some rainy day. */
840 }
841
842 /* GMM */
843 RT_ZERO(GMMReq.Stats);
844 for (unsigned i = 0; i < RT_ELEMENTS(g_aGMMStats); i++)
845 if (stamR3MultiMatch(papszExpressions, cExpressions, NULL, g_aGMMStats[i].pszName))
846 {
847 *((uint8_t *)&GMMReq.Stats + g_aGMMStats[i].offVar) = 0xff;
848 fGMMMatched = true;
849 }
850
851 RTMemTmpFree(papszExpressions);
852 RTStrFree(pszCopy);
853 }
854
855 STAM_LOCK_WR(pUVM);
856
857 if (fGVMMMatched)
858 {
859 PVM pVM = pUVM->pVM;
860 GVMMReq.Hdr.cbReq = sizeof(GVMMReq);
861 GVMMReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
862 GVMMReq.pSession = pVM->pSession;
863 rc = SUPR3CallVMMR0Ex(pVM->pVMR0, NIL_VMCPUID, VMMR0_DO_GVMM_RESET_STATISTICS, 0, &GVMMReq.Hdr);
864 }
865
866 if (fGMMMatched)
867 {
868 PVM pVM = pUVM->pVM;
869 GMMReq.Hdr.cbReq = sizeof(GMMReq);
870 GMMReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
871 GMMReq.pSession = pVM->pSession;
872 rc = SUPR3CallVMMR0Ex(pVM->pVMR0, NIL_VMCPUID, VMMR0_DO_GMM_RESET_STATISTICS, 0, &GMMReq.Hdr);
873 }
874
875 /* and the reset */
876 stamR3EnumU(pUVM, pszPat, false /* fUpdateRing0 */, stamR3ResetOne, pUVM->pVM);
877
878 STAM_UNLOCK_WR(pUVM);
879 return rc;
880}
881
882
883/**
884 * Resets one statistics sample.
885 * Callback for stamR3EnumU().
886 *
887 * @returns VINF_SUCCESS
888 * @param pDesc Pointer to the current descriptor.
889 * @param pvArg User argument - Pointer to the VM.
890 */
891static int stamR3ResetOne(PSTAMDESC pDesc, void *pvArg)
892{
893 switch (pDesc->enmType)
894 {
895 case STAMTYPE_COUNTER:
896 ASMAtomicXchgU64(&pDesc->u.pCounter->c, 0);
897 break;
898
899 case STAMTYPE_PROFILE:
900 case STAMTYPE_PROFILE_ADV:
901 ASMAtomicXchgU64(&pDesc->u.pProfile->cPeriods, 0);
902 ASMAtomicXchgU64(&pDesc->u.pProfile->cTicks, 0);
903 ASMAtomicXchgU64(&pDesc->u.pProfile->cTicksMax, 0);
904 ASMAtomicXchgU64(&pDesc->u.pProfile->cTicksMin, ~0);
905 break;
906
907 case STAMTYPE_RATIO_U32_RESET:
908 ASMAtomicXchgU32(&pDesc->u.pRatioU32->u32A, 0);
909 ASMAtomicXchgU32(&pDesc->u.pRatioU32->u32B, 0);
910 break;
911
912 case STAMTYPE_CALLBACK:
913 if (pDesc->u.Callback.pfnReset)
914 pDesc->u.Callback.pfnReset((PVM)pvArg, pDesc->u.Callback.pvSample);
915 break;
916
917 case STAMTYPE_U8_RESET:
918 case STAMTYPE_X8_RESET:
919 ASMAtomicXchgU8(pDesc->u.pu8, 0);
920 break;
921
922 case STAMTYPE_U16_RESET:
923 case STAMTYPE_X16_RESET:
924 ASMAtomicXchgU16(pDesc->u.pu16, 0);
925 break;
926
927 case STAMTYPE_U32_RESET:
928 case STAMTYPE_X32_RESET:
929 ASMAtomicXchgU32(pDesc->u.pu32, 0);
930 break;
931
932 case STAMTYPE_U64_RESET:
933 case STAMTYPE_X64_RESET:
934 ASMAtomicXchgU64(pDesc->u.pu64, 0);
935 break;
936
937 case STAMTYPE_BOOL_RESET:
938 ASMAtomicXchgBool(pDesc->u.pf, false);
939 break;
940
941 /* These are custom and will not be touched. */
942 case STAMTYPE_U8:
943 case STAMTYPE_X8:
944 case STAMTYPE_U16:
945 case STAMTYPE_X16:
946 case STAMTYPE_U32:
947 case STAMTYPE_X32:
948 case STAMTYPE_U64:
949 case STAMTYPE_X64:
950 case STAMTYPE_RATIO_U32:
951 case STAMTYPE_BOOL:
952 break;
953
954 default:
955 AssertMsgFailed(("enmType=%d\n", pDesc->enmType));
956 break;
957 }
958 NOREF(pvArg);
959 return VINF_SUCCESS;
960}
961
962
963/**
964 * Get a snapshot of the statistics.
965 * It's possible to select a subset of the samples.
966 *
967 * @returns VBox status. (Basically, it cannot fail.)
968 * @param pUVM The user mode VM handle.
969 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
970 * If NULL all samples are reset.
971 * @param fWithDesc Whether to include the descriptions.
972 * @param ppszSnapshot Where to store the pointer to the snapshot data.
973 * The format of the snapshot should be XML, but that will have to be discussed
974 * when this function is implemented.
975 * The returned pointer must be freed by calling STAMR3SnapshotFree().
976 * @param pcchSnapshot Where to store the size of the snapshot data. (Excluding the trailing '\0')
977 */
978VMMR3DECL(int) STAMR3Snapshot(PUVM pUVM, const char *pszPat, char **ppszSnapshot, size_t *pcchSnapshot, bool fWithDesc)
979{
980 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
981 VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
982
983 STAMR3SNAPSHOTONE State = { NULL, NULL, NULL, pUVM->pVM, 0, VINF_SUCCESS, fWithDesc };
984
985 /*
986 * Write the XML header.
987 */
988 /** @todo Make this proper & valid XML. */
989 stamR3SnapshotPrintf(&State, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
990
991 /*
992 * Write the content.
993 */
994 stamR3SnapshotPrintf(&State, "<Statistics>\n");
995 int rc = stamR3EnumU(pUVM, pszPat, true /* fUpdateRing0 */, stamR3SnapshotOne, &State);
996 stamR3SnapshotPrintf(&State, "</Statistics>\n");
997
998 if (RT_SUCCESS(rc))
999 rc = State.rc;
1000 else
1001 {
1002 RTMemFree(State.pszStart);
1003 State.pszStart = State.pszEnd = State.psz = NULL;
1004 State.cbAllocated = 0;
1005 }
1006
1007 /*
1008 * Done.
1009 */
1010 *ppszSnapshot = State.pszStart;
1011 if (pcchSnapshot)
1012 *pcchSnapshot = State.psz - State.pszStart;
1013 return rc;
1014}
1015
1016
1017/**
1018 * stamR3EnumU callback employed by STAMR3Snapshot.
1019 *
1020 * @returns VBox status code, but it's interpreted as 0 == success / !0 == failure by enmR3Enum.
1021 * @param pDesc The sample.
1022 * @param pvArg The snapshot status structure.
1023 */
1024static int stamR3SnapshotOne(PSTAMDESC pDesc, void *pvArg)
1025{
1026 PSTAMR3SNAPSHOTONE pThis = (PSTAMR3SNAPSHOTONE)pvArg;
1027
1028 switch (pDesc->enmType)
1029 {
1030 case STAMTYPE_COUNTER:
1031 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pDesc->u.pCounter->c == 0)
1032 return VINF_SUCCESS;
1033 stamR3SnapshotPrintf(pThis, "<Counter c=\"%lld\"", pDesc->u.pCounter->c);
1034 break;
1035
1036 case STAMTYPE_PROFILE:
1037 case STAMTYPE_PROFILE_ADV:
1038 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pDesc->u.pProfile->cPeriods == 0)
1039 return VINF_SUCCESS;
1040 stamR3SnapshotPrintf(pThis, "<Profile cPeriods=\"%lld\" cTicks=\"%lld\" cTicksMin=\"%lld\" cTicksMax=\"%lld\"",
1041 pDesc->u.pProfile->cPeriods, pDesc->u.pProfile->cTicks, pDesc->u.pProfile->cTicksMin,
1042 pDesc->u.pProfile->cTicksMax);
1043 break;
1044
1045 case STAMTYPE_RATIO_U32:
1046 case STAMTYPE_RATIO_U32_RESET:
1047 if (pDesc->enmVisibility == STAMVISIBILITY_USED && !pDesc->u.pRatioU32->u32A && !pDesc->u.pRatioU32->u32B)
1048 return VINF_SUCCESS;
1049 stamR3SnapshotPrintf(pThis, "<Ratio32 u32A=\"%lld\" u32B=\"%lld\"",
1050 pDesc->u.pRatioU32->u32A, pDesc->u.pRatioU32->u32B);
1051 break;
1052
1053 case STAMTYPE_CALLBACK:
1054 {
1055 char szBuf[512];
1056 pDesc->u.Callback.pfnPrint(pThis->pVM, pDesc->u.Callback.pvSample, szBuf, sizeof(szBuf));
1057 stamR3SnapshotPrintf(pThis, "<Callback val=\"%s\"", szBuf);
1058 break;
1059 }
1060
1061 case STAMTYPE_U8:
1062 case STAMTYPE_U8_RESET:
1063 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu8 == 0)
1064 return VINF_SUCCESS;
1065 stamR3SnapshotPrintf(pThis, "<U8 val=\"%u\"", *pDesc->u.pu8);
1066 break;
1067
1068 case STAMTYPE_X8:
1069 case STAMTYPE_X8_RESET:
1070 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu8 == 0)
1071 return VINF_SUCCESS;
1072 stamR3SnapshotPrintf(pThis, "<X8 val=\"%#x\"", *pDesc->u.pu8);
1073 break;
1074
1075 case STAMTYPE_U16:
1076 case STAMTYPE_U16_RESET:
1077 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu16 == 0)
1078 return VINF_SUCCESS;
1079 stamR3SnapshotPrintf(pThis, "<U16 val=\"%u\"", *pDesc->u.pu16);
1080 break;
1081
1082 case STAMTYPE_X16:
1083 case STAMTYPE_X16_RESET:
1084 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu16 == 0)
1085 return VINF_SUCCESS;
1086 stamR3SnapshotPrintf(pThis, "<X16 val=\"%#x\"", *pDesc->u.pu16);
1087 break;
1088
1089 case STAMTYPE_U32:
1090 case STAMTYPE_U32_RESET:
1091 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu32 == 0)
1092 return VINF_SUCCESS;
1093 stamR3SnapshotPrintf(pThis, "<U32 val=\"%u\"", *pDesc->u.pu32);
1094 break;
1095
1096 case STAMTYPE_X32:
1097 case STAMTYPE_X32_RESET:
1098 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu32 == 0)
1099 return VINF_SUCCESS;
1100 stamR3SnapshotPrintf(pThis, "<X32 val=\"%#x\"", *pDesc->u.pu32);
1101 break;
1102
1103 case STAMTYPE_U64:
1104 case STAMTYPE_U64_RESET:
1105 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu64 == 0)
1106 return VINF_SUCCESS;
1107 stamR3SnapshotPrintf(pThis, "<U64 val=\"%llu\"", *pDesc->u.pu64);
1108 break;
1109
1110 case STAMTYPE_X64:
1111 case STAMTYPE_X64_RESET:
1112 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu64 == 0)
1113 return VINF_SUCCESS;
1114 stamR3SnapshotPrintf(pThis, "<X64 val=\"%#llx\"", *pDesc->u.pu64);
1115 break;
1116
1117 case STAMTYPE_BOOL:
1118 case STAMTYPE_BOOL_RESET:
1119 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pf == false)
1120 return VINF_SUCCESS;
1121 stamR3SnapshotPrintf(pThis, "<BOOL val=\"%RTbool\"", *pDesc->u.pf);
1122 break;
1123
1124 default:
1125 AssertMsgFailed(("%d\n", pDesc->enmType));
1126 return 0;
1127 }
1128
1129 stamR3SnapshotPrintf(pThis, " unit=\"%s\"", STAMR3GetUnit(pDesc->enmUnit));
1130
1131 switch (pDesc->enmVisibility)
1132 {
1133 default:
1134 case STAMVISIBILITY_ALWAYS:
1135 break;
1136 case STAMVISIBILITY_USED:
1137 stamR3SnapshotPrintf(pThis, " vis=\"used\"");
1138 break;
1139 case STAMVISIBILITY_NOT_GUI:
1140 stamR3SnapshotPrintf(pThis, " vis=\"not-gui\"");
1141 break;
1142 }
1143
1144 stamR3SnapshotPrintf(pThis, " name=\"%s\"", pDesc->pszName);
1145
1146 if (pThis->fWithDesc && pDesc->pszDesc)
1147 {
1148 /*
1149 * The description is a bit tricky as it may include chars that
1150 * xml requires to be escaped.
1151 */
1152 const char *pszBadChar = strpbrk(pDesc->pszDesc, "&<>\"'");
1153 if (!pszBadChar)
1154 return stamR3SnapshotPrintf(pThis, " desc=\"%s\"/>\n", pDesc->pszDesc);
1155
1156 stamR3SnapshotPrintf(pThis, " desc=\"");
1157 const char *pszCur = pDesc->pszDesc;
1158 do
1159 {
1160 stamR3SnapshotPrintf(pThis, "%.*s", pszBadChar - pszCur, pszCur);
1161 switch (*pszBadChar)
1162 {
1163 case '&': stamR3SnapshotPrintf(pThis, "&amp;"); break;
1164 case '<': stamR3SnapshotPrintf(pThis, "&lt;"); break;
1165 case '>': stamR3SnapshotPrintf(pThis, "&gt;"); break;
1166 case '"': stamR3SnapshotPrintf(pThis, "&quot;"); break;
1167 case '\'': stamR3SnapshotPrintf(pThis, "&apos;"); break;
1168 default: AssertMsgFailed(("%c", *pszBadChar)); break;
1169 }
1170 pszCur = pszBadChar + 1;
1171 pszBadChar = strpbrk(pszCur, "&<>\"'");
1172 } while (pszBadChar);
1173 return stamR3SnapshotPrintf(pThis, "%s\"/>\n", pszCur);
1174 }
1175 return stamR3SnapshotPrintf(pThis, "/>\n");
1176}
1177
1178
1179/**
1180 * Output callback for stamR3SnapshotPrintf.
1181 *
1182 * @returns number of bytes written.
1183 * @param pvArg The snapshot status structure.
1184 * @param pach Pointer to an array of characters (bytes).
1185 * @param cch The number or chars (bytes) to write from the array.
1186 */
1187static DECLCALLBACK(size_t) stamR3SnapshotOutput(void *pvArg, const char *pach, size_t cch)
1188{
1189 PSTAMR3SNAPSHOTONE pThis = (PSTAMR3SNAPSHOTONE)pvArg;
1190
1191 /*
1192 * Make sure we've got space for it.
1193 */
1194 if (RT_UNLIKELY((uintptr_t)pThis->pszEnd - (uintptr_t)pThis->psz < cch + 1))
1195 {
1196 if (RT_FAILURE(pThis->rc))
1197 return 0;
1198
1199 size_t cbNewSize = pThis->cbAllocated;
1200 if (cbNewSize > cch)
1201 cbNewSize *= 2;
1202 else
1203 cbNewSize += RT_ALIGN(cch + 1, 0x1000);
1204 char *pszNew = (char *)RTMemRealloc(pThis->pszStart, cbNewSize);
1205 if (!pszNew)
1206 {
1207 /*
1208 * Free up immediately, out-of-memory is bad news and this
1209 * isn't an important allocations / API.
1210 */
1211 pThis->rc = VERR_NO_MEMORY;
1212 RTMemFree(pThis->pszStart);
1213 pThis->pszStart = pThis->pszEnd = pThis->psz = NULL;
1214 pThis->cbAllocated = 0;
1215 return 0;
1216 }
1217
1218 pThis->psz = pszNew + (pThis->psz - pThis->pszStart);
1219 pThis->pszStart = pszNew;
1220 pThis->pszEnd = pszNew + cbNewSize;
1221 pThis->cbAllocated = cbNewSize;
1222 }
1223
1224 /*
1225 * Copy the chars to the buffer and terminate it.
1226 */
1227 memcpy(pThis->psz, pach, cch);
1228 pThis->psz += cch;
1229 *pThis->psz = '\0';
1230 return cch;
1231}
1232
1233
1234/**
1235 * Wrapper around RTStrFormatV for use by the snapshot API.
1236 *
1237 * @returns VBox status code.
1238 * @param pThis The snapshot status structure.
1239 * @param pszFormat The format string.
1240 * @param ... Optional arguments.
1241 */
1242static int stamR3SnapshotPrintf(PSTAMR3SNAPSHOTONE pThis, const char *pszFormat, ...)
1243{
1244 va_list va;
1245 va_start(va, pszFormat);
1246 RTStrFormatV(stamR3SnapshotOutput, pThis, NULL, NULL, pszFormat, va);
1247 va_end(va);
1248 return pThis->rc;
1249}
1250
1251
1252/**
1253 * Releases a statistics snapshot returned by STAMR3Snapshot().
1254 *
1255 * @returns VBox status.
1256 * @param pUVM The user mode VM handle.
1257 * @param pszSnapshot The snapshot data pointer returned by STAMR3Snapshot().
1258 * NULL is allowed.
1259 */
1260VMMR3DECL(int) STAMR3SnapshotFree(PUVM pUVM, char *pszSnapshot)
1261{
1262 if (!pszSnapshot)
1263 RTMemFree(pszSnapshot);
1264 NOREF(pUVM);
1265 return VINF_SUCCESS;
1266}
1267
1268
1269/**
1270 * Dumps the selected statistics to the log.
1271 *
1272 * @returns VBox status.
1273 * @param pUVM Pointer to the user mode VM structure.
1274 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
1275 * If NULL all samples are written to the log.
1276 */
1277VMMR3DECL(int) STAMR3Dump(PUVM pUVM, const char *pszPat)
1278{
1279 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
1280 VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
1281
1282 STAMR3PRINTONEARGS Args;
1283 Args.pUVM = pUVM;
1284 Args.pvArg = NULL;
1285 Args.pfnPrintf = stamR3EnumLogPrintf;
1286
1287 stamR3EnumU(pUVM, pszPat, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
1288 return VINF_SUCCESS;
1289}
1290
1291
1292/**
1293 * Prints to the log.
1294 *
1295 * @param pArgs Pointer to the print one argument structure.
1296 * @param pszFormat Format string.
1297 * @param ... Format arguments.
1298 */
1299static DECLCALLBACK(void) stamR3EnumLogPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...)
1300{
1301 va_list va;
1302 va_start(va, pszFormat);
1303 RTLogPrintfV(pszFormat, va);
1304 va_end(va);
1305 NOREF(pArgs);
1306}
1307
1308
1309/**
1310 * Dumps the selected statistics to the release log.
1311 *
1312 * @returns VBox status.
1313 * @param pUVM Pointer to the user mode VM structure.
1314 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
1315 * If NULL all samples are written to the log.
1316 */
1317VMMR3DECL(int) STAMR3DumpToReleaseLog(PUVM pUVM, const char *pszPat)
1318{
1319 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
1320 VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
1321
1322 STAMR3PRINTONEARGS Args;
1323 Args.pUVM = pUVM;
1324 Args.pvArg = NULL;
1325 Args.pfnPrintf = stamR3EnumRelLogPrintf;
1326
1327 stamR3EnumU(pUVM, pszPat, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
1328 return VINF_SUCCESS;
1329}
1330
1331/**
1332 * Prints to the release log.
1333 *
1334 * @param pArgs Pointer to the print one argument structure.
1335 * @param pszFormat Format string.
1336 * @param ... Format arguments.
1337 */
1338static DECLCALLBACK(void) stamR3EnumRelLogPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...)
1339{
1340 va_list va;
1341 va_start(va, pszFormat);
1342 RTLogRelPrintfV(pszFormat, va);
1343 va_end(va);
1344 NOREF(pArgs);
1345}
1346
1347
1348/**
1349 * Prints the selected statistics to standard out.
1350 *
1351 * @returns VBox status.
1352 * @param pUVM The user mode VM handle.
1353 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
1354 * If NULL all samples are reset.
1355 */
1356VMMR3DECL(int) STAMR3Print(PUVM pUVM, const char *pszPat)
1357{
1358 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
1359 VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
1360
1361 STAMR3PRINTONEARGS Args;
1362 Args.pUVM = pUVM;
1363 Args.pvArg = NULL;
1364 Args.pfnPrintf = stamR3EnumPrintf;
1365
1366 stamR3EnumU(pUVM, pszPat, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
1367 return VINF_SUCCESS;
1368}
1369
1370
1371/**
1372 * Prints to stdout.
1373 *
1374 * @param pArgs Pointer to the print one argument structure.
1375 * @param pszFormat Format string.
1376 * @param ... Format arguments.
1377 */
1378static DECLCALLBACK(void) stamR3EnumPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...)
1379{
1380 va_list va;
1381 va_start(va, pszFormat);
1382 RTPrintfV(pszFormat, va);
1383 va_end(va);
1384 NOREF(pArgs);
1385}
1386
1387
1388/**
1389 * Prints one sample.
1390 * Callback for stamR3EnumU().
1391 *
1392 * @returns VINF_SUCCESS
1393 * @param pDesc Pointer to the current descriptor.
1394 * @param pvArg User argument - STAMR3PRINTONEARGS.
1395 */
1396static int stamR3PrintOne(PSTAMDESC pDesc, void *pvArg)
1397{
1398 PSTAMR3PRINTONEARGS pArgs = (PSTAMR3PRINTONEARGS)pvArg;
1399
1400 switch (pDesc->enmType)
1401 {
1402 case STAMTYPE_COUNTER:
1403 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pDesc->u.pCounter->c == 0)
1404 return VINF_SUCCESS;
1405
1406 pArgs->pfnPrintf(pArgs, "%-32s %8llu %s\n", pDesc->pszName, pDesc->u.pCounter->c, STAMR3GetUnit(pDesc->enmUnit));
1407 break;
1408
1409 case STAMTYPE_PROFILE:
1410 case STAMTYPE_PROFILE_ADV:
1411 {
1412 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pDesc->u.pProfile->cPeriods == 0)
1413 return VINF_SUCCESS;
1414
1415 uint64_t u64 = pDesc->u.pProfile->cPeriods ? pDesc->u.pProfile->cPeriods : 1;
1416 pArgs->pfnPrintf(pArgs, "%-32s %8llu %s (%12llu ticks, %7llu times, max %9llu, min %7lld)\n", pDesc->pszName,
1417 pDesc->u.pProfile->cTicks / u64, STAMR3GetUnit(pDesc->enmUnit),
1418 pDesc->u.pProfile->cTicks, pDesc->u.pProfile->cPeriods, pDesc->u.pProfile->cTicksMax, pDesc->u.pProfile->cTicksMin);
1419 break;
1420 }
1421
1422 case STAMTYPE_RATIO_U32:
1423 case STAMTYPE_RATIO_U32_RESET:
1424 if (pDesc->enmVisibility == STAMVISIBILITY_USED && !pDesc->u.pRatioU32->u32A && !pDesc->u.pRatioU32->u32B)
1425 return VINF_SUCCESS;
1426 pArgs->pfnPrintf(pArgs, "%-32s %8u:%-8u %s\n", pDesc->pszName,
1427 pDesc->u.pRatioU32->u32A, pDesc->u.pRatioU32->u32B, STAMR3GetUnit(pDesc->enmUnit));
1428 break;
1429
1430 case STAMTYPE_CALLBACK:
1431 {
1432 char szBuf[512];
1433 pDesc->u.Callback.pfnPrint(pArgs->pUVM->pVM, pDesc->u.Callback.pvSample, szBuf, sizeof(szBuf));
1434 pArgs->pfnPrintf(pArgs, "%-32s %s %s\n", pDesc->pszName, szBuf, STAMR3GetUnit(pDesc->enmUnit));
1435 break;
1436 }
1437
1438 case STAMTYPE_U8:
1439 case STAMTYPE_U8_RESET:
1440 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu8 == 0)
1441 return VINF_SUCCESS;
1442 pArgs->pfnPrintf(pArgs, "%-32s %8u %s\n", pDesc->pszName, *pDesc->u.pu8, STAMR3GetUnit(pDesc->enmUnit));
1443 break;
1444
1445 case STAMTYPE_X8:
1446 case STAMTYPE_X8_RESET:
1447 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu8 == 0)
1448 return VINF_SUCCESS;
1449 pArgs->pfnPrintf(pArgs, "%-32s %8x %s\n", pDesc->pszName, *pDesc->u.pu8, STAMR3GetUnit(pDesc->enmUnit));
1450 break;
1451
1452 case STAMTYPE_U16:
1453 case STAMTYPE_U16_RESET:
1454 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu16 == 0)
1455 return VINF_SUCCESS;
1456 pArgs->pfnPrintf(pArgs, "%-32s %8u %s\n", pDesc->pszName, *pDesc->u.pu16, STAMR3GetUnit(pDesc->enmUnit));
1457 break;
1458
1459 case STAMTYPE_X16:
1460 case STAMTYPE_X16_RESET:
1461 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu16 == 0)
1462 return VINF_SUCCESS;
1463 pArgs->pfnPrintf(pArgs, "%-32s %8x %s\n", pDesc->pszName, *pDesc->u.pu16, STAMR3GetUnit(pDesc->enmUnit));
1464 break;
1465
1466 case STAMTYPE_U32:
1467 case STAMTYPE_U32_RESET:
1468 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu32 == 0)
1469 return VINF_SUCCESS;
1470 pArgs->pfnPrintf(pArgs, "%-32s %8u %s\n", pDesc->pszName, *pDesc->u.pu32, STAMR3GetUnit(pDesc->enmUnit));
1471 break;
1472
1473 case STAMTYPE_X32:
1474 case STAMTYPE_X32_RESET:
1475 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu32 == 0)
1476 return VINF_SUCCESS;
1477 pArgs->pfnPrintf(pArgs, "%-32s %8x %s\n", pDesc->pszName, *pDesc->u.pu32, STAMR3GetUnit(pDesc->enmUnit));
1478 break;
1479
1480 case STAMTYPE_U64:
1481 case STAMTYPE_U64_RESET:
1482 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu64 == 0)
1483 return VINF_SUCCESS;
1484 pArgs->pfnPrintf(pArgs, "%-32s %8llu %s\n", pDesc->pszName, *pDesc->u.pu64, STAMR3GetUnit(pDesc->enmUnit));
1485 break;
1486
1487 case STAMTYPE_X64:
1488 case STAMTYPE_X64_RESET:
1489 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu64 == 0)
1490 return VINF_SUCCESS;
1491 pArgs->pfnPrintf(pArgs, "%-32s %8llx %s\n", pDesc->pszName, *pDesc->u.pu64, STAMR3GetUnit(pDesc->enmUnit));
1492 break;
1493
1494 case STAMTYPE_BOOL:
1495 case STAMTYPE_BOOL_RESET:
1496 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pf == false)
1497 return VINF_SUCCESS;
1498 pArgs->pfnPrintf(pArgs, "%-32s %s %s\n", pDesc->pszName, *pDesc->u.pf ? "true " : "false ", STAMR3GetUnit(pDesc->enmUnit));
1499 break;
1500
1501 default:
1502 AssertMsgFailed(("enmType=%d\n", pDesc->enmType));
1503 break;
1504 }
1505 NOREF(pvArg);
1506 return VINF_SUCCESS;
1507}
1508
1509
1510/**
1511 * Enumerate the statistics by the means of a callback function.
1512 *
1513 * @returns Whatever the callback returns.
1514 *
1515 * @param pUVM The user mode VM handle.
1516 * @param pszPat The pattern to match samples.
1517 * @param pfnEnum The callback function.
1518 * @param pvUser The pvUser argument of the callback function.
1519 */
1520VMMR3DECL(int) STAMR3Enum(PUVM pUVM, const char *pszPat, PFNSTAMR3ENUM pfnEnum, void *pvUser)
1521{
1522 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
1523 VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
1524
1525 STAMR3ENUMONEARGS Args;
1526 Args.pVM = pUVM->pVM;
1527 Args.pfnEnum = pfnEnum;
1528 Args.pvUser = pvUser;
1529
1530 return stamR3EnumU(pUVM, pszPat, true /* fUpdateRing0 */, stamR3EnumOne, &Args);
1531}
1532
1533
1534/**
1535 * Callback function for STARTR3Enum().
1536 *
1537 * @returns whatever the callback returns.
1538 * @param pDesc Pointer to the current descriptor.
1539 * @param pvArg Points to a STAMR3ENUMONEARGS structure.
1540 */
1541static int stamR3EnumOne(PSTAMDESC pDesc, void *pvArg)
1542{
1543 PSTAMR3ENUMONEARGS pArgs = (PSTAMR3ENUMONEARGS)pvArg;
1544 int rc;
1545 if (pDesc->enmType == STAMTYPE_CALLBACK)
1546 {
1547 /* Give the enumerator something useful. */
1548 char szBuf[512];
1549 pDesc->u.Callback.pfnPrint(pArgs->pVM, pDesc->u.Callback.pvSample, szBuf, sizeof(szBuf));
1550 rc = pArgs->pfnEnum(pDesc->pszName, pDesc->enmType, szBuf, pDesc->enmUnit,
1551 pDesc->enmVisibility, pDesc->pszDesc, pArgs->pvUser);
1552 }
1553 else
1554 rc = pArgs->pfnEnum(pDesc->pszName, pDesc->enmType, pDesc->u.pv, pDesc->enmUnit,
1555 pDesc->enmVisibility, pDesc->pszDesc, pArgs->pvUser);
1556 return rc;
1557}
1558
1559
1560/**
1561 * Match a name against an array of patterns.
1562 *
1563 * @returns true if it matches, false if it doesn't match.
1564 * @param papszExpressions The array of pattern expressions.
1565 * @param cExpressions The number of array entries.
1566 * @param piExpression Where to read/store the current skip index. Optional.
1567 * @param pszName The name to match.
1568 */
1569static bool stamR3MultiMatch(const char * const *papszExpressions, unsigned cExpressions,
1570 unsigned *piExpression, const char *pszName)
1571{
1572 for (unsigned i = piExpression ? *piExpression : 0; i < cExpressions; i++)
1573 {
1574 const char *pszPat = papszExpressions[i];
1575 if (RTStrSimplePatternMatch(pszPat, pszName))
1576 {
1577 /* later:
1578 if (piExpression && i > *piExpression)
1579 {
1580 check if we can skip some expressions
1581 }*/
1582 return true;
1583 }
1584 }
1585 return false;
1586}
1587
1588
1589/**
1590 * Splits a multi pattern into single ones.
1591 *
1592 * @returns Pointer to an array of single patterns. Free it with RTMemTmpFree.
1593 * @param pszPat The pattern to split.
1594 * @param pcExpressions The number of array elements.
1595 * @param pszCopy The pattern copy to free using RTStrFree.
1596 */
1597static char **stamR3SplitPattern(const char *pszPat, unsigned *pcExpressions, char **ppszCopy)
1598{
1599 Assert(pszPat && *pszPat);
1600
1601 char *pszCopy = RTStrDup(pszPat);
1602 if (!pszCopy)
1603 return NULL;
1604
1605 /* count them & allocate array. */
1606 char *psz = pszCopy;
1607 unsigned cExpressions = 1;
1608 while ((psz = strchr(psz, '|')) != NULL)
1609 cExpressions++, psz++;
1610
1611 char **papszExpressions = (char **)RTMemTmpAllocZ((cExpressions + 1) * sizeof(char *));
1612 if (!papszExpressions)
1613 {
1614 RTStrFree(pszCopy);
1615 return NULL;
1616 }
1617
1618 /* split */
1619 psz = pszCopy;
1620 for (unsigned i = 0;;)
1621 {
1622 papszExpressions[i] = psz;
1623 if (++i >= cExpressions)
1624 break;
1625 psz = strchr(psz, '|');
1626 *psz++ = '\0';
1627 }
1628
1629 /* sort the array, putting '*' last. */
1630 /** @todo sort it... */
1631
1632 *pcExpressions = cExpressions;
1633 *ppszCopy = pszCopy;
1634 return papszExpressions;
1635}
1636
1637
1638/**
1639 * Enumerates the nodes selected by a pattern or all nodes if no pattern
1640 * is specified.
1641 *
1642 * The call may lock STAM for writing before calling this function, however do
1643 * not lock it for reading as this function may need to write lock STAM.
1644 *
1645 * @returns The rc from the callback.
1646 * @param pUVM Pointer to the user mode VM structure.
1647 * @param pszPat Pattern.
1648 * @param fUpdateRing0 Update the ring-0 .
1649 * @param pfnCallback Callback function which shall be called for matching nodes.
1650 * If it returns anything but VINF_SUCCESS the enumeration is
1651 * terminated and the status code returned to the caller.
1652 * @param pvArg User parameter for the callback.
1653 */
1654static int stamR3EnumU(PUVM pUVM, const char *pszPat, bool fUpdateRing0,
1655 int (*pfnCallback)(PSTAMDESC pDesc, void *pvArg), void *pvArg)
1656{
1657 int rc = VINF_SUCCESS;
1658
1659 /*
1660 * All
1661 */
1662 if (!pszPat || !*pszPat || !strcmp(pszPat, "*"))
1663 {
1664 if (fUpdateRing0)
1665 stamR3Ring0StatsUpdateU(pUVM, "*");
1666
1667 STAM_LOCK_RD(pUVM);
1668 PSTAMDESC pCur = pUVM->stam.s.pHead;
1669 while (pCur)
1670 {
1671 rc = pfnCallback(pCur, pvArg);
1672 if (rc)
1673 break;
1674
1675 /* next */
1676 pCur = pCur->pNext;
1677 }
1678 STAM_UNLOCK_RD(pUVM);
1679 }
1680
1681 /*
1682 * Single expression pattern.
1683 */
1684 else if (!strchr(pszPat, '|'))
1685 {
1686 if (fUpdateRing0)
1687 stamR3Ring0StatsUpdateU(pUVM, pszPat);
1688
1689 STAM_LOCK_RD(pUVM);
1690 /** @todo This needs to be optimized since the GUI is using this path for the VM info dialog.
1691 * Note that it's doing exact matching. Organizing the samples in a tree would speed up thing
1692 * no end (at least for debug and profile builds). */
1693 for (PSTAMDESC pCur = pUVM->stam.s.pHead; pCur; pCur = pCur->pNext)
1694 if (RTStrSimplePatternMatch(pszPat, pCur->pszName))
1695 {
1696 rc = pfnCallback(pCur, pvArg);
1697 if (rc)
1698 break;
1699 }
1700 STAM_UNLOCK_RD(pUVM);
1701 }
1702
1703 /*
1704 * Multi expression pattern.
1705 */
1706 else
1707 {
1708 /*
1709 * Split up the pattern first.
1710 */
1711 char *pszCopy;
1712 unsigned cExpressions;
1713 char **papszExpressions = stamR3SplitPattern(pszPat, &cExpressions, &pszCopy);
1714 if (!papszExpressions)
1715 return VERR_NO_MEMORY;
1716
1717 /*
1718 * Perform the enumeration.
1719 */
1720 if (fUpdateRing0)
1721 stamR3Ring0StatsUpdateMultiU(pUVM, papszExpressions, cExpressions);
1722
1723 STAM_LOCK_RD(pUVM);
1724 unsigned iExpression = 0;
1725 for (PSTAMDESC pCur = pUVM->stam.s.pHead; pCur; pCur = pCur->pNext)
1726 if (stamR3MultiMatch(papszExpressions, cExpressions, &iExpression, pCur->pszName))
1727 {
1728 rc = pfnCallback(pCur, pvArg);
1729 if (rc)
1730 break;
1731 }
1732 STAM_UNLOCK_RD(pUVM);
1733
1734 RTMemTmpFree(papszExpressions);
1735 RTStrFree(pszCopy);
1736 }
1737
1738 return rc;
1739}
1740
1741
1742/**
1743 * Registers the ring-0 statistics.
1744 *
1745 * @param pUVM Pointer to the user mode VM structure.
1746 */
1747static void stamR3Ring0StatsRegisterU(PUVM pUVM)
1748{
1749 /* GVMM */
1750 for (unsigned i = 0; i < RT_ELEMENTS(g_aGVMMStats); i++)
1751 stamR3RegisterU(pUVM, (uint8_t *)&pUVM->stam.s.GVMMStats + g_aGVMMStats[i].offVar, NULL, NULL,
1752 g_aGVMMStats[i].enmType, STAMVISIBILITY_ALWAYS, g_aGVMMStats[i].pszName,
1753 g_aGVMMStats[i].enmUnit, g_aGVMMStats[i].pszDesc);
1754 pUVM->stam.s.cRegisteredHostCpus = 0;
1755
1756 /* GMM */
1757 for (unsigned i = 0; i < RT_ELEMENTS(g_aGMMStats); i++)
1758 stamR3RegisterU(pUVM, (uint8_t *)&pUVM->stam.s.GMMStats + g_aGMMStats[i].offVar, NULL, NULL,
1759 g_aGMMStats[i].enmType, STAMVISIBILITY_ALWAYS, g_aGMMStats[i].pszName,
1760 g_aGMMStats[i].enmUnit, g_aGMMStats[i].pszDesc);
1761}
1762
1763
1764/**
1765 * Updates the ring-0 statistics (the copy).
1766 *
1767 * @param pUVM Pointer to the user mode VM structure.
1768 * @param pszPat The pattern.
1769 */
1770static void stamR3Ring0StatsUpdateU(PUVM pUVM, const char *pszPat)
1771{
1772 stamR3Ring0StatsUpdateMultiU(pUVM, &pszPat, 1);
1773}
1774
1775
1776/**
1777 * Updates the ring-0 statistics.
1778 *
1779 * The ring-0 statistics aren't directly addressable from ring-3 and must be
1780 * copied when needed.
1781 *
1782 * @param pUVM Pointer to the user mode VM structure.
1783 * @param pszPat The pattern (for knowing when to skip).
1784 */
1785static void stamR3Ring0StatsUpdateMultiU(PUVM pUVM, const char * const *papszExpressions, unsigned cExpressions)
1786{
1787 PVM pVM = pUVM->pVM;
1788 if (!pVM || !pVM->pSession)
1789 return;
1790
1791 /*
1792 * GVMM
1793 */
1794 bool fUpdate = false;
1795 for (unsigned i = 0; i < RT_ELEMENTS(g_aGVMMStats); i++)
1796 if (stamR3MultiMatch(papszExpressions, cExpressions, NULL, g_aGVMMStats[i].pszName))
1797 {
1798 fUpdate = true;
1799 break;
1800 }
1801 if (!fUpdate)
1802 {
1803 /** @todo check the cpu leaves - rainy day. */
1804 }
1805 if (fUpdate)
1806 {
1807 GVMMQUERYSTATISTICSSREQ Req;
1808 Req.Hdr.cbReq = sizeof(Req);
1809 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
1810 Req.pSession = pVM->pSession;
1811 int rc = SUPR3CallVMMR0Ex(pVM->pVMR0, NIL_VMCPUID, VMMR0_DO_GVMM_QUERY_STATISTICS, 0, &Req.Hdr);
1812 if (RT_SUCCESS(rc))
1813 {
1814 pUVM->stam.s.GVMMStats = Req.Stats;
1815
1816 /*
1817 * Check if the number of host CPUs has changed (it will the first
1818 * time around and normally never again).
1819 */
1820 if (RT_UNLIKELY(pUVM->stam.s.GVMMStats.cHostCpus > pUVM->stam.s.cRegisteredHostCpus))
1821 {
1822 STAM_LOCK_WR(pUVM);
1823 if (RT_UNLIKELY(pUVM->stam.s.GVMMStats.cHostCpus > pUVM->stam.s.cRegisteredHostCpus))
1824 {
1825 uint32_t cCpus = pUVM->stam.s.GVMMStats.cHostCpus;
1826 for (uint32_t iCpu = pUVM->stam.s.cRegisteredHostCpus; iCpu < cCpus; iCpu++)
1827 {
1828 char szName[120];
1829 size_t cchBase = RTStrPrintf(szName, sizeof(szName), "/GVMM/HostCpus/%u", iCpu);
1830 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].idCpu, NULL, NULL,
1831 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_NONE, "Host CPU ID");
1832 strcpy(&szName[cchBase], "/idxCpuSet");
1833 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].idxCpuSet, NULL, NULL,
1834 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_NONE, "CPU Set index");
1835 strcpy(&szName[cchBase], "/DesiredHz");
1836 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].uDesiredHz, NULL, NULL,
1837 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_HZ, "The desired frequency");
1838 strcpy(&szName[cchBase], "/CurTimerHz");
1839 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].uTimerHz, NULL, NULL,
1840 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_HZ, "The current timer frequency");
1841 strcpy(&szName[cchBase], "/PPTChanges");
1842 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].cChanges, NULL, NULL,
1843 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_OCCURENCES, "RTTimerChangeInterval calls");
1844 strcpy(&szName[cchBase], "/PPTStarts");
1845 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].cStarts, NULL, NULL,
1846 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_OCCURENCES, "RTTimerStart calls");
1847 }
1848 pUVM->stam.s.cRegisteredHostCpus = cCpus;
1849 }
1850 STAM_UNLOCK_WR(pUVM);
1851 }
1852 }
1853 }
1854
1855 /*
1856 * GMM
1857 */
1858 fUpdate = false;
1859 for (unsigned i = 0; i < RT_ELEMENTS(g_aGMMStats); i++)
1860 if (stamR3MultiMatch(papszExpressions, cExpressions, NULL, g_aGMMStats[i].pszName))
1861 {
1862 fUpdate = true;
1863 break;
1864 }
1865 if (fUpdate)
1866 {
1867 GMMQUERYSTATISTICSSREQ Req;
1868 Req.Hdr.cbReq = sizeof(Req);
1869 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
1870 Req.pSession = pVM->pSession;
1871 int rc = SUPR3CallVMMR0Ex(pVM->pVMR0, NIL_VMCPUID, VMMR0_DO_GMM_QUERY_STATISTICS, 0, &Req.Hdr);
1872 if (RT_SUCCESS(rc))
1873 pUVM->stam.s.GMMStats = Req.Stats;
1874 }
1875}
1876
1877
1878/**
1879 * Get the unit string.
1880 *
1881 * @returns Pointer to read only unit string.
1882 * @param enmUnit The unit.
1883 */
1884VMMR3DECL(const char *) STAMR3GetUnit(STAMUNIT enmUnit)
1885{
1886 switch (enmUnit)
1887 {
1888 case STAMUNIT_NONE: return "";
1889 case STAMUNIT_CALLS: return "calls";
1890 case STAMUNIT_COUNT: return "count";
1891 case STAMUNIT_BYTES: return "bytes";
1892 case STAMUNIT_PAGES: return "pages";
1893 case STAMUNIT_ERRORS: return "errors";
1894 case STAMUNIT_OCCURENCES: return "times";
1895 case STAMUNIT_TICKS: return "ticks";
1896 case STAMUNIT_TICKS_PER_CALL: return "ticks/call";
1897 case STAMUNIT_TICKS_PER_OCCURENCE: return "ticks/time";
1898 case STAMUNIT_GOOD_BAD: return "good:bad";
1899 case STAMUNIT_MEGABYTES: return "megabytes";
1900 case STAMUNIT_KILOBYTES: return "kilobytes";
1901 case STAMUNIT_NS: return "ns";
1902 case STAMUNIT_NS_PER_CALL: return "ns/call";
1903 case STAMUNIT_NS_PER_OCCURENCE: return "ns/time";
1904 case STAMUNIT_PCT: return "%";
1905 case STAMUNIT_HZ: return "Hz";
1906
1907 default:
1908 AssertMsgFailed(("Unknown unit %d\n", enmUnit));
1909 return "(?unit?)";
1910 }
1911}
1912
1913#ifdef VBOX_WITH_DEBUGGER
1914
1915/**
1916 * @callback_method_impl{FNDBGCCMD, The '.stats' command.}
1917 */
1918static DECLCALLBACK(int) stamR3CmdStats(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1919{
1920 /*
1921 * Validate input.
1922 */
1923 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
1924 if (!pUVM->stam.s.pHead)
1925 return DBGCCmdHlpFail(pCmdHlp, pCmd, "No statistics present");
1926
1927 /*
1928 * Do the printing.
1929 */
1930 STAMR3PRINTONEARGS Args;
1931 Args.pUVM = pUVM;
1932 Args.pvArg = pCmdHlp;
1933 Args.pfnPrintf = stamR3EnumDbgfPrintf;
1934
1935 return stamR3EnumU(pUVM, cArgs ? paArgs[0].u.pszString : NULL, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
1936}
1937
1938
1939/**
1940 * Display one sample in the debugger.
1941 *
1942 * @param pArgs Pointer to the print one argument structure.
1943 * @param pszFormat Format string.
1944 * @param ... Format arguments.
1945 */
1946static DECLCALLBACK(void) stamR3EnumDbgfPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...)
1947{
1948 PDBGCCMDHLP pCmdHlp = (PDBGCCMDHLP)pArgs->pvArg;
1949
1950 va_list va;
1951 va_start(va, pszFormat);
1952 pCmdHlp->pfnPrintfV(pCmdHlp, NULL, pszFormat, va);
1953 va_end(va);
1954 NOREF(pArgs);
1955}
1956
1957
1958/**
1959 * @callback_method_impl{FNDBGCCMD, The '.statsreset' command.}
1960 */
1961static DECLCALLBACK(int) stamR3CmdStatsReset(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
1962{
1963 /*
1964 * Validate input.
1965 */
1966 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
1967 if (!pUVM->stam.s.pHead)
1968 return DBGCCmdHlpFail(pCmdHlp, pCmd, "No statistics present");
1969
1970 /*
1971 * Execute reset.
1972 */
1973 int rc = STAMR3Reset(pUVM, cArgs ? paArgs[0].u.pszString : NULL);
1974 if (RT_SUCCESS(rc))
1975 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "STAMR3ResetU");
1976 return DBGCCmdHlpPrintf(pCmdHlp, "Statistics have been reset.\n");
1977}
1978
1979#endif /* VBOX_WITH_DEBUGGER */
1980
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