VirtualBox

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

Last change on this file since 107044 was 106308, checked in by vboxsync, 6 weeks ago

VMM/STAM: More space when printing. bugref:10720

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