VirtualBox

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

Last change on this file was 107194, checked in by vboxsync, 7 weeks ago

VMM: More adjustments for VBOX_WITH_ONLY_PGM_NEM_MODE, VBOX_WITH_MINIMAL_R0, VBOX_WITH_HWVIRT and such. jiraref:VBP-1466

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 147.1 KB
Line 
1/* $Id: STAM.cpp 107194 2024-11-29 14:47:06Z vboxsync $ */
2/** @file
3 * STAM - The Statistics Manager.
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/** @page pg_stam STAM - The Statistics Manager
29 *
30 * The purpose for the statistics manager is to present the rest of the system
31 * with a somewhat uniform way of accessing VMM statistics. STAM sports a
32 * couple of different APIs for accessing them: STAMR3EnumU, STAMR3SnapshotU,
33 * STAMR3DumpU, STAMR3DumpToReleaseLogU and the debugger. Main is exposing the
34 * XML based one, STAMR3SnapshotU.
35 *
36 * The rest of the VMM together with the devices and drivers registers their
37 * statistics with STAM giving them a name. The name is hierarchical, the
38 * components separated by slashes ('/') and must start with a slash.
39 *
40 * Each item registered with STAM - also, half incorrectly, called a sample -
41 * has a type, unit, visibility, data pointer and description associated with it
42 * in addition to the name (described above). The type tells STAM what kind of
43 * structure the pointer is pointing to. The visibility allows unused
44 * statistics from cluttering the output or showing up in the GUI. All the bits
45 * together makes STAM able to present the items in a sensible way to the user.
46 * Some types also allows STAM to reset the data, which is very convenient when
47 * digging into specific operations and such.
48 *
49 * PS. The VirtualBox Debugger GUI has a viewer for inspecting the statistics
50 * STAM provides. You will also find statistics in the release and debug logs.
51 * And as mentioned in the introduction, the debugger console features a couple
52 * of command: .stats and .statsreset.
53 *
54 * @see grp_stam
55 */
56
57
58/*********************************************************************************************************************************
59* Header Files *
60*********************************************************************************************************************************/
61#define LOG_GROUP LOG_GROUP_STAM
62#include <VBox/vmm/stam.h>
63#include "STAMInternal.h"
64#include <VBox/vmm/vmcc.h>
65
66#include <VBox/err.h>
67#include <VBox/dbg.h>
68#include <VBox/log.h>
69
70#include <iprt/assert.h>
71#include <iprt/asm.h>
72#include <iprt/mem.h>
73#include <iprt/stream.h>
74#include <iprt/string.h>
75
76
77/*********************************************************************************************************************************
78* Defined Constants And Macros *
79*********************************************************************************************************************************/
80/** The maximum name length excluding the terminator. */
81#define STAM_MAX_NAME_LEN 239
82
83
84/*********************************************************************************************************************************
85* Structures and Typedefs *
86*********************************************************************************************************************************/
87/**
88 * Argument structure for stamR3PrintOne().
89 */
90typedef struct STAMR3PRINTONEARGS
91{
92 PUVM pUVM;
93 void *pvArg;
94 DECLCALLBACKMEMBER(void, pfnPrintf,(struct STAMR3PRINTONEARGS *pvArg, const char *pszFormat, ...));
95} STAMR3PRINTONEARGS, *PSTAMR3PRINTONEARGS;
96
97
98/**
99 * Argument structure to stamR3EnumOne().
100 */
101typedef struct STAMR3ENUMONEARGS
102{
103 PVM pVM;
104 PFNSTAMR3ENUM pfnEnum;
105 void *pvUser;
106} STAMR3ENUMONEARGS, *PSTAMR3ENUMONEARGS;
107
108
109/**
110 * The snapshot status structure.
111 * Argument package passed to stamR3SnapshotOne, stamR3SnapshotPrintf and stamR3SnapshotOutput.
112 */
113typedef struct STAMR3SNAPSHOTONE
114{
115 /** Pointer to the buffer start. */
116 char *pszStart;
117 /** Pointer to the buffer end. */
118 char *pszEnd;
119 /** Pointer to the current buffer position. */
120 char *psz;
121 /** Pointer to the VM. */
122 PVM pVM;
123 /** The number of bytes allocated. */
124 size_t cbAllocated;
125 /** The status code. */
126 int rc;
127 /** Whether to include the description strings. */
128 bool fWithDesc;
129} STAMR3SNAPSHOTONE, *PSTAMR3SNAPSHOTONE;
130
131
132/**
133 * Init record for a ring-0 statistic sample.
134 */
135typedef struct STAMR0SAMPLE
136{
137 /** The GVMMSTATS structure offset of the variable. */
138 unsigned offVar;
139 /** The type. */
140 STAMTYPE enmType;
141 /** The unit. */
142 STAMUNIT enmUnit;
143 /** The name. */
144 const char *pszName;
145 /** The description. */
146 const char *pszDesc;
147} STAMR0SAMPLE;
148
149
150/*********************************************************************************************************************************
151* Internal Functions *
152*********************************************************************************************************************************/
153static void stamR3LookupDestroyTree(PSTAMLOOKUP pRoot);
154static int stamR3RegisterU(PUVM pUVM, void *pvSample, PFNSTAMR3CALLBACKRESET pfnReset,
155 PFNSTAMR3CALLBACKPRINT pfnPrint, STAMTYPE enmType, STAMVISIBILITY enmVisibility,
156 const char *pszName, STAMUNIT enmUnit, const char *pszDesc, uint8_t iRefreshGrp);
157static int stamR3ResetOne(PSTAMDESC pDesc, void *pvArg);
158static DECLCALLBACK(void) stamR3EnumLogPrintf(PSTAMR3PRINTONEARGS pvArg, const char *pszFormat, ...);
159static DECLCALLBACK(void) stamR3EnumRelLogPrintf(PSTAMR3PRINTONEARGS pvArg, const char *pszFormat, ...);
160static DECLCALLBACK(void) stamR3EnumPrintf(PSTAMR3PRINTONEARGS pvArg, const char *pszFormat, ...);
161static int stamR3SnapshotOne(PSTAMDESC pDesc, void *pvArg);
162static int stamR3SnapshotPrintf(PSTAMR3SNAPSHOTONE pThis, const char *pszFormat, ...);
163static int stamR3PrintOne(PSTAMDESC pDesc, void *pvArg);
164static int stamR3EnumOne(PSTAMDESC pDesc, void *pvArg);
165static bool stamR3MultiMatch(const char * const *papszExpressions, unsigned cExpressions, unsigned *piExpression, const char *pszName);
166static char ** stamR3SplitPattern(const char *pszPat, unsigned *pcExpressions, char **ppszCopy);
167static int stamR3EnumU(PUVM pUVM, const char *pszPat, bool fUpdateRing0, int (pfnCallback)(PSTAMDESC pDesc, void *pvArg), void *pvArg);
168#ifdef VBOX_WITH_R0_MODULES
169static void stamR3Ring0StatsRegisterU(PUVM pUVM);
170#endif
171
172#ifdef VBOX_WITH_DEBUGGER
173static FNDBGCCMD stamR3CmdStats;
174static DECLCALLBACK(void) stamR3EnumDbgfPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...);
175static FNDBGCCMD stamR3CmdStatsReset;
176#endif
177
178
179/*********************************************************************************************************************************
180* Global Variables *
181*********************************************************************************************************************************/
182#ifdef VBOX_WITH_DEBUGGER
183/** Pattern argument. */
184static const DBGCVARDESC g_aArgPat[] =
185{
186 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
187 { 0, 1, DBGCVAR_CAT_STRING, 0, "pattern", "Which samples the command shall be applied to. Use '*' as wildcard. Use ';' to separate expression." }
188};
189
190/** Command descriptors. */
191static const DBGCCMD g_aCmds[] =
192{
193 /* pszCmd, cArgsMin, cArgsMax, paArgDesc, cArgDescs, fFlags, pfnHandler pszSyntax, ....pszDescription */
194 { "stats", 0, 1, &g_aArgPat[0], RT_ELEMENTS(g_aArgPat), 0, stamR3CmdStats, "[pattern]", "Display statistics." },
195 { "statsreset", 0, 1, &g_aArgPat[0], RT_ELEMENTS(g_aArgPat), 0, stamR3CmdStatsReset,"[pattern]", "Resets statistics." }
196};
197#endif
198
199
200#ifdef VBOX_WITH_R0_MODULES
201/**
202 * The GVMM mapping records - sans the host cpus.
203 */
204static const STAMR0SAMPLE g_aGVMMStats[] =
205{
206 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cHaltCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/HaltCalls", "The number of calls to GVMMR0SchedHalt." },
207 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cHaltBlocking), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/HaltBlocking", "The number of times we did go to sleep in GVMMR0SchedHalt." },
208 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cHaltTimeouts), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/HaltTimeouts", "The number of times we timed out in GVMMR0SchedHalt." },
209 { 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." },
210 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cHaltWakeUps), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/HaltWakeUps", "The number of wake ups done during GVMMR0SchedHalt." },
211 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cWakeUpCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/WakeUpCalls", "The number of calls to GVMMR0WakeUp." },
212 { 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." },
213 { 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)." },
214 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cPokeCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/PokeCalls", "The number of calls to GVMMR0Poke." },
215 { 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." },
216 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cPollCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/PollCalls", "The number of calls to GVMMR0SchedPoll." },
217 { 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." },
218 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cPollWakeUps), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/PollWakeUps", "The number of wake ups done during GVMMR0SchedPoll." },
219
220 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cHaltCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/HaltCalls", "The number of calls to GVMMR0SchedHalt." },
221 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cHaltBlocking), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/HaltBlocking", "The number of times we did go to sleep in GVMMR0SchedHalt." },
222 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cHaltTimeouts), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/HaltTimeouts", "The number of times we timed out in GVMMR0SchedHalt." },
223 { 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." },
224 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cHaltWakeUps), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/HaltWakeUps", "The number of wake ups done during GVMMR0SchedHalt." },
225 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cWakeUpCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/WakeUpCalls", "The number of calls to GVMMR0WakeUp." },
226 { 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." },
227 { 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)." },
228 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cPokeCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/PokeCalls", "The number of calls to GVMMR0Poke." },
229 { 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." },
230 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cPollCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/PollCalls", "The number of calls to GVMMR0SchedPoll." },
231 { 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." },
232 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cPollWakeUps), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/PollWakeUps", "The number of wake ups done during GVMMR0SchedPoll." },
233
234 { RT_UOFFSETOF(GVMMSTATS, cVMs), STAMTYPE_U32, STAMUNIT_COUNT, "/GVMM/VMs", "The number of VMs accessible to the caller." },
235 { RT_UOFFSETOF(GVMMSTATS, cEMTs), STAMTYPE_U32, STAMUNIT_COUNT, "/GVMM/EMTs", "The number of emulation threads." },
236 { RT_UOFFSETOF(GVMMSTATS, cHostCpus), STAMTYPE_U32, STAMUNIT_COUNT, "/GVMM/HostCPUs", "The number of host CPUs." },
237};
238
239
240# ifndef VBOX_WITH_MINIMAL_R0
241/**
242 * The GMM mapping records.
243 */
244static const STAMR0SAMPLE g_aGMMStats[] =
245{
246 { RT_UOFFSETOF(GMMSTATS, cMaxPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/cMaxPages", "The maximum number of pages GMM is allowed to allocate." },
247 { RT_UOFFSETOF(GMMSTATS, cReservedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/cReservedPages", "The number of pages that has been reserved." },
248 { RT_UOFFSETOF(GMMSTATS, cOverCommittedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/cOverCommittedPages", "The number of pages that we have over-committed in reservations." },
249 { RT_UOFFSETOF(GMMSTATS, cAllocatedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/cAllocatedPages", "The number of actually allocated (committed if you like) pages." },
250 { RT_UOFFSETOF(GMMSTATS, cSharedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/cSharedPages", "The number of pages that are shared. A subset of cAllocatedPages." },
251 { RT_UOFFSETOF(GMMSTATS, cDuplicatePages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/cDuplicatePages", "The number of pages that are actually shared between VMs." },
252 { 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." },
253 { RT_UOFFSETOF(GMMSTATS, cBalloonedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/cBalloonedPages", "The number of current ballooned pages." },
254 { RT_UOFFSETOF(GMMSTATS, cChunks), STAMTYPE_U32, STAMUNIT_COUNT, "/GMM/cChunks", "The number of allocation chunks." },
255 { RT_UOFFSETOF(GMMSTATS, cFreedChunks), STAMTYPE_U32, STAMUNIT_COUNT, "/GMM/cFreedChunks", "The number of freed chunks ever." },
256 { RT_UOFFSETOF(GMMSTATS, cShareableModules), STAMTYPE_U32, STAMUNIT_COUNT, "/GMM/cShareableModules", "The number of shareable modules." },
257 { RT_UOFFSETOF(GMMSTATS, idFreeGeneration), STAMTYPE_U64, STAMUNIT_NONE, "/GMM/idFreeGeneration", "The current chunk freeing generation number (for per-VM chunk lookup TLB versioning)." },
258 { 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." },
259 { RT_UOFFSETOF(GMMSTATS, VMStats.Reserved.cShadowPages), STAMTYPE_U32, STAMUNIT_PAGES, "/GMM/VM/Reserved/cShadowPages", "The amount of memory reserved for shadow/nested page tables." },
260 { 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." },
261 { 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." },
262 { RT_UOFFSETOF(GMMSTATS, VMStats.Allocated.cShadowPages), STAMTYPE_U32, STAMUNIT_PAGES, "/GMM/VM/Allocated/cShadowPages", "The amount of memory allocated for shadow/nested page tables." },
263 { 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." },
264 { RT_UOFFSETOF(GMMSTATS, VMStats.cPrivatePages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/VM/cPrivatePages", "The current number of private pages." },
265 { RT_UOFFSETOF(GMMSTATS, VMStats.cSharedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/VM/cSharedPages", "The current number of shared pages." },
266 { RT_UOFFSETOF(GMMSTATS, VMStats.cBalloonedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/VM/cBalloonedPages", "The current number of ballooned pages." },
267 { RT_UOFFSETOF(GMMSTATS, VMStats.cMaxBalloonedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/VM/cMaxBalloonedPages", "The max number of pages that can be ballooned." },
268 { 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." },
269 { 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." },
270 { 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." },
271 { RT_UOFFSETOF(GMMSTATS, VMStats.cShareableModules), STAMTYPE_U32, STAMUNIT_COUNT, "/GMM/VM/cShareableModules", "The number of shareable modules traced by the VM." },
272 { RT_UOFFSETOF(GMMSTATS, VMStats.enmPolicy), STAMTYPE_U32, STAMUNIT_NONE, "/GMM/VM/enmPolicy", "The current over-commit policy." },
273 { 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." },
274 { RT_UOFFSETOF(GMMSTATS, VMStats.fBallooningEnabled), STAMTYPE_BOOL, STAMUNIT_NONE, "/GMM/VM/fBallooningEnabled", "Whether ballooning is enabled or not." },
275 { RT_UOFFSETOF(GMMSTATS, VMStats.fSharedPagingEnabled), STAMTYPE_BOOL, STAMUNIT_NONE, "/GMM/VM/fSharedPagingEnabled", "Whether shared paging is enabled or not." },
276 { RT_UOFFSETOF(GMMSTATS, VMStats.fMayAllocate), STAMTYPE_BOOL, STAMUNIT_NONE, "/GMM/VM/fMayAllocate", "Whether the VM is allowed to allocate memory or not." },
277};
278# endif /* !VBOX_WITH_MINIMAL_R0 */
279#endif /* VBOX_WITH_R0_MODULES */
280
281
282/**
283 * Initializes the STAM.
284 *
285 * @returns VBox status code.
286 * @param pUVM The user mode VM structure.
287 */
288VMMR3DECL(int) STAMR3InitUVM(PUVM pUVM)
289{
290 LogFlow(("STAMR3Init\n"));
291
292 /*
293 * Assert alignment and sizes.
294 */
295 AssertCompile(sizeof(pUVM->stam.s) <= sizeof(pUVM->stam.padding));
296 AssertRelease(sizeof(pUVM->stam.s) <= sizeof(pUVM->stam.padding));
297
298 /*
299 * Initialize the read/write lock and list.
300 */
301 int rc = RTSemRWCreate(&pUVM->stam.s.RWSem);
302 AssertRCReturn(rc, rc);
303
304 RTListInit(&pUVM->stam.s.List);
305
306 /*
307 * Initialize the root node.
308 */
309 PSTAMLOOKUP pRoot = (PSTAMLOOKUP)RTMemAlloc(sizeof(STAMLOOKUP));
310 if (!pRoot)
311 {
312 RTSemRWDestroy(pUVM->stam.s.RWSem);
313 pUVM->stam.s.RWSem = NIL_RTSEMRW;
314 return VERR_NO_MEMORY;
315 }
316 pRoot->pParent = NULL;
317 pRoot->papChildren = NULL;
318 pRoot->pDesc = NULL;
319 pRoot->cDescsInTree = 0;
320 pRoot->cChildren = 0;
321 pRoot->iParent = UINT16_MAX;
322 pRoot->off = 0;
323 pRoot->cch = 0;
324 pRoot->szName[0] = '\0';
325
326 pUVM->stam.s.pRoot = pRoot;
327
328#ifdef VBOX_WITH_R0_MODULES
329 /*
330 * Register the ring-0 statistics (GVMM/GMM).
331 */
332 if (!SUPR3IsDriverless())
333 stamR3Ring0StatsRegisterU(pUVM);
334#endif
335
336#ifdef VBOX_WITH_DEBUGGER
337 /*
338 * Register debugger commands.
339 */
340 static bool fRegisteredCmds = false;
341 if (!fRegisteredCmds)
342 {
343 rc = DBGCRegisterCommands(&g_aCmds[0], RT_ELEMENTS(g_aCmds));
344 if (RT_SUCCESS(rc))
345 fRegisteredCmds = true;
346 }
347#endif
348
349 return VINF_SUCCESS;
350}
351
352
353/**
354 * Terminates the STAM.
355 *
356 * @param pUVM Pointer to the user mode VM structure.
357 */
358VMMR3DECL(void) STAMR3TermUVM(PUVM pUVM)
359{
360 /*
361 * Free used memory and the RWLock.
362 */
363 PSTAMDESC pCur, pNext;
364 RTListForEachSafe(&pUVM->stam.s.List, pCur, pNext, STAMDESC, ListEntry)
365 {
366 pCur->pLookup->pDesc = NULL;
367 if ( pCur->enmType != STAMTYPE_INTERNAL_SUM
368 && pCur->enmType != STAMTYPE_INTERNAL_PCT_OF_SUM)
369 { /* likely*/ }
370 else
371 RTMemFree(pCur->u.pSum);
372 RTMemFree(pCur);
373 }
374
375 stamR3LookupDestroyTree(pUVM->stam.s.pRoot);
376 pUVM->stam.s.pRoot = NULL;
377
378 Assert(pUVM->stam.s.RWSem != NIL_RTSEMRW);
379 RTSemRWDestroy(pUVM->stam.s.RWSem);
380 pUVM->stam.s.RWSem = NIL_RTSEMRW;
381}
382
383
384/**
385 * Registers a sample with the statistics manager.
386 *
387 * Statistics are maintained on a per VM basis and is normally registered
388 * during the VM init stage, but there is nothing preventing you from
389 * register them at runtime.
390 *
391 * Use STAMR3Deregister() to deregister statistics at runtime, however do
392 * not bother calling at termination time.
393 *
394 * It is not possible to register the same sample twice.
395 *
396 * @returns VBox status code.
397 * @param pUVM Pointer to the user mode VM structure.
398 * @param pvSample Pointer to the sample.
399 * @param enmType Sample type. This indicates what pvSample is pointing at.
400 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
401 * @param pszName Sample name. The name is on this form "/<component>/<sample>".
402 * Further nesting is possible.
403 * @param enmUnit Sample unit.
404 * @param pszDesc Sample description.
405 */
406VMMR3DECL(int) STAMR3RegisterU(PUVM pUVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, const char *pszName,
407 STAMUNIT enmUnit, const char *pszDesc)
408{
409 AssertReturn(enmType != STAMTYPE_CALLBACK && enmType < STAMTYPE_FIRST_INTERNAL_TYPE, VERR_INVALID_PARAMETER);
410 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
411 return stamR3RegisterU(pUVM, pvSample, NULL, NULL, enmType, enmVisibility, pszName, enmUnit, pszDesc, STAM_REFRESH_GRP_NONE);
412}
413
414
415/**
416 * Registers a sample with the statistics manager.
417 *
418 * Statistics are maintained on a per VM basis and is normally registered
419 * during the VM init stage, but there is nothing preventing you from
420 * register them at runtime.
421 *
422 * Use STAMR3Deregister() to deregister statistics at runtime, however do
423 * not bother calling at termination time.
424 *
425 * It is not possible to register the same sample twice.
426 *
427 * @returns VBox status code.
428 * @param pVM The cross context VM structure.
429 * @param pvSample Pointer to the sample.
430 * @param enmType Sample type. This indicates what pvSample is pointing at.
431 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
432 * @param pszName Sample name. The name is on this form "/<component>/<sample>".
433 * Further nesting is possible.
434 * @param enmUnit Sample unit.
435 * @param pszDesc Sample description.
436 */
437VMMR3DECL(int) STAMR3Register(PVM pVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, const char *pszName,
438 STAMUNIT enmUnit, const char *pszDesc)
439{
440 AssertReturn(enmType != STAMTYPE_CALLBACK && enmType < STAMTYPE_FIRST_INTERNAL_TYPE, VERR_INVALID_PARAMETER);
441 return stamR3RegisterU(pVM->pUVM, pvSample, NULL, NULL, enmType, enmVisibility, pszName, enmUnit, pszDesc,
442 STAM_REFRESH_GRP_NONE);
443}
444
445
446/**
447 * Same as STAMR3RegisterU except that the name is specified in a
448 * RTStrPrintf like fashion.
449 *
450 * @returns VBox status code.
451 * @param pUVM Pointer to the user mode VM structure.
452 * @param pvSample Pointer to the sample.
453 * @param enmType Sample type. This indicates what pvSample is pointing at.
454 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
455 * @param enmUnit Sample unit.
456 * @param pszDesc Sample description.
457 * @param pszName The sample name format string.
458 * @param ... Arguments to the format string.
459 */
460VMMR3DECL(int) STAMR3RegisterFU(PUVM pUVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
461 const char *pszDesc, const char *pszName, ...)
462{
463 va_list args;
464 va_start(args, pszName);
465 int rc = STAMR3RegisterVU(pUVM, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName, args);
466 va_end(args);
467 return rc;
468}
469
470
471/**
472 * Same as STAMR3Register except that the name is specified in a
473 * RTStrPrintf like fashion.
474 *
475 * @returns VBox status code.
476 * @param pVM The cross context VM structure.
477 * @param pvSample Pointer to the sample.
478 * @param enmType Sample type. This indicates what pvSample is pointing at.
479 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
480 * @param enmUnit Sample unit.
481 * @param pszDesc Sample description.
482 * @param pszName The sample name format string.
483 * @param ... Arguments to the format string.
484 */
485VMMR3DECL(int) STAMR3RegisterF(PVM pVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
486 const char *pszDesc, const char *pszName, ...)
487{
488 va_list args;
489 va_start(args, pszName);
490 int rc = STAMR3RegisterVU(pVM->pUVM, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName, args);
491 va_end(args);
492 return rc;
493}
494
495
496/**
497 * Same as STAMR3Register except that the name is specified in a
498 * RTStrPrintfV like fashion.
499 *
500 * @returns VBox status code.
501 * @param pUVM The user mode VM structure.
502 * @param pvSample Pointer to the sample.
503 * @param enmType Sample type. This indicates what pvSample is pointing at.
504 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
505 * @param enmUnit Sample unit.
506 * @param pszDesc Sample description.
507 * @param pszName The sample name format string.
508 * @param args Arguments to the format string.
509 */
510VMMR3DECL(int) STAMR3RegisterVU(PUVM pUVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
511 const char *pszDesc, const char *pszName, va_list args)
512{
513 AssertReturn(enmType != STAMTYPE_CALLBACK, VERR_INVALID_PARAMETER);
514
515 char szFormattedName[STAM_MAX_NAME_LEN + 8];
516 size_t cch = RTStrPrintfV(szFormattedName, sizeof(szFormattedName), pszName, args);
517 AssertReturn(cch <= STAM_MAX_NAME_LEN, VERR_OUT_OF_RANGE);
518
519 return STAMR3RegisterU(pUVM, pvSample, enmType, enmVisibility, szFormattedName, enmUnit, pszDesc);
520}
521
522
523/**
524 * Same as STAMR3Register except that the name is specified in a
525 * RTStrPrintfV like fashion.
526 *
527 * @returns VBox status code.
528 * @param pVM The cross context VM structure.
529 * @param pvSample Pointer to the sample.
530 * @param enmType Sample type. This indicates what pvSample is pointing at.
531 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
532 * @param enmUnit Sample unit.
533 * @param pszDesc Sample description.
534 * @param pszName The sample name format string.
535 * @param args Arguments to the format string.
536 */
537VMMR3DECL(int) STAMR3RegisterV(PVM pVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
538 const char *pszDesc, const char *pszName, va_list args)
539{
540 return STAMR3RegisterVU(pVM->pUVM, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName, args);
541}
542
543
544/**
545 * Similar to STAMR3Register except for the two callbacks, the implied type (STAMTYPE_CALLBACK),
546 * and name given in an RTStrPrintf like fashion.
547 *
548 * @returns VBox status code.
549 * @param pVM The cross context VM structure.
550 * @param pvSample Pointer to the sample.
551 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
552 * @param enmUnit Sample unit.
553 * @param pfnReset Callback for resetting the sample. NULL should be used if the sample can't be reset.
554 * @param pfnPrint Print the sample.
555 * @param pszDesc Sample description.
556 * @param pszName The sample name format string.
557 * @param ... Arguments to the format string.
558 * @remark There is currently no device or driver variant of this API. Add one if it should become necessary!
559 */
560VMMR3DECL(int) STAMR3RegisterCallback(PVM pVM, void *pvSample, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
561 PFNSTAMR3CALLBACKRESET pfnReset, PFNSTAMR3CALLBACKPRINT pfnPrint,
562 const char *pszDesc, const char *pszName, ...)
563{
564 va_list args;
565 va_start(args, pszName);
566 int rc = STAMR3RegisterCallbackV(pVM, pvSample, enmVisibility, enmUnit, pfnReset, pfnPrint, pszDesc, pszName, args);
567 va_end(args);
568 return rc;
569}
570
571
572/**
573 * Same as STAMR3RegisterCallback() except for the ellipsis which is a va_list here.
574 *
575 * @returns VBox status code.
576 * @param pVM The cross context VM structure.
577 * @param pvSample Pointer to the sample.
578 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
579 * @param enmUnit Sample unit.
580 * @param pfnReset Callback for resetting the sample. NULL should be used if the sample can't be reset.
581 * @param pfnPrint Print the sample.
582 * @param pszDesc Sample description.
583 * @param pszName The sample name format string.
584 * @param args Arguments to the format string.
585 * @remark There is currently no device or driver variant of this API. Add one if it should become necessary!
586 */
587VMMR3DECL(int) STAMR3RegisterCallbackV(PVM pVM, void *pvSample, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
588 PFNSTAMR3CALLBACKRESET pfnReset, PFNSTAMR3CALLBACKPRINT pfnPrint,
589 const char *pszDesc, const char *pszName, va_list args)
590{
591 char *pszFormattedName;
592 RTStrAPrintfV(&pszFormattedName, pszName, args);
593 if (!pszFormattedName)
594 return VERR_NO_MEMORY;
595
596 int rc = stamR3RegisterU(pVM->pUVM, pvSample, pfnReset, pfnPrint, STAMTYPE_CALLBACK, enmVisibility, pszFormattedName,
597 enmUnit, pszDesc, STAM_REFRESH_GRP_NONE);
598 RTStrFree(pszFormattedName);
599 return rc;
600}
601
602
603/**
604 * Same as STAMR3RegisterFU, except there is an extra refresh group parameter.
605 *
606 * @returns VBox status code.
607 * @param pUVM Pointer to the user mode VM structure.
608 * @param pvSample Pointer to the sample.
609 * @param enmType Sample type. This indicates what pvSample is pointing at.
610 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
611 * @param enmUnit Sample unit.
612 * @param iRefreshGrp The refresh group, STAM_REFRESH_GRP_XXX.
613 * @param pszDesc Sample description.
614 * @param pszName The sample name format string.
615 * @param ... Arguments to the format string.
616 */
617VMMR3DECL(int) STAMR3RegisterRefresh(PUVM pUVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
618 uint8_t iRefreshGrp, const char *pszDesc, const char *pszName, ...)
619{
620 va_list args;
621 va_start(args, pszName);
622 int rc = STAMR3RegisterRefreshV(pUVM, pvSample, enmType, enmVisibility, enmUnit, iRefreshGrp, pszDesc, pszName, args);
623 va_end(args);
624 return rc;
625}
626
627
628/**
629 * Same as STAMR3RegisterVU, except there is an extra refresh group parameter.
630 *
631 * @returns VBox status code.
632 * @param pUVM The user mode VM structure.
633 * @param pvSample Pointer to the sample.
634 * @param enmType Sample type. This indicates what pvSample is pointing at.
635 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
636 * @param enmUnit Sample unit.
637 * @param iRefreshGrp The refresh group, STAM_REFRESH_GRP_XXX.
638 * @param pszDesc Sample description.
639 * @param pszName The sample name format string.
640 * @param va Arguments to the format string.
641 */
642VMMR3DECL(int) STAMR3RegisterRefreshV(PUVM pUVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
643 uint8_t iRefreshGrp, const char *pszDesc, const char *pszName, va_list va)
644{
645 AssertReturn(enmType != STAMTYPE_CALLBACK && enmType < STAMTYPE_FIRST_INTERNAL_TYPE, VERR_INVALID_PARAMETER);
646
647 char szFormattedName[STAM_MAX_NAME_LEN + 8];
648 size_t cch = RTStrPrintfV(szFormattedName, sizeof(szFormattedName), pszName, va);
649 AssertReturn(cch <= STAM_MAX_NAME_LEN, VERR_OUT_OF_RANGE);
650
651 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
652 return stamR3RegisterU(pUVM, pvSample, NULL, NULL, enmType, enmVisibility, pszName, enmUnit, pszDesc, iRefreshGrp);
653}
654
655
656/**
657 * Refreshes the cached sum (STAMSUMSAMPLE::u) of a sum sample.
658 */
659static void stamR3SumRefresh(PSTAMSUMSAMPLE pSum)
660{
661 switch (pSum->enmType)
662 {
663 case STAMTYPE_COUNTER:
664 {
665 uint64_t uSum = 0;
666 uintptr_t i = pSum->cSummands;
667 while (i-- > 0)
668 {
669 PSTAMDESC const pDesc = pSum->apSummands[i];
670 switch (pDesc->enmType)
671 {
672 case STAMTYPE_COUNTER:
673 uSum += pDesc->u.pCounter->c;
674 break;
675
676 case STAMTYPE_U64:
677 case STAMTYPE_U64_RESET:
678 case STAMTYPE_X64:
679 case STAMTYPE_X64_RESET:
680 uSum += *pDesc->u.pu64;
681 break;
682
683 case STAMTYPE_U32:
684 case STAMTYPE_U32_RESET:
685 case STAMTYPE_X32:
686 case STAMTYPE_X32_RESET:
687 uSum += *pDesc->u.pu32;
688 break;
689
690 case STAMTYPE_U16:
691 case STAMTYPE_U16_RESET:
692 case STAMTYPE_X16:
693 case STAMTYPE_X16_RESET:
694 uSum += *pDesc->u.pu16;
695 break;
696
697 case STAMTYPE_U8:
698 case STAMTYPE_U8_RESET:
699 case STAMTYPE_X8:
700 case STAMTYPE_X8_RESET:
701 uSum += *pDesc->u.pu8;
702 break;
703
704 default:
705 AssertFailedBreak();
706 }
707 }
708 pSum->u.Counter.c = uSum;
709 break;
710 }
711
712 case STAMTYPE_PROFILE:
713 {
714 uint64_t cPeriods = 0;
715 uint64_t uTotal = 0;
716 uint64_t uMax = 0;
717 uint64_t uMin = UINT64_MAX;
718 uintptr_t i = pSum->cSummands;
719 while (i-- > 0)
720 {
721 PSTAMDESC const pDesc = pSum->apSummands[i];
722 AssertContinue( pDesc->enmType == STAMTYPE_PROFILE
723 || pDesc->enmType == STAMTYPE_PROFILE_ADV);
724 PSTAMPROFILE const pProfile = pDesc->u.pProfile;
725 cPeriods += pProfile->cPeriods;
726 uTotal += pProfile->cTicks;
727 uint64_t u = pProfile->cTicksMax;
728 if (u > uMax)
729 uMax = u;
730 u = pProfile->cTicksMin;
731 if (u < uMin)
732 uMin = u;
733 }
734
735 pSum->u.Profile.cTicks = uTotal;
736 pSum->u.Profile.cPeriods = cPeriods;
737 pSum->u.Profile.cTicksMin = uMin;
738 pSum->u.Profile.cTicksMax = uMax;
739 break;
740 }
741
742 default:
743 AssertFailedReturnVoid();
744 }
745}
746
747
748/**
749 * Used by STAMR3RegisterSumV to locate the samples to sum up.
750 */
751static int stamR3RegisterSumEnumCallback(PSTAMDESC pDesc, void *pvArg)
752{
753 PSTAMSUMSAMPLE const pSum = (PSTAMSUMSAMPLE)pvArg;
754 if (pSum->cSummands == 0)
755 {
756 /*
757 * The first time around we check that the type is a supported one
758 * and just set the unit.
759 */
760 switch (pDesc->enmType)
761 {
762 case STAMTYPE_COUNTER:
763 case STAMTYPE_U64:
764 case STAMTYPE_U64_RESET:
765 case STAMTYPE_X64:
766 case STAMTYPE_X64_RESET:
767 case STAMTYPE_U32:
768 case STAMTYPE_U32_RESET:
769 case STAMTYPE_X32:
770 case STAMTYPE_X32_RESET:
771 case STAMTYPE_U16:
772 case STAMTYPE_U16_RESET:
773 case STAMTYPE_X16:
774 case STAMTYPE_X16_RESET:
775 case STAMTYPE_U8:
776 case STAMTYPE_U8_RESET:
777 case STAMTYPE_X8:
778 case STAMTYPE_X8_RESET:
779 pSum->enmType = STAMTYPE_COUNTER;
780 break;
781
782 case STAMTYPE_PROFILE:
783 case STAMTYPE_PROFILE_ADV:
784 pSum->enmType = STAMTYPE_PROFILE;
785 break;
786
787 default:
788 AssertMsgFailedReturn(("Summing up enmType=%d types have not been implemented yet! Sorry.\n", pDesc->enmType),
789 VERR_WRONG_TYPE);
790 }
791 pSum->enmTypeFirst = pDesc->enmType;
792 pSum->enmUnit = pDesc->enmUnit;
793 }
794 else
795 {
796 /*
797 * Make sure additional sample compatible with the first,
798 * both type and unit.
799 */
800 if (RT_LIKELY( pDesc->enmType == pSum->enmType
801 || pDesc->enmType == (STAMTYPE)pSum->enmTypeFirst))
802 { /* likely */ }
803 else
804 {
805 switch (pSum->enmType)
806 {
807 case STAMTYPE_COUNTER:
808 AssertMsgReturn( pDesc->enmType == STAMTYPE_COUNTER
809 || pDesc->enmType == STAMTYPE_U64
810 || pDesc->enmType == STAMTYPE_U64_RESET
811 || pDesc->enmType == STAMTYPE_X64
812 || pDesc->enmType == STAMTYPE_X64_RESET
813 || pDesc->enmType == STAMTYPE_U32
814 || pDesc->enmType == STAMTYPE_U32_RESET
815 || pDesc->enmType == STAMTYPE_X32
816 || pDesc->enmType == STAMTYPE_X32_RESET
817 || pDesc->enmType == STAMTYPE_U16
818 || pDesc->enmType == STAMTYPE_U16_RESET
819 || pDesc->enmType == STAMTYPE_X16
820 || pDesc->enmType == STAMTYPE_X16_RESET
821 || pDesc->enmType == STAMTYPE_U8
822 || pDesc->enmType == STAMTYPE_U8_RESET
823 || pDesc->enmType == STAMTYPE_X8
824 || pDesc->enmType == STAMTYPE_X8_RESET,
825 ("Unsupported type mixup: %d & %d (%s)\n", pSum->enmType, pDesc->enmType, pDesc->pszName),
826 VERR_MISMATCH);
827 break;
828
829 case STAMTYPE_PROFILE:
830 AssertMsgReturn( pDesc->enmType == STAMTYPE_PROFILE
831 || pDesc->enmType == STAMTYPE_PROFILE_ADV,
832 ("Unsupported type mixup: %d & %d (%s)\n", pSum->enmType, pDesc->enmType, pDesc->pszName),
833 VERR_MISMATCH);
834 break;
835
836 default:
837 AssertFailedReturn(VERR_MISMATCH);
838 }
839 }
840
841 if (RT_LIKELY(pDesc->enmUnit == pSum->enmUnit))
842 { /* likely */ }
843 else if (pDesc->enmUnit != STAMUNIT_NONE)
844 {
845 AssertReturn(pSum->enmUnit == STAMUNIT_NONE, VERR_MISMATCH);
846 pSum->enmUnit = pDesc->enmUnit;
847 }
848
849 AssertReturn(pSum->cSummands < pSum->cSummandsAlloc, VERR_TOO_MUCH_DATA);
850 }
851 pSum->apSummands[pSum->cSummands++] = pDesc;
852 return VINF_SUCCESS;
853}
854
855
856/**
857 * Registers a sum that is to be calculated from the @a pszSummandPattern hits.
858 *
859 * @returns VBox status code.
860 * @param pUVM Pointer to the user mode VM structure.
861 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
862 * @param pszSummandPattern A simple pattern for the elements that should be
863 * summed up. These must have matching types and
864 * units.
865 * @param pszDesc Sample description.
866 * @param pszName The sample name format string.
867 * @param va Arguments to the format string.
868 */
869VMMR3DECL(int) STAMR3RegisterSumV(PUVM pUVM, STAMVISIBILITY enmVisibility, const char *pszSummandPattern,
870 const char *pszDesc, const char *pszName, va_list va)
871{
872 char szFormattedName[STAM_MAX_NAME_LEN + 8];
873 size_t cch = RTStrPrintfV(szFormattedName, sizeof(szFormattedName), pszName, va);
874 AssertReturn(cch <= STAM_MAX_NAME_LEN, VERR_OUT_OF_RANGE);
875
876 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
877
878 /*
879 * We have to resolve the summands before we continue with the actual registration.
880 */
881 uint8_t const cMaxSummands = 32;
882 PSTAMSUMSAMPLE const pSum = (PSTAMSUMSAMPLE)RTMemAllocZ(RT_UOFFSETOF_DYN(STAMSUMSAMPLE, apSummands[cMaxSummands]));
883 AssertReturn(pSum, VERR_NO_MEMORY);
884 pSum->cSummandsAlloc = cMaxSummands;
885
886 STAM_LOCK_WR(pUVM);
887
888 int rc = stamR3EnumU(pUVM, pszSummandPattern, false /*fUpdateRing0*/, stamR3RegisterSumEnumCallback, pSum);
889 if (RT_SUCCESS(rc))
890 {
891 if (pSum->cSummands > 0)
892 rc = stamR3RegisterU(pUVM, pSum, NULL, NULL, STAMTYPE_INTERNAL_SUM, enmVisibility, szFormattedName,
893 (STAMUNIT)pSum->enmUnit, pszDesc, STAM_REFRESH_GRP_NONE);
894 else
895 AssertFailedStmt(rc = VERR_NO_DATA);
896 }
897
898 STAM_UNLOCK_WR(pUVM);
899
900 if (RT_FAILURE(rc))
901 RTMemFree(pSum);
902 return rc;
903}
904
905
906/**
907 * Registers a sum that is to be calculated from the @a pszSummandPattern hits.
908 *
909 * @returns VBox status code.
910 * @param pUVM Pointer to the user mode VM structure.
911 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
912 * @param pszSummandPattern A simple pattern for the elements that should be
913 * summed up. These must have matching types and
914 * units.
915 * @param pszDesc Sample description.
916 * @param pszName The sample name format string.
917 * @param ... Arguments to the format string.
918 */
919VMMR3DECL(int) STAMR3RegisterSum(PUVM pUVM, STAMVISIBILITY enmVisibility, const char *pszSummandPattern,
920 const char *pszDesc, const char *pszName, ...)
921{
922 va_list va;
923 va_start(va, pszName);
924 int rc = STAMR3RegisterSumV(pUVM, enmVisibility, pszSummandPattern, pszDesc, pszName, va);
925 va_end(va);
926 return rc;
927}
928
929
930/**
931 * Refreshes the cached value (STAMSUMSAMPLE::u) of a percent-of-sum sample.
932 */
933static void stamR3PctOfSumRefresh(PSTAMDESC pDesc, PSTAMSUMSAMPLE pSum)
934{
935 /*
936 * First the value so we only read it once.
937 */
938 PSTAMDESC const pValDesc = pSum->apSummands[0];
939 uint64_t uValue;
940 switch (pValDesc->enmType)
941 {
942 case STAMTYPE_COUNTER:
943 uValue = pValDesc->u.pCounter->c;
944 break;
945
946 case STAMTYPE_U64:
947 case STAMTYPE_U64_RESET:
948 case STAMTYPE_X64:
949 case STAMTYPE_X64_RESET:
950 uValue = *pValDesc->u.pu64;
951 break;
952
953 case STAMTYPE_U32:
954 case STAMTYPE_U32_RESET:
955 case STAMTYPE_X32:
956 case STAMTYPE_X32_RESET:
957 uValue = *pValDesc->u.pu32;
958 break;
959
960 case STAMTYPE_U16:
961 case STAMTYPE_U16_RESET:
962 case STAMTYPE_X16:
963 case STAMTYPE_X16_RESET:
964 uValue = *pValDesc->u.pu16;
965 break;
966
967 case STAMTYPE_U8:
968 case STAMTYPE_U8_RESET:
969 case STAMTYPE_X8:
970 case STAMTYPE_X8_RESET:
971 uValue = *pValDesc->u.pu8;
972 break;
973
974 case STAMTYPE_PROFILE:
975 case STAMTYPE_PROFILE_ADV:
976 uValue = pValDesc->u.pProfile->cTicks;
977 break;
978
979 case STAMTYPE_INTERNAL_SUM:
980 {
981 PSTAMSUMSAMPLE const pSubSum = pValDesc->u.pSum;
982 stamR3SumRefresh(pSubSum);
983 if (pSubSum->enmType == STAMTYPE_COUNTER)
984 uValue = pSubSum->u.Counter.c;
985 else
986 uValue = pSubSum->u.Profile.cTicks;
987 break;
988 }
989
990 default:
991 AssertFailedReturnVoid();
992 }
993
994 /*
995 * Sum it up with the rest.
996 */
997 uint64_t uSum = pSum->fAddValueToSum ? uValue : 0;
998 uintptr_t i = pSum->cSummands;
999 while (i-- > 1)
1000 {
1001 PSTAMDESC const pSummandDesc = pSum->apSummands[i];
1002 switch (pSummandDesc->enmType)
1003 {
1004 case STAMTYPE_COUNTER:
1005 uSum += pSummandDesc->u.pCounter->c;
1006 break;
1007
1008 case STAMTYPE_U64:
1009 case STAMTYPE_U64_RESET:
1010 case STAMTYPE_X64:
1011 case STAMTYPE_X64_RESET:
1012 uSum += *pSummandDesc->u.pu64;
1013 break;
1014
1015 case STAMTYPE_U32:
1016 case STAMTYPE_U32_RESET:
1017 case STAMTYPE_X32:
1018 case STAMTYPE_X32_RESET:
1019 uSum += *pSummandDesc->u.pu32;
1020 break;
1021
1022 case STAMTYPE_U16:
1023 case STAMTYPE_U16_RESET:
1024 case STAMTYPE_X16:
1025 case STAMTYPE_X16_RESET:
1026 uSum += *pSummandDesc->u.pu16;
1027 break;
1028
1029 case STAMTYPE_U8:
1030 case STAMTYPE_U8_RESET:
1031 case STAMTYPE_X8:
1032 case STAMTYPE_X8_RESET:
1033 uSum += *pSummandDesc->u.pu8;
1034 break;
1035
1036 case STAMTYPE_PROFILE:
1037 case STAMTYPE_PROFILE_ADV:
1038 uSum += pSummandDesc->u.pProfile->cTicks;
1039 break;
1040
1041 case STAMTYPE_INTERNAL_SUM:
1042 {
1043 PSTAMSUMSAMPLE const pSubSum = pSummandDesc->u.pSum;
1044 stamR3SumRefresh(pSubSum);
1045 if (pSubSum->enmType == STAMTYPE_COUNTER)
1046 uSum += pSubSum->u.Counter.c;
1047 else
1048 uSum += pSubSum->u.Profile.cTicks;
1049 break;
1050 }
1051
1052 default:
1053 AssertFailedBreak();
1054 }
1055 }
1056
1057 /*
1058 * Calculate the percentage.
1059 */
1060 if (uSum && uValue)
1061 {
1062 switch (pDesc->enmUnit)
1063 {
1064 case STAMUNIT_PCT:
1065 pSum->u.Counter.c = uValue * 100 / uSum;
1066 break;
1067 case STAMUNIT_PP1K:
1068 pSum->u.Counter.c = uValue * 1000 / uSum;
1069 break;
1070 case STAMUNIT_PP10K:
1071 pSum->u.Counter.c = uValue * 10000 / uSum;
1072 break;
1073 default:
1074 AssertFailed();
1075 RT_FALL_THROUGH();
1076 case STAMUNIT_PPM:
1077 pSum->u.Counter.c = uValue * 1000000 / uSum;
1078 break;
1079 case STAMUNIT_PPB:
1080 pSum->u.Counter.c = uValue * 1000000000 / uSum;
1081 break;
1082 }
1083 }
1084 else
1085 pSum->u.Counter.c = 0;
1086}
1087
1088
1089/**
1090 * Used by STAMR3RegisterPctOfSumV to locate the value to turn into a
1091 * percentage.
1092 */
1093static int stamR3RegisterPctOfSumEnumCallbackForValue(PSTAMDESC pDesc, void *pvArg)
1094{
1095 PSTAMSUMSAMPLE const pSum = (PSTAMSUMSAMPLE)pvArg;
1096 AssertReturn(pSum->cSummands == 0, VERR_TOO_MUCH_DATA);
1097
1098 /*
1099 * Check for compatibility.
1100 */
1101 switch (pDesc->enmType)
1102 {
1103 case STAMTYPE_COUNTER:
1104 case STAMTYPE_U64:
1105 case STAMTYPE_U64_RESET:
1106 case STAMTYPE_X64:
1107 case STAMTYPE_X64_RESET:
1108 case STAMTYPE_U32:
1109 case STAMTYPE_U32_RESET:
1110 case STAMTYPE_X32:
1111 case STAMTYPE_X32_RESET:
1112 case STAMTYPE_U16:
1113 case STAMTYPE_U16_RESET:
1114 case STAMTYPE_X16:
1115 case STAMTYPE_X16_RESET:
1116 case STAMTYPE_U8:
1117 case STAMTYPE_U8_RESET:
1118 case STAMTYPE_X8:
1119 case STAMTYPE_X8_RESET:
1120 case STAMTYPE_PROFILE:
1121 case STAMTYPE_PROFILE_ADV:
1122 case STAMTYPE_INTERNAL_SUM:
1123 break;
1124
1125 default:
1126 AssertMsgFailedReturn(("Pct-of-sum for enmType=%d types have not been implemented yet! Sorry.\n", pDesc->enmType),
1127 VERR_WRONG_TYPE);
1128 }
1129 pSum->enmTypeFirst = pDesc->enmType;
1130 pSum->apSummands[0] = pDesc;
1131 pSum->cSummands = 1;
1132 return VINF_SUCCESS;
1133}
1134
1135
1136/**
1137 * Used by STAMR3RegisterPctOfSumV to locate the samples to sum up.
1138 */
1139static int stamR3RegisterPctOfSumEnumCallbackForSummands(PSTAMDESC pDesc, void *pvArg)
1140{
1141 PSTAMSUMSAMPLE const pSum = (PSTAMSUMSAMPLE)pvArg;
1142
1143 /*
1144 * Skip if the same as the value we're calculating the percentage for.
1145 */
1146 if (pDesc == pSum->apSummands[0])
1147 return VINF_SUCCESS;
1148
1149 /*
1150 * Make sure additional samples are compatible with the first as far as type.
1151 */
1152 if (RT_LIKELY( pDesc->enmType == pSum->enmType
1153 || pDesc->enmType == (STAMTYPE)pSum->enmTypeFirst))
1154 { /* likely */ }
1155 else
1156 {
1157 switch (pDesc->enmType)
1158 {
1159 case STAMTYPE_COUNTER:
1160 case STAMTYPE_U64:
1161 case STAMTYPE_U64_RESET:
1162 case STAMTYPE_X64:
1163 case STAMTYPE_X64_RESET:
1164 case STAMTYPE_U32:
1165 case STAMTYPE_U32_RESET:
1166 case STAMTYPE_X32:
1167 case STAMTYPE_X32_RESET:
1168 case STAMTYPE_U16:
1169 case STAMTYPE_U16_RESET:
1170 case STAMTYPE_X16:
1171 case STAMTYPE_X16_RESET:
1172 case STAMTYPE_U8:
1173 case STAMTYPE_U8_RESET:
1174 case STAMTYPE_X8:
1175 case STAMTYPE_X8_RESET:
1176 case STAMTYPE_PROFILE:
1177 case STAMTYPE_PROFILE_ADV:
1178 case STAMTYPE_INTERNAL_SUM:
1179 break;
1180
1181 default:
1182 AssertMsgFailedReturn(("Unsupported pct-of-sum type: %d (%s)\n", pDesc->enmType, pDesc->pszName), VERR_MISMATCH);
1183 }
1184 }
1185
1186 AssertReturn(pSum->cSummands < pSum->cSummandsAlloc, VERR_TOO_MUCH_DATA);
1187 pSum->apSummands[pSum->cSummands++] = pDesc;
1188 return VINF_SUCCESS;
1189}
1190
1191
1192/**
1193 * Registers a percentage of a sum that is to be calculated from @a pszValue and
1194 * the @a pszSummandPattern hits.
1195 *
1196 * @returns VBox status code.
1197 * @param pUVM Pointer to the user mode VM structure.
1198 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
1199 * @param enmUnit The sample unit: STAMUNIT_PCT, STAMUNIT_PP1K,
1200 * STAMUNIT_PP10K, STAMUNIT_PPM or STAMUNIT_PPB.
1201 * @param pszValue Name of the sample which value should be put
1202 * against the sum of all.
1203 * @param pszSummandPattern A simple pattern for the elements that should be
1204 * summed up and used to divide @a pszName by when
1205 * calculating the percentage. These must have
1206 * compatible types.
1207 * @param fAddValueToSum Whether to add @a pszValue to the values that
1208 * @a pszSummandPattern specifies (@c true) or not
1209 * (@c false).
1210 * @param pszDesc Sample description.
1211 * @param pszName The sample name format string.
1212 * @param va Arguments to the format string.
1213 */
1214VMMR3DECL(int) STAMR3RegisterPctOfSumV(PUVM pUVM, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit, const char *pszValue,
1215 bool fAddValueToSum, const char *pszSummandPattern, const char *pszDesc,
1216 const char *pszName, va_list va)
1217{
1218 char szFormattedName[STAM_MAX_NAME_LEN + 8];
1219 size_t cch = RTStrPrintfV(szFormattedName, sizeof(szFormattedName), pszName, va);
1220 AssertReturn(cch <= STAM_MAX_NAME_LEN, VERR_OUT_OF_RANGE);
1221 switch (enmUnit)
1222 {
1223 case STAMUNIT_PCT:
1224 case STAMUNIT_PP1K:
1225 case STAMUNIT_PP10K:
1226 case STAMUNIT_PPM:
1227 case STAMUNIT_PPB:
1228 break;
1229 default:
1230 AssertFailedReturn(VERR_INVALID_PARAMETER);
1231 }
1232
1233 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
1234
1235 /*
1236 * We have to resolve the value and summands before we continue with the
1237 * actual registration. We reuse the STAMSUMSAMPLE structure here.
1238 */
1239 uint8_t const cMaxSummands = 32;
1240 PSTAMSUMSAMPLE const pSum = (PSTAMSUMSAMPLE)RTMemAllocZ(RT_UOFFSETOF_DYN(STAMSUMSAMPLE, apSummands[cMaxSummands]));
1241 AssertReturn(pSum, VERR_NO_MEMORY);
1242 pSum->cSummandsAlloc = cMaxSummands;
1243 pSum->enmType = STAMTYPE_COUNTER;
1244 pSum->enmUnit = enmUnit;
1245 pSum->fAddValueToSum = fAddValueToSum;
1246
1247 STAM_LOCK_WR(pUVM);
1248
1249 /* The first summand entry is the value. */
1250 int rc = stamR3EnumU(pUVM, pszValue, false /*fUpdateRing0*/, stamR3RegisterPctOfSumEnumCallbackForValue, pSum);
1251 if (RT_SUCCESS(rc))
1252 {
1253 if (pSum->cSummands == 1)
1254 {
1255 /* The additional ones are part of the sum we should divide the value by. */
1256 rc = stamR3EnumU(pUVM, pszSummandPattern, false /*fUpdateRing0*/, stamR3RegisterPctOfSumEnumCallbackForSummands, pSum);
1257 if (RT_SUCCESS(rc))
1258 {
1259 /* Now, register it. */
1260 if (pSum->cSummands > 1)
1261 rc = stamR3RegisterU(pUVM, pSum, NULL, NULL, STAMTYPE_INTERNAL_PCT_OF_SUM, enmVisibility, szFormattedName,
1262 (STAMUNIT)pSum->enmUnit, pszDesc, STAM_REFRESH_GRP_NONE);
1263 else
1264 AssertFailedStmt(rc = VERR_NO_DATA);
1265 }
1266 }
1267 else
1268 AssertFailedStmt(rc = VERR_NO_DATA);
1269 }
1270
1271 STAM_UNLOCK_WR(pUVM);
1272
1273 if (RT_FAILURE(rc))
1274 RTMemFree(pSum);
1275 return rc;
1276}
1277
1278
1279/**
1280 * Registers a percentage of a sum that is to be calculated from @a pszValue and
1281 * the @a pszSummandPattern hits.
1282 *
1283 * @returns VBox status code.
1284 * @param pUVM Pointer to the user mode VM structure.
1285 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
1286 * @param enmUnit The sample unit: STAMUNIT_PCT, STAMUNIT_PP1K,
1287 * STAMUNIT_PP10K, STAMUNIT_PPM or STAMUNIT_PPB.
1288 * @param pszValue Name of the sample which value should be put
1289 * against the sum of all.
1290 * @param pszSummandPattern A simple pattern for the elements that should be
1291 * summed up and used to divide @a pszName by when
1292 * calculating the percentage. These must have
1293 * compatible types.
1294 * @param fAddValueToSum Whether to add @a pszValue to the values that
1295 * @a pszSummandPattern specifies (@c true) or not
1296 * (@c false).
1297 * @param pszDesc Sample description.
1298 * @param pszName The sample name format string.
1299 * @param ... Arguments to the format string.
1300 */
1301VMMR3DECL(int) STAMR3RegisterPctOfSum(PUVM pUVM, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit, const char *pszValue,
1302 bool fAddValueToSum, const char *pszSummandPattern, const char *pszDesc,
1303 const char *pszName, ...)
1304{
1305 va_list va;
1306 va_start(va, pszName);
1307 int rc = STAMR3RegisterPctOfSumV(pUVM, enmVisibility, enmUnit, pszValue, fAddValueToSum, pszSummandPattern,
1308 pszDesc, pszName, va);
1309 va_end(va);
1310 return rc;
1311}
1312
1313
1314#ifdef VBOX_STRICT
1315/**
1316 * Divide the strings into sub-strings using '/' as delimiter
1317 * and then compare them in strcmp fashion.
1318 *
1319 * @returns Difference.
1320 * @retval 0 if equal.
1321 * @retval < 0 if psz1 is less than psz2.
1322 * @retval > 0 if psz1 greater than psz2.
1323 *
1324 * @param psz1 The first string.
1325 * @param psz2 The second string.
1326 */
1327static int stamR3SlashCompare(const char *psz1, const char *psz2)
1328{
1329 for (;;)
1330 {
1331 unsigned int ch1 = *psz1++;
1332 unsigned int ch2 = *psz2++;
1333 if (ch1 != ch2)
1334 {
1335 /* slash is end-of-sub-string, so it trumps everything but '\0'. */
1336 if (ch1 == '/')
1337 return ch2 ? -1 : 1;
1338 if (ch2 == '/')
1339 return ch1 ? 1 : -1;
1340 return ch1 - ch2;
1341 }
1342
1343 /* done? */
1344 if (ch1 == '\0')
1345 return 0;
1346 }
1347}
1348#endif /* VBOX_STRICT */
1349
1350
1351/**
1352 * Compares a lookup node with a name.
1353 *
1354 * @returns like strcmp and memcmp.
1355 * @param pNode The lookup node.
1356 * @param pchName The name, not necessarily terminated.
1357 * @param cchName The length of the name.
1358 */
1359DECL_FORCE_INLINE(int) stamR3LookupCmp(PSTAMLOOKUP pNode, const char *pchName, uint32_t cchName)
1360{
1361 uint32_t cchComp = RT_MIN(pNode->cch, cchName);
1362 int iDiff = memcmp(pNode->szName, pchName, cchComp);
1363 if (!iDiff && pNode->cch != cchName)
1364 iDiff = pNode->cch > cchName ? 2 : -2;
1365 return iDiff;
1366}
1367
1368
1369/**
1370 * Creates a new lookup child node.
1371 *
1372 * @returns Pointer to the newly created lookup node.
1373 * @param pParent The parent node.
1374 * @param pchName The name (not necessarily terminated).
1375 * @param cchName The length of the name.
1376 * @param offName The offset of the node in a path.
1377 * @param iChild Child index of a node that's before the one
1378 * we're inserting (returned by
1379 * stamR3LookupFindChild).
1380 */
1381static PSTAMLOOKUP stamR3LookupNewChild(PSTAMLOOKUP pParent, const char *pchName, uint32_t cchName, uint32_t offName,
1382 uint32_t iChild)
1383{
1384 Assert(cchName <= UINT8_MAX);
1385 Assert(offName <= UINT8_MAX);
1386 Assert(iChild < UINT16_MAX);
1387
1388 /*
1389 * Allocate a new entry.
1390 */
1391 PSTAMLOOKUP pNew = (PSTAMLOOKUP)RTMemAlloc(RT_UOFFSETOF_DYN(STAMLOOKUP, szName[cchName + 1]));
1392 if (!pNew)
1393 return NULL;
1394 pNew->pParent = pParent;
1395 pNew->papChildren = NULL;
1396 pNew->pDesc = NULL;
1397 pNew->cDescsInTree = 0;
1398 pNew->cChildren = 0;
1399 pNew->cch = (uint16_t)cchName;
1400 pNew->off = (uint16_t)offName;
1401 memcpy(pNew->szName, pchName, cchName);
1402 pNew->szName[cchName] = '\0';
1403
1404 /*
1405 * Reallocate the array?
1406 */
1407 if (RT_IS_POWER_OF_TWO(pParent->cChildren))
1408 {
1409 uint32_t cNew = pParent->cChildren ? (uint32_t)pParent->cChildren * 2 : 8;
1410 AssertReturnStmt(cNew <= 0x8000, RTMemFree(pNew), NULL);
1411 void *pvNew = RTMemRealloc(pParent->papChildren, cNew * sizeof(pParent->papChildren[0]));
1412 if (!pvNew)
1413 {
1414 RTMemFree(pNew);
1415 return NULL;
1416 }
1417 pParent->papChildren = (PSTAMLOOKUP *)pvNew;
1418 }
1419
1420 /*
1421 * Find the exact insertion point using iChild as a very good clue from
1422 * the find function.
1423 */
1424 if (!pParent->cChildren)
1425 iChild = 0;
1426 else
1427 {
1428 if (iChild >= pParent->cChildren)
1429 iChild = pParent->cChildren - 1;
1430 while ( iChild < pParent->cChildren
1431 && stamR3LookupCmp(pParent->papChildren[iChild], pchName, cchName) < 0)
1432 iChild++;
1433 }
1434
1435 /*
1436 * Insert it.
1437 */
1438 if (iChild < pParent->cChildren)
1439 {
1440 /* Do shift. */
1441 uint32_t i = pParent->cChildren;
1442 while (i > iChild)
1443 {
1444 PSTAMLOOKUP pNode = pParent->papChildren[i - 1];
1445 pParent->papChildren[i] = pNode;
1446 pNode->iParent = i;
1447 i--;
1448 }
1449 }
1450
1451 pNew->iParent = iChild;
1452 pParent->papChildren[iChild] = pNew;
1453 pParent->cChildren++;
1454
1455 return pNew;
1456}
1457
1458
1459/**
1460 * Looks up a child.
1461 *
1462 * @returns Pointer to child node if found, NULL if not.
1463 * @param pParent The parent node.
1464 * @param pchName The name (not necessarily terminated).
1465 * @param cchName The length of the name.
1466 * @param piChild Where to store a child index suitable for
1467 * passing to stamR3LookupNewChild when NULL is
1468 * returned.
1469 */
1470static PSTAMLOOKUP stamR3LookupFindChild(PSTAMLOOKUP pParent, const char *pchName, uint32_t cchName, uint32_t *piChild)
1471{
1472 uint32_t iChild = pParent->cChildren;
1473 if (iChild > 4)
1474 {
1475 uint32_t iFirst = 0;
1476 uint32_t iEnd = iChild;
1477 iChild /= 2;
1478 for (;;)
1479 {
1480 int iDiff = stamR3LookupCmp(pParent->papChildren[iChild], pchName, cchName);
1481 if (!iDiff)
1482 {
1483 if (piChild)
1484 *piChild = iChild;
1485 return pParent->papChildren[iChild];
1486 }
1487
1488 /* Split. */
1489 if (iDiff < 0)
1490 {
1491 iFirst = iChild + 1;
1492 if (iFirst >= iEnd)
1493 {
1494 if (piChild)
1495 *piChild = iChild;
1496 break;
1497 }
1498 }
1499 else
1500 {
1501 if (iChild == iFirst)
1502 {
1503 if (piChild)
1504 *piChild = iChild ? iChild - 1 : 0;
1505 break;
1506 }
1507 iEnd = iChild;
1508 }
1509
1510 /* Calc next child. */
1511 iChild = (iEnd - iFirst) / 2 + iFirst;
1512 }
1513 return NULL;
1514 }
1515
1516 /*
1517 * Linear search.
1518 */
1519 while (iChild-- > 0)
1520 {
1521 int iDiff = stamR3LookupCmp(pParent->papChildren[iChild], pchName, cchName);
1522 if (iDiff <= 0)
1523 {
1524 if (piChild)
1525 *piChild = iChild;
1526 return !iDiff ? pParent->papChildren[iChild] : NULL;
1527 }
1528 }
1529 if (piChild)
1530 *piChild = 0;
1531 return NULL;
1532}
1533
1534
1535/**
1536 * Find the next sample descriptor node.
1537 *
1538 * This is for use with insertion in the big list and pattern range lookups.
1539 *
1540 * @returns Pointer to the next sample descriptor. NULL if not found (i.e.
1541 * we're at the end of the list).
1542 * @param pLookup The current node.
1543 */
1544static PSTAMDESC stamR3LookupFindNextWithDesc(PSTAMLOOKUP pLookup)
1545{
1546 Assert(!pLookup->pDesc);
1547 PSTAMLOOKUP pCur = pLookup;
1548 uint32_t iCur = 0;
1549 for (;;)
1550 {
1551 /*
1552 * Check all children.
1553 */
1554 uint32_t cChildren = pCur->cChildren;
1555 if (iCur < cChildren)
1556 {
1557 PSTAMLOOKUP *papChildren = pCur->papChildren;
1558 do
1559 {
1560 PSTAMLOOKUP pChild = papChildren[iCur];
1561 if (pChild->pDesc)
1562 return pChild->pDesc;
1563
1564 if (pChild->cChildren > 0)
1565 {
1566 /* One level down. */
1567 iCur = 0;
1568 pCur = pChild;
1569 break;
1570 }
1571 } while (++iCur < cChildren);
1572 }
1573 else
1574 {
1575 /*
1576 * One level up, resuming after the current.
1577 */
1578 iCur = pCur->iParent + 1;
1579 pCur = pCur->pParent;
1580 if (!pCur)
1581 return NULL;
1582 }
1583 }
1584}
1585
1586
1587/**
1588 * Look up a sample descriptor by name.
1589 *
1590 * @returns Pointer to a sample descriptor.
1591 * @param pRoot The root node.
1592 * @param pszName The name to lookup.
1593 */
1594static PSTAMDESC stamR3LookupFindDesc(PSTAMLOOKUP pRoot, const char *pszName)
1595{
1596 Assert(!pRoot->pParent);
1597 while (*pszName++ == '/')
1598 {
1599 const char *pszEnd = strchr(pszName, '/');
1600 uint32_t cch = pszEnd ? pszEnd - pszName : (uint32_t)strlen(pszName);
1601 PSTAMLOOKUP pChild = stamR3LookupFindChild(pRoot, pszName, cch, NULL);
1602 if (!pChild)
1603 break;
1604 if (!pszEnd)
1605 return pChild->pDesc;
1606 pszName = pszEnd;
1607 pRoot = pChild;
1608 }
1609
1610 return NULL;
1611}
1612
1613
1614/**
1615 * Finds the first sample descriptor for a given lookup range.
1616 *
1617 * This is for pattern range lookups.
1618 *
1619 * @returns Pointer to the first descriptor.
1620 * @param pFirst The first node in the range.
1621 * @param pLast The last node in the range.
1622 */
1623static PSTAMDESC stamR3LookupFindFirstDescForRange(PSTAMLOOKUP pFirst, PSTAMLOOKUP pLast)
1624{
1625 if (pFirst->pDesc)
1626 return pFirst->pDesc;
1627
1628 PSTAMLOOKUP pCur = pFirst;
1629 uint32_t iCur = 0;
1630 for (;;)
1631 {
1632 uint32_t cChildren = pCur->cChildren;
1633 if (iCur < pCur->cChildren)
1634 {
1635 /*
1636 * Check all children.
1637 */
1638 PSTAMLOOKUP * const papChildren = pCur->papChildren;
1639 do
1640 {
1641 PSTAMLOOKUP pChild = papChildren[iCur];
1642 if (pChild->pDesc)
1643 return pChild->pDesc;
1644 if (pChild->cChildren > 0)
1645 {
1646 /* One level down. */
1647 iCur = 0;
1648 pCur = pChild;
1649 break;
1650 }
1651 if (pChild == pLast)
1652 return NULL;
1653 } while (++iCur < cChildren);
1654 }
1655 else
1656 {
1657 /*
1658 * One level up, checking current and its 'older' sibilings.
1659 */
1660 if (pCur == pLast)
1661 return NULL;
1662 iCur = pCur->iParent + 1;
1663 pCur = pCur->pParent;
1664 if (!pCur)
1665 break;
1666 }
1667 }
1668
1669 return NULL;
1670}
1671
1672
1673/**
1674 * Finds the last sample descriptor for a given lookup range.
1675 *
1676 * This is for pattern range lookups.
1677 *
1678 * @returns Pointer to the first descriptor.
1679 * @param pFirst The first node in the range.
1680 * @param pLast The last node in the range.
1681 */
1682static PSTAMDESC stamR3LookupFindLastDescForRange(PSTAMLOOKUP pFirst, PSTAMLOOKUP pLast)
1683{
1684 PSTAMLOOKUP pCur = pLast;
1685 uint32_t iCur = pCur->cChildren - 1;
1686 for (;;)
1687 {
1688 if (iCur < pCur->cChildren)
1689 {
1690 /*
1691 * Check children backwards, depth first.
1692 */
1693 PSTAMLOOKUP * const papChildren = pCur->papChildren;
1694 do
1695 {
1696 PSTAMLOOKUP pChild = papChildren[iCur];
1697 if (pChild->cChildren > 0)
1698 {
1699 /* One level down. */
1700 iCur = pChild->cChildren - 1;
1701 pCur = pChild;
1702 break;
1703 }
1704
1705 if (pChild->pDesc)
1706 return pChild->pDesc;
1707 if (pChild == pFirst)
1708 return NULL;
1709 } while (iCur-- > 0); /* (underflow handled above) */
1710 }
1711 else
1712 {
1713 /*
1714 * One level up, checking current and its 'older' sibilings.
1715 */
1716 if (pCur->pDesc)
1717 return pCur->pDesc;
1718 if (pCur == pFirst)
1719 return NULL;
1720 iCur = pCur->iParent - 1; /* (underflow handled above) */
1721 pCur = pCur->pParent;
1722 if (!pCur)
1723 break;
1724 }
1725 }
1726
1727 return NULL;
1728}
1729
1730
1731/**
1732 * Look up the first and last descriptors for a (single) pattern expression.
1733 *
1734 * This is used to optimize pattern enumerations and doesn't have to return 100%
1735 * accurate results if that costs too much.
1736 *
1737 * @returns Pointer to the first descriptor in the range.
1738 * @param pRoot The root node.
1739 * @param pList The descriptor list anchor.
1740 * @param pszPat The name patter to lookup.
1741 * @param ppLastDesc Where to store the address of the last
1742 * descriptor (approximate).
1743 */
1744static PSTAMDESC stamR3LookupFindPatternDescRange(PSTAMLOOKUP pRoot, PRTLISTANCHOR pList, const char *pszPat,
1745 PSTAMDESC *ppLastDesc)
1746{
1747 Assert(!pRoot->pParent);
1748
1749 /*
1750 * If there is an early enough wildcard, the whole list needs to be searched.
1751 */
1752 if ( pszPat[0] == '*' || pszPat[0] == '?'
1753 || pszPat[1] == '*' || pszPat[1] == '?')
1754 {
1755 *ppLastDesc = RTListGetLast(pList, STAMDESC, ListEntry);
1756 return RTListGetFirst(pList, STAMDESC, ListEntry);
1757 }
1758
1759 /*
1760 * All statistics starts with a slash.
1761 */
1762 while ( *pszPat++ == '/'
1763 && pRoot->cDescsInTree > 0
1764 && pRoot->cChildren > 0)
1765 {
1766 const char *pszEnd = strchr(pszPat, '/');
1767 uint32_t cch = pszEnd ? pszEnd - pszPat : (uint32_t)strlen(pszPat);
1768 if (!cch)
1769 break;
1770
1771 const char *pszPat1 = (const char *)memchr(pszPat, '*', cch);
1772 const char *pszPat2 = (const char *)memchr(pszPat, '?', cch);
1773 if (pszPat1 || pszPat2)
1774 {
1775 /* We've narrowed it down to a sub-tree now. */
1776 PSTAMLOOKUP pFirst = pRoot->papChildren[0];
1777 PSTAMLOOKUP pLast = pRoot->papChildren[pRoot->cChildren - 1];
1778 /** @todo narrow the range further if both pszPat1/2 != pszPat. */
1779
1780 *ppLastDesc = stamR3LookupFindLastDescForRange(pFirst, pLast);
1781 return stamR3LookupFindFirstDescForRange(pFirst, pLast);
1782 }
1783
1784 PSTAMLOOKUP pChild = stamR3LookupFindChild(pRoot, pszPat, cch, NULL);
1785 if (!pChild)
1786 break;
1787
1788 /* Advance */
1789 if (!pszEnd)
1790 return *ppLastDesc = pChild->pDesc;
1791 pszPat = pszEnd;
1792 pRoot = pChild;
1793 }
1794
1795 /* No match. */
1796 *ppLastDesc = NULL;
1797 return NULL;
1798}
1799
1800
1801/**
1802 * Look up the first descriptors for starts-with name string.
1803 *
1804 * This is used to optimize deletion.
1805 *
1806 * @returns Pointer to the first descriptor in the range.
1807 * @param pRoot The root node.
1808 * @param pchPrefix The name prefix.
1809 * @param cchPrefix The name prefix length (can be shorter than the
1810 * actual string).
1811 * @param ppLastDesc Where to store the address of the last descriptor.
1812 * @sa stamR3LookupFindPatternDescRange
1813 */
1814static PSTAMDESC stamR3LookupFindByPrefixRange(PSTAMLOOKUP pRoot, const char *pchPrefix, uint32_t cchPrefix,
1815 PSTAMDESC *ppLastDesc)
1816
1817{
1818 *ppLastDesc = NULL;
1819 Assert(!pRoot->pParent);
1820 AssertReturn(cchPrefix > 0, NULL);
1821
1822 /*
1823 * We start with a root slash.
1824 */
1825 if (!cchPrefix || *pchPrefix != '/')
1826 return NULL;
1827
1828 /*
1829 * Walk thru the prefix component by component, since that's how
1830 * the lookup tree is organized.
1831 */
1832 while ( cchPrefix
1833 && *pchPrefix == '/'
1834 && pRoot->cDescsInTree > 0
1835 && pRoot->cChildren > 0)
1836 {
1837 cchPrefix -= 1;
1838 pchPrefix += 1;
1839
1840 const char *pszEnd = (const char *)memchr(pchPrefix, '/', cchPrefix);
1841 if (!pszEnd)
1842 {
1843 /*
1844 * We've narrowed it down to a sub-tree now. If we've no more prefix to work
1845 * with now (e.g. '/Devices/'), the prefix matches all the children. Otherwise,
1846 * traverse the children to find the ones matching the prefix.
1847 */
1848 if (!cchPrefix)
1849 {
1850 *ppLastDesc = stamR3LookupFindLastDescForRange(pRoot->papChildren[0], pRoot->papChildren[pRoot->cChildren - 1]);
1851 return stamR3LookupFindFirstDescForRange(pRoot->papChildren[0], pRoot->papChildren[pRoot->cChildren - 1]);
1852 }
1853
1854 size_t iEnd = pRoot->cChildren;
1855 if (iEnd < 16)
1856 {
1857 /* Linear scan of the children: */
1858 for (size_t i = 0; i < pRoot->cChildren; i++)
1859 {
1860 PSTAMLOOKUP pCur = pRoot->papChildren[i];
1861 if (pCur->cch >= cchPrefix)
1862 {
1863 int iDiff = memcmp(pCur->szName, pchPrefix, cchPrefix);
1864 if (iDiff == 0)
1865 {
1866 size_t iLast = i;
1867 while (++iLast < pRoot->cChildren)
1868 {
1869 PSTAMLOOKUP pCur2 = pRoot->papChildren[iLast];
1870 if ( pCur2->cch < cchPrefix
1871 || memcmp(pCur2->szName, pchPrefix, cchPrefix) != 0)
1872 break;
1873 }
1874 iLast--;
1875
1876 *ppLastDesc = stamR3LookupFindLastDescForRange(pCur, pRoot->papChildren[iLast]);
1877 return stamR3LookupFindFirstDescForRange(pCur, pRoot->papChildren[iLast]);
1878 }
1879 if (iDiff > 0)
1880 break;
1881 }
1882 }
1883 }
1884 else
1885 {
1886 /* Binary search to find something matching the prefix, followed
1887 by a reverse scan to locate the first child: */
1888 size_t iFirst = 0;
1889 size_t i = iEnd / 2;
1890 for (;;)
1891 {
1892 PSTAMLOOKUP pCur = pRoot->papChildren[i];
1893 int iDiff;
1894 if (pCur->cch >= cchPrefix)
1895 iDiff = memcmp(pCur->szName, pchPrefix, cchPrefix);
1896 else
1897 {
1898 iDiff = memcmp(pCur->szName, pchPrefix, pCur->cch);
1899 if (!iDiff)
1900 iDiff = -1;
1901 }
1902 if (iDiff > 0)
1903 {
1904 if (iFirst < i)
1905 iEnd = i;
1906 else
1907 return NULL;
1908 }
1909 else if (iDiff < 0)
1910 {
1911 i += 1;
1912 if (i < iEnd)
1913 iFirst = i;
1914 else
1915 return NULL;
1916 }
1917 else
1918 {
1919 /* Match. Reverse scan to find the first. */
1920 iFirst = i;
1921 while ( iFirst > 0
1922 && (pCur = pRoot->papChildren[iFirst - 1])->cch >= cchPrefix
1923 && memcmp(pCur->szName, pchPrefix, cchPrefix) == 0)
1924 iFirst--;
1925
1926 /* Forward scan to find the last.*/
1927 size_t iLast = i;
1928 while (++iLast < pRoot->cChildren)
1929 {
1930 pCur = pRoot->papChildren[iLast];
1931 if ( pCur->cch < cchPrefix
1932 || memcmp(pCur->szName, pchPrefix, cchPrefix) != 0)
1933 break;
1934 }
1935 iLast--;
1936
1937 *ppLastDesc = stamR3LookupFindLastDescForRange(pRoot->papChildren[iFirst], pRoot->papChildren[iLast]);
1938 return stamR3LookupFindFirstDescForRange(pRoot->papChildren[iFirst], pRoot->papChildren[iLast]);
1939 }
1940
1941 i = iFirst + (iEnd - iFirst) / 2;
1942 }
1943 }
1944 break;
1945 }
1946
1947 /* Find child matching the path component: */
1948 uint32_t cchChild = pszEnd - pchPrefix;
1949 PSTAMLOOKUP pChild = stamR3LookupFindChild(pRoot, pchPrefix, cchChild, NULL);
1950 if (!pChild)
1951 break;
1952
1953 /* Advance: */
1954 cchPrefix -= cchChild;
1955 pchPrefix = pszEnd;
1956 pRoot = pChild;
1957 }
1958 return NULL;
1959}
1960
1961
1962/**
1963 * Increments the cDescInTree member of the given node an all ancestors.
1964 *
1965 * @param pLookup The lookup node.
1966 */
1967static void stamR3LookupIncUsage(PSTAMLOOKUP pLookup)
1968{
1969 Assert(pLookup->pDesc);
1970
1971 PSTAMLOOKUP pCur = pLookup;
1972 while (pCur != NULL)
1973 {
1974 pCur->cDescsInTree++;
1975 pCur = pCur->pParent;
1976 }
1977}
1978
1979
1980/**
1981 * Descrements the cDescInTree member of the given node an all ancestors.
1982 *
1983 * @param pLookup The lookup node.
1984 */
1985static void stamR3LookupDecUsage(PSTAMLOOKUP pLookup)
1986{
1987 Assert(!pLookup->pDesc);
1988
1989 PSTAMLOOKUP pCur = pLookup;
1990 while (pCur != NULL)
1991 {
1992 Assert(pCur->cDescsInTree > 0);
1993 pCur->cDescsInTree--;
1994 pCur = pCur->pParent;
1995 }
1996}
1997
1998
1999/**
2000 * Frees empty lookup nodes if it's worth it.
2001 *
2002 * @param pLookup The lookup node.
2003 */
2004static void stamR3LookupMaybeFree(PSTAMLOOKUP pLookup)
2005{
2006 Assert(!pLookup->pDesc);
2007
2008 /*
2009 * Free between two and three levels of nodes. Freeing too much most
2010 * likely wasted effort since we're either going to repopluate the tree
2011 * or quit the whole thing.
2012 */
2013 if (pLookup->cDescsInTree > 0)
2014 return;
2015
2016 PSTAMLOOKUP pCur = pLookup->pParent;
2017 if (!pCur)
2018 return;
2019 if (pCur->cDescsInTree > 0)
2020 return;
2021 PSTAMLOOKUP pParent = pCur->pParent;
2022 if (!pParent)
2023 return;
2024
2025 if (pParent->cDescsInTree == 0 && pParent->pParent)
2026 {
2027 pCur = pParent;
2028 pParent = pCur->pParent;
2029 }
2030
2031 /*
2032 * Remove pCur from pParent.
2033 */
2034 PSTAMLOOKUP *papChildren = pParent->papChildren;
2035 uint32_t cChildren = --pParent->cChildren;
2036 for (uint32_t i = pCur->iParent; i < cChildren; i++)
2037 {
2038 PSTAMLOOKUP pChild = papChildren[i + 1];
2039 pChild->iParent = i;
2040 papChildren[i] = pChild;
2041 }
2042 pCur->pParent = NULL;
2043 pCur->iParent = UINT16_MAX;
2044
2045 /*
2046 * Destroy pCur.
2047 */
2048 stamR3LookupDestroyTree(pCur);
2049}
2050
2051
2052/**
2053 * Destroys a lookup tree.
2054 *
2055 * This is used by STAMR3Term as well as stamR3LookupMaybeFree.
2056 *
2057 * @param pRoot The root of the tree (must have no parent).
2058 */
2059static void stamR3LookupDestroyTree(PSTAMLOOKUP pRoot)
2060{
2061 Assert(pRoot); Assert(!pRoot->pParent);
2062 PSTAMLOOKUP pCur = pRoot;
2063 for (;;)
2064 {
2065 uint32_t i = pCur->cChildren;
2066 if (i > 0)
2067 {
2068 /*
2069 * Push child (with leaf optimization).
2070 */
2071 PSTAMLOOKUP pChild = pCur->papChildren[--i];
2072 if (pChild->cChildren != 0)
2073 pCur = pChild;
2074 else
2075 {
2076 /* free leaves. */
2077 for (;;)
2078 {
2079 if (pChild->papChildren)
2080 {
2081 RTMemFree(pChild->papChildren);
2082 pChild->papChildren = NULL;
2083 }
2084 RTMemFree(pChild);
2085 pCur->papChildren[i] = NULL;
2086
2087 /* next */
2088 if (i == 0)
2089 {
2090 pCur->cChildren = 0;
2091 break;
2092 }
2093 pChild = pCur->papChildren[--i];
2094 if (pChild->cChildren != 0)
2095 {
2096 pCur->cChildren = i + 1;
2097 pCur = pChild;
2098 break;
2099 }
2100 }
2101 }
2102 }
2103 else
2104 {
2105 /*
2106 * Pop and free current.
2107 */
2108 Assert(!pCur->pDesc);
2109
2110 PSTAMLOOKUP pParent = pCur->pParent;
2111 Assert(pCur->iParent == (pParent ? pParent->cChildren - 1 : UINT16_MAX));
2112
2113 RTMemFree(pCur->papChildren);
2114 pCur->papChildren = NULL;
2115 RTMemFree(pCur);
2116
2117 pCur = pParent;
2118 if (!pCur)
2119 break;
2120 pCur->papChildren[--pCur->cChildren] = NULL;
2121 }
2122 }
2123}
2124
2125
2126/**
2127 * Internal worker for the different register calls.
2128 *
2129 * @returns VBox status code.
2130 * @param pUVM Pointer to the user mode VM structure.
2131 * @param pvSample Pointer to the sample.
2132 * @param pfnReset Callback for resetting the sample. NULL should be used if the sample can't be reset.
2133 * @param pfnPrint Print the sample.
2134 * @param enmType Sample type. This indicates what pvSample is pointing at.
2135 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
2136 * @param pszName The sample name format string.
2137 * @param enmUnit Sample unit.
2138 * @param pszDesc Sample description.
2139 * @param iRefreshGrp The refresh group, STAM_REFRESH_GRP_XXX.
2140 * @remark There is currently no device or driver variant of this API. Add one if it should become necessary!
2141 */
2142static int stamR3RegisterU(PUVM pUVM, void *pvSample, PFNSTAMR3CALLBACKRESET pfnReset, PFNSTAMR3CALLBACKPRINT pfnPrint,
2143 STAMTYPE enmType, STAMVISIBILITY enmVisibility,
2144 const char *pszName, STAMUNIT enmUnit, const char *pszDesc, uint8_t iRefreshGrp)
2145{
2146 AssertReturn(pszName[0] == '/', VERR_INVALID_NAME);
2147 AssertReturn(pszName[1] != '/' && pszName[1], VERR_INVALID_NAME);
2148 uint32_t const cchName = (uint32_t)strlen(pszName);
2149 AssertReturn(cchName <= STAM_MAX_NAME_LEN, VERR_OUT_OF_RANGE);
2150 AssertReturn(pszName[cchName - 1] != '/', VERR_INVALID_NAME);
2151 AssertReturn(memchr(pszName, '\\', cchName) == NULL, VERR_INVALID_NAME);
2152 AssertReturn(iRefreshGrp == STAM_REFRESH_GRP_NONE || iRefreshGrp < 64, VERR_INVALID_PARAMETER);
2153
2154 STAM_LOCK_WR(pUVM);
2155
2156 /*
2157 * Look up the tree location, populating the lookup tree as we walk it.
2158 */
2159 PSTAMLOOKUP pLookup = pUVM->stam.s.pRoot; Assert(pLookup);
2160 uint32_t offName = 1;
2161 for (;;)
2162 {
2163 /* Get the next part of the path. */
2164 const char *pszStart = &pszName[offName];
2165 const char *pszEnd = strchr(pszStart, '/');
2166 uint32_t cch = pszEnd ? (uint32_t)(pszEnd - pszStart) : cchName - offName;
2167 if (cch == 0)
2168 {
2169 STAM_UNLOCK_WR(pUVM);
2170 AssertMsgFailed(("No double or trailing slashes are allowed: '%s'\n", pszName));
2171 return VERR_INVALID_NAME;
2172 }
2173
2174 /* Do the looking up. */
2175 uint32_t iChild = 0;
2176 PSTAMLOOKUP pChild = stamR3LookupFindChild(pLookup, pszStart, cch, &iChild);
2177 if (!pChild)
2178 {
2179 pChild = stamR3LookupNewChild(pLookup, pszStart, cch, offName, iChild);
2180 if (!pChild)
2181 {
2182 STAM_UNLOCK_WR(pUVM);
2183 return VERR_NO_MEMORY;
2184 }
2185 }
2186
2187 /* Advance. */
2188 pLookup = pChild;
2189 if (!pszEnd)
2190 break;
2191 offName += cch + 1;
2192 }
2193 if (pLookup->pDesc)
2194 {
2195 STAM_UNLOCK_WR(pUVM);
2196 AssertMsgFailed(("Duplicate sample name: %s\n", pszName));
2197 return VERR_ALREADY_EXISTS;
2198 }
2199
2200 PSTAMDESC pCur = stamR3LookupFindNextWithDesc(pLookup);
2201
2202 /*
2203 * Check that the name doesn't screw up sorting order when taking
2204 * slashes into account. The QT GUI makes some assumptions.
2205 * Problematic chars are: !"#$%&'()*+,-.
2206 */
2207#ifdef VBOX_STRICT
2208 Assert(pszName[0] == '/');
2209 PSTAMDESC pPrev = pCur
2210 ? RTListGetPrev(&pUVM->stam.s.List, pCur, STAMDESC, ListEntry)
2211 : RTListGetLast(&pUVM->stam.s.List, STAMDESC, ListEntry);
2212 Assert(!pPrev || strcmp(pszName, pPrev->pszName) > 0);
2213 Assert(!pCur || strcmp(pszName, pCur->pszName) < 0);
2214 Assert(!pPrev || stamR3SlashCompare(pPrev->pszName, pszName) < 0);
2215 Assert(!pCur || stamR3SlashCompare(pCur->pszName, pszName) > 0);
2216
2217 /*
2218 * Check alignment requirements.
2219 */
2220 switch (enmType)
2221 {
2222 /* 8 byte / 64-bit */
2223 case STAMTYPE_U64:
2224 case STAMTYPE_U64_RESET:
2225 case STAMTYPE_X64:
2226 case STAMTYPE_X64_RESET:
2227 case STAMTYPE_COUNTER:
2228 case STAMTYPE_PROFILE:
2229 case STAMTYPE_PROFILE_ADV:
2230 AssertMsg(!((uintptr_t)pvSample & 7), ("%p - %s\n", pvSample, pszName));
2231 break;
2232
2233 /* 4 byte / 32-bit */
2234 case STAMTYPE_RATIO_U32:
2235 case STAMTYPE_RATIO_U32_RESET:
2236 case STAMTYPE_U32:
2237 case STAMTYPE_U32_RESET:
2238 case STAMTYPE_X32:
2239 case STAMTYPE_X32_RESET:
2240 AssertMsg(!((uintptr_t)pvSample & 3), ("%p - %s\n", pvSample, pszName));
2241 break;
2242
2243 /* 2 byte / 32-bit */
2244 case STAMTYPE_U16:
2245 case STAMTYPE_U16_RESET:
2246 case STAMTYPE_X16:
2247 case STAMTYPE_X16_RESET:
2248 AssertMsg(!((uintptr_t)pvSample & 1), ("%p - %s\n", pvSample, pszName));
2249 break;
2250
2251 /* 1 byte / 8-bit / unaligned */
2252 case STAMTYPE_U8:
2253 case STAMTYPE_U8_RESET:
2254 case STAMTYPE_X8:
2255 case STAMTYPE_X8_RESET:
2256 case STAMTYPE_BOOL:
2257 case STAMTYPE_BOOL_RESET:
2258 case STAMTYPE_CALLBACK:
2259 case STAMTYPE_INTERNAL_SUM:
2260 case STAMTYPE_INTERNAL_PCT_OF_SUM:
2261 break;
2262
2263 default:
2264 AssertMsgFailed(("%d\n", enmType));
2265 break;
2266 }
2267#endif /* VBOX_STRICT */
2268
2269 /*
2270 * Create a new node and insert it at the current location.
2271 */
2272 int rc;
2273 size_t cbDesc = pszDesc ? strlen(pszDesc) + 1 : 0;
2274 PSTAMDESC pNew = (PSTAMDESC)RTMemAlloc(sizeof(*pNew) + cchName + 1 + cbDesc);
2275 if (pNew)
2276 {
2277 pNew->pszName = (char *)memcpy((char *)(pNew + 1), pszName, cchName + 1);
2278 pNew->enmType = enmType;
2279 pNew->enmVisibility = enmVisibility;
2280 if (enmType != STAMTYPE_CALLBACK)
2281 pNew->u.pv = pvSample;
2282 else
2283 {
2284 pNew->u.Callback.pvSample = pvSample;
2285 pNew->u.Callback.pfnReset = pfnReset;
2286 pNew->u.Callback.pfnPrint = pfnPrint;
2287 }
2288 pNew->enmUnit = enmUnit;
2289 pNew->iRefreshGroup = iRefreshGrp;
2290 pNew->pszDesc = NULL;
2291 if (pszDesc)
2292 pNew->pszDesc = (char *)memcpy((char *)(pNew + 1) + cchName + 1, pszDesc, cbDesc);
2293
2294 if (pCur)
2295 RTListNodeInsertBefore(&pCur->ListEntry, &pNew->ListEntry);
2296 else
2297 RTListAppend(&pUVM->stam.s.List, &pNew->ListEntry);
2298
2299 pNew->pLookup = pLookup;
2300 pLookup->pDesc = pNew;
2301 stamR3LookupIncUsage(pLookup);
2302
2303 stamR3ResetOne(pNew, pUVM->pVM);
2304 rc = VINF_SUCCESS;
2305 }
2306 else
2307 rc = VERR_NO_MEMORY;
2308
2309 STAM_UNLOCK_WR(pUVM);
2310 return rc;
2311}
2312
2313
2314/**
2315 * Destroys the statistics descriptor, unlinking it and freeing all resources.
2316 *
2317 * @returns VINF_SUCCESS
2318 * @param pCur The descriptor to destroy.
2319 */
2320static int stamR3DestroyDesc(PSTAMDESC pCur)
2321{
2322 RTListNodeRemove(&pCur->ListEntry);
2323 pCur->pLookup->pDesc = NULL; /** @todo free lookup nodes once it's working. */
2324 stamR3LookupDecUsage(pCur->pLookup);
2325 stamR3LookupMaybeFree(pCur->pLookup);
2326 if ( pCur->enmType != STAMTYPE_INTERNAL_SUM
2327 && pCur->enmType != STAMTYPE_INTERNAL_PCT_OF_SUM)
2328 { /* likely */ }
2329 else
2330 RTMemFree(pCur->u.pSum);
2331 RTMemFree(pCur);
2332
2333 return VINF_SUCCESS;
2334}
2335
2336
2337/**
2338 * Deregisters a sample previously registered by STAR3Register() given its
2339 * address.
2340 *
2341 * This is intended used for devices which can be unplugged and for
2342 * temporary samples.
2343 *
2344 * @returns VBox status code.
2345 * @param pUVM Pointer to the user mode VM structure.
2346 * @param pvSample Pointer to the sample registered with STAMR3Register().
2347 */
2348VMMR3DECL(int) STAMR3DeregisterByAddr(PUVM pUVM, void *pvSample)
2349{
2350 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2351
2352 /* This is a complete waste of time when shutting down. */
2353 VMSTATE enmState = VMR3GetStateU(pUVM);
2354 if (enmState >= VMSTATE_DESTROYING)
2355 return VINF_SUCCESS;
2356
2357 STAM_LOCK_WR(pUVM);
2358
2359 /*
2360 * Search for it.
2361 */
2362 int rc = VERR_INVALID_HANDLE;
2363 PSTAMDESC pCur, pNext;
2364 RTListForEachSafe(&pUVM->stam.s.List, pCur, pNext, STAMDESC, ListEntry)
2365 {
2366 if (pCur->u.pv == pvSample)
2367 rc = stamR3DestroyDesc(pCur);
2368 }
2369
2370 STAM_UNLOCK_WR(pUVM);
2371 return rc;
2372}
2373
2374
2375/**
2376 * Worker for STAMR3Deregister, STAMR3DeregisterV and STAMR3DeregisterF.
2377 *
2378 * @returns VBox status code.
2379 * @retval VWRN_NOT_FOUND if no matching names found.
2380 *
2381 * @param pUVM Pointer to the user mode VM structure.
2382 * @param pszPat The name pattern.
2383 */
2384static int stamR3DeregisterByPattern(PUVM pUVM, const char *pszPat)
2385{
2386 Assert(!strchr(pszPat, '|')); /* single pattern! */
2387
2388 int rc = VWRN_NOT_FOUND;
2389 STAM_LOCK_WR(pUVM);
2390
2391 PSTAMDESC pLast;
2392 PSTAMDESC pCur = stamR3LookupFindPatternDescRange(pUVM->stam.s.pRoot, &pUVM->stam.s.List, pszPat, &pLast);
2393 if (pCur)
2394 {
2395 for (;;)
2396 {
2397 PSTAMDESC pNext = RTListNodeGetNext(&pCur->ListEntry, STAMDESC, ListEntry);
2398
2399 if (RTStrSimplePatternMatch(pszPat, pCur->pszName))
2400 rc = stamR3DestroyDesc(pCur);
2401
2402 /* advance. */
2403 if (pCur == pLast)
2404 break;
2405 pCur = pNext;
2406 }
2407 Assert(pLast);
2408 }
2409 else
2410 Assert(!pLast);
2411
2412 STAM_UNLOCK_WR(pUVM);
2413 return rc;
2414}
2415
2416
2417/**
2418 * Deregister zero or more samples given a (single) pattern matching their
2419 * names.
2420 *
2421 * @returns VBox status code.
2422 * @param pUVM Pointer to the user mode VM structure.
2423 * @param pszPat The name pattern.
2424 * @sa STAMR3DeregisterF, STAMR3DeregisterV
2425 */
2426VMMR3DECL(int) STAMR3Deregister(PUVM pUVM, const char *pszPat)
2427{
2428 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2429
2430 /* This is a complete waste of time when shutting down. */
2431 VMSTATE enmState = VMR3GetStateU(pUVM);
2432 if (enmState >= VMSTATE_DESTROYING)
2433 return VINF_SUCCESS;
2434
2435 return stamR3DeregisterByPattern(pUVM, pszPat);
2436}
2437
2438
2439/**
2440 * Deregister zero or more samples given a (single) pattern matching their
2441 * names.
2442 *
2443 * @returns VBox status code.
2444 * @param pUVM Pointer to the user mode VM structure.
2445 * @param pszPatFmt The name pattern format string.
2446 * @param ... Format string arguments.
2447 * @sa STAMR3Deregister, STAMR3DeregisterV
2448 */
2449VMMR3DECL(int) STAMR3DeregisterF(PUVM pUVM, const char *pszPatFmt, ...)
2450{
2451 va_list va;
2452 va_start(va, pszPatFmt);
2453 int rc = STAMR3DeregisterV(pUVM, pszPatFmt, va);
2454 va_end(va);
2455 return rc;
2456}
2457
2458
2459/**
2460 * Deregister zero or more samples given a (single) pattern matching their
2461 * names.
2462 *
2463 * @returns VBox status code.
2464 * @param pUVM Pointer to the user mode VM structure.
2465 * @param pszPatFmt The name pattern format string.
2466 * @param va Format string arguments.
2467 * @sa STAMR3Deregister, STAMR3DeregisterF
2468 */
2469VMMR3DECL(int) STAMR3DeregisterV(PUVM pUVM, const char *pszPatFmt, va_list va)
2470{
2471 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2472
2473 /* This is a complete waste of time when shutting down. */
2474 VMSTATE enmState = VMR3GetStateU(pUVM);
2475 if (enmState >= VMSTATE_DESTROYING)
2476 return VINF_SUCCESS;
2477
2478 char szPat[STAM_MAX_NAME_LEN + 8];
2479 size_t cchPat = RTStrPrintfV(szPat, sizeof(szPat), pszPatFmt, va);
2480 AssertReturn(cchPat <= STAM_MAX_NAME_LEN, VERR_OUT_OF_RANGE);
2481
2482 return stamR3DeregisterByPattern(pUVM, szPat);
2483}
2484
2485
2486/**
2487 * Deregister zero or more samples given their name prefix.
2488 *
2489 * @returns VBox status code.
2490 * @param pUVM Pointer to the user mode VM structure.
2491 * @param pszPrefix The name prefix of the samples to remove.
2492 * @sa STAMR3Deregister, STAMR3DeregisterF, STAMR3DeregisterV
2493 */
2494VMMR3DECL(int) STAMR3DeregisterByPrefix(PUVM pUVM, const char *pszPrefix)
2495{
2496 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2497
2498 /* This is a complete waste of time when shutting down. */
2499 VMSTATE enmState = VMR3GetStateU(pUVM);
2500 if (enmState >= VMSTATE_DESTROYING)
2501 return VINF_SUCCESS;
2502
2503 size_t const cchPrefix = strlen(pszPrefix);
2504 int rc = VWRN_NOT_FOUND;
2505 STAM_LOCK_WR(pUVM);
2506
2507 PSTAMDESC pLast;
2508 PSTAMDESC pCur = stamR3LookupFindByPrefixRange(pUVM->stam.s.pRoot, pszPrefix, (uint32_t)cchPrefix, &pLast);
2509 if (pCur)
2510 for (;;)
2511 {
2512 PSTAMDESC const pNext = RTListNodeGetNext(&pCur->ListEntry, STAMDESC, ListEntry);
2513 Assert(strncmp(pCur->pszName, pszPrefix, cchPrefix) == 0);
2514
2515 rc = stamR3DestroyDesc(pCur);
2516
2517 /* advance. */
2518 if (pCur == pLast)
2519 break;
2520 pCur = pNext;
2521 }
2522
2523 STAM_UNLOCK_WR(pUVM);
2524 return rc;
2525}
2526
2527
2528/**
2529 * Resets statistics for the specified VM.
2530 * It's possible to select a subset of the samples.
2531 *
2532 * @returns VBox status code. (Basically, it cannot fail.)
2533 * @param pUVM The user mode VM handle.
2534 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
2535 * If NULL all samples are reset.
2536 * @remarks Don't confuse this with the other 'XYZR3Reset' methods, it's not called at VM reset.
2537 */
2538VMMR3DECL(int) STAMR3Reset(PUVM pUVM, const char *pszPat)
2539{
2540 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2541 VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
2542
2543 int rc = VINF_SUCCESS;
2544
2545#ifdef VBOX_WITH_R0_MODULES
2546 /*
2547 * Check if the reset patterns cover anything related to ring-0.
2548 */
2549 GVMMRESETSTATISTICSSREQ GVMMReq;
2550 bool fGVMMMatched = (!pszPat || !*pszPat) && !SUPR3IsDriverless();
2551# ifndef VBOX_WITH_MINIMAL_R0
2552 GMMRESETSTATISTICSSREQ GMMReq;
2553 bool fGMMMatched = fGVMMMatched;
2554# endif
2555 if (fGVMMMatched)
2556 {
2557 memset(&GVMMReq.Stats, 0xff, sizeof(GVMMReq.Stats));
2558# ifndef VBOX_WITH_MINIMAL_R0
2559 memset(&GMMReq.Stats, 0xff, sizeof(GMMReq.Stats));
2560# endif
2561 }
2562 else
2563 {
2564 char *pszCopy;
2565 unsigned cExpressions;
2566 char **papszExpressions = stamR3SplitPattern(pszPat, &cExpressions, &pszCopy);
2567 if (!papszExpressions)
2568 return VERR_NO_MEMORY;
2569
2570 /* GVMM */
2571 RT_ZERO(GVMMReq.Stats);
2572 for (unsigned i = 0; i < RT_ELEMENTS(g_aGVMMStats); i++)
2573 if (stamR3MultiMatch(papszExpressions, cExpressions, NULL, g_aGVMMStats[i].pszName))
2574 {
2575 *((uint8_t *)&GVMMReq.Stats + g_aGVMMStats[i].offVar) = 0xff;
2576 fGVMMMatched = true;
2577 }
2578 if (!fGVMMMatched)
2579 {
2580 /** @todo match cpu leaves some rainy day. */
2581 }
2582
2583# ifndef VBOX_WITH_MINIMAL_R0
2584 /* GMM */
2585 RT_ZERO(GMMReq.Stats);
2586 for (unsigned i = 0; i < RT_ELEMENTS(g_aGMMStats); i++)
2587 if (stamR3MultiMatch(papszExpressions, cExpressions, NULL, g_aGMMStats[i].pszName))
2588 {
2589 *((uint8_t *)&GMMReq.Stats + g_aGMMStats[i].offVar) = 0xff;
2590 fGMMMatched = true;
2591 }
2592# endif
2593
2594 RTMemTmpFree(papszExpressions);
2595 RTStrFree(pszCopy);
2596 }
2597#endif /* !VBOX_WITH_R0_MODULES */
2598
2599
2600 /*
2601 * Grab the lock and do the resetting.
2602 */
2603 STAM_LOCK_WR(pUVM);
2604
2605#ifdef VBOX_WITH_R0_MODULES
2606 /* Reset ring-0 stats first. */
2607 if (fGVMMMatched)
2608 {
2609 PVM pVM = pUVM->pVM;
2610 GVMMReq.Hdr.cbReq = sizeof(GVMMReq);
2611 GVMMReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
2612 GVMMReq.pSession = pVM->pSession;
2613 rc = SUPR3CallVMMR0Ex(VMCC_GET_VMR0_FOR_CALL(pVM), NIL_VMCPUID, VMMR0_DO_GVMM_RESET_STATISTICS, 0, &GVMMReq.Hdr);
2614 }
2615
2616# ifndef VBOX_WITH_MINIMAL_R0
2617 if (fGMMMatched)
2618 {
2619 PVM pVM = pUVM->pVM;
2620 GMMReq.Hdr.cbReq = sizeof(GMMReq);
2621 GMMReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
2622 GMMReq.pSession = pVM->pSession;
2623 rc = SUPR3CallVMMR0Ex(VMCC_GET_VMR0_FOR_CALL(pVM), NIL_VMCPUID, VMMR0_DO_GMM_RESET_STATISTICS, 0, &GMMReq.Hdr);
2624 }
2625# endif
2626#endif
2627
2628 /* and the reset */
2629 stamR3EnumU(pUVM, pszPat, false /* fUpdateRing0 */, stamR3ResetOne, pUVM->pVM);
2630
2631 STAM_UNLOCK_WR(pUVM);
2632 return rc;
2633}
2634
2635
2636/**
2637 * Resets one statistics sample.
2638 * Callback for stamR3EnumU().
2639 *
2640 * @returns VINF_SUCCESS
2641 * @param pDesc Pointer to the current descriptor.
2642 * @param pvArg User argument - Pointer to the VM.
2643 */
2644static int stamR3ResetOne(PSTAMDESC pDesc, void *pvArg)
2645{
2646 switch (pDesc->enmType)
2647 {
2648 case STAMTYPE_COUNTER:
2649 ASMAtomicXchgU64(&pDesc->u.pCounter->c, 0);
2650 break;
2651
2652 case STAMTYPE_PROFILE:
2653 case STAMTYPE_PROFILE_ADV:
2654 ASMAtomicXchgU64(&pDesc->u.pProfile->cPeriods, 0);
2655 ASMAtomicXchgU64(&pDesc->u.pProfile->cTicks, 0);
2656 ASMAtomicXchgU64(&pDesc->u.pProfile->cTicksMax, 0);
2657 ASMAtomicXchgU64(&pDesc->u.pProfile->cTicksMin, UINT64_MAX);
2658 break;
2659
2660 case STAMTYPE_RATIO_U32_RESET:
2661 ASMAtomicXchgU32(&pDesc->u.pRatioU32->u32A, 0);
2662 ASMAtomicXchgU32(&pDesc->u.pRatioU32->u32B, 0);
2663 break;
2664
2665 case STAMTYPE_CALLBACK:
2666 if (pDesc->u.Callback.pfnReset)
2667 pDesc->u.Callback.pfnReset((PVM)pvArg, pDesc->u.Callback.pvSample);
2668 break;
2669
2670 case STAMTYPE_U8_RESET:
2671 case STAMTYPE_X8_RESET:
2672 ASMAtomicXchgU8(pDesc->u.pu8, 0);
2673 break;
2674
2675 case STAMTYPE_U16_RESET:
2676 case STAMTYPE_X16_RESET:
2677 ASMAtomicXchgU16(pDesc->u.pu16, 0);
2678 break;
2679
2680 case STAMTYPE_U32_RESET:
2681 case STAMTYPE_X32_RESET:
2682 ASMAtomicXchgU32(pDesc->u.pu32, 0);
2683 break;
2684
2685 case STAMTYPE_U64_RESET:
2686 case STAMTYPE_X64_RESET:
2687 ASMAtomicXchgU64(pDesc->u.pu64, 0);
2688 break;
2689
2690 case STAMTYPE_BOOL_RESET:
2691 ASMAtomicXchgBool(pDesc->u.pf, false);
2692 break;
2693
2694 /* These are custom and will not be touched. */
2695 case STAMTYPE_U8:
2696 case STAMTYPE_X8:
2697 case STAMTYPE_U16:
2698 case STAMTYPE_X16:
2699 case STAMTYPE_U32:
2700 case STAMTYPE_X32:
2701 case STAMTYPE_U64:
2702 case STAMTYPE_X64:
2703 case STAMTYPE_RATIO_U32:
2704 case STAMTYPE_BOOL:
2705 case STAMTYPE_INTERNAL_SUM:
2706 case STAMTYPE_INTERNAL_PCT_OF_SUM:
2707 break;
2708
2709 default:
2710 AssertMsgFailed(("enmType=%d\n", pDesc->enmType));
2711 break;
2712 }
2713 NOREF(pvArg);
2714 return VINF_SUCCESS;
2715}
2716
2717
2718/**
2719 * Get a snapshot of the statistics.
2720 * It's possible to select a subset of the samples.
2721 *
2722 * @returns VBox status code. (Basically, it cannot fail.)
2723 * @param pUVM The user mode VM handle.
2724 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
2725 * If NULL all samples are reset.
2726 * @param fWithDesc Whether to include the descriptions.
2727 * @param ppszSnapshot Where to store the pointer to the snapshot data.
2728 * The format of the snapshot should be XML, but that will have to be discussed
2729 * when this function is implemented.
2730 * The returned pointer must be freed by calling STAMR3SnapshotFree().
2731 * @param pcchSnapshot Where to store the size of the snapshot data. (Excluding the trailing '\0')
2732 */
2733VMMR3DECL(int) STAMR3Snapshot(PUVM pUVM, const char *pszPat, char **ppszSnapshot, size_t *pcchSnapshot, bool fWithDesc)
2734{
2735 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2736 VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
2737
2738 STAMR3SNAPSHOTONE State = { NULL, NULL, NULL, pUVM->pVM, 0, VINF_SUCCESS, fWithDesc };
2739
2740 /*
2741 * Write the XML header.
2742 */
2743 /** @todo Make this proper & valid XML. */
2744 stamR3SnapshotPrintf(&State, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
2745
2746 /*
2747 * Write the content.
2748 */
2749 stamR3SnapshotPrintf(&State, "<Statistics>\n");
2750 int rc = stamR3EnumU(pUVM, pszPat, true /* fUpdateRing0 */, stamR3SnapshotOne, &State);
2751 stamR3SnapshotPrintf(&State, "</Statistics>\n");
2752
2753 if (RT_SUCCESS(rc))
2754 rc = State.rc;
2755 else
2756 {
2757 RTMemFree(State.pszStart);
2758 State.pszStart = State.pszEnd = State.psz = NULL;
2759 State.cbAllocated = 0;
2760 }
2761
2762 /*
2763 * Done.
2764 */
2765 *ppszSnapshot = State.pszStart;
2766 if (pcchSnapshot)
2767 *pcchSnapshot = State.psz - State.pszStart;
2768 return rc;
2769}
2770
2771
2772/**
2773 * stamR3EnumU callback employed by STAMR3Snapshot.
2774 *
2775 * @returns VBox status code, but it's interpreted as 0 == success / !0 == failure by enmR3Enum.
2776 * @param pDesc The sample.
2777 * @param pvArg The snapshot status structure.
2778 */
2779static int stamR3SnapshotOne(PSTAMDESC pDesc, void *pvArg)
2780{
2781 PSTAMR3SNAPSHOTONE pThis = (PSTAMR3SNAPSHOTONE)pvArg;
2782
2783 switch (pDesc->enmType)
2784 {
2785 case STAMTYPE_COUNTER:
2786 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pDesc->u.pCounter->c == 0)
2787 return VINF_SUCCESS;
2788 stamR3SnapshotPrintf(pThis, "<Counter c=\"%lld\"", pDesc->u.pCounter->c);
2789 break;
2790
2791 case STAMTYPE_PROFILE:
2792 case STAMTYPE_PROFILE_ADV:
2793 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pDesc->u.pProfile->cPeriods == 0)
2794 return VINF_SUCCESS;
2795 stamR3SnapshotPrintf(pThis, "<Profile cPeriods=\"%lld\" cTicks=\"%lld\" cTicksMin=\"%lld\" cTicksMax=\"%lld\"",
2796 pDesc->u.pProfile->cPeriods, pDesc->u.pProfile->cTicks, pDesc->u.pProfile->cTicksMin,
2797 pDesc->u.pProfile->cTicksMax);
2798 break;
2799
2800 case STAMTYPE_RATIO_U32:
2801 case STAMTYPE_RATIO_U32_RESET:
2802 if (pDesc->enmVisibility == STAMVISIBILITY_USED && !pDesc->u.pRatioU32->u32A && !pDesc->u.pRatioU32->u32B)
2803 return VINF_SUCCESS;
2804 stamR3SnapshotPrintf(pThis, "<Ratio32 u32A=\"%lld\" u32B=\"%lld\"",
2805 pDesc->u.pRatioU32->u32A, pDesc->u.pRatioU32->u32B);
2806 break;
2807
2808 case STAMTYPE_CALLBACK:
2809 {
2810 char szBuf[512];
2811 pDesc->u.Callback.pfnPrint(pThis->pVM, pDesc->u.Callback.pvSample, szBuf, sizeof(szBuf));
2812 stamR3SnapshotPrintf(pThis, "<Callback val=\"%s\"", szBuf);
2813 break;
2814 }
2815
2816 case STAMTYPE_U8:
2817 case STAMTYPE_U8_RESET:
2818 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu8 == 0)
2819 return VINF_SUCCESS;
2820 stamR3SnapshotPrintf(pThis, "<U8 val=\"%u\"", *pDesc->u.pu8);
2821 break;
2822
2823 case STAMTYPE_X8:
2824 case STAMTYPE_X8_RESET:
2825 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu8 == 0)
2826 return VINF_SUCCESS;
2827 stamR3SnapshotPrintf(pThis, "<X8 val=\"%#x\"", *pDesc->u.pu8);
2828 break;
2829
2830 case STAMTYPE_U16:
2831 case STAMTYPE_U16_RESET:
2832 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu16 == 0)
2833 return VINF_SUCCESS;
2834 stamR3SnapshotPrintf(pThis, "<U16 val=\"%u\"", *pDesc->u.pu16);
2835 break;
2836
2837 case STAMTYPE_X16:
2838 case STAMTYPE_X16_RESET:
2839 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu16 == 0)
2840 return VINF_SUCCESS;
2841 stamR3SnapshotPrintf(pThis, "<X16 val=\"%#x\"", *pDesc->u.pu16);
2842 break;
2843
2844 case STAMTYPE_U32:
2845 case STAMTYPE_U32_RESET:
2846 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu32 == 0)
2847 return VINF_SUCCESS;
2848 stamR3SnapshotPrintf(pThis, "<U32 val=\"%u\"", *pDesc->u.pu32);
2849 break;
2850
2851 case STAMTYPE_X32:
2852 case STAMTYPE_X32_RESET:
2853 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu32 == 0)
2854 return VINF_SUCCESS;
2855 stamR3SnapshotPrintf(pThis, "<X32 val=\"%#x\"", *pDesc->u.pu32);
2856 break;
2857
2858 case STAMTYPE_U64:
2859 case STAMTYPE_U64_RESET:
2860 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu64 == 0)
2861 return VINF_SUCCESS;
2862 stamR3SnapshotPrintf(pThis, "<U64 val=\"%llu\"", *pDesc->u.pu64);
2863 break;
2864
2865 case STAMTYPE_X64:
2866 case STAMTYPE_X64_RESET:
2867 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu64 == 0)
2868 return VINF_SUCCESS;
2869 stamR3SnapshotPrintf(pThis, "<X64 val=\"%#llx\"", *pDesc->u.pu64);
2870 break;
2871
2872 case STAMTYPE_BOOL:
2873 case STAMTYPE_BOOL_RESET:
2874 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pf == false)
2875 return VINF_SUCCESS;
2876 stamR3SnapshotPrintf(pThis, "<BOOL val=\"%RTbool\"", *pDesc->u.pf);
2877 break;
2878
2879 case STAMTYPE_INTERNAL_SUM:
2880 {
2881 PSTAMSUMSAMPLE const pSum = pDesc->u.pSum;
2882 stamR3SumRefresh(pSum);
2883 switch (pSum->enmType)
2884 {
2885 case STAMTYPE_COUNTER:
2886 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pSum->u.Counter.c == 0)
2887 return VINF_SUCCESS;
2888 stamR3SnapshotPrintf(pThis, "<Counter c=\"%lld\"", pSum->u.Counter.c);
2889 break;
2890
2891 case STAMTYPE_PROFILE:
2892 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pSum->u.Profile.cPeriods == 0)
2893 return VINF_SUCCESS;
2894 stamR3SnapshotPrintf(pThis, "<Profile cPeriods=\"%lld\" cTicks=\"%lld\" cTicksMin=\"%lld\" cTicksMax=\"%lld\"",
2895 pSum->u.Profile.cPeriods, pSum->u.Profile.cTicks, pSum->u.Profile.cTicksMin,
2896 pSum->u.Profile.cTicksMax);
2897 break;
2898
2899 default:
2900 AssertMsgFailedReturn(("%d\n", pSum->enmType), VINF_SUCCESS);
2901 }
2902 break;
2903 }
2904
2905 case STAMTYPE_INTERNAL_PCT_OF_SUM:
2906 {
2907 PSTAMSUMSAMPLE const pSum = pDesc->u.pSum;
2908 stamR3PctOfSumRefresh(pDesc, pSum);
2909 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pSum->u.Counter.c == 0)
2910 return VINF_SUCCESS;
2911 stamR3SnapshotPrintf(pThis, "<Counter c=\"%lld\"", pSum->u.Counter.c);
2912 break;
2913 }
2914
2915 default:
2916 AssertMsgFailedReturn(("%d\n", pDesc->enmType), VINF_SUCCESS);
2917 }
2918
2919 stamR3SnapshotPrintf(pThis, " unit=\"%s\"", STAMR3GetUnit(pDesc->enmUnit));
2920
2921 switch (pDesc->enmVisibility)
2922 {
2923 default:
2924 case STAMVISIBILITY_ALWAYS:
2925 break;
2926 case STAMVISIBILITY_USED:
2927 stamR3SnapshotPrintf(pThis, " vis=\"used\"");
2928 break;
2929 case STAMVISIBILITY_NOT_GUI:
2930 stamR3SnapshotPrintf(pThis, " vis=\"not-gui\"");
2931 break;
2932 }
2933
2934 stamR3SnapshotPrintf(pThis, " name=\"%s\"", pDesc->pszName);
2935
2936 if (pThis->fWithDesc && pDesc->pszDesc)
2937 {
2938 /*
2939 * The description is a bit tricky as it may include chars that
2940 * xml requires to be escaped.
2941 */
2942 const char *pszBadChar = strpbrk(pDesc->pszDesc, "&<>\"'");
2943 if (!pszBadChar)
2944 return stamR3SnapshotPrintf(pThis, " desc=\"%s\"/>\n", pDesc->pszDesc);
2945
2946 stamR3SnapshotPrintf(pThis, " desc=\"");
2947 const char *pszCur = pDesc->pszDesc;
2948 do
2949 {
2950 stamR3SnapshotPrintf(pThis, "%.*s", pszBadChar - pszCur, pszCur);
2951 switch (*pszBadChar)
2952 {
2953 case '&': stamR3SnapshotPrintf(pThis, "&amp;"); break;
2954 case '<': stamR3SnapshotPrintf(pThis, "&lt;"); break;
2955 case '>': stamR3SnapshotPrintf(pThis, "&gt;"); break;
2956 case '"': stamR3SnapshotPrintf(pThis, "&quot;"); break;
2957 case '\'': stamR3SnapshotPrintf(pThis, "&apos;"); break;
2958 default: AssertMsgFailed(("%c", *pszBadChar)); break;
2959 }
2960 pszCur = pszBadChar + 1;
2961 pszBadChar = strpbrk(pszCur, "&<>\"'");
2962 } while (pszBadChar);
2963 return stamR3SnapshotPrintf(pThis, "%s\"/>\n", pszCur);
2964 }
2965 return stamR3SnapshotPrintf(pThis, "/>\n");
2966}
2967
2968
2969/**
2970 * Output callback for stamR3SnapshotPrintf.
2971 *
2972 * @returns number of bytes written.
2973 * @param pvArg The snapshot status structure.
2974 * @param pach Pointer to an array of characters (bytes).
2975 * @param cch The number or chars (bytes) to write from the array.
2976 */
2977static DECLCALLBACK(size_t) stamR3SnapshotOutput(void *pvArg, const char *pach, size_t cch)
2978{
2979 PSTAMR3SNAPSHOTONE pThis = (PSTAMR3SNAPSHOTONE)pvArg;
2980
2981 /*
2982 * Make sure we've got space for it.
2983 */
2984 if (RT_UNLIKELY((uintptr_t)pThis->pszEnd - (uintptr_t)pThis->psz < cch + 1))
2985 {
2986 if (RT_FAILURE(pThis->rc))
2987 return 0;
2988
2989 size_t cbNewSize = pThis->cbAllocated;
2990 if (cbNewSize > cch)
2991 cbNewSize *= 2;
2992 else
2993 cbNewSize += RT_ALIGN(cch + 1, 0x1000);
2994 char *pszNew = (char *)RTMemRealloc(pThis->pszStart, cbNewSize);
2995 if (!pszNew)
2996 {
2997 /*
2998 * Free up immediately, out-of-memory is bad news and this
2999 * isn't an important allocations / API.
3000 */
3001 pThis->rc = VERR_NO_MEMORY;
3002 RTMemFree(pThis->pszStart);
3003 pThis->pszStart = pThis->pszEnd = pThis->psz = NULL;
3004 pThis->cbAllocated = 0;
3005 return 0;
3006 }
3007
3008 pThis->psz = pszNew + (pThis->psz - pThis->pszStart);
3009 pThis->pszStart = pszNew;
3010 pThis->pszEnd = pszNew + cbNewSize;
3011 pThis->cbAllocated = cbNewSize;
3012 }
3013
3014 /*
3015 * Copy the chars to the buffer and terminate it.
3016 */
3017 if (cch)
3018 {
3019 memcpy(pThis->psz, pach, cch);
3020 pThis->psz += cch;
3021 }
3022 *pThis->psz = '\0';
3023 return cch;
3024}
3025
3026
3027/**
3028 * Wrapper around RTStrFormatV for use by the snapshot API.
3029 *
3030 * @returns VBox status code.
3031 * @param pThis The snapshot status structure.
3032 * @param pszFormat The format string.
3033 * @param ... Optional arguments.
3034 */
3035static int stamR3SnapshotPrintf(PSTAMR3SNAPSHOTONE pThis, const char *pszFormat, ...)
3036{
3037 va_list va;
3038 va_start(va, pszFormat);
3039 RTStrFormatV(stamR3SnapshotOutput, pThis, NULL, NULL, pszFormat, va);
3040 va_end(va);
3041 return pThis->rc;
3042}
3043
3044
3045/**
3046 * Releases a statistics snapshot returned by STAMR3Snapshot().
3047 *
3048 * @returns VBox status code.
3049 * @param pUVM The user mode VM handle.
3050 * @param pszSnapshot The snapshot data pointer returned by STAMR3Snapshot().
3051 * NULL is allowed.
3052 */
3053VMMR3DECL(int) STAMR3SnapshotFree(PUVM pUVM, char *pszSnapshot)
3054{
3055 if (pszSnapshot)
3056 RTMemFree(pszSnapshot);
3057 NOREF(pUVM);
3058 return VINF_SUCCESS;
3059}
3060
3061
3062/**
3063 * Dumps the selected statistics to the log.
3064 *
3065 * @returns VBox status code.
3066 * @param pUVM Pointer to the user mode VM structure.
3067 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
3068 * If NULL all samples are written to the log.
3069 */
3070VMMR3DECL(int) STAMR3Dump(PUVM pUVM, const char *pszPat)
3071{
3072 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
3073 VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
3074
3075 STAMR3PRINTONEARGS Args;
3076 Args.pUVM = pUVM;
3077 Args.pvArg = NULL;
3078 Args.pfnPrintf = stamR3EnumLogPrintf;
3079
3080 stamR3EnumU(pUVM, pszPat, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
3081 return VINF_SUCCESS;
3082}
3083
3084
3085/**
3086 * Prints to the log.
3087 *
3088 * @param pArgs Pointer to the print one argument structure.
3089 * @param pszFormat Format string.
3090 * @param ... Format arguments.
3091 */
3092static DECLCALLBACK(void) stamR3EnumLogPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...)
3093{
3094 va_list va;
3095 va_start(va, pszFormat);
3096 RTLogPrintfV(pszFormat, va);
3097 va_end(va);
3098 NOREF(pArgs);
3099}
3100
3101
3102/**
3103 * Dumps the selected statistics to the release log.
3104 *
3105 * @returns VBox status code.
3106 * @param pUVM Pointer to the user mode VM structure.
3107 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
3108 * If NULL all samples are written to the log.
3109 */
3110VMMR3DECL(int) STAMR3DumpToReleaseLog(PUVM pUVM, const char *pszPat)
3111{
3112 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
3113 VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
3114
3115 STAMR3PRINTONEARGS Args;
3116 Args.pUVM = pUVM;
3117 Args.pvArg = NULL;
3118 Args.pfnPrintf = stamR3EnumRelLogPrintf;
3119
3120 stamR3EnumU(pUVM, pszPat, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
3121 return VINF_SUCCESS;
3122}
3123
3124/**
3125 * Prints to the release log.
3126 *
3127 * @param pArgs Pointer to the print one argument structure.
3128 * @param pszFormat Format string.
3129 * @param ... Format arguments.
3130 */
3131static DECLCALLBACK(void) stamR3EnumRelLogPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...)
3132{
3133 va_list va;
3134 va_start(va, pszFormat);
3135 RTLogRelPrintfV(pszFormat, va);
3136 va_end(va);
3137 NOREF(pArgs);
3138}
3139
3140
3141/**
3142 * Prints the selected statistics to standard out.
3143 *
3144 * @returns VBox status code.
3145 * @param pUVM The user mode VM handle.
3146 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
3147 * If NULL all samples are reset.
3148 */
3149VMMR3DECL(int) STAMR3Print(PUVM pUVM, const char *pszPat)
3150{
3151 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
3152 VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
3153
3154 STAMR3PRINTONEARGS Args;
3155 Args.pUVM = pUVM;
3156 Args.pvArg = NULL;
3157 Args.pfnPrintf = stamR3EnumPrintf;
3158
3159 stamR3EnumU(pUVM, pszPat, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
3160 return VINF_SUCCESS;
3161}
3162
3163
3164/**
3165 * Prints to stdout.
3166 *
3167 * @param pArgs Pointer to the print one argument structure.
3168 * @param pszFormat Format string.
3169 * @param ... Format arguments.
3170 */
3171static DECLCALLBACK(void) stamR3EnumPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...)
3172{
3173 va_list va;
3174 va_start(va, pszFormat);
3175 RTPrintfV(pszFormat, va);
3176 va_end(va);
3177 NOREF(pArgs);
3178}
3179
3180
3181/**
3182 * Prints one sample.
3183 * Callback for stamR3EnumU().
3184 *
3185 * @returns VINF_SUCCESS
3186 * @param pDesc Pointer to the current descriptor.
3187 * @param pvArg User argument - STAMR3PRINTONEARGS.
3188 */
3189static int stamR3PrintOne(PSTAMDESC pDesc, void *pvArg)
3190{
3191 PSTAMR3PRINTONEARGS pArgs = (PSTAMR3PRINTONEARGS)pvArg;
3192
3193 switch (pDesc->enmType)
3194 {
3195 case STAMTYPE_COUNTER:
3196 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pDesc->u.pCounter->c == 0)
3197 return VINF_SUCCESS;
3198
3199 pArgs->pfnPrintf(pArgs, "%-42s %9llu %s\n", pDesc->pszName, pDesc->u.pCounter->c, STAMR3GetUnit(pDesc->enmUnit));
3200 break;
3201
3202 case STAMTYPE_PROFILE:
3203 case STAMTYPE_PROFILE_ADV:
3204 {
3205 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pDesc->u.pProfile->cPeriods == 0)
3206 return VINF_SUCCESS;
3207
3208 uint64_t const u64 = pDesc->u.pProfile->cPeriods ? pDesc->u.pProfile->cPeriods : 1;
3209 pArgs->pfnPrintf(pArgs, "%-42s %9llu %s (%12llu %s, %7llu %s, max %9llu, min %7lld)\n", pDesc->pszName,
3210 pDesc->u.pProfile->cTicks / u64, STAMR3GetUnit(pDesc->enmUnit),
3211 pDesc->u.pProfile->cTicks, STAMR3GetUnit1(pDesc->enmUnit),
3212 pDesc->u.pProfile->cPeriods, STAMR3GetUnit2(pDesc->enmUnit),
3213 pDesc->u.pProfile->cTicksMax, pDesc->u.pProfile->cTicksMin);
3214 break;
3215 }
3216
3217 case STAMTYPE_RATIO_U32:
3218 case STAMTYPE_RATIO_U32_RESET:
3219 if (pDesc->enmVisibility == STAMVISIBILITY_USED && !pDesc->u.pRatioU32->u32A && !pDesc->u.pRatioU32->u32B)
3220 return VINF_SUCCESS;
3221 pArgs->pfnPrintf(pArgs, "%-42s %9u:%-8u %s\n", pDesc->pszName,
3222 pDesc->u.pRatioU32->u32A, pDesc->u.pRatioU32->u32B, STAMR3GetUnit(pDesc->enmUnit));
3223 break;
3224
3225 case STAMTYPE_CALLBACK:
3226 {
3227 char szBuf[512];
3228 pDesc->u.Callback.pfnPrint(pArgs->pUVM->pVM, pDesc->u.Callback.pvSample, szBuf, sizeof(szBuf));
3229 pArgs->pfnPrintf(pArgs, "%-42s %s %s\n", pDesc->pszName, szBuf, STAMR3GetUnit(pDesc->enmUnit));
3230 break;
3231 }
3232
3233 case STAMTYPE_U8:
3234 case STAMTYPE_U8_RESET:
3235 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu8 == 0)
3236 return VINF_SUCCESS;
3237 pArgs->pfnPrintf(pArgs, "%-42s %9u %s\n", pDesc->pszName, *pDesc->u.pu8, STAMR3GetUnit(pDesc->enmUnit));
3238 break;
3239
3240 case STAMTYPE_X8:
3241 case STAMTYPE_X8_RESET:
3242 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu8 == 0)
3243 return VINF_SUCCESS;
3244 pArgs->pfnPrintf(pArgs, "%-42s %9x %s\n", pDesc->pszName, *pDesc->u.pu8, STAMR3GetUnit(pDesc->enmUnit));
3245 break;
3246
3247 case STAMTYPE_U16:
3248 case STAMTYPE_U16_RESET:
3249 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu16 == 0)
3250 return VINF_SUCCESS;
3251 pArgs->pfnPrintf(pArgs, "%-42s %9u %s\n", pDesc->pszName, *pDesc->u.pu16, STAMR3GetUnit(pDesc->enmUnit));
3252 break;
3253
3254 case STAMTYPE_X16:
3255 case STAMTYPE_X16_RESET:
3256 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu16 == 0)
3257 return VINF_SUCCESS;
3258 pArgs->pfnPrintf(pArgs, "%-42s %9x %s\n", pDesc->pszName, *pDesc->u.pu16, STAMR3GetUnit(pDesc->enmUnit));
3259 break;
3260
3261 case STAMTYPE_U32:
3262 case STAMTYPE_U32_RESET:
3263 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu32 == 0)
3264 return VINF_SUCCESS;
3265 pArgs->pfnPrintf(pArgs, "%-42s %9u %s\n", pDesc->pszName, *pDesc->u.pu32, STAMR3GetUnit(pDesc->enmUnit));
3266 break;
3267
3268 case STAMTYPE_X32:
3269 case STAMTYPE_X32_RESET:
3270 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu32 == 0)
3271 return VINF_SUCCESS;
3272 pArgs->pfnPrintf(pArgs, "%-42s %9x %s\n", pDesc->pszName, *pDesc->u.pu32, STAMR3GetUnit(pDesc->enmUnit));
3273 break;
3274
3275 case STAMTYPE_U64:
3276 case STAMTYPE_U64_RESET:
3277 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu64 == 0)
3278 return VINF_SUCCESS;
3279 pArgs->pfnPrintf(pArgs, "%-42s %9llu %s\n", pDesc->pszName, *pDesc->u.pu64, STAMR3GetUnit(pDesc->enmUnit));
3280 break;
3281
3282 case STAMTYPE_X64:
3283 case STAMTYPE_X64_RESET:
3284 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu64 == 0)
3285 return VINF_SUCCESS;
3286 pArgs->pfnPrintf(pArgs, "%-42s %9llx %s\n", pDesc->pszName, *pDesc->u.pu64, STAMR3GetUnit(pDesc->enmUnit));
3287 break;
3288
3289 case STAMTYPE_BOOL:
3290 case STAMTYPE_BOOL_RESET:
3291 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pf == false)
3292 return VINF_SUCCESS;
3293 pArgs->pfnPrintf(pArgs, "%-42s %s %s\n", pDesc->pszName, *pDesc->u.pf ? "true " : "false ", STAMR3GetUnit(pDesc->enmUnit));
3294 break;
3295
3296 case STAMTYPE_INTERNAL_SUM:
3297 {
3298 PSTAMSUMSAMPLE const pSum = pDesc->u.pSum;
3299 stamR3SumRefresh(pSum);
3300 switch (pSum->enmType)
3301 {
3302 case STAMTYPE_COUNTER:
3303 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pSum->u.Counter.c == 0)
3304 return VINF_SUCCESS;
3305 pArgs->pfnPrintf(pArgs, "%-42s %9llu %s\n", pDesc->pszName, pSum->u.Counter.c, STAMR3GetUnit(pDesc->enmUnit));
3306 break;
3307
3308 case STAMTYPE_PROFILE:
3309 {
3310 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pSum->u.Profile.cPeriods == 0)
3311 return VINF_SUCCESS;
3312
3313 uint64_t const u64 = pSum->u.Profile.cPeriods ? pSum->u.Profile.cPeriods : 1;
3314 pArgs->pfnPrintf(pArgs, "%-42s %9llu %s (%12llu %s, %7llu %s, max %9llu, min %7lld)\n", pDesc->pszName,
3315 pSum->u.Profile.cTicks / u64, STAMR3GetUnit(pDesc->enmUnit),
3316 pSum->u.Profile.cTicks, STAMR3GetUnit1(pDesc->enmUnit),
3317 pSum->u.Profile.cPeriods, STAMR3GetUnit2(pDesc->enmUnit),
3318 pSum->u.Profile.cTicksMax, pSum->u.Profile.cTicksMin);
3319 break;
3320 }
3321
3322 default:
3323 AssertMsgFailed(("%d\n", pSum->enmType));
3324 break;
3325 }
3326 break;
3327 }
3328
3329 case STAMTYPE_INTERNAL_PCT_OF_SUM:
3330 {
3331 PSTAMSUMSAMPLE const pSum = pDesc->u.pSum;
3332 stamR3PctOfSumRefresh(pDesc, pSum);
3333 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pSum->u.Counter.c == 0)
3334 return VINF_SUCCESS;
3335 pArgs->pfnPrintf(pArgs, "%-42s %9llu %s\n", pDesc->pszName, pSum->u.Counter.c, STAMR3GetUnit(pDesc->enmUnit));
3336 break;
3337 }
3338
3339 default:
3340 AssertMsgFailed(("enmType=%d\n", pDesc->enmType));
3341 break;
3342 }
3343 return VINF_SUCCESS;
3344}
3345
3346
3347/**
3348 * Enumerate the statistics by the means of a callback function.
3349 *
3350 * @returns Whatever the callback returns.
3351 *
3352 * @param pUVM The user mode VM handle.
3353 * @param pszPat The pattern to match samples.
3354 * @param pfnEnum The callback function.
3355 * @param pvUser The pvUser argument of the callback function.
3356 */
3357VMMR3DECL(int) STAMR3Enum(PUVM pUVM, const char *pszPat, PFNSTAMR3ENUM pfnEnum, void *pvUser)
3358{
3359 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
3360 VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
3361
3362 STAMR3ENUMONEARGS Args;
3363 Args.pVM = pUVM->pVM;
3364 Args.pfnEnum = pfnEnum;
3365 Args.pvUser = pvUser;
3366
3367 return stamR3EnumU(pUVM, pszPat, true /* fUpdateRing0 */, stamR3EnumOne, &Args);
3368}
3369
3370
3371/**
3372 * Callback function for STARTR3Enum().
3373 *
3374 * @returns whatever the callback returns.
3375 * @param pDesc Pointer to the current descriptor.
3376 * @param pvArg Points to a STAMR3ENUMONEARGS structure.
3377 */
3378static int stamR3EnumOne(PSTAMDESC pDesc, void *pvArg)
3379{
3380 PSTAMR3ENUMONEARGS const pArgs = (PSTAMR3ENUMONEARGS)pvArg;
3381 const char * const pszUnit = STAMR3GetUnit(pDesc->enmUnit);
3382 switch (pDesc->enmType)
3383 {
3384 default:
3385 return pArgs->pfnEnum(pDesc->pszName, pDesc->enmType, pDesc->u.pv, pDesc->enmUnit, pszUnit,
3386 pDesc->enmVisibility, pDesc->pszDesc, pArgs->pvUser);
3387
3388 case STAMTYPE_CALLBACK:
3389 {
3390 /* Give the enumerator something useful. */
3391 char szBuf[512];
3392 pDesc->u.Callback.pfnPrint(pArgs->pVM, pDesc->u.Callback.pvSample, szBuf, sizeof(szBuf));
3393 return pArgs->pfnEnum(pDesc->pszName, pDesc->enmType, szBuf, pDesc->enmUnit, pszUnit,
3394 pDesc->enmVisibility, pDesc->pszDesc, pArgs->pvUser);
3395 }
3396
3397 case STAMTYPE_INTERNAL_SUM:
3398 {
3399 PSTAMSUMSAMPLE const pSum = pDesc->u.pSum;
3400 stamR3SumRefresh(pSum);
3401 return pArgs->pfnEnum(pDesc->pszName, pSum->enmType, &pSum->u, pDesc->enmUnit, pszUnit,
3402 pDesc->enmVisibility, pDesc->pszDesc, pArgs->pvUser);
3403 }
3404
3405 case STAMTYPE_INTERNAL_PCT_OF_SUM:
3406 {
3407 PSTAMSUMSAMPLE const pSum = pDesc->u.pSum;
3408 stamR3PctOfSumRefresh(pDesc, pSum);
3409 return pArgs->pfnEnum(pDesc->pszName, pSum->enmType, &pSum->u, pDesc->enmUnit, pszUnit,
3410 pDesc->enmVisibility, pDesc->pszDesc, pArgs->pvUser);
3411 }
3412 }
3413}
3414
3415static void stamR3RefreshGroup(PUVM pUVM, uint8_t iRefreshGroup, uint64_t *pbmRefreshedGroups)
3416{
3417 *pbmRefreshedGroups |= RT_BIT_64(iRefreshGroup);
3418
3419 PVM pVM = pUVM->pVM;
3420 if (pVM && pVM->pSession)
3421 {
3422 switch (iRefreshGroup)
3423 {
3424#ifdef VBOX_WITH_R0_MODULES
3425 /*
3426 * GVMM
3427 */
3428 case STAM_REFRESH_GRP_GVMM:
3429 {
3430 GVMMQUERYSTATISTICSSREQ Req;
3431 Req.Hdr.cbReq = sizeof(Req);
3432 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
3433 Req.pSession = pVM->pSession;
3434 int rc = SUPR3CallVMMR0Ex(VMCC_GET_VMR0_FOR_CALL(pVM), NIL_VMCPUID, VMMR0_DO_GVMM_QUERY_STATISTICS, 0, &Req.Hdr);
3435 if (RT_SUCCESS(rc))
3436 {
3437 pUVM->stam.s.GVMMStats = Req.Stats;
3438
3439 /*
3440 * Check if the number of host CPUs has changed (it will the first
3441 * time around and normally never again).
3442 */
3443 if (RT_UNLIKELY(pUVM->stam.s.GVMMStats.cHostCpus > pUVM->stam.s.cRegisteredHostCpus))
3444 {
3445 if (RT_UNLIKELY(pUVM->stam.s.GVMMStats.cHostCpus > pUVM->stam.s.cRegisteredHostCpus))
3446 {
3447 STAM_UNLOCK_RD(pUVM);
3448 STAM_LOCK_WR(pUVM);
3449 uint32_t cCpus = pUVM->stam.s.GVMMStats.cHostCpus;
3450 for (uint32_t iCpu = pUVM->stam.s.cRegisteredHostCpus; iCpu < cCpus; iCpu++)
3451 {
3452 char szName[120];
3453 size_t cchBase = RTStrPrintf(szName, sizeof(szName), "/GVMM/HostCpus/%u", iCpu);
3454 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].idCpu, NULL, NULL,
3455 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_NONE,
3456 "Host CPU ID", STAM_REFRESH_GRP_GVMM);
3457 strcpy(&szName[cchBase], "/idxCpuSet");
3458 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].idxCpuSet, NULL, NULL,
3459 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_NONE,
3460 "CPU Set index", STAM_REFRESH_GRP_GVMM);
3461 strcpy(&szName[cchBase], "/DesiredHz");
3462 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].uDesiredHz, NULL, NULL,
3463 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_HZ,
3464 "The desired frequency", STAM_REFRESH_GRP_GVMM);
3465 strcpy(&szName[cchBase], "/CurTimerHz");
3466 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].uTimerHz, NULL, NULL,
3467 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_HZ,
3468 "The current timer frequency", STAM_REFRESH_GRP_GVMM);
3469 strcpy(&szName[cchBase], "/PPTChanges");
3470 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].cChanges, NULL, NULL,
3471 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_OCCURENCES,
3472 "RTTimerChangeInterval calls", STAM_REFRESH_GRP_GVMM);
3473 strcpy(&szName[cchBase], "/PPTStarts");
3474 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].cStarts, NULL, NULL,
3475 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_OCCURENCES,
3476 "RTTimerStart calls", STAM_REFRESH_GRP_GVMM);
3477 }
3478 pUVM->stam.s.cRegisteredHostCpus = cCpus;
3479 STAM_UNLOCK_WR(pUVM);
3480 STAM_LOCK_RD(pUVM);
3481 }
3482 }
3483 }
3484 break;
3485 }
3486
3487# ifndef VBOX_WITH_MINIMAL_R0
3488 /*
3489 * GMM
3490 */
3491 case STAM_REFRESH_GRP_GMM:
3492 {
3493 GMMQUERYSTATISTICSSREQ Req;
3494 Req.Hdr.cbReq = sizeof(Req);
3495 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
3496 Req.pSession = pVM->pSession;
3497 int rc = SUPR3CallVMMR0Ex(VMCC_GET_VMR0_FOR_CALL(pVM), NIL_VMCPUID, VMMR0_DO_GMM_QUERY_STATISTICS, 0, &Req.Hdr);
3498 if (RT_SUCCESS(rc))
3499 pUVM->stam.s.GMMStats = Req.Stats;
3500 break;
3501 }
3502
3503 /*
3504 * NEM.
3505 */
3506 case STAM_REFRESH_GRP_NEM:
3507 SUPR3CallVMMR0(VMCC_GET_VMR0_FOR_CALL(pVM), NIL_VMCPUID, VMMR0_DO_NEM_UPDATE_STATISTICS, NULL);
3508 break;
3509# endif
3510#endif /* VBOX_WITH_R0_MODULES */
3511
3512 default:
3513 AssertMsgFailed(("iRefreshGroup=%d\n", iRefreshGroup));
3514 }
3515 }
3516}
3517
3518
3519/**
3520 * Refreshes the statistics behind the given entry, if necessary.
3521 *
3522 * This helps implement fetching global ring-0 stats into ring-3 accessible
3523 * storage. GVMM, GMM and NEM makes use of this.
3524 *
3525 * @param pUVM The user mode VM handle.
3526 * @param pCur The statistics descriptor which group to check
3527 * and maybe update.
3528 * @param pbmRefreshedGroups Bitmap tracking what has already been updated.
3529 */
3530DECLINLINE(void) stamR3Refresh(PUVM pUVM, PSTAMDESC pCur, uint64_t *pbmRefreshedGroups)
3531{
3532 uint8_t const iRefreshGroup = pCur->iRefreshGroup;
3533 if (RT_LIKELY(iRefreshGroup == STAM_REFRESH_GRP_NONE))
3534 { /* likely */ }
3535 else if (!(*pbmRefreshedGroups & RT_BIT_64(iRefreshGroup)))
3536 stamR3RefreshGroup(pUVM, iRefreshGroup, pbmRefreshedGroups);
3537}
3538
3539
3540/**
3541 * Match a name against an array of patterns.
3542 *
3543 * @returns true if it matches, false if it doesn't match.
3544 * @param papszExpressions The array of pattern expressions.
3545 * @param cExpressions The number of array entries.
3546 * @param piExpression Where to read/store the current skip index. Optional.
3547 * @param pszName The name to match.
3548 */
3549static bool stamR3MultiMatch(const char * const *papszExpressions, unsigned cExpressions,
3550 unsigned *piExpression, const char *pszName)
3551{
3552 for (unsigned i = piExpression ? *piExpression : 0; i < cExpressions; i++)
3553 {
3554 const char *pszPat = papszExpressions[i];
3555 if (RTStrSimplePatternMatch(pszPat, pszName))
3556 {
3557 /* later:
3558 if (piExpression && i > *piExpression)
3559 {
3560 Check if we can skip some expressions.
3561 Requires the expressions to be sorted.
3562 }*/
3563 return true;
3564 }
3565 }
3566 return false;
3567}
3568
3569
3570/**
3571 * Splits a multi pattern into single ones.
3572 *
3573 * @returns Pointer to an array of single patterns. Free it with RTMemTmpFree.
3574 * @param pszPat The pattern to split.
3575 * @param pcExpressions The number of array elements.
3576 * @param ppszCopy The pattern copy to free using RTStrFree.
3577 */
3578static char **stamR3SplitPattern(const char *pszPat, unsigned *pcExpressions, char **ppszCopy)
3579{
3580 Assert(pszPat && *pszPat);
3581
3582 char *pszCopy = RTStrDup(pszPat);
3583 if (!pszCopy)
3584 return NULL;
3585
3586 /* count them & allocate array. */
3587 char *psz = pszCopy;
3588 unsigned cExpressions = 1;
3589 while ((psz = strchr(psz, '|')) != NULL)
3590 cExpressions++, psz++;
3591
3592 char **papszExpressions = (char **)RTMemTmpAllocZ((cExpressions + 1) * sizeof(char *));
3593 if (!papszExpressions)
3594 {
3595 RTStrFree(pszCopy);
3596 return NULL;
3597 }
3598
3599 /* split */
3600 psz = pszCopy;
3601 for (unsigned i = 0;;)
3602 {
3603 papszExpressions[i] = psz;
3604 if (++i >= cExpressions)
3605 break;
3606 psz = strchr(psz, '|');
3607 *psz++ = '\0';
3608 }
3609
3610 /* sort the array, putting '*' last. */
3611 /** @todo sort it... */
3612
3613 *pcExpressions = cExpressions;
3614 *ppszCopy = pszCopy;
3615 return papszExpressions;
3616}
3617
3618
3619/**
3620 * Enumerates the nodes selected by a pattern or all nodes if no pattern
3621 * is specified.
3622 *
3623 * The call may lock STAM for writing before calling this function, however do
3624 * not lock it for reading as this function may need to write lock STAM.
3625 *
3626 * @returns The rc from the callback.
3627 * @param pUVM Pointer to the user mode VM structure.
3628 * @param pszPat Pattern.
3629 * @param fUpdateRing0 Update the stats residing in ring-0.
3630 * @param pfnCallback Callback function which shall be called for matching nodes.
3631 * If it returns anything but VINF_SUCCESS the enumeration is
3632 * terminated and the status code returned to the caller.
3633 * @param pvArg User parameter for the callback.
3634 */
3635static int stamR3EnumU(PUVM pUVM, const char *pszPat, bool fUpdateRing0,
3636 int (*pfnCallback)(PSTAMDESC pDesc, void *pvArg), void *pvArg)
3637{
3638 size_t const cchPat = pszPat ? strlen(pszPat) : 0;
3639 int rc = VINF_SUCCESS;
3640 uint64_t bmRefreshedGroups = 0;
3641 PSTAMDESC pCur;
3642
3643 /*
3644 * All.
3645 */
3646 if ( cchPat < 1
3647 || ( cchPat == 1
3648 && *pszPat == '*'))
3649 {
3650 STAM_LOCK_RD(pUVM);
3651 RTListForEach(&pUVM->stam.s.List, pCur, STAMDESC, ListEntry)
3652 {
3653 if (fUpdateRing0)
3654 stamR3Refresh(pUVM, pCur, &bmRefreshedGroups);
3655 rc = pfnCallback(pCur, pvArg);
3656 if (rc)
3657 break;
3658 }
3659 STAM_UNLOCK_RD(pUVM);
3660 }
3661
3662 /*
3663 * Single expression pattern.
3664 */
3665 else if (memchr(pszPat, '|', cchPat) == NULL)
3666 {
3667 const char *pszAsterisk = (const char *)memchr(pszPat, '*', cchPat);
3668 const char *pszQuestion = (const char *)memchr(pszPat, '?', cchPat);
3669
3670 STAM_LOCK_RD(pUVM);
3671 if (!pszAsterisk && !pszQuestion)
3672 {
3673 pCur = stamR3LookupFindDesc(pUVM->stam.s.pRoot, pszPat);
3674 if (pCur)
3675 {
3676 if (fUpdateRing0)
3677 stamR3Refresh(pUVM, pCur, &bmRefreshedGroups);
3678 rc = pfnCallback(pCur, pvArg);
3679 }
3680 }
3681 /* Is this a prefix expression where we can use the lookup tree to
3682 efficiently figure out the exact range? */
3683 else if ( pszAsterisk == &pszPat[cchPat - 1]
3684 && pszPat[0] == '/'
3685 && !pszQuestion)
3686 {
3687 PSTAMDESC pLast;
3688 pCur = stamR3LookupFindByPrefixRange(pUVM->stam.s.pRoot, pszPat, (uint32_t)(cchPat - 1), &pLast);
3689 if (pCur)
3690 {
3691 for (;;)
3692 {
3693 Assert(strncmp(pCur->pszName, pszPat, cchPat - 1) == 0);
3694 if (fUpdateRing0)
3695 stamR3Refresh(pUVM, pCur, &bmRefreshedGroups);
3696 rc = pfnCallback(pCur, pvArg);
3697 if (rc)
3698 break;
3699 if (pCur == pLast)
3700 break;
3701 pCur = RTListNodeGetNext(&pCur->ListEntry, STAMDESC, ListEntry);
3702 }
3703 Assert(pLast);
3704 }
3705 else
3706 Assert(!pLast);
3707 }
3708 else
3709 {
3710 /* It's a more complicated pattern. Find the approximate range
3711 and scan it for matches. */
3712 PSTAMDESC pLast;
3713 pCur = stamR3LookupFindPatternDescRange(pUVM->stam.s.pRoot, &pUVM->stam.s.List, pszPat, &pLast);
3714 if (pCur)
3715 {
3716 for (;;)
3717 {
3718 if (RTStrSimplePatternMatch(pszPat, pCur->pszName))
3719 {
3720 if (fUpdateRing0)
3721 stamR3Refresh(pUVM, pCur, &bmRefreshedGroups);
3722 rc = pfnCallback(pCur, pvArg);
3723 if (rc)
3724 break;
3725 }
3726 if (pCur == pLast)
3727 break;
3728 pCur = RTListNodeGetNext(&pCur->ListEntry, STAMDESC, ListEntry);
3729 }
3730 Assert(pLast);
3731 }
3732 else
3733 Assert(!pLast);
3734 }
3735 STAM_UNLOCK_RD(pUVM);
3736 }
3737
3738 /*
3739 * Multi expression pattern.
3740 */
3741 else
3742 {
3743 /*
3744 * Split up the pattern first.
3745 */
3746 char *pszCopy;
3747 unsigned cExpressions;
3748 char **papszExpressions = stamR3SplitPattern(pszPat, &cExpressions, &pszCopy);
3749 if (!papszExpressions)
3750 return VERR_NO_MEMORY;
3751
3752 /*
3753 * Perform the enumeration.
3754 */
3755 STAM_LOCK_RD(pUVM);
3756 unsigned iExpression = 0;
3757 RTListForEach(&pUVM->stam.s.List, pCur, STAMDESC, ListEntry)
3758 {
3759 if (stamR3MultiMatch(papszExpressions, cExpressions, &iExpression, pCur->pszName))
3760 {
3761 if (fUpdateRing0)
3762 stamR3Refresh(pUVM, pCur, &bmRefreshedGroups);
3763 rc = pfnCallback(pCur, pvArg);
3764 if (rc)
3765 break;
3766 }
3767 }
3768 STAM_UNLOCK_RD(pUVM);
3769
3770 RTMemTmpFree(papszExpressions);
3771 RTStrFree(pszCopy);
3772 }
3773
3774 return rc;
3775}
3776
3777
3778#ifdef VBOX_WITH_R0_MODULES
3779/**
3780 * Registers the ring-0 statistics.
3781 *
3782 * @param pUVM Pointer to the user mode VM structure.
3783 */
3784static void stamR3Ring0StatsRegisterU(PUVM pUVM)
3785{
3786 /* GVMM */
3787 for (unsigned i = 0; i < RT_ELEMENTS(g_aGVMMStats); i++)
3788 stamR3RegisterU(pUVM, (uint8_t *)&pUVM->stam.s.GVMMStats + g_aGVMMStats[i].offVar, NULL, NULL,
3789 g_aGVMMStats[i].enmType, STAMVISIBILITY_ALWAYS, g_aGVMMStats[i].pszName,
3790 g_aGVMMStats[i].enmUnit, g_aGVMMStats[i].pszDesc, STAM_REFRESH_GRP_GVMM);
3791
3792 for (unsigned i = 0; i < pUVM->cCpus; i++)
3793 {
3794 char szName[120];
3795 size_t cchBase = RTStrPrintf(szName, sizeof(szName), pUVM->cCpus < 10 ? "/GVMM/VCpus/%u/" : "/GVMM/VCpus/%02u/", i);
3796
3797 strcpy(&szName[cchBase], "cWakeUpTimerHits");
3798 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aVCpus[i].cWakeUpTimerHits, NULL, NULL,
3799 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_OCCURENCES, "", STAM_REFRESH_GRP_GVMM);
3800
3801 strcpy(&szName[cchBase], "cWakeUpTimerMisses");
3802 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aVCpus[i].cWakeUpTimerMisses, NULL, NULL,
3803 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_OCCURENCES, "", STAM_REFRESH_GRP_GVMM);
3804
3805 strcpy(&szName[cchBase], "cWakeUpTimerCanceled");
3806 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aVCpus[i].cWakeUpTimerCanceled, NULL, NULL,
3807 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_OCCURENCES, "", STAM_REFRESH_GRP_GVMM);
3808
3809 strcpy(&szName[cchBase], "cWakeUpTimerSameCpu");
3810 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aVCpus[i].cWakeUpTimerSameCpu, NULL, NULL,
3811 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_OCCURENCES, "", STAM_REFRESH_GRP_GVMM);
3812
3813 strcpy(&szName[cchBase], "Start");
3814 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aVCpus[i].Start, NULL, NULL,
3815 STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_TICKS_PER_CALL, "", STAM_REFRESH_GRP_GVMM);
3816
3817 strcpy(&szName[cchBase], "Stop");
3818 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aVCpus[i].Stop, NULL, NULL,
3819 STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_TICKS_PER_CALL, "", STAM_REFRESH_GRP_GVMM);
3820 }
3821 pUVM->stam.s.cRegisteredHostCpus = 0;
3822
3823# ifndef VBOX_WITH_MINIMAL_R0
3824 /* GMM */
3825 for (unsigned i = 0; i < RT_ELEMENTS(g_aGMMStats); i++)
3826 stamR3RegisterU(pUVM, (uint8_t *)&pUVM->stam.s.GMMStats + g_aGMMStats[i].offVar, NULL, NULL,
3827 g_aGMMStats[i].enmType, STAMVISIBILITY_ALWAYS, g_aGMMStats[i].pszName,
3828 g_aGMMStats[i].enmUnit, g_aGMMStats[i].pszDesc, STAM_REFRESH_GRP_GMM);
3829# endif
3830}
3831#endif /* VBOX_WITH_R0_MODULES */
3832
3833
3834/**
3835 * Get the unit string.
3836 *
3837 * @returns Pointer to read only unit string.
3838 * @param enmUnit The unit.
3839 */
3840VMMR3DECL(const char *) STAMR3GetUnit(STAMUNIT enmUnit)
3841{
3842 switch (enmUnit)
3843 {
3844 case STAMUNIT_NONE: return "";
3845 case STAMUNIT_CALLS: return "calls";
3846 case STAMUNIT_CALLS_PER_TB: return "calls/tb";
3847 case STAMUNIT_COUNT: return "count";
3848 case STAMUNIT_BYTES: return "bytes";
3849 case STAMUNIT_BYTES_PER_CALL: return "bytes/call";
3850 case STAMUNIT_PAGES: return "pages";
3851 case STAMUNIT_ERRORS: return "errors";
3852 case STAMUNIT_OCCURENCES: return "times";
3853 case STAMUNIT_TICKS: return "ticks";
3854 case STAMUNIT_TICKS_PER_CALL: return "ticks/call";
3855 case STAMUNIT_TICKS_PER_OCCURENCE: return "ticks/time";
3856 case STAMUNIT_GOOD_BAD: return "good:bad";
3857 case STAMUNIT_MEGABYTES: return "megabytes";
3858 case STAMUNIT_KILOBYTES: return "kilobytes";
3859 case STAMUNIT_NS: return "ns";
3860 case STAMUNIT_NS_PER_CALL: return "ns/call";
3861 case STAMUNIT_NS_PER_OCCURENCE: return "ns/time";
3862 case STAMUNIT_PCT: return "%";
3863 case STAMUNIT_PP1K: return "pp1k";
3864 case STAMUNIT_PP10K: return "pp10k";
3865 case STAMUNIT_PPM: return "ppm";
3866 case STAMUNIT_PPB: return "ppb";
3867 case STAMUNIT_HZ: return "Hz";
3868 case STAMUNIT_INSTR: return "instr";
3869 case STAMUNIT_INSTR_PER_TB: return "instr/tb";
3870 case STAMUNIT_BYTES_PER_TB: return "bytes/tb";
3871
3872 default:
3873 AssertMsgFailed(("Unknown unit %d\n", enmUnit));
3874 return "(?unit?)";
3875 }
3876}
3877
3878
3879/**
3880 * For something per something-else unit, get the first something.
3881 *
3882 * @returns Pointer to read only unit string.
3883 * @param enmUnit The unit.
3884 */
3885VMMR3DECL(const char *) STAMR3GetUnit1(STAMUNIT enmUnit)
3886{
3887 switch (enmUnit)
3888 {
3889 case STAMUNIT_NONE: return "";
3890 case STAMUNIT_CALLS: return "calls";
3891 case STAMUNIT_CALLS_PER_TB: return "calls";
3892 case STAMUNIT_COUNT: return "count";
3893 case STAMUNIT_BYTES: return "bytes";
3894 case STAMUNIT_BYTES_PER_CALL: return "bytes";
3895 case STAMUNIT_PAGES: return "pages";
3896 case STAMUNIT_ERRORS: return "errors";
3897 case STAMUNIT_OCCURENCES: return "times";
3898 case STAMUNIT_TICKS: return "ticks";
3899 case STAMUNIT_TICKS_PER_CALL: return "ticks";
3900 case STAMUNIT_TICKS_PER_OCCURENCE: return "ticks";
3901 case STAMUNIT_GOOD_BAD: return "good";
3902 case STAMUNIT_MEGABYTES: return "megabytes";
3903 case STAMUNIT_KILOBYTES: return "kilobytes";
3904 case STAMUNIT_NS: return "ns";
3905 case STAMUNIT_NS_PER_CALL: return "ns";
3906 case STAMUNIT_NS_PER_OCCURENCE: return "ns";
3907 case STAMUNIT_PCT: return "%";
3908 case STAMUNIT_PP1K: return "pp1k";
3909 case STAMUNIT_PP10K: return "pp10k";
3910 case STAMUNIT_PPM: return "ppm";
3911 case STAMUNIT_PPB: return "ppb";
3912 case STAMUNIT_HZ: return "Hz";
3913 case STAMUNIT_INSTR: return "instr";
3914 case STAMUNIT_INSTR_PER_TB: return "instr";
3915 case STAMUNIT_BYTES_PER_TB: return "bytes";
3916
3917 default:
3918 AssertMsgFailed(("Unknown unit %d\n", enmUnit));
3919 return "(?unit?)";
3920 }
3921}
3922
3923
3924/**
3925 * For something per something-else unit, get the something-else.
3926 *
3927 * @returns Pointer to read only unit string.
3928 * @param enmUnit The unit.
3929 */
3930VMMR3DECL(const char *) STAMR3GetUnit2(STAMUNIT enmUnit)
3931{
3932 switch (enmUnit)
3933 {
3934 case STAMUNIT_TICKS_PER_CALL: return "calls";
3935 case STAMUNIT_NS_PER_CALL: return "calls";
3936 case STAMUNIT_BYTES_PER_CALL: return "calls";
3937 case STAMUNIT_TICKS_PER_OCCURENCE: return "times";
3938 case STAMUNIT_NS_PER_OCCURENCE: return "times";
3939 case STAMUNIT_NONE: return "times";
3940 case STAMUNIT_GOOD_BAD: return "bad";
3941 case STAMUNIT_CALLS_PER_TB: return "tbs";
3942 case STAMUNIT_INSTR_PER_TB: return "tbs";
3943 case STAMUNIT_BYTES_PER_TB: return "tbs";
3944 default:
3945 AssertMsgFailed(("Wrong unit %d\n", enmUnit));
3946 return "times";
3947 }
3948}
3949
3950#ifdef VBOX_WITH_DEBUGGER
3951
3952/**
3953 * @callback_method_impl{FNDBGCCMD, The '.stats' command.}
3954 */
3955static DECLCALLBACK(int) stamR3CmdStats(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
3956{
3957 /*
3958 * Validate input.
3959 */
3960 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
3961 if (RTListIsEmpty(&pUVM->stam.s.List))
3962 return DBGCCmdHlpFail(pCmdHlp, pCmd, "No statistics present");
3963
3964 /*
3965 * Do the printing.
3966 */
3967 STAMR3PRINTONEARGS Args;
3968 Args.pUVM = pUVM;
3969 Args.pvArg = pCmdHlp;
3970 Args.pfnPrintf = stamR3EnumDbgfPrintf;
3971
3972 return stamR3EnumU(pUVM, cArgs ? paArgs[0].u.pszString : NULL, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
3973}
3974
3975
3976/**
3977 * Display one sample in the debugger.
3978 *
3979 * @param pArgs Pointer to the print one argument structure.
3980 * @param pszFormat Format string.
3981 * @param ... Format arguments.
3982 */
3983static DECLCALLBACK(void) stamR3EnumDbgfPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...)
3984{
3985 PDBGCCMDHLP pCmdHlp = (PDBGCCMDHLP)pArgs->pvArg;
3986
3987 va_list va;
3988 va_start(va, pszFormat);
3989 pCmdHlp->pfnPrintfV(pCmdHlp, NULL, pszFormat, va);
3990 va_end(va);
3991 NOREF(pArgs);
3992}
3993
3994
3995/**
3996 * @callback_method_impl{FNDBGCCMD, The '.statsreset' command.}
3997 */
3998static DECLCALLBACK(int) stamR3CmdStatsReset(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
3999{
4000 /*
4001 * Validate input.
4002 */
4003 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
4004 if (RTListIsEmpty(&pUVM->stam.s.List))
4005 return DBGCCmdHlpFail(pCmdHlp, pCmd, "No statistics present");
4006
4007 /*
4008 * Execute reset.
4009 */
4010 int rc = STAMR3Reset(pUVM, cArgs ? paArgs[0].u.pszString : NULL);
4011 if (RT_SUCCESS(rc))
4012 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "STAMR3ResetU");
4013 return DBGCCmdHlpPrintf(pCmdHlp, "Statistics have been reset.\n");
4014}
4015
4016#endif /* VBOX_WITH_DEBUGGER */
4017
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