VirtualBox

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

Last change on this file since 102828 was 102828, checked in by vboxsync, 12 months ago

VMM/STAM: Added two new aggregate sample types. bugref:10371

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 145.8 KB
Line 
1/* $Id: STAM.cpp 102828 2024-01-11 01:47:40Z vboxsync $ */
2/** @file
3 * STAM - The Statistics Manager.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/** @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 = uValue;
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 pszName 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 *
1200 * The @a pszName is implicitly included in the sum.
1201 *
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 const char *pszSummandPattern, const char *pszDesc, const char *pszName, va_list va)
1208{
1209 char szFormattedName[STAM_MAX_NAME_LEN + 8];
1210 size_t cch = RTStrPrintfV(szFormattedName, sizeof(szFormattedName), pszName, va);
1211 AssertReturn(cch <= STAM_MAX_NAME_LEN, VERR_OUT_OF_RANGE);
1212 switch (enmUnit)
1213 {
1214 case STAMUNIT_PCT:
1215 case STAMUNIT_PP1K:
1216 case STAMUNIT_PP10K:
1217 case STAMUNIT_PPM:
1218 case STAMUNIT_PPB:
1219 break;
1220 default:
1221 AssertFailedReturn(VERR_INVALID_PARAMETER);
1222 }
1223
1224 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
1225
1226 /*
1227 * We have to resolve the value and summands before we continue with the
1228 * actual registration. We reuse the STAMSUMSAMPLE structure here.
1229 */
1230 uint8_t const cMaxSummands = 32;
1231 PSTAMSUMSAMPLE const pSum = (PSTAMSUMSAMPLE)RTMemAllocZ(RT_UOFFSETOF_DYN(STAMSUMSAMPLE, apSummands[cMaxSummands]));
1232 AssertReturn(pSum, VERR_NO_MEMORY);
1233 pSum->cSummandsAlloc = cMaxSummands;
1234 pSum->enmType = STAMTYPE_COUNTER;
1235 pSum->enmUnit = enmUnit;
1236
1237 STAM_LOCK_WR(pUVM);
1238
1239 /* The first summand entry is the value. */
1240 int rc = stamR3EnumU(pUVM, pszValue, false /*fUpdateRing0*/, stamR3RegisterPctOfSumEnumCallbackForValue, pSum);
1241 if (RT_SUCCESS(rc))
1242 {
1243 if (pSum->cSummands == 1)
1244 {
1245 /* The additional ones are part of the sum we should divide the value by. */
1246 rc = stamR3EnumU(pUVM, pszSummandPattern, false /*fUpdateRing0*/, stamR3RegisterPctOfSumEnumCallbackForSummands, pSum);
1247 if (RT_SUCCESS(rc))
1248 {
1249 /* Now, register it. */
1250 if (pSum->cSummands > 1)
1251 rc = stamR3RegisterU(pUVM, pSum, NULL, NULL, STAMTYPE_INTERNAL_PCT_OF_SUM, enmVisibility, szFormattedName,
1252 (STAMUNIT)pSum->enmUnit, pszDesc, STAM_REFRESH_GRP_NONE);
1253 else
1254 AssertFailedStmt(rc = VERR_NO_DATA);
1255 }
1256 }
1257 else
1258 AssertFailedStmt(rc = VERR_NO_DATA);
1259 }
1260
1261 STAM_UNLOCK_WR(pUVM);
1262
1263 if (RT_FAILURE(rc))
1264 RTMemFree(pSum);
1265 return rc;
1266}
1267
1268
1269/**
1270 * Registers a percentage of a sum that is to be calculated from @a pszValue and
1271 * the @a pszSummandPattern hits.
1272 *
1273 * @returns VBox status code.
1274 * @param pUVM Pointer to the user mode VM structure.
1275 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
1276 * @param enmUnit The sample unit: STAMUNIT_PCT, STAMUNIT_PP1K,
1277 * STAMUNIT_PP10K, STAMUNIT_PPM or STAMUNIT_PPB.
1278 * @param pszName Name of the sample which value should be put
1279 * against the sum of all.
1280 * @param pszSummandPattern A simple pattern for the elements that should be
1281 * summed up and used to divide @a pszName by when
1282 * calculating the percentage. These must have
1283 * compatible types.
1284 *
1285 * The @a pszName is implicitly included in the sum.
1286 *
1287 * @param pszDesc Sample description.
1288 * @param pszName The sample name format string.
1289 * @param ... Arguments to the format string.
1290 */
1291VMMR3DECL(int) STAMR3RegisterPctOfSum(PUVM pUVM, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit, const char *pszValue,
1292 const char *pszSummandPattern, const char *pszDesc, const char *pszName, ...)
1293{
1294 va_list va;
1295 va_start(va, pszName);
1296 int rc = STAMR3RegisterPctOfSumV(pUVM, enmVisibility, enmUnit, pszValue, pszSummandPattern, pszDesc, pszName, va);
1297 va_end(va);
1298 return rc;
1299}
1300
1301
1302#ifdef VBOX_STRICT
1303/**
1304 * Divide the strings into sub-strings using '/' as delimiter
1305 * and then compare them in strcmp fashion.
1306 *
1307 * @returns Difference.
1308 * @retval 0 if equal.
1309 * @retval < 0 if psz1 is less than psz2.
1310 * @retval > 0 if psz1 greater than psz2.
1311 *
1312 * @param psz1 The first string.
1313 * @param psz2 The second string.
1314 */
1315static int stamR3SlashCompare(const char *psz1, const char *psz2)
1316{
1317 for (;;)
1318 {
1319 unsigned int ch1 = *psz1++;
1320 unsigned int ch2 = *psz2++;
1321 if (ch1 != ch2)
1322 {
1323 /* slash is end-of-sub-string, so it trumps everything but '\0'. */
1324 if (ch1 == '/')
1325 return ch2 ? -1 : 1;
1326 if (ch2 == '/')
1327 return ch1 ? 1 : -1;
1328 return ch1 - ch2;
1329 }
1330
1331 /* done? */
1332 if (ch1 == '\0')
1333 return 0;
1334 }
1335}
1336#endif /* VBOX_STRICT */
1337
1338
1339/**
1340 * Compares a lookup node with a name.
1341 *
1342 * @returns like strcmp and memcmp.
1343 * @param pNode The lookup node.
1344 * @param pchName The name, not necessarily terminated.
1345 * @param cchName The length of the name.
1346 */
1347DECL_FORCE_INLINE(int) stamR3LookupCmp(PSTAMLOOKUP pNode, const char *pchName, uint32_t cchName)
1348{
1349 uint32_t cchComp = RT_MIN(pNode->cch, cchName);
1350 int iDiff = memcmp(pNode->szName, pchName, cchComp);
1351 if (!iDiff && pNode->cch != cchName)
1352 iDiff = pNode->cch > cchName ? 2 : -2;
1353 return iDiff;
1354}
1355
1356
1357/**
1358 * Creates a new lookup child node.
1359 *
1360 * @returns Pointer to the newly created lookup node.
1361 * @param pParent The parent node.
1362 * @param pchName The name (not necessarily terminated).
1363 * @param cchName The length of the name.
1364 * @param offName The offset of the node in a path.
1365 * @param iChild Child index of a node that's before the one
1366 * we're inserting (returned by
1367 * stamR3LookupFindChild).
1368 */
1369static PSTAMLOOKUP stamR3LookupNewChild(PSTAMLOOKUP pParent, const char *pchName, uint32_t cchName, uint32_t offName,
1370 uint32_t iChild)
1371{
1372 Assert(cchName <= UINT8_MAX);
1373 Assert(offName <= UINT8_MAX);
1374 Assert(iChild < UINT16_MAX);
1375
1376 /*
1377 * Allocate a new entry.
1378 */
1379 PSTAMLOOKUP pNew = (PSTAMLOOKUP)RTMemAlloc(RT_UOFFSETOF_DYN(STAMLOOKUP, szName[cchName + 1]));
1380 if (!pNew)
1381 return NULL;
1382 pNew->pParent = pParent;
1383 pNew->papChildren = NULL;
1384 pNew->pDesc = NULL;
1385 pNew->cDescsInTree = 0;
1386 pNew->cChildren = 0;
1387 pNew->cch = (uint16_t)cchName;
1388 pNew->off = (uint16_t)offName;
1389 memcpy(pNew->szName, pchName, cchName);
1390 pNew->szName[cchName] = '\0';
1391
1392 /*
1393 * Reallocate the array?
1394 */
1395 if (RT_IS_POWER_OF_TWO(pParent->cChildren))
1396 {
1397 uint32_t cNew = pParent->cChildren ? (uint32_t)pParent->cChildren * 2 : 8;
1398 AssertReturnStmt(cNew <= 0x8000, RTMemFree(pNew), NULL);
1399 void *pvNew = RTMemRealloc(pParent->papChildren, cNew * sizeof(pParent->papChildren[0]));
1400 if (!pvNew)
1401 {
1402 RTMemFree(pNew);
1403 return NULL;
1404 }
1405 pParent->papChildren = (PSTAMLOOKUP *)pvNew;
1406 }
1407
1408 /*
1409 * Find the exact insertion point using iChild as a very good clue from
1410 * the find function.
1411 */
1412 if (!pParent->cChildren)
1413 iChild = 0;
1414 else
1415 {
1416 if (iChild >= pParent->cChildren)
1417 iChild = pParent->cChildren - 1;
1418 while ( iChild < pParent->cChildren
1419 && stamR3LookupCmp(pParent->papChildren[iChild], pchName, cchName) < 0)
1420 iChild++;
1421 }
1422
1423 /*
1424 * Insert it.
1425 */
1426 if (iChild < pParent->cChildren)
1427 {
1428 /* Do shift. */
1429 uint32_t i = pParent->cChildren;
1430 while (i > iChild)
1431 {
1432 PSTAMLOOKUP pNode = pParent->papChildren[i - 1];
1433 pParent->papChildren[i] = pNode;
1434 pNode->iParent = i;
1435 i--;
1436 }
1437 }
1438
1439 pNew->iParent = iChild;
1440 pParent->papChildren[iChild] = pNew;
1441 pParent->cChildren++;
1442
1443 return pNew;
1444}
1445
1446
1447/**
1448 * Looks up a child.
1449 *
1450 * @returns Pointer to child node if found, NULL if not.
1451 * @param pParent The parent node.
1452 * @param pchName The name (not necessarily terminated).
1453 * @param cchName The length of the name.
1454 * @param piChild Where to store a child index suitable for
1455 * passing to stamR3LookupNewChild when NULL is
1456 * returned.
1457 */
1458static PSTAMLOOKUP stamR3LookupFindChild(PSTAMLOOKUP pParent, const char *pchName, uint32_t cchName, uint32_t *piChild)
1459{
1460 uint32_t iChild = pParent->cChildren;
1461 if (iChild > 4)
1462 {
1463 uint32_t iFirst = 0;
1464 uint32_t iEnd = iChild;
1465 iChild /= 2;
1466 for (;;)
1467 {
1468 int iDiff = stamR3LookupCmp(pParent->papChildren[iChild], pchName, cchName);
1469 if (!iDiff)
1470 {
1471 if (piChild)
1472 *piChild = iChild;
1473 return pParent->papChildren[iChild];
1474 }
1475
1476 /* Split. */
1477 if (iDiff < 0)
1478 {
1479 iFirst = iChild + 1;
1480 if (iFirst >= iEnd)
1481 {
1482 if (piChild)
1483 *piChild = iChild;
1484 break;
1485 }
1486 }
1487 else
1488 {
1489 if (iChild == iFirst)
1490 {
1491 if (piChild)
1492 *piChild = iChild ? iChild - 1 : 0;
1493 break;
1494 }
1495 iEnd = iChild;
1496 }
1497
1498 /* Calc next child. */
1499 iChild = (iEnd - iFirst) / 2 + iFirst;
1500 }
1501 return NULL;
1502 }
1503
1504 /*
1505 * Linear search.
1506 */
1507 while (iChild-- > 0)
1508 {
1509 int iDiff = stamR3LookupCmp(pParent->papChildren[iChild], pchName, cchName);
1510 if (iDiff <= 0)
1511 {
1512 if (piChild)
1513 *piChild = iChild;
1514 return !iDiff ? pParent->papChildren[iChild] : NULL;
1515 }
1516 }
1517 if (piChild)
1518 *piChild = 0;
1519 return NULL;
1520}
1521
1522
1523/**
1524 * Find the next sample descriptor node.
1525 *
1526 * This is for use with insertion in the big list and pattern range lookups.
1527 *
1528 * @returns Pointer to the next sample descriptor. NULL if not found (i.e.
1529 * we're at the end of the list).
1530 * @param pLookup The current node.
1531 */
1532static PSTAMDESC stamR3LookupFindNextWithDesc(PSTAMLOOKUP pLookup)
1533{
1534 Assert(!pLookup->pDesc);
1535 PSTAMLOOKUP pCur = pLookup;
1536 uint32_t iCur = 0;
1537 for (;;)
1538 {
1539 /*
1540 * Check all children.
1541 */
1542 uint32_t cChildren = pCur->cChildren;
1543 if (iCur < cChildren)
1544 {
1545 PSTAMLOOKUP *papChildren = pCur->papChildren;
1546 do
1547 {
1548 PSTAMLOOKUP pChild = papChildren[iCur];
1549 if (pChild->pDesc)
1550 return pChild->pDesc;
1551
1552 if (pChild->cChildren > 0)
1553 {
1554 /* One level down. */
1555 iCur = 0;
1556 pCur = pChild;
1557 break;
1558 }
1559 } while (++iCur < cChildren);
1560 }
1561 else
1562 {
1563 /*
1564 * One level up, resuming after the current.
1565 */
1566 iCur = pCur->iParent + 1;
1567 pCur = pCur->pParent;
1568 if (!pCur)
1569 return NULL;
1570 }
1571 }
1572}
1573
1574
1575/**
1576 * Look up a sample descriptor by name.
1577 *
1578 * @returns Pointer to a sample descriptor.
1579 * @param pRoot The root node.
1580 * @param pszName The name to lookup.
1581 */
1582static PSTAMDESC stamR3LookupFindDesc(PSTAMLOOKUP pRoot, const char *pszName)
1583{
1584 Assert(!pRoot->pParent);
1585 while (*pszName++ == '/')
1586 {
1587 const char *pszEnd = strchr(pszName, '/');
1588 uint32_t cch = pszEnd ? pszEnd - pszName : (uint32_t)strlen(pszName);
1589 PSTAMLOOKUP pChild = stamR3LookupFindChild(pRoot, pszName, cch, NULL);
1590 if (!pChild)
1591 break;
1592 if (!pszEnd)
1593 return pChild->pDesc;
1594 pszName = pszEnd;
1595 pRoot = pChild;
1596 }
1597
1598 return NULL;
1599}
1600
1601
1602/**
1603 * Finds the first sample descriptor for a given lookup range.
1604 *
1605 * This is for pattern range lookups.
1606 *
1607 * @returns Pointer to the first descriptor.
1608 * @param pFirst The first node in the range.
1609 * @param pLast The last node in the range.
1610 */
1611static PSTAMDESC stamR3LookupFindFirstDescForRange(PSTAMLOOKUP pFirst, PSTAMLOOKUP pLast)
1612{
1613 if (pFirst->pDesc)
1614 return pFirst->pDesc;
1615
1616 PSTAMLOOKUP pCur = pFirst;
1617 uint32_t iCur = 0;
1618 for (;;)
1619 {
1620 uint32_t cChildren = pCur->cChildren;
1621 if (iCur < pCur->cChildren)
1622 {
1623 /*
1624 * Check all children.
1625 */
1626 PSTAMLOOKUP * const papChildren = pCur->papChildren;
1627 do
1628 {
1629 PSTAMLOOKUP pChild = papChildren[iCur];
1630 if (pChild->pDesc)
1631 return pChild->pDesc;
1632 if (pChild->cChildren > 0)
1633 {
1634 /* One level down. */
1635 iCur = 0;
1636 pCur = pChild;
1637 break;
1638 }
1639 if (pChild == pLast)
1640 return NULL;
1641 } while (++iCur < cChildren);
1642 }
1643 else
1644 {
1645 /*
1646 * One level up, checking current and its 'older' sibilings.
1647 */
1648 if (pCur == pLast)
1649 return NULL;
1650 iCur = pCur->iParent + 1;
1651 pCur = pCur->pParent;
1652 if (!pCur)
1653 break;
1654 }
1655 }
1656
1657 return NULL;
1658}
1659
1660
1661/**
1662 * Finds the last sample descriptor for a given lookup range.
1663 *
1664 * This is for pattern range lookups.
1665 *
1666 * @returns Pointer to the first descriptor.
1667 * @param pFirst The first node in the range.
1668 * @param pLast The last node in the range.
1669 */
1670static PSTAMDESC stamR3LookupFindLastDescForRange(PSTAMLOOKUP pFirst, PSTAMLOOKUP pLast)
1671{
1672 PSTAMLOOKUP pCur = pLast;
1673 uint32_t iCur = pCur->cChildren - 1;
1674 for (;;)
1675 {
1676 if (iCur < pCur->cChildren)
1677 {
1678 /*
1679 * Check children backwards, depth first.
1680 */
1681 PSTAMLOOKUP * const papChildren = pCur->papChildren;
1682 do
1683 {
1684 PSTAMLOOKUP pChild = papChildren[iCur];
1685 if (pChild->cChildren > 0)
1686 {
1687 /* One level down. */
1688 iCur = pChild->cChildren - 1;
1689 pCur = pChild;
1690 break;
1691 }
1692
1693 if (pChild->pDesc)
1694 return pChild->pDesc;
1695 if (pChild == pFirst)
1696 return NULL;
1697 } while (iCur-- > 0); /* (underflow handled above) */
1698 }
1699 else
1700 {
1701 /*
1702 * One level up, checking current and its 'older' sibilings.
1703 */
1704 if (pCur->pDesc)
1705 return pCur->pDesc;
1706 if (pCur == pFirst)
1707 return NULL;
1708 iCur = pCur->iParent - 1; /* (underflow handled above) */
1709 pCur = pCur->pParent;
1710 if (!pCur)
1711 break;
1712 }
1713 }
1714
1715 return NULL;
1716}
1717
1718
1719/**
1720 * Look up the first and last descriptors for a (single) pattern expression.
1721 *
1722 * This is used to optimize pattern enumerations and doesn't have to return 100%
1723 * accurate results if that costs too much.
1724 *
1725 * @returns Pointer to the first descriptor in the range.
1726 * @param pRoot The root node.
1727 * @param pList The descriptor list anchor.
1728 * @param pszPat The name patter to lookup.
1729 * @param ppLastDesc Where to store the address of the last
1730 * descriptor (approximate).
1731 */
1732static PSTAMDESC stamR3LookupFindPatternDescRange(PSTAMLOOKUP pRoot, PRTLISTANCHOR pList, const char *pszPat,
1733 PSTAMDESC *ppLastDesc)
1734{
1735 Assert(!pRoot->pParent);
1736
1737 /*
1738 * If there is an early enough wildcard, the whole list needs to be searched.
1739 */
1740 if ( pszPat[0] == '*' || pszPat[0] == '?'
1741 || pszPat[1] == '*' || pszPat[1] == '?')
1742 {
1743 *ppLastDesc = RTListGetLast(pList, STAMDESC, ListEntry);
1744 return RTListGetFirst(pList, STAMDESC, ListEntry);
1745 }
1746
1747 /*
1748 * All statistics starts with a slash.
1749 */
1750 while ( *pszPat++ == '/'
1751 && pRoot->cDescsInTree > 0
1752 && pRoot->cChildren > 0)
1753 {
1754 const char *pszEnd = strchr(pszPat, '/');
1755 uint32_t cch = pszEnd ? pszEnd - pszPat : (uint32_t)strlen(pszPat);
1756 if (!cch)
1757 break;
1758
1759 const char *pszPat1 = (const char *)memchr(pszPat, '*', cch);
1760 const char *pszPat2 = (const char *)memchr(pszPat, '?', cch);
1761 if (pszPat1 || pszPat2)
1762 {
1763 /* We've narrowed it down to a sub-tree now. */
1764 PSTAMLOOKUP pFirst = pRoot->papChildren[0];
1765 PSTAMLOOKUP pLast = pRoot->papChildren[pRoot->cChildren - 1];
1766 /** @todo narrow the range further if both pszPat1/2 != pszPat. */
1767
1768 *ppLastDesc = stamR3LookupFindLastDescForRange(pFirst, pLast);
1769 return stamR3LookupFindFirstDescForRange(pFirst, pLast);
1770 }
1771
1772 PSTAMLOOKUP pChild = stamR3LookupFindChild(pRoot, pszPat, cch, NULL);
1773 if (!pChild)
1774 break;
1775
1776 /* Advance */
1777 if (!pszEnd)
1778 return *ppLastDesc = pChild->pDesc;
1779 pszPat = pszEnd;
1780 pRoot = pChild;
1781 }
1782
1783 /* No match. */
1784 *ppLastDesc = NULL;
1785 return NULL;
1786}
1787
1788
1789/**
1790 * Look up the first descriptors for starts-with name string.
1791 *
1792 * This is used to optimize deletion.
1793 *
1794 * @returns Pointer to the first descriptor in the range.
1795 * @param pRoot The root node.
1796 * @param pchPrefix The name prefix.
1797 * @param cchPrefix The name prefix length (can be shorter than the
1798 * actual string).
1799 * @param ppLastDesc Where to store the address of the last descriptor.
1800 * @sa stamR3LookupFindPatternDescRange
1801 */
1802static PSTAMDESC stamR3LookupFindByPrefixRange(PSTAMLOOKUP pRoot, const char *pchPrefix, uint32_t cchPrefix,
1803 PSTAMDESC *ppLastDesc)
1804
1805{
1806 *ppLastDesc = NULL;
1807 Assert(!pRoot->pParent);
1808 AssertReturn(cchPrefix > 0, NULL);
1809
1810 /*
1811 * We start with a root slash.
1812 */
1813 if (!cchPrefix || *pchPrefix != '/')
1814 return NULL;
1815
1816 /*
1817 * Walk thru the prefix component by component, since that's how
1818 * the lookup tree is organized.
1819 */
1820 while ( cchPrefix
1821 && *pchPrefix == '/'
1822 && pRoot->cDescsInTree > 0
1823 && pRoot->cChildren > 0)
1824 {
1825 cchPrefix -= 1;
1826 pchPrefix += 1;
1827
1828 const char *pszEnd = (const char *)memchr(pchPrefix, '/', cchPrefix);
1829 if (!pszEnd)
1830 {
1831 /*
1832 * We've narrowed it down to a sub-tree now. If we've no more prefix to work
1833 * with now (e.g. '/Devices/'), the prefix matches all the children. Otherwise,
1834 * traverse the children to find the ones matching the prefix.
1835 */
1836 if (!cchPrefix)
1837 {
1838 *ppLastDesc = stamR3LookupFindLastDescForRange(pRoot->papChildren[0], pRoot->papChildren[pRoot->cChildren - 1]);
1839 return stamR3LookupFindFirstDescForRange(pRoot->papChildren[0], pRoot->papChildren[pRoot->cChildren - 1]);
1840 }
1841
1842 size_t iEnd = pRoot->cChildren;
1843 if (iEnd < 16)
1844 {
1845 /* Linear scan of the children: */
1846 for (size_t i = 0; i < pRoot->cChildren; i++)
1847 {
1848 PSTAMLOOKUP pCur = pRoot->papChildren[i];
1849 if (pCur->cch >= cchPrefix)
1850 {
1851 int iDiff = memcmp(pCur->szName, pchPrefix, cchPrefix);
1852 if (iDiff == 0)
1853 {
1854 size_t iLast = i;
1855 while (++iLast < pRoot->cChildren)
1856 {
1857 PSTAMLOOKUP pCur2 = pRoot->papChildren[iLast];
1858 if ( pCur2->cch < cchPrefix
1859 || memcmp(pCur2->szName, pchPrefix, cchPrefix) != 0)
1860 break;
1861 }
1862 iLast--;
1863
1864 *ppLastDesc = stamR3LookupFindLastDescForRange(pCur, pRoot->papChildren[iLast]);
1865 return stamR3LookupFindFirstDescForRange(pCur, pRoot->papChildren[iLast]);
1866 }
1867 if (iDiff > 0)
1868 break;
1869 }
1870 }
1871 }
1872 else
1873 {
1874 /* Binary search to find something matching the prefix, followed
1875 by a reverse scan to locate the first child: */
1876 size_t iFirst = 0;
1877 size_t i = iEnd / 2;
1878 for (;;)
1879 {
1880 PSTAMLOOKUP pCur = pRoot->papChildren[i];
1881 int iDiff;
1882 if (pCur->cch >= cchPrefix)
1883 iDiff = memcmp(pCur->szName, pchPrefix, cchPrefix);
1884 else
1885 {
1886 iDiff = memcmp(pCur->szName, pchPrefix, pCur->cch);
1887 if (!iDiff)
1888 iDiff = 1;
1889 }
1890 if (iDiff > 0)
1891 {
1892 if (iFirst < i)
1893 iEnd = i;
1894 else
1895 return NULL;
1896 }
1897 else if (iDiff < 0)
1898 {
1899 i += 1;
1900 if (i < iEnd)
1901 iFirst = i;
1902 else
1903 return NULL;
1904 }
1905 else
1906 {
1907 /* Match. Reverse scan to find the first. */
1908 iFirst = i;
1909 while ( iFirst > 0
1910 && (pCur = pRoot->papChildren[iFirst - 1])->cch >= cchPrefix
1911 && memcmp(pCur->szName, pchPrefix, cchPrefix) == 0)
1912 iFirst--;
1913
1914 /* Forward scan to find the last.*/
1915 size_t iLast = i;
1916 while (++iLast < pRoot->cChildren)
1917 {
1918 pCur = pRoot->papChildren[iLast];
1919 if ( pCur->cch < cchPrefix
1920 || memcmp(pCur->szName, pchPrefix, cchPrefix) != 0)
1921 break;
1922 }
1923 iLast--;
1924
1925 *ppLastDesc = stamR3LookupFindLastDescForRange(pRoot->papChildren[iFirst], pRoot->papChildren[iLast]);
1926 return stamR3LookupFindFirstDescForRange(pRoot->papChildren[iFirst], pRoot->papChildren[iLast]);
1927 }
1928
1929 i = iFirst + (iEnd - iFirst) / 2;
1930 }
1931 }
1932 break;
1933 }
1934
1935 /* Find child matching the path component: */
1936 uint32_t cchChild = pszEnd - pchPrefix;
1937 PSTAMLOOKUP pChild = stamR3LookupFindChild(pRoot, pchPrefix, cchChild, NULL);
1938 if (!pChild)
1939 break;
1940
1941 /* Advance: */
1942 cchPrefix -= cchChild;
1943 pchPrefix = pszEnd;
1944 pRoot = pChild;
1945 }
1946 return NULL;
1947}
1948
1949
1950/**
1951 * Increments the cDescInTree member of the given node an all ancestors.
1952 *
1953 * @param pLookup The lookup node.
1954 */
1955static void stamR3LookupIncUsage(PSTAMLOOKUP pLookup)
1956{
1957 Assert(pLookup->pDesc);
1958
1959 PSTAMLOOKUP pCur = pLookup;
1960 while (pCur != NULL)
1961 {
1962 pCur->cDescsInTree++;
1963 pCur = pCur->pParent;
1964 }
1965}
1966
1967
1968/**
1969 * Descrements the cDescInTree member of the given node an all ancestors.
1970 *
1971 * @param pLookup The lookup node.
1972 */
1973static void stamR3LookupDecUsage(PSTAMLOOKUP pLookup)
1974{
1975 Assert(!pLookup->pDesc);
1976
1977 PSTAMLOOKUP pCur = pLookup;
1978 while (pCur != NULL)
1979 {
1980 Assert(pCur->cDescsInTree > 0);
1981 pCur->cDescsInTree--;
1982 pCur = pCur->pParent;
1983 }
1984}
1985
1986
1987/**
1988 * Frees empty lookup nodes if it's worth it.
1989 *
1990 * @param pLookup The lookup node.
1991 */
1992static void stamR3LookupMaybeFree(PSTAMLOOKUP pLookup)
1993{
1994 Assert(!pLookup->pDesc);
1995
1996 /*
1997 * Free between two and three levels of nodes. Freeing too much most
1998 * likely wasted effort since we're either going to repopluate the tree
1999 * or quit the whole thing.
2000 */
2001 if (pLookup->cDescsInTree > 0)
2002 return;
2003
2004 PSTAMLOOKUP pCur = pLookup->pParent;
2005 if (!pCur)
2006 return;
2007 if (pCur->cDescsInTree > 0)
2008 return;
2009 PSTAMLOOKUP pParent = pCur->pParent;
2010 if (!pParent)
2011 return;
2012
2013 if (pParent->cDescsInTree == 0 && pParent->pParent)
2014 {
2015 pCur = pParent;
2016 pParent = pCur->pParent;
2017 }
2018
2019 /*
2020 * Remove pCur from pParent.
2021 */
2022 PSTAMLOOKUP *papChildren = pParent->papChildren;
2023 uint32_t cChildren = --pParent->cChildren;
2024 for (uint32_t i = pCur->iParent; i < cChildren; i++)
2025 {
2026 PSTAMLOOKUP pChild = papChildren[i + 1];
2027 pChild->iParent = i;
2028 papChildren[i] = pChild;
2029 }
2030 pCur->pParent = NULL;
2031 pCur->iParent = UINT16_MAX;
2032
2033 /*
2034 * Destroy pCur.
2035 */
2036 stamR3LookupDestroyTree(pCur);
2037}
2038
2039
2040/**
2041 * Destroys a lookup tree.
2042 *
2043 * This is used by STAMR3Term as well as stamR3LookupMaybeFree.
2044 *
2045 * @param pRoot The root of the tree (must have no parent).
2046 */
2047static void stamR3LookupDestroyTree(PSTAMLOOKUP pRoot)
2048{
2049 Assert(pRoot); Assert(!pRoot->pParent);
2050 PSTAMLOOKUP pCur = pRoot;
2051 for (;;)
2052 {
2053 uint32_t i = pCur->cChildren;
2054 if (i > 0)
2055 {
2056 /*
2057 * Push child (with leaf optimization).
2058 */
2059 PSTAMLOOKUP pChild = pCur->papChildren[--i];
2060 if (pChild->cChildren != 0)
2061 pCur = pChild;
2062 else
2063 {
2064 /* free leaves. */
2065 for (;;)
2066 {
2067 if (pChild->papChildren)
2068 {
2069 RTMemFree(pChild->papChildren);
2070 pChild->papChildren = NULL;
2071 }
2072 RTMemFree(pChild);
2073 pCur->papChildren[i] = NULL;
2074
2075 /* next */
2076 if (i == 0)
2077 {
2078 pCur->cChildren = 0;
2079 break;
2080 }
2081 pChild = pCur->papChildren[--i];
2082 if (pChild->cChildren != 0)
2083 {
2084 pCur->cChildren = i + 1;
2085 pCur = pChild;
2086 break;
2087 }
2088 }
2089 }
2090 }
2091 else
2092 {
2093 /*
2094 * Pop and free current.
2095 */
2096 Assert(!pCur->pDesc);
2097
2098 PSTAMLOOKUP pParent = pCur->pParent;
2099 Assert(pCur->iParent == (pParent ? pParent->cChildren - 1 : UINT16_MAX));
2100
2101 RTMemFree(pCur->papChildren);
2102 pCur->papChildren = NULL;
2103 RTMemFree(pCur);
2104
2105 pCur = pParent;
2106 if (!pCur)
2107 break;
2108 pCur->papChildren[--pCur->cChildren] = NULL;
2109 }
2110 }
2111}
2112
2113
2114/**
2115 * Internal worker for the different register calls.
2116 *
2117 * @returns VBox status code.
2118 * @param pUVM Pointer to the user mode VM structure.
2119 * @param pvSample Pointer to the sample.
2120 * @param pfnReset Callback for resetting the sample. NULL should be used if the sample can't be reset.
2121 * @param pfnPrint Print the sample.
2122 * @param enmType Sample type. This indicates what pvSample is pointing at.
2123 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
2124 * @param pszName The sample name format string.
2125 * @param enmUnit Sample unit.
2126 * @param pszDesc Sample description.
2127 * @param iRefreshGrp The refresh group, STAM_REFRESH_GRP_XXX.
2128 * @remark There is currently no device or driver variant of this API. Add one if it should become necessary!
2129 */
2130static int stamR3RegisterU(PUVM pUVM, void *pvSample, PFNSTAMR3CALLBACKRESET pfnReset, PFNSTAMR3CALLBACKPRINT pfnPrint,
2131 STAMTYPE enmType, STAMVISIBILITY enmVisibility,
2132 const char *pszName, STAMUNIT enmUnit, const char *pszDesc, uint8_t iRefreshGrp)
2133{
2134 AssertReturn(pszName[0] == '/', VERR_INVALID_NAME);
2135 AssertReturn(pszName[1] != '/' && pszName[1], VERR_INVALID_NAME);
2136 uint32_t const cchName = (uint32_t)strlen(pszName);
2137 AssertReturn(cchName <= STAM_MAX_NAME_LEN, VERR_OUT_OF_RANGE);
2138 AssertReturn(pszName[cchName - 1] != '/', VERR_INVALID_NAME);
2139 AssertReturn(memchr(pszName, '\\', cchName) == NULL, VERR_INVALID_NAME);
2140 AssertReturn(iRefreshGrp == STAM_REFRESH_GRP_NONE || iRefreshGrp < 64, VERR_INVALID_PARAMETER);
2141
2142 STAM_LOCK_WR(pUVM);
2143
2144 /*
2145 * Look up the tree location, populating the lookup tree as we walk it.
2146 */
2147 PSTAMLOOKUP pLookup = pUVM->stam.s.pRoot; Assert(pLookup);
2148 uint32_t offName = 1;
2149 for (;;)
2150 {
2151 /* Get the next part of the path. */
2152 const char *pszStart = &pszName[offName];
2153 const char *pszEnd = strchr(pszStart, '/');
2154 uint32_t cch = pszEnd ? (uint32_t)(pszEnd - pszStart) : cchName - offName;
2155 if (cch == 0)
2156 {
2157 STAM_UNLOCK_WR(pUVM);
2158 AssertMsgFailed(("No double or trailing slashes are allowed: '%s'\n", pszName));
2159 return VERR_INVALID_NAME;
2160 }
2161
2162 /* Do the looking up. */
2163 uint32_t iChild = 0;
2164 PSTAMLOOKUP pChild = stamR3LookupFindChild(pLookup, pszStart, cch, &iChild);
2165 if (!pChild)
2166 {
2167 pChild = stamR3LookupNewChild(pLookup, pszStart, cch, offName, iChild);
2168 if (!pChild)
2169 {
2170 STAM_UNLOCK_WR(pUVM);
2171 return VERR_NO_MEMORY;
2172 }
2173 }
2174
2175 /* Advance. */
2176 pLookup = pChild;
2177 if (!pszEnd)
2178 break;
2179 offName += cch + 1;
2180 }
2181 if (pLookup->pDesc)
2182 {
2183 STAM_UNLOCK_WR(pUVM);
2184 AssertMsgFailed(("Duplicate sample name: %s\n", pszName));
2185 return VERR_ALREADY_EXISTS;
2186 }
2187
2188 PSTAMDESC pCur = stamR3LookupFindNextWithDesc(pLookup);
2189
2190 /*
2191 * Check that the name doesn't screw up sorting order when taking
2192 * slashes into account. The QT GUI makes some assumptions.
2193 * Problematic chars are: !"#$%&'()*+,-.
2194 */
2195#ifdef VBOX_STRICT
2196 Assert(pszName[0] == '/');
2197 PSTAMDESC pPrev = pCur
2198 ? RTListGetPrev(&pUVM->stam.s.List, pCur, STAMDESC, ListEntry)
2199 : RTListGetLast(&pUVM->stam.s.List, STAMDESC, ListEntry);
2200 Assert(!pPrev || strcmp(pszName, pPrev->pszName) > 0);
2201 Assert(!pCur || strcmp(pszName, pCur->pszName) < 0);
2202 Assert(!pPrev || stamR3SlashCompare(pPrev->pszName, pszName) < 0);
2203 Assert(!pCur || stamR3SlashCompare(pCur->pszName, pszName) > 0);
2204
2205 /*
2206 * Check alignment requirements.
2207 */
2208 switch (enmType)
2209 {
2210 /* 8 byte / 64-bit */
2211 case STAMTYPE_U64:
2212 case STAMTYPE_U64_RESET:
2213 case STAMTYPE_X64:
2214 case STAMTYPE_X64_RESET:
2215 case STAMTYPE_COUNTER:
2216 case STAMTYPE_PROFILE:
2217 case STAMTYPE_PROFILE_ADV:
2218 AssertMsg(!((uintptr_t)pvSample & 7), ("%p - %s\n", pvSample, pszName));
2219 break;
2220
2221 /* 4 byte / 32-bit */
2222 case STAMTYPE_RATIO_U32:
2223 case STAMTYPE_RATIO_U32_RESET:
2224 case STAMTYPE_U32:
2225 case STAMTYPE_U32_RESET:
2226 case STAMTYPE_X32:
2227 case STAMTYPE_X32_RESET:
2228 AssertMsg(!((uintptr_t)pvSample & 3), ("%p - %s\n", pvSample, pszName));
2229 break;
2230
2231 /* 2 byte / 32-bit */
2232 case STAMTYPE_U16:
2233 case STAMTYPE_U16_RESET:
2234 case STAMTYPE_X16:
2235 case STAMTYPE_X16_RESET:
2236 AssertMsg(!((uintptr_t)pvSample & 1), ("%p - %s\n", pvSample, pszName));
2237 break;
2238
2239 /* 1 byte / 8-bit / unaligned */
2240 case STAMTYPE_U8:
2241 case STAMTYPE_U8_RESET:
2242 case STAMTYPE_X8:
2243 case STAMTYPE_X8_RESET:
2244 case STAMTYPE_BOOL:
2245 case STAMTYPE_BOOL_RESET:
2246 case STAMTYPE_CALLBACK:
2247 case STAMTYPE_INTERNAL_SUM:
2248 case STAMTYPE_INTERNAL_PCT_OF_SUM:
2249 break;
2250
2251 default:
2252 AssertMsgFailed(("%d\n", enmType));
2253 break;
2254 }
2255#endif /* VBOX_STRICT */
2256
2257 /*
2258 * Create a new node and insert it at the current location.
2259 */
2260 int rc;
2261 size_t cbDesc = pszDesc ? strlen(pszDesc) + 1 : 0;
2262 PSTAMDESC pNew = (PSTAMDESC)RTMemAlloc(sizeof(*pNew) + cchName + 1 + cbDesc);
2263 if (pNew)
2264 {
2265 pNew->pszName = (char *)memcpy((char *)(pNew + 1), pszName, cchName + 1);
2266 pNew->enmType = enmType;
2267 pNew->enmVisibility = enmVisibility;
2268 if (enmType != STAMTYPE_CALLBACK)
2269 pNew->u.pv = pvSample;
2270 else
2271 {
2272 pNew->u.Callback.pvSample = pvSample;
2273 pNew->u.Callback.pfnReset = pfnReset;
2274 pNew->u.Callback.pfnPrint = pfnPrint;
2275 }
2276 pNew->enmUnit = enmUnit;
2277 pNew->iRefreshGroup = iRefreshGrp;
2278 pNew->pszDesc = NULL;
2279 if (pszDesc)
2280 pNew->pszDesc = (char *)memcpy((char *)(pNew + 1) + cchName + 1, pszDesc, cbDesc);
2281
2282 if (pCur)
2283 RTListNodeInsertBefore(&pCur->ListEntry, &pNew->ListEntry);
2284 else
2285 RTListAppend(&pUVM->stam.s.List, &pNew->ListEntry);
2286
2287 pNew->pLookup = pLookup;
2288 pLookup->pDesc = pNew;
2289 stamR3LookupIncUsage(pLookup);
2290
2291 stamR3ResetOne(pNew, pUVM->pVM);
2292 rc = VINF_SUCCESS;
2293 }
2294 else
2295 rc = VERR_NO_MEMORY;
2296
2297 STAM_UNLOCK_WR(pUVM);
2298 return rc;
2299}
2300
2301
2302/**
2303 * Destroys the statistics descriptor, unlinking it and freeing all resources.
2304 *
2305 * @returns VINF_SUCCESS
2306 * @param pCur The descriptor to destroy.
2307 */
2308static int stamR3DestroyDesc(PSTAMDESC pCur)
2309{
2310 RTListNodeRemove(&pCur->ListEntry);
2311 pCur->pLookup->pDesc = NULL; /** @todo free lookup nodes once it's working. */
2312 stamR3LookupDecUsage(pCur->pLookup);
2313 stamR3LookupMaybeFree(pCur->pLookup);
2314 if ( pCur->enmType != STAMTYPE_INTERNAL_SUM
2315 && pCur->enmType != STAMTYPE_INTERNAL_PCT_OF_SUM)
2316 { /* likely */ }
2317 else
2318 RTMemFree(pCur->u.pSum);
2319 RTMemFree(pCur);
2320
2321 return VINF_SUCCESS;
2322}
2323
2324
2325/**
2326 * Deregisters a sample previously registered by STAR3Register() given its
2327 * address.
2328 *
2329 * This is intended used for devices which can be unplugged and for
2330 * temporary samples.
2331 *
2332 * @returns VBox status code.
2333 * @param pUVM Pointer to the user mode VM structure.
2334 * @param pvSample Pointer to the sample registered with STAMR3Register().
2335 */
2336VMMR3DECL(int) STAMR3DeregisterByAddr(PUVM pUVM, void *pvSample)
2337{
2338 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2339
2340 /* This is a complete waste of time when shutting down. */
2341 VMSTATE enmState = VMR3GetStateU(pUVM);
2342 if (enmState >= VMSTATE_DESTROYING)
2343 return VINF_SUCCESS;
2344
2345 STAM_LOCK_WR(pUVM);
2346
2347 /*
2348 * Search for it.
2349 */
2350 int rc = VERR_INVALID_HANDLE;
2351 PSTAMDESC pCur, pNext;
2352 RTListForEachSafe(&pUVM->stam.s.List, pCur, pNext, STAMDESC, ListEntry)
2353 {
2354 if (pCur->u.pv == pvSample)
2355 rc = stamR3DestroyDesc(pCur);
2356 }
2357
2358 STAM_UNLOCK_WR(pUVM);
2359 return rc;
2360}
2361
2362
2363/**
2364 * Worker for STAMR3Deregister, STAMR3DeregisterV and STAMR3DeregisterF.
2365 *
2366 * @returns VBox status code.
2367 * @retval VWRN_NOT_FOUND if no matching names found.
2368 *
2369 * @param pUVM Pointer to the user mode VM structure.
2370 * @param pszPat The name pattern.
2371 */
2372static int stamR3DeregisterByPattern(PUVM pUVM, const char *pszPat)
2373{
2374 Assert(!strchr(pszPat, '|')); /* single pattern! */
2375
2376 int rc = VWRN_NOT_FOUND;
2377 STAM_LOCK_WR(pUVM);
2378
2379 PSTAMDESC pLast;
2380 PSTAMDESC pCur = stamR3LookupFindPatternDescRange(pUVM->stam.s.pRoot, &pUVM->stam.s.List, pszPat, &pLast);
2381 if (pCur)
2382 {
2383 for (;;)
2384 {
2385 PSTAMDESC pNext = RTListNodeGetNext(&pCur->ListEntry, STAMDESC, ListEntry);
2386
2387 if (RTStrSimplePatternMatch(pszPat, pCur->pszName))
2388 rc = stamR3DestroyDesc(pCur);
2389
2390 /* advance. */
2391 if (pCur == pLast)
2392 break;
2393 pCur = pNext;
2394 }
2395 Assert(pLast);
2396 }
2397 else
2398 Assert(!pLast);
2399
2400 STAM_UNLOCK_WR(pUVM);
2401 return rc;
2402}
2403
2404
2405/**
2406 * Deregister zero or more samples given a (single) pattern matching their
2407 * names.
2408 *
2409 * @returns VBox status code.
2410 * @param pUVM Pointer to the user mode VM structure.
2411 * @param pszPat The name pattern.
2412 * @sa STAMR3DeregisterF, STAMR3DeregisterV
2413 */
2414VMMR3DECL(int) STAMR3Deregister(PUVM pUVM, const char *pszPat)
2415{
2416 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2417
2418 /* This is a complete waste of time when shutting down. */
2419 VMSTATE enmState = VMR3GetStateU(pUVM);
2420 if (enmState >= VMSTATE_DESTROYING)
2421 return VINF_SUCCESS;
2422
2423 return stamR3DeregisterByPattern(pUVM, pszPat);
2424}
2425
2426
2427/**
2428 * Deregister zero or more samples given a (single) pattern matching their
2429 * names.
2430 *
2431 * @returns VBox status code.
2432 * @param pUVM Pointer to the user mode VM structure.
2433 * @param pszPatFmt The name pattern format string.
2434 * @param ... Format string arguments.
2435 * @sa STAMR3Deregister, STAMR3DeregisterV
2436 */
2437VMMR3DECL(int) STAMR3DeregisterF(PUVM pUVM, const char *pszPatFmt, ...)
2438{
2439 va_list va;
2440 va_start(va, pszPatFmt);
2441 int rc = STAMR3DeregisterV(pUVM, pszPatFmt, va);
2442 va_end(va);
2443 return rc;
2444}
2445
2446
2447/**
2448 * Deregister zero or more samples given a (single) pattern matching their
2449 * names.
2450 *
2451 * @returns VBox status code.
2452 * @param pUVM Pointer to the user mode VM structure.
2453 * @param pszPatFmt The name pattern format string.
2454 * @param va Format string arguments.
2455 * @sa STAMR3Deregister, STAMR3DeregisterF
2456 */
2457VMMR3DECL(int) STAMR3DeregisterV(PUVM pUVM, const char *pszPatFmt, va_list va)
2458{
2459 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2460
2461 /* This is a complete waste of time when shutting down. */
2462 VMSTATE enmState = VMR3GetStateU(pUVM);
2463 if (enmState >= VMSTATE_DESTROYING)
2464 return VINF_SUCCESS;
2465
2466 char szPat[STAM_MAX_NAME_LEN + 8];
2467 size_t cchPat = RTStrPrintfV(szPat, sizeof(szPat), pszPatFmt, va);
2468 AssertReturn(cchPat <= STAM_MAX_NAME_LEN, VERR_OUT_OF_RANGE);
2469
2470 return stamR3DeregisterByPattern(pUVM, szPat);
2471}
2472
2473
2474/**
2475 * Deregister zero or more samples given their name prefix.
2476 *
2477 * @returns VBox status code.
2478 * @param pUVM Pointer to the user mode VM structure.
2479 * @param pszPrefix The name prefix of the samples to remove.
2480 * @sa STAMR3Deregister, STAMR3DeregisterF, STAMR3DeregisterV
2481 */
2482VMMR3DECL(int) STAMR3DeregisterByPrefix(PUVM pUVM, const char *pszPrefix)
2483{
2484 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2485
2486 /* This is a complete waste of time when shutting down. */
2487 VMSTATE enmState = VMR3GetStateU(pUVM);
2488 if (enmState >= VMSTATE_DESTROYING)
2489 return VINF_SUCCESS;
2490
2491 size_t const cchPrefix = strlen(pszPrefix);
2492 int rc = VWRN_NOT_FOUND;
2493 STAM_LOCK_WR(pUVM);
2494
2495 PSTAMDESC pLast;
2496 PSTAMDESC pCur = stamR3LookupFindByPrefixRange(pUVM->stam.s.pRoot, pszPrefix, (uint32_t)cchPrefix, &pLast);
2497 if (pCur)
2498 for (;;)
2499 {
2500 PSTAMDESC const pNext = RTListNodeGetNext(&pCur->ListEntry, STAMDESC, ListEntry);
2501 Assert(strncmp(pCur->pszName, pszPrefix, cchPrefix) == 0);
2502
2503 rc = stamR3DestroyDesc(pCur);
2504
2505 /* advance. */
2506 if (pCur == pLast)
2507 break;
2508 pCur = pNext;
2509 }
2510
2511 STAM_UNLOCK_WR(pUVM);
2512 return rc;
2513}
2514
2515
2516/**
2517 * Resets statistics for the specified VM.
2518 * It's possible to select a subset of the samples.
2519 *
2520 * @returns VBox status code. (Basically, it cannot fail.)
2521 * @param pUVM The user mode VM handle.
2522 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
2523 * If NULL all samples are reset.
2524 * @remarks Don't confuse this with the other 'XYZR3Reset' methods, it's not called at VM reset.
2525 */
2526VMMR3DECL(int) STAMR3Reset(PUVM pUVM, const char *pszPat)
2527{
2528 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2529 VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
2530
2531 int rc = VINF_SUCCESS;
2532
2533 /* ring-0 */
2534 GVMMRESETSTATISTICSSREQ GVMMReq;
2535 GMMRESETSTATISTICSSREQ GMMReq;
2536 bool fGVMMMatched = (!pszPat || !*pszPat) && !SUPR3IsDriverless();
2537 bool fGMMMatched = fGVMMMatched;
2538 if (fGVMMMatched)
2539 {
2540 memset(&GVMMReq.Stats, 0xff, sizeof(GVMMReq.Stats));
2541 memset(&GMMReq.Stats, 0xff, sizeof(GMMReq.Stats));
2542 }
2543 else
2544 {
2545 char *pszCopy;
2546 unsigned cExpressions;
2547 char **papszExpressions = stamR3SplitPattern(pszPat, &cExpressions, &pszCopy);
2548 if (!papszExpressions)
2549 return VERR_NO_MEMORY;
2550
2551 /* GVMM */
2552 RT_ZERO(GVMMReq.Stats);
2553 for (unsigned i = 0; i < RT_ELEMENTS(g_aGVMMStats); i++)
2554 if (stamR3MultiMatch(papszExpressions, cExpressions, NULL, g_aGVMMStats[i].pszName))
2555 {
2556 *((uint8_t *)&GVMMReq.Stats + g_aGVMMStats[i].offVar) = 0xff;
2557 fGVMMMatched = true;
2558 }
2559 if (!fGVMMMatched)
2560 {
2561 /** @todo match cpu leaves some rainy day. */
2562 }
2563
2564 /* GMM */
2565 RT_ZERO(GMMReq.Stats);
2566 for (unsigned i = 0; i < RT_ELEMENTS(g_aGMMStats); i++)
2567 if (stamR3MultiMatch(papszExpressions, cExpressions, NULL, g_aGMMStats[i].pszName))
2568 {
2569 *((uint8_t *)&GMMReq.Stats + g_aGMMStats[i].offVar) = 0xff;
2570 fGMMMatched = true;
2571 }
2572
2573 RTMemTmpFree(papszExpressions);
2574 RTStrFree(pszCopy);
2575 }
2576
2577 STAM_LOCK_WR(pUVM);
2578
2579 if (fGVMMMatched)
2580 {
2581 PVM pVM = pUVM->pVM;
2582 GVMMReq.Hdr.cbReq = sizeof(GVMMReq);
2583 GVMMReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
2584 GVMMReq.pSession = pVM->pSession;
2585 rc = SUPR3CallVMMR0Ex(VMCC_GET_VMR0_FOR_CALL(pVM), NIL_VMCPUID, VMMR0_DO_GVMM_RESET_STATISTICS, 0, &GVMMReq.Hdr);
2586 }
2587
2588 if (fGMMMatched)
2589 {
2590 PVM pVM = pUVM->pVM;
2591 GMMReq.Hdr.cbReq = sizeof(GMMReq);
2592 GMMReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
2593 GMMReq.pSession = pVM->pSession;
2594 rc = SUPR3CallVMMR0Ex(VMCC_GET_VMR0_FOR_CALL(pVM), NIL_VMCPUID, VMMR0_DO_GMM_RESET_STATISTICS, 0, &GMMReq.Hdr);
2595 }
2596
2597 /* and the reset */
2598 stamR3EnumU(pUVM, pszPat, false /* fUpdateRing0 */, stamR3ResetOne, pUVM->pVM);
2599
2600 STAM_UNLOCK_WR(pUVM);
2601 return rc;
2602}
2603
2604
2605/**
2606 * Resets one statistics sample.
2607 * Callback for stamR3EnumU().
2608 *
2609 * @returns VINF_SUCCESS
2610 * @param pDesc Pointer to the current descriptor.
2611 * @param pvArg User argument - Pointer to the VM.
2612 */
2613static int stamR3ResetOne(PSTAMDESC pDesc, void *pvArg)
2614{
2615 switch (pDesc->enmType)
2616 {
2617 case STAMTYPE_COUNTER:
2618 ASMAtomicXchgU64(&pDesc->u.pCounter->c, 0);
2619 break;
2620
2621 case STAMTYPE_PROFILE:
2622 case STAMTYPE_PROFILE_ADV:
2623 ASMAtomicXchgU64(&pDesc->u.pProfile->cPeriods, 0);
2624 ASMAtomicXchgU64(&pDesc->u.pProfile->cTicks, 0);
2625 ASMAtomicXchgU64(&pDesc->u.pProfile->cTicksMax, 0);
2626 ASMAtomicXchgU64(&pDesc->u.pProfile->cTicksMin, UINT64_MAX);
2627 break;
2628
2629 case STAMTYPE_RATIO_U32_RESET:
2630 ASMAtomicXchgU32(&pDesc->u.pRatioU32->u32A, 0);
2631 ASMAtomicXchgU32(&pDesc->u.pRatioU32->u32B, 0);
2632 break;
2633
2634 case STAMTYPE_CALLBACK:
2635 if (pDesc->u.Callback.pfnReset)
2636 pDesc->u.Callback.pfnReset((PVM)pvArg, pDesc->u.Callback.pvSample);
2637 break;
2638
2639 case STAMTYPE_U8_RESET:
2640 case STAMTYPE_X8_RESET:
2641 ASMAtomicXchgU8(pDesc->u.pu8, 0);
2642 break;
2643
2644 case STAMTYPE_U16_RESET:
2645 case STAMTYPE_X16_RESET:
2646 ASMAtomicXchgU16(pDesc->u.pu16, 0);
2647 break;
2648
2649 case STAMTYPE_U32_RESET:
2650 case STAMTYPE_X32_RESET:
2651 ASMAtomicXchgU32(pDesc->u.pu32, 0);
2652 break;
2653
2654 case STAMTYPE_U64_RESET:
2655 case STAMTYPE_X64_RESET:
2656 ASMAtomicXchgU64(pDesc->u.pu64, 0);
2657 break;
2658
2659 case STAMTYPE_BOOL_RESET:
2660 ASMAtomicXchgBool(pDesc->u.pf, false);
2661 break;
2662
2663 /* These are custom and will not be touched. */
2664 case STAMTYPE_U8:
2665 case STAMTYPE_X8:
2666 case STAMTYPE_U16:
2667 case STAMTYPE_X16:
2668 case STAMTYPE_U32:
2669 case STAMTYPE_X32:
2670 case STAMTYPE_U64:
2671 case STAMTYPE_X64:
2672 case STAMTYPE_RATIO_U32:
2673 case STAMTYPE_BOOL:
2674 case STAMTYPE_INTERNAL_SUM:
2675 case STAMTYPE_INTERNAL_PCT_OF_SUM:
2676 break;
2677
2678 default:
2679 AssertMsgFailed(("enmType=%d\n", pDesc->enmType));
2680 break;
2681 }
2682 NOREF(pvArg);
2683 return VINF_SUCCESS;
2684}
2685
2686
2687/**
2688 * Get a snapshot of the statistics.
2689 * It's possible to select a subset of the samples.
2690 *
2691 * @returns VBox status code. (Basically, it cannot fail.)
2692 * @param pUVM The user mode VM handle.
2693 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
2694 * If NULL all samples are reset.
2695 * @param fWithDesc Whether to include the descriptions.
2696 * @param ppszSnapshot Where to store the pointer to the snapshot data.
2697 * The format of the snapshot should be XML, but that will have to be discussed
2698 * when this function is implemented.
2699 * The returned pointer must be freed by calling STAMR3SnapshotFree().
2700 * @param pcchSnapshot Where to store the size of the snapshot data. (Excluding the trailing '\0')
2701 */
2702VMMR3DECL(int) STAMR3Snapshot(PUVM pUVM, const char *pszPat, char **ppszSnapshot, size_t *pcchSnapshot, bool fWithDesc)
2703{
2704 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2705 VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
2706
2707 STAMR3SNAPSHOTONE State = { NULL, NULL, NULL, pUVM->pVM, 0, VINF_SUCCESS, fWithDesc };
2708
2709 /*
2710 * Write the XML header.
2711 */
2712 /** @todo Make this proper & valid XML. */
2713 stamR3SnapshotPrintf(&State, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
2714
2715 /*
2716 * Write the content.
2717 */
2718 stamR3SnapshotPrintf(&State, "<Statistics>\n");
2719 int rc = stamR3EnumU(pUVM, pszPat, true /* fUpdateRing0 */, stamR3SnapshotOne, &State);
2720 stamR3SnapshotPrintf(&State, "</Statistics>\n");
2721
2722 if (RT_SUCCESS(rc))
2723 rc = State.rc;
2724 else
2725 {
2726 RTMemFree(State.pszStart);
2727 State.pszStart = State.pszEnd = State.psz = NULL;
2728 State.cbAllocated = 0;
2729 }
2730
2731 /*
2732 * Done.
2733 */
2734 *ppszSnapshot = State.pszStart;
2735 if (pcchSnapshot)
2736 *pcchSnapshot = State.psz - State.pszStart;
2737 return rc;
2738}
2739
2740
2741/**
2742 * stamR3EnumU callback employed by STAMR3Snapshot.
2743 *
2744 * @returns VBox status code, but it's interpreted as 0 == success / !0 == failure by enmR3Enum.
2745 * @param pDesc The sample.
2746 * @param pvArg The snapshot status structure.
2747 */
2748static int stamR3SnapshotOne(PSTAMDESC pDesc, void *pvArg)
2749{
2750 PSTAMR3SNAPSHOTONE pThis = (PSTAMR3SNAPSHOTONE)pvArg;
2751
2752 switch (pDesc->enmType)
2753 {
2754 case STAMTYPE_COUNTER:
2755 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pDesc->u.pCounter->c == 0)
2756 return VINF_SUCCESS;
2757 stamR3SnapshotPrintf(pThis, "<Counter c=\"%lld\"", pDesc->u.pCounter->c);
2758 break;
2759
2760 case STAMTYPE_PROFILE:
2761 case STAMTYPE_PROFILE_ADV:
2762 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pDesc->u.pProfile->cPeriods == 0)
2763 return VINF_SUCCESS;
2764 stamR3SnapshotPrintf(pThis, "<Profile cPeriods=\"%lld\" cTicks=\"%lld\" cTicksMin=\"%lld\" cTicksMax=\"%lld\"",
2765 pDesc->u.pProfile->cPeriods, pDesc->u.pProfile->cTicks, pDesc->u.pProfile->cTicksMin,
2766 pDesc->u.pProfile->cTicksMax);
2767 break;
2768
2769 case STAMTYPE_RATIO_U32:
2770 case STAMTYPE_RATIO_U32_RESET:
2771 if (pDesc->enmVisibility == STAMVISIBILITY_USED && !pDesc->u.pRatioU32->u32A && !pDesc->u.pRatioU32->u32B)
2772 return VINF_SUCCESS;
2773 stamR3SnapshotPrintf(pThis, "<Ratio32 u32A=\"%lld\" u32B=\"%lld\"",
2774 pDesc->u.pRatioU32->u32A, pDesc->u.pRatioU32->u32B);
2775 break;
2776
2777 case STAMTYPE_CALLBACK:
2778 {
2779 char szBuf[512];
2780 pDesc->u.Callback.pfnPrint(pThis->pVM, pDesc->u.Callback.pvSample, szBuf, sizeof(szBuf));
2781 stamR3SnapshotPrintf(pThis, "<Callback val=\"%s\"", szBuf);
2782 break;
2783 }
2784
2785 case STAMTYPE_U8:
2786 case STAMTYPE_U8_RESET:
2787 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu8 == 0)
2788 return VINF_SUCCESS;
2789 stamR3SnapshotPrintf(pThis, "<U8 val=\"%u\"", *pDesc->u.pu8);
2790 break;
2791
2792 case STAMTYPE_X8:
2793 case STAMTYPE_X8_RESET:
2794 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu8 == 0)
2795 return VINF_SUCCESS;
2796 stamR3SnapshotPrintf(pThis, "<X8 val=\"%#x\"", *pDesc->u.pu8);
2797 break;
2798
2799 case STAMTYPE_U16:
2800 case STAMTYPE_U16_RESET:
2801 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu16 == 0)
2802 return VINF_SUCCESS;
2803 stamR3SnapshotPrintf(pThis, "<U16 val=\"%u\"", *pDesc->u.pu16);
2804 break;
2805
2806 case STAMTYPE_X16:
2807 case STAMTYPE_X16_RESET:
2808 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu16 == 0)
2809 return VINF_SUCCESS;
2810 stamR3SnapshotPrintf(pThis, "<X16 val=\"%#x\"", *pDesc->u.pu16);
2811 break;
2812
2813 case STAMTYPE_U32:
2814 case STAMTYPE_U32_RESET:
2815 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu32 == 0)
2816 return VINF_SUCCESS;
2817 stamR3SnapshotPrintf(pThis, "<U32 val=\"%u\"", *pDesc->u.pu32);
2818 break;
2819
2820 case STAMTYPE_X32:
2821 case STAMTYPE_X32_RESET:
2822 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu32 == 0)
2823 return VINF_SUCCESS;
2824 stamR3SnapshotPrintf(pThis, "<X32 val=\"%#x\"", *pDesc->u.pu32);
2825 break;
2826
2827 case STAMTYPE_U64:
2828 case STAMTYPE_U64_RESET:
2829 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu64 == 0)
2830 return VINF_SUCCESS;
2831 stamR3SnapshotPrintf(pThis, "<U64 val=\"%llu\"", *pDesc->u.pu64);
2832 break;
2833
2834 case STAMTYPE_X64:
2835 case STAMTYPE_X64_RESET:
2836 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu64 == 0)
2837 return VINF_SUCCESS;
2838 stamR3SnapshotPrintf(pThis, "<X64 val=\"%#llx\"", *pDesc->u.pu64);
2839 break;
2840
2841 case STAMTYPE_BOOL:
2842 case STAMTYPE_BOOL_RESET:
2843 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pf == false)
2844 return VINF_SUCCESS;
2845 stamR3SnapshotPrintf(pThis, "<BOOL val=\"%RTbool\"", *pDesc->u.pf);
2846 break;
2847
2848 case STAMTYPE_INTERNAL_SUM:
2849 {
2850 PSTAMSUMSAMPLE const pSum = pDesc->u.pSum;
2851 stamR3SumRefresh(pSum);
2852 switch (pSum->enmType)
2853 {
2854 case STAMTYPE_COUNTER:
2855 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pSum->u.Counter.c == 0)
2856 return VINF_SUCCESS;
2857 stamR3SnapshotPrintf(pThis, "<Counter c=\"%lld\"", pSum->u.Counter.c);
2858 break;
2859
2860 case STAMTYPE_PROFILE:
2861 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pSum->u.Profile.cPeriods == 0)
2862 return VINF_SUCCESS;
2863 stamR3SnapshotPrintf(pThis, "<Profile cPeriods=\"%lld\" cTicks=\"%lld\" cTicksMin=\"%lld\" cTicksMax=\"%lld\"",
2864 pSum->u.Profile.cPeriods, pSum->u.Profile.cTicks, pSum->u.Profile.cTicksMin,
2865 pSum->u.Profile.cTicksMax);
2866 break;
2867
2868 default:
2869 AssertMsgFailedReturn(("%d\n", pSum->enmType), VINF_SUCCESS);
2870 }
2871 break;
2872 }
2873
2874 case STAMTYPE_INTERNAL_PCT_OF_SUM:
2875 {
2876 PSTAMSUMSAMPLE const pSum = pDesc->u.pSum;
2877 stamR3PctOfSumRefresh(pDesc, pSum);
2878 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pSum->u.Counter.c == 0)
2879 return VINF_SUCCESS;
2880 stamR3SnapshotPrintf(pThis, "<Counter c=\"%lld\"", pSum->u.Counter.c);
2881 break;
2882 }
2883
2884 default:
2885 AssertMsgFailedReturn(("%d\n", pDesc->enmType), VINF_SUCCESS);
2886 }
2887
2888 stamR3SnapshotPrintf(pThis, " unit=\"%s\"", STAMR3GetUnit(pDesc->enmUnit));
2889
2890 switch (pDesc->enmVisibility)
2891 {
2892 default:
2893 case STAMVISIBILITY_ALWAYS:
2894 break;
2895 case STAMVISIBILITY_USED:
2896 stamR3SnapshotPrintf(pThis, " vis=\"used\"");
2897 break;
2898 case STAMVISIBILITY_NOT_GUI:
2899 stamR3SnapshotPrintf(pThis, " vis=\"not-gui\"");
2900 break;
2901 }
2902
2903 stamR3SnapshotPrintf(pThis, " name=\"%s\"", pDesc->pszName);
2904
2905 if (pThis->fWithDesc && pDesc->pszDesc)
2906 {
2907 /*
2908 * The description is a bit tricky as it may include chars that
2909 * xml requires to be escaped.
2910 */
2911 const char *pszBadChar = strpbrk(pDesc->pszDesc, "&<>\"'");
2912 if (!pszBadChar)
2913 return stamR3SnapshotPrintf(pThis, " desc=\"%s\"/>\n", pDesc->pszDesc);
2914
2915 stamR3SnapshotPrintf(pThis, " desc=\"");
2916 const char *pszCur = pDesc->pszDesc;
2917 do
2918 {
2919 stamR3SnapshotPrintf(pThis, "%.*s", pszBadChar - pszCur, pszCur);
2920 switch (*pszBadChar)
2921 {
2922 case '&': stamR3SnapshotPrintf(pThis, "&amp;"); break;
2923 case '<': stamR3SnapshotPrintf(pThis, "&lt;"); break;
2924 case '>': stamR3SnapshotPrintf(pThis, "&gt;"); break;
2925 case '"': stamR3SnapshotPrintf(pThis, "&quot;"); break;
2926 case '\'': stamR3SnapshotPrintf(pThis, "&apos;"); break;
2927 default: AssertMsgFailed(("%c", *pszBadChar)); break;
2928 }
2929 pszCur = pszBadChar + 1;
2930 pszBadChar = strpbrk(pszCur, "&<>\"'");
2931 } while (pszBadChar);
2932 return stamR3SnapshotPrintf(pThis, "%s\"/>\n", pszCur);
2933 }
2934 return stamR3SnapshotPrintf(pThis, "/>\n");
2935}
2936
2937
2938/**
2939 * Output callback for stamR3SnapshotPrintf.
2940 *
2941 * @returns number of bytes written.
2942 * @param pvArg The snapshot status structure.
2943 * @param pach Pointer to an array of characters (bytes).
2944 * @param cch The number or chars (bytes) to write from the array.
2945 */
2946static DECLCALLBACK(size_t) stamR3SnapshotOutput(void *pvArg, const char *pach, size_t cch)
2947{
2948 PSTAMR3SNAPSHOTONE pThis = (PSTAMR3SNAPSHOTONE)pvArg;
2949
2950 /*
2951 * Make sure we've got space for it.
2952 */
2953 if (RT_UNLIKELY((uintptr_t)pThis->pszEnd - (uintptr_t)pThis->psz < cch + 1))
2954 {
2955 if (RT_FAILURE(pThis->rc))
2956 return 0;
2957
2958 size_t cbNewSize = pThis->cbAllocated;
2959 if (cbNewSize > cch)
2960 cbNewSize *= 2;
2961 else
2962 cbNewSize += RT_ALIGN(cch + 1, 0x1000);
2963 char *pszNew = (char *)RTMemRealloc(pThis->pszStart, cbNewSize);
2964 if (!pszNew)
2965 {
2966 /*
2967 * Free up immediately, out-of-memory is bad news and this
2968 * isn't an important allocations / API.
2969 */
2970 pThis->rc = VERR_NO_MEMORY;
2971 RTMemFree(pThis->pszStart);
2972 pThis->pszStart = pThis->pszEnd = pThis->psz = NULL;
2973 pThis->cbAllocated = 0;
2974 return 0;
2975 }
2976
2977 pThis->psz = pszNew + (pThis->psz - pThis->pszStart);
2978 pThis->pszStart = pszNew;
2979 pThis->pszEnd = pszNew + cbNewSize;
2980 pThis->cbAllocated = cbNewSize;
2981 }
2982
2983 /*
2984 * Copy the chars to the buffer and terminate it.
2985 */
2986 if (cch)
2987 {
2988 memcpy(pThis->psz, pach, cch);
2989 pThis->psz += cch;
2990 }
2991 *pThis->psz = '\0';
2992 return cch;
2993}
2994
2995
2996/**
2997 * Wrapper around RTStrFormatV for use by the snapshot API.
2998 *
2999 * @returns VBox status code.
3000 * @param pThis The snapshot status structure.
3001 * @param pszFormat The format string.
3002 * @param ... Optional arguments.
3003 */
3004static int stamR3SnapshotPrintf(PSTAMR3SNAPSHOTONE pThis, const char *pszFormat, ...)
3005{
3006 va_list va;
3007 va_start(va, pszFormat);
3008 RTStrFormatV(stamR3SnapshotOutput, pThis, NULL, NULL, pszFormat, va);
3009 va_end(va);
3010 return pThis->rc;
3011}
3012
3013
3014/**
3015 * Releases a statistics snapshot returned by STAMR3Snapshot().
3016 *
3017 * @returns VBox status code.
3018 * @param pUVM The user mode VM handle.
3019 * @param pszSnapshot The snapshot data pointer returned by STAMR3Snapshot().
3020 * NULL is allowed.
3021 */
3022VMMR3DECL(int) STAMR3SnapshotFree(PUVM pUVM, char *pszSnapshot)
3023{
3024 if (pszSnapshot)
3025 RTMemFree(pszSnapshot);
3026 NOREF(pUVM);
3027 return VINF_SUCCESS;
3028}
3029
3030
3031/**
3032 * Dumps the selected statistics to the log.
3033 *
3034 * @returns VBox status code.
3035 * @param pUVM Pointer to the user mode VM structure.
3036 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
3037 * If NULL all samples are written to the log.
3038 */
3039VMMR3DECL(int) STAMR3Dump(PUVM pUVM, const char *pszPat)
3040{
3041 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
3042 VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
3043
3044 STAMR3PRINTONEARGS Args;
3045 Args.pUVM = pUVM;
3046 Args.pvArg = NULL;
3047 Args.pfnPrintf = stamR3EnumLogPrintf;
3048
3049 stamR3EnumU(pUVM, pszPat, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
3050 return VINF_SUCCESS;
3051}
3052
3053
3054/**
3055 * Prints to the log.
3056 *
3057 * @param pArgs Pointer to the print one argument structure.
3058 * @param pszFormat Format string.
3059 * @param ... Format arguments.
3060 */
3061static DECLCALLBACK(void) stamR3EnumLogPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...)
3062{
3063 va_list va;
3064 va_start(va, pszFormat);
3065 RTLogPrintfV(pszFormat, va);
3066 va_end(va);
3067 NOREF(pArgs);
3068}
3069
3070
3071/**
3072 * Dumps the selected statistics to the release log.
3073 *
3074 * @returns VBox status code.
3075 * @param pUVM Pointer to the user mode VM structure.
3076 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
3077 * If NULL all samples are written to the log.
3078 */
3079VMMR3DECL(int) STAMR3DumpToReleaseLog(PUVM pUVM, const char *pszPat)
3080{
3081 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
3082 VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
3083
3084 STAMR3PRINTONEARGS Args;
3085 Args.pUVM = pUVM;
3086 Args.pvArg = NULL;
3087 Args.pfnPrintf = stamR3EnumRelLogPrintf;
3088
3089 stamR3EnumU(pUVM, pszPat, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
3090 return VINF_SUCCESS;
3091}
3092
3093/**
3094 * Prints to the release log.
3095 *
3096 * @param pArgs Pointer to the print one argument structure.
3097 * @param pszFormat Format string.
3098 * @param ... Format arguments.
3099 */
3100static DECLCALLBACK(void) stamR3EnumRelLogPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...)
3101{
3102 va_list va;
3103 va_start(va, pszFormat);
3104 RTLogRelPrintfV(pszFormat, va);
3105 va_end(va);
3106 NOREF(pArgs);
3107}
3108
3109
3110/**
3111 * Prints the selected statistics to standard out.
3112 *
3113 * @returns VBox status code.
3114 * @param pUVM The user mode VM handle.
3115 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
3116 * If NULL all samples are reset.
3117 */
3118VMMR3DECL(int) STAMR3Print(PUVM pUVM, const char *pszPat)
3119{
3120 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
3121 VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
3122
3123 STAMR3PRINTONEARGS Args;
3124 Args.pUVM = pUVM;
3125 Args.pvArg = NULL;
3126 Args.pfnPrintf = stamR3EnumPrintf;
3127
3128 stamR3EnumU(pUVM, pszPat, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
3129 return VINF_SUCCESS;
3130}
3131
3132
3133/**
3134 * Prints to stdout.
3135 *
3136 * @param pArgs Pointer to the print one argument structure.
3137 * @param pszFormat Format string.
3138 * @param ... Format arguments.
3139 */
3140static DECLCALLBACK(void) stamR3EnumPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...)
3141{
3142 va_list va;
3143 va_start(va, pszFormat);
3144 RTPrintfV(pszFormat, va);
3145 va_end(va);
3146 NOREF(pArgs);
3147}
3148
3149
3150/**
3151 * Prints one sample.
3152 * Callback for stamR3EnumU().
3153 *
3154 * @returns VINF_SUCCESS
3155 * @param pDesc Pointer to the current descriptor.
3156 * @param pvArg User argument - STAMR3PRINTONEARGS.
3157 */
3158static int stamR3PrintOne(PSTAMDESC pDesc, void *pvArg)
3159{
3160 PSTAMR3PRINTONEARGS pArgs = (PSTAMR3PRINTONEARGS)pvArg;
3161
3162 switch (pDesc->enmType)
3163 {
3164 case STAMTYPE_COUNTER:
3165 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pDesc->u.pCounter->c == 0)
3166 return VINF_SUCCESS;
3167
3168 pArgs->pfnPrintf(pArgs, "%-32s %8llu %s\n", pDesc->pszName, pDesc->u.pCounter->c, STAMR3GetUnit(pDesc->enmUnit));
3169 break;
3170
3171 case STAMTYPE_PROFILE:
3172 case STAMTYPE_PROFILE_ADV:
3173 {
3174 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pDesc->u.pProfile->cPeriods == 0)
3175 return VINF_SUCCESS;
3176
3177 uint64_t const u64 = pDesc->u.pProfile->cPeriods ? pDesc->u.pProfile->cPeriods : 1;
3178 pArgs->pfnPrintf(pArgs, "%-32s %8llu %s (%12llu %s, %7llu %s, max %9llu, min %7lld)\n", pDesc->pszName,
3179 pDesc->u.pProfile->cTicks / u64, STAMR3GetUnit(pDesc->enmUnit),
3180 pDesc->u.pProfile->cTicks, STAMR3GetUnit1(pDesc->enmUnit),
3181 pDesc->u.pProfile->cPeriods, STAMR3GetUnit2(pDesc->enmUnit),
3182 pDesc->u.pProfile->cTicksMax, pDesc->u.pProfile->cTicksMin);
3183 break;
3184 }
3185
3186 case STAMTYPE_RATIO_U32:
3187 case STAMTYPE_RATIO_U32_RESET:
3188 if (pDesc->enmVisibility == STAMVISIBILITY_USED && !pDesc->u.pRatioU32->u32A && !pDesc->u.pRatioU32->u32B)
3189 return VINF_SUCCESS;
3190 pArgs->pfnPrintf(pArgs, "%-32s %8u:%-8u %s\n", pDesc->pszName,
3191 pDesc->u.pRatioU32->u32A, pDesc->u.pRatioU32->u32B, STAMR3GetUnit(pDesc->enmUnit));
3192 break;
3193
3194 case STAMTYPE_CALLBACK:
3195 {
3196 char szBuf[512];
3197 pDesc->u.Callback.pfnPrint(pArgs->pUVM->pVM, pDesc->u.Callback.pvSample, szBuf, sizeof(szBuf));
3198 pArgs->pfnPrintf(pArgs, "%-32s %s %s\n", pDesc->pszName, szBuf, STAMR3GetUnit(pDesc->enmUnit));
3199 break;
3200 }
3201
3202 case STAMTYPE_U8:
3203 case STAMTYPE_U8_RESET:
3204 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu8 == 0)
3205 return VINF_SUCCESS;
3206 pArgs->pfnPrintf(pArgs, "%-32s %8u %s\n", pDesc->pszName, *pDesc->u.pu8, STAMR3GetUnit(pDesc->enmUnit));
3207 break;
3208
3209 case STAMTYPE_X8:
3210 case STAMTYPE_X8_RESET:
3211 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu8 == 0)
3212 return VINF_SUCCESS;
3213 pArgs->pfnPrintf(pArgs, "%-32s %8x %s\n", pDesc->pszName, *pDesc->u.pu8, STAMR3GetUnit(pDesc->enmUnit));
3214 break;
3215
3216 case STAMTYPE_U16:
3217 case STAMTYPE_U16_RESET:
3218 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu16 == 0)
3219 return VINF_SUCCESS;
3220 pArgs->pfnPrintf(pArgs, "%-32s %8u %s\n", pDesc->pszName, *pDesc->u.pu16, STAMR3GetUnit(pDesc->enmUnit));
3221 break;
3222
3223 case STAMTYPE_X16:
3224 case STAMTYPE_X16_RESET:
3225 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu16 == 0)
3226 return VINF_SUCCESS;
3227 pArgs->pfnPrintf(pArgs, "%-32s %8x %s\n", pDesc->pszName, *pDesc->u.pu16, STAMR3GetUnit(pDesc->enmUnit));
3228 break;
3229
3230 case STAMTYPE_U32:
3231 case STAMTYPE_U32_RESET:
3232 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu32 == 0)
3233 return VINF_SUCCESS;
3234 pArgs->pfnPrintf(pArgs, "%-32s %8u %s\n", pDesc->pszName, *pDesc->u.pu32, STAMR3GetUnit(pDesc->enmUnit));
3235 break;
3236
3237 case STAMTYPE_X32:
3238 case STAMTYPE_X32_RESET:
3239 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu32 == 0)
3240 return VINF_SUCCESS;
3241 pArgs->pfnPrintf(pArgs, "%-32s %8x %s\n", pDesc->pszName, *pDesc->u.pu32, STAMR3GetUnit(pDesc->enmUnit));
3242 break;
3243
3244 case STAMTYPE_U64:
3245 case STAMTYPE_U64_RESET:
3246 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu64 == 0)
3247 return VINF_SUCCESS;
3248 pArgs->pfnPrintf(pArgs, "%-32s %8llu %s\n", pDesc->pszName, *pDesc->u.pu64, STAMR3GetUnit(pDesc->enmUnit));
3249 break;
3250
3251 case STAMTYPE_X64:
3252 case STAMTYPE_X64_RESET:
3253 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu64 == 0)
3254 return VINF_SUCCESS;
3255 pArgs->pfnPrintf(pArgs, "%-32s %8llx %s\n", pDesc->pszName, *pDesc->u.pu64, STAMR3GetUnit(pDesc->enmUnit));
3256 break;
3257
3258 case STAMTYPE_BOOL:
3259 case STAMTYPE_BOOL_RESET:
3260 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pf == false)
3261 return VINF_SUCCESS;
3262 pArgs->pfnPrintf(pArgs, "%-32s %s %s\n", pDesc->pszName, *pDesc->u.pf ? "true " : "false ", STAMR3GetUnit(pDesc->enmUnit));
3263 break;
3264
3265 case STAMTYPE_INTERNAL_SUM:
3266 {
3267 PSTAMSUMSAMPLE const pSum = pDesc->u.pSum;
3268 stamR3SumRefresh(pSum);
3269 switch (pSum->enmType)
3270 {
3271 case STAMTYPE_COUNTER:
3272 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pSum->u.Counter.c == 0)
3273 return VINF_SUCCESS;
3274 pArgs->pfnPrintf(pArgs, "%-32s %8llu %s\n", pDesc->pszName, pSum->u.Counter.c, STAMR3GetUnit(pDesc->enmUnit));
3275 break;
3276
3277 case STAMTYPE_PROFILE:
3278 {
3279 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pSum->u.Profile.cPeriods == 0)
3280 return VINF_SUCCESS;
3281
3282 uint64_t const u64 = pSum->u.Profile.cPeriods ? pSum->u.Profile.cPeriods : 1;
3283 pArgs->pfnPrintf(pArgs, "%-32s %8llu %s (%12llu %s, %7llu %s, max %9llu, min %7lld)\n", pDesc->pszName,
3284 pSum->u.Profile.cTicks / u64, STAMR3GetUnit(pDesc->enmUnit),
3285 pSum->u.Profile.cTicks, STAMR3GetUnit1(pDesc->enmUnit),
3286 pSum->u.Profile.cPeriods, STAMR3GetUnit2(pDesc->enmUnit),
3287 pSum->u.Profile.cTicksMax, pSum->u.Profile.cTicksMin);
3288 break;
3289 }
3290
3291 default:
3292 AssertMsgFailed(("%d\n", pSum->enmType));
3293 break;
3294 }
3295 break;
3296 }
3297
3298 case STAMTYPE_INTERNAL_PCT_OF_SUM:
3299 {
3300 PSTAMSUMSAMPLE const pSum = pDesc->u.pSum;
3301 stamR3PctOfSumRefresh(pDesc, pSum);
3302 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pSum->u.Counter.c == 0)
3303 return VINF_SUCCESS;
3304 pArgs->pfnPrintf(pArgs, "%-32s %8llu %s\n", pDesc->pszName, pSum->u.Counter.c, STAMR3GetUnit(pDesc->enmUnit));
3305 break;
3306 }
3307
3308 default:
3309 AssertMsgFailed(("enmType=%d\n", pDesc->enmType));
3310 break;
3311 }
3312 return VINF_SUCCESS;
3313}
3314
3315
3316/**
3317 * Enumerate the statistics by the means of a callback function.
3318 *
3319 * @returns Whatever the callback returns.
3320 *
3321 * @param pUVM The user mode VM handle.
3322 * @param pszPat The pattern to match samples.
3323 * @param pfnEnum The callback function.
3324 * @param pvUser The pvUser argument of the callback function.
3325 */
3326VMMR3DECL(int) STAMR3Enum(PUVM pUVM, const char *pszPat, PFNSTAMR3ENUM pfnEnum, void *pvUser)
3327{
3328 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
3329 VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE);
3330
3331 STAMR3ENUMONEARGS Args;
3332 Args.pVM = pUVM->pVM;
3333 Args.pfnEnum = pfnEnum;
3334 Args.pvUser = pvUser;
3335
3336 return stamR3EnumU(pUVM, pszPat, true /* fUpdateRing0 */, stamR3EnumOne, &Args);
3337}
3338
3339
3340/**
3341 * Callback function for STARTR3Enum().
3342 *
3343 * @returns whatever the callback returns.
3344 * @param pDesc Pointer to the current descriptor.
3345 * @param pvArg Points to a STAMR3ENUMONEARGS structure.
3346 */
3347static int stamR3EnumOne(PSTAMDESC pDesc, void *pvArg)
3348{
3349 PSTAMR3ENUMONEARGS const pArgs = (PSTAMR3ENUMONEARGS)pvArg;
3350 const char * const pszUnit = STAMR3GetUnit(pDesc->enmUnit);
3351 switch (pDesc->enmType)
3352 {
3353 default:
3354 return pArgs->pfnEnum(pDesc->pszName, pDesc->enmType, pDesc->u.pv, pDesc->enmUnit, pszUnit,
3355 pDesc->enmVisibility, pDesc->pszDesc, pArgs->pvUser);
3356
3357 case STAMTYPE_CALLBACK:
3358 {
3359 /* Give the enumerator something useful. */
3360 char szBuf[512];
3361 pDesc->u.Callback.pfnPrint(pArgs->pVM, pDesc->u.Callback.pvSample, szBuf, sizeof(szBuf));
3362 return pArgs->pfnEnum(pDesc->pszName, pDesc->enmType, szBuf, pDesc->enmUnit, pszUnit,
3363 pDesc->enmVisibility, pDesc->pszDesc, pArgs->pvUser);
3364 }
3365
3366 case STAMTYPE_INTERNAL_SUM:
3367 {
3368 PSTAMSUMSAMPLE const pSum = pDesc->u.pSum;
3369 stamR3SumRefresh(pSum);
3370 return pArgs->pfnEnum(pDesc->pszName, pSum->enmType, &pSum->u, pDesc->enmUnit, pszUnit,
3371 pDesc->enmVisibility, pDesc->pszDesc, pArgs->pvUser);
3372 }
3373
3374 case STAMTYPE_INTERNAL_PCT_OF_SUM:
3375 {
3376 PSTAMSUMSAMPLE const pSum = pDesc->u.pSum;
3377 stamR3PctOfSumRefresh(pDesc, pSum);
3378 return pArgs->pfnEnum(pDesc->pszName, pSum->enmType, &pSum->u, pDesc->enmUnit, pszUnit,
3379 pDesc->enmVisibility, pDesc->pszDesc, pArgs->pvUser);
3380 }
3381 }
3382}
3383
3384static void stamR3RefreshGroup(PUVM pUVM, uint8_t iRefreshGroup, uint64_t *pbmRefreshedGroups)
3385{
3386 *pbmRefreshedGroups |= RT_BIT_64(iRefreshGroup);
3387
3388 PVM pVM = pUVM->pVM;
3389 if (pVM && pVM->pSession)
3390 {
3391 switch (iRefreshGroup)
3392 {
3393 /*
3394 * GVMM
3395 */
3396 case STAM_REFRESH_GRP_GVMM:
3397 {
3398 GVMMQUERYSTATISTICSSREQ Req;
3399 Req.Hdr.cbReq = sizeof(Req);
3400 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
3401 Req.pSession = pVM->pSession;
3402 int rc = SUPR3CallVMMR0Ex(VMCC_GET_VMR0_FOR_CALL(pVM), NIL_VMCPUID, VMMR0_DO_GVMM_QUERY_STATISTICS, 0, &Req.Hdr);
3403 if (RT_SUCCESS(rc))
3404 {
3405 pUVM->stam.s.GVMMStats = Req.Stats;
3406
3407 /*
3408 * Check if the number of host CPUs has changed (it will the first
3409 * time around and normally never again).
3410 */
3411 if (RT_UNLIKELY(pUVM->stam.s.GVMMStats.cHostCpus > pUVM->stam.s.cRegisteredHostCpus))
3412 {
3413 if (RT_UNLIKELY(pUVM->stam.s.GVMMStats.cHostCpus > pUVM->stam.s.cRegisteredHostCpus))
3414 {
3415 STAM_UNLOCK_RD(pUVM);
3416 STAM_LOCK_WR(pUVM);
3417 uint32_t cCpus = pUVM->stam.s.GVMMStats.cHostCpus;
3418 for (uint32_t iCpu = pUVM->stam.s.cRegisteredHostCpus; iCpu < cCpus; iCpu++)
3419 {
3420 char szName[120];
3421 size_t cchBase = RTStrPrintf(szName, sizeof(szName), "/GVMM/HostCpus/%u", iCpu);
3422 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].idCpu, NULL, NULL,
3423 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_NONE,
3424 "Host CPU ID", STAM_REFRESH_GRP_GVMM);
3425 strcpy(&szName[cchBase], "/idxCpuSet");
3426 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].idxCpuSet, NULL, NULL,
3427 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_NONE,
3428 "CPU Set index", STAM_REFRESH_GRP_GVMM);
3429 strcpy(&szName[cchBase], "/DesiredHz");
3430 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].uDesiredHz, NULL, NULL,
3431 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_HZ,
3432 "The desired frequency", STAM_REFRESH_GRP_GVMM);
3433 strcpy(&szName[cchBase], "/CurTimerHz");
3434 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].uTimerHz, NULL, NULL,
3435 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_HZ,
3436 "The current timer frequency", STAM_REFRESH_GRP_GVMM);
3437 strcpy(&szName[cchBase], "/PPTChanges");
3438 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].cChanges, NULL, NULL,
3439 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_OCCURENCES,
3440 "RTTimerChangeInterval calls", STAM_REFRESH_GRP_GVMM);
3441 strcpy(&szName[cchBase], "/PPTStarts");
3442 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].cStarts, NULL, NULL,
3443 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_OCCURENCES,
3444 "RTTimerStart calls", STAM_REFRESH_GRP_GVMM);
3445 }
3446 pUVM->stam.s.cRegisteredHostCpus = cCpus;
3447 STAM_UNLOCK_WR(pUVM);
3448 STAM_LOCK_RD(pUVM);
3449 }
3450 }
3451 }
3452 break;
3453 }
3454
3455 /*
3456 * GMM
3457 */
3458 case STAM_REFRESH_GRP_GMM:
3459 {
3460 GMMQUERYSTATISTICSSREQ Req;
3461 Req.Hdr.cbReq = sizeof(Req);
3462 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
3463 Req.pSession = pVM->pSession;
3464 int rc = SUPR3CallVMMR0Ex(VMCC_GET_VMR0_FOR_CALL(pVM), NIL_VMCPUID, VMMR0_DO_GMM_QUERY_STATISTICS, 0, &Req.Hdr);
3465 if (RT_SUCCESS(rc))
3466 pUVM->stam.s.GMMStats = Req.Stats;
3467 break;
3468 }
3469
3470 /*
3471 * NEM.
3472 */
3473 case STAM_REFRESH_GRP_NEM:
3474 SUPR3CallVMMR0(VMCC_GET_VMR0_FOR_CALL(pVM), NIL_VMCPUID, VMMR0_DO_NEM_UPDATE_STATISTICS, NULL);
3475 break;
3476
3477 default:
3478 AssertMsgFailed(("iRefreshGroup=%d\n", iRefreshGroup));
3479 }
3480 }
3481}
3482
3483
3484/**
3485 * Refreshes the statistics behind the given entry, if necessary.
3486 *
3487 * This helps implement fetching global ring-0 stats into ring-3 accessible
3488 * storage. GVMM, GMM and NEM makes use of this.
3489 *
3490 * @param pUVM The user mode VM handle.
3491 * @param pCur The statistics descriptor which group to check
3492 * and maybe update.
3493 * @param pbmRefreshedGroups Bitmap tracking what has already been updated.
3494 */
3495DECLINLINE(void) stamR3Refresh(PUVM pUVM, PSTAMDESC pCur, uint64_t *pbmRefreshedGroups)
3496{
3497 uint8_t const iRefreshGroup = pCur->iRefreshGroup;
3498 if (RT_LIKELY(iRefreshGroup == STAM_REFRESH_GRP_NONE))
3499 { /* likely */ }
3500 else if (!(*pbmRefreshedGroups & RT_BIT_64(iRefreshGroup)))
3501 stamR3RefreshGroup(pUVM, iRefreshGroup, pbmRefreshedGroups);
3502}
3503
3504
3505/**
3506 * Match a name against an array of patterns.
3507 *
3508 * @returns true if it matches, false if it doesn't match.
3509 * @param papszExpressions The array of pattern expressions.
3510 * @param cExpressions The number of array entries.
3511 * @param piExpression Where to read/store the current skip index. Optional.
3512 * @param pszName The name to match.
3513 */
3514static bool stamR3MultiMatch(const char * const *papszExpressions, unsigned cExpressions,
3515 unsigned *piExpression, const char *pszName)
3516{
3517 for (unsigned i = piExpression ? *piExpression : 0; i < cExpressions; i++)
3518 {
3519 const char *pszPat = papszExpressions[i];
3520 if (RTStrSimplePatternMatch(pszPat, pszName))
3521 {
3522 /* later:
3523 if (piExpression && i > *piExpression)
3524 {
3525 Check if we can skip some expressions.
3526 Requires the expressions to be sorted.
3527 }*/
3528 return true;
3529 }
3530 }
3531 return false;
3532}
3533
3534
3535/**
3536 * Splits a multi pattern into single ones.
3537 *
3538 * @returns Pointer to an array of single patterns. Free it with RTMemTmpFree.
3539 * @param pszPat The pattern to split.
3540 * @param pcExpressions The number of array elements.
3541 * @param ppszCopy The pattern copy to free using RTStrFree.
3542 */
3543static char **stamR3SplitPattern(const char *pszPat, unsigned *pcExpressions, char **ppszCopy)
3544{
3545 Assert(pszPat && *pszPat);
3546
3547 char *pszCopy = RTStrDup(pszPat);
3548 if (!pszCopy)
3549 return NULL;
3550
3551 /* count them & allocate array. */
3552 char *psz = pszCopy;
3553 unsigned cExpressions = 1;
3554 while ((psz = strchr(psz, '|')) != NULL)
3555 cExpressions++, psz++;
3556
3557 char **papszExpressions = (char **)RTMemTmpAllocZ((cExpressions + 1) * sizeof(char *));
3558 if (!papszExpressions)
3559 {
3560 RTStrFree(pszCopy);
3561 return NULL;
3562 }
3563
3564 /* split */
3565 psz = pszCopy;
3566 for (unsigned i = 0;;)
3567 {
3568 papszExpressions[i] = psz;
3569 if (++i >= cExpressions)
3570 break;
3571 psz = strchr(psz, '|');
3572 *psz++ = '\0';
3573 }
3574
3575 /* sort the array, putting '*' last. */
3576 /** @todo sort it... */
3577
3578 *pcExpressions = cExpressions;
3579 *ppszCopy = pszCopy;
3580 return papszExpressions;
3581}
3582
3583
3584/**
3585 * Enumerates the nodes selected by a pattern or all nodes if no pattern
3586 * is specified.
3587 *
3588 * The call may lock STAM for writing before calling this function, however do
3589 * not lock it for reading as this function may need to write lock STAM.
3590 *
3591 * @returns The rc from the callback.
3592 * @param pUVM Pointer to the user mode VM structure.
3593 * @param pszPat Pattern.
3594 * @param fUpdateRing0 Update the stats residing in ring-0.
3595 * @param pfnCallback Callback function which shall be called for matching nodes.
3596 * If it returns anything but VINF_SUCCESS the enumeration is
3597 * terminated and the status code returned to the caller.
3598 * @param pvArg User parameter for the callback.
3599 */
3600static int stamR3EnumU(PUVM pUVM, const char *pszPat, bool fUpdateRing0,
3601 int (*pfnCallback)(PSTAMDESC pDesc, void *pvArg), void *pvArg)
3602{
3603 size_t const cchPat = pszPat ? strlen(pszPat) : 0;
3604 int rc = VINF_SUCCESS;
3605 uint64_t bmRefreshedGroups = 0;
3606 PSTAMDESC pCur;
3607
3608 /*
3609 * All.
3610 */
3611 if ( cchPat < 1
3612 || ( cchPat == 1
3613 && *pszPat == '*'))
3614 {
3615 STAM_LOCK_RD(pUVM);
3616 RTListForEach(&pUVM->stam.s.List, pCur, STAMDESC, ListEntry)
3617 {
3618 if (fUpdateRing0)
3619 stamR3Refresh(pUVM, pCur, &bmRefreshedGroups);
3620 rc = pfnCallback(pCur, pvArg);
3621 if (rc)
3622 break;
3623 }
3624 STAM_UNLOCK_RD(pUVM);
3625 }
3626
3627 /*
3628 * Single expression pattern.
3629 */
3630 else if (memchr(pszPat, '|', cchPat) == NULL)
3631 {
3632 const char *pszAsterisk = (const char *)memchr(pszPat, '*', cchPat);
3633 const char *pszQuestion = (const char *)memchr(pszPat, '?', cchPat);
3634
3635 STAM_LOCK_RD(pUVM);
3636 if (!pszAsterisk && !pszQuestion)
3637 {
3638 pCur = stamR3LookupFindDesc(pUVM->stam.s.pRoot, pszPat);
3639 if (pCur)
3640 {
3641 if (fUpdateRing0)
3642 stamR3Refresh(pUVM, pCur, &bmRefreshedGroups);
3643 rc = pfnCallback(pCur, pvArg);
3644 }
3645 }
3646 /* Is this a prefix expression where we can use the lookup tree to
3647 efficiently figure out the exact range? */
3648 else if ( pszAsterisk == &pszPat[cchPat - 1]
3649 && pszPat[0] == '/'
3650 && !pszQuestion)
3651 {
3652 PSTAMDESC pLast;
3653 pCur = stamR3LookupFindByPrefixRange(pUVM->stam.s.pRoot, pszPat, (uint32_t)(cchPat - 1), &pLast);
3654 if (pCur)
3655 {
3656 for (;;)
3657 {
3658 Assert(strncmp(pCur->pszName, pszPat, cchPat - 1) == 0);
3659 if (fUpdateRing0)
3660 stamR3Refresh(pUVM, pCur, &bmRefreshedGroups);
3661 rc = pfnCallback(pCur, pvArg);
3662 if (rc)
3663 break;
3664 if (pCur == pLast)
3665 break;
3666 pCur = RTListNodeGetNext(&pCur->ListEntry, STAMDESC, ListEntry);
3667 }
3668 Assert(pLast);
3669 }
3670 else
3671 Assert(!pLast);
3672 }
3673 else
3674 {
3675 /* It's a more complicated pattern. Find the approximate range
3676 and scan it for matches. */
3677 PSTAMDESC pLast;
3678 pCur = stamR3LookupFindPatternDescRange(pUVM->stam.s.pRoot, &pUVM->stam.s.List, pszPat, &pLast);
3679 if (pCur)
3680 {
3681 for (;;)
3682 {
3683 if (RTStrSimplePatternMatch(pszPat, pCur->pszName))
3684 {
3685 if (fUpdateRing0)
3686 stamR3Refresh(pUVM, pCur, &bmRefreshedGroups);
3687 rc = pfnCallback(pCur, pvArg);
3688 if (rc)
3689 break;
3690 }
3691 if (pCur == pLast)
3692 break;
3693 pCur = RTListNodeGetNext(&pCur->ListEntry, STAMDESC, ListEntry);
3694 }
3695 Assert(pLast);
3696 }
3697 else
3698 Assert(!pLast);
3699 }
3700 STAM_UNLOCK_RD(pUVM);
3701 }
3702
3703 /*
3704 * Multi expression pattern.
3705 */
3706 else
3707 {
3708 /*
3709 * Split up the pattern first.
3710 */
3711 char *pszCopy;
3712 unsigned cExpressions;
3713 char **papszExpressions = stamR3SplitPattern(pszPat, &cExpressions, &pszCopy);
3714 if (!papszExpressions)
3715 return VERR_NO_MEMORY;
3716
3717 /*
3718 * Perform the enumeration.
3719 */
3720 STAM_LOCK_RD(pUVM);
3721 unsigned iExpression = 0;
3722 RTListForEach(&pUVM->stam.s.List, pCur, STAMDESC, ListEntry)
3723 {
3724 if (stamR3MultiMatch(papszExpressions, cExpressions, &iExpression, pCur->pszName))
3725 {
3726 if (fUpdateRing0)
3727 stamR3Refresh(pUVM, pCur, &bmRefreshedGroups);
3728 rc = pfnCallback(pCur, pvArg);
3729 if (rc)
3730 break;
3731 }
3732 }
3733 STAM_UNLOCK_RD(pUVM);
3734
3735 RTMemTmpFree(papszExpressions);
3736 RTStrFree(pszCopy);
3737 }
3738
3739 return rc;
3740}
3741
3742
3743/**
3744 * Registers the ring-0 statistics.
3745 *
3746 * @param pUVM Pointer to the user mode VM structure.
3747 */
3748static void stamR3Ring0StatsRegisterU(PUVM pUVM)
3749{
3750 /* GVMM */
3751 for (unsigned i = 0; i < RT_ELEMENTS(g_aGVMMStats); i++)
3752 stamR3RegisterU(pUVM, (uint8_t *)&pUVM->stam.s.GVMMStats + g_aGVMMStats[i].offVar, NULL, NULL,
3753 g_aGVMMStats[i].enmType, STAMVISIBILITY_ALWAYS, g_aGVMMStats[i].pszName,
3754 g_aGVMMStats[i].enmUnit, g_aGVMMStats[i].pszDesc, STAM_REFRESH_GRP_GVMM);
3755
3756 for (unsigned i = 0; i < pUVM->cCpus; i++)
3757 {
3758 char szName[120];
3759 size_t cchBase = RTStrPrintf(szName, sizeof(szName), pUVM->cCpus < 10 ? "/GVMM/VCpus/%u/" : "/GVMM/VCpus/%02u/", i);
3760
3761 strcpy(&szName[cchBase], "cWakeUpTimerHits");
3762 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aVCpus[i].cWakeUpTimerHits, NULL, NULL,
3763 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_OCCURENCES, "", STAM_REFRESH_GRP_GVMM);
3764
3765 strcpy(&szName[cchBase], "cWakeUpTimerMisses");
3766 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aVCpus[i].cWakeUpTimerMisses, NULL, NULL,
3767 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_OCCURENCES, "", STAM_REFRESH_GRP_GVMM);
3768
3769 strcpy(&szName[cchBase], "cWakeUpTimerCanceled");
3770 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aVCpus[i].cWakeUpTimerCanceled, NULL, NULL,
3771 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_OCCURENCES, "", STAM_REFRESH_GRP_GVMM);
3772
3773 strcpy(&szName[cchBase], "cWakeUpTimerSameCpu");
3774 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aVCpus[i].cWakeUpTimerSameCpu, NULL, NULL,
3775 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_OCCURENCES, "", STAM_REFRESH_GRP_GVMM);
3776
3777 strcpy(&szName[cchBase], "Start");
3778 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aVCpus[i].Start, NULL, NULL,
3779 STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_TICKS_PER_CALL, "", STAM_REFRESH_GRP_GVMM);
3780
3781 strcpy(&szName[cchBase], "Stop");
3782 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aVCpus[i].Stop, NULL, NULL,
3783 STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_TICKS_PER_CALL, "", STAM_REFRESH_GRP_GVMM);
3784 }
3785 pUVM->stam.s.cRegisteredHostCpus = 0;
3786
3787 /* GMM */
3788 for (unsigned i = 0; i < RT_ELEMENTS(g_aGMMStats); i++)
3789 stamR3RegisterU(pUVM, (uint8_t *)&pUVM->stam.s.GMMStats + g_aGMMStats[i].offVar, NULL, NULL,
3790 g_aGMMStats[i].enmType, STAMVISIBILITY_ALWAYS, g_aGMMStats[i].pszName,
3791 g_aGMMStats[i].enmUnit, g_aGMMStats[i].pszDesc, STAM_REFRESH_GRP_GMM);
3792}
3793
3794
3795/**
3796 * Get the unit string.
3797 *
3798 * @returns Pointer to read only unit string.
3799 * @param enmUnit The unit.
3800 */
3801VMMR3DECL(const char *) STAMR3GetUnit(STAMUNIT enmUnit)
3802{
3803 switch (enmUnit)
3804 {
3805 case STAMUNIT_NONE: return "";
3806 case STAMUNIT_CALLS: return "calls";
3807 case STAMUNIT_CALLS_PER_TB: return "calls/tb";
3808 case STAMUNIT_COUNT: return "count";
3809 case STAMUNIT_BYTES: return "bytes";
3810 case STAMUNIT_BYTES_PER_CALL: return "bytes/call";
3811 case STAMUNIT_PAGES: return "pages";
3812 case STAMUNIT_ERRORS: return "errors";
3813 case STAMUNIT_OCCURENCES: return "times";
3814 case STAMUNIT_TICKS: return "ticks";
3815 case STAMUNIT_TICKS_PER_CALL: return "ticks/call";
3816 case STAMUNIT_TICKS_PER_OCCURENCE: return "ticks/time";
3817 case STAMUNIT_GOOD_BAD: return "good:bad";
3818 case STAMUNIT_MEGABYTES: return "megabytes";
3819 case STAMUNIT_KILOBYTES: return "kilobytes";
3820 case STAMUNIT_NS: return "ns";
3821 case STAMUNIT_NS_PER_CALL: return "ns/call";
3822 case STAMUNIT_NS_PER_OCCURENCE: return "ns/time";
3823 case STAMUNIT_PCT: return "%";
3824 case STAMUNIT_PP1K: return "pp1k";
3825 case STAMUNIT_PP10K: return "pp10k";
3826 case STAMUNIT_PPM: return "ppm";
3827 case STAMUNIT_PPB: return "ppb";
3828 case STAMUNIT_HZ: return "Hz";
3829 case STAMUNIT_INSTR: return "instr";
3830 case STAMUNIT_INSTR_PER_TB: return "instr/tb";
3831 case STAMUNIT_BYTES_PER_TB: return "bytes/tb";
3832
3833 default:
3834 AssertMsgFailed(("Unknown unit %d\n", enmUnit));
3835 return "(?unit?)";
3836 }
3837}
3838
3839
3840/**
3841 * For something per something-else unit, get the first something.
3842 *
3843 * @returns Pointer to read only unit string.
3844 * @param enmUnit The unit.
3845 */
3846VMMR3DECL(const char *) STAMR3GetUnit1(STAMUNIT enmUnit)
3847{
3848 switch (enmUnit)
3849 {
3850 case STAMUNIT_NONE: return "";
3851 case STAMUNIT_CALLS: return "calls";
3852 case STAMUNIT_CALLS_PER_TB: return "calls";
3853 case STAMUNIT_COUNT: return "count";
3854 case STAMUNIT_BYTES: return "bytes";
3855 case STAMUNIT_BYTES_PER_CALL: return "bytes";
3856 case STAMUNIT_PAGES: return "pages";
3857 case STAMUNIT_ERRORS: return "errors";
3858 case STAMUNIT_OCCURENCES: return "times";
3859 case STAMUNIT_TICKS: return "ticks";
3860 case STAMUNIT_TICKS_PER_CALL: return "ticks";
3861 case STAMUNIT_TICKS_PER_OCCURENCE: return "ticks";
3862 case STAMUNIT_GOOD_BAD: return "good";
3863 case STAMUNIT_MEGABYTES: return "megabytes";
3864 case STAMUNIT_KILOBYTES: return "kilobytes";
3865 case STAMUNIT_NS: return "ns";
3866 case STAMUNIT_NS_PER_CALL: return "ns";
3867 case STAMUNIT_NS_PER_OCCURENCE: return "ns";
3868 case STAMUNIT_PCT: return "%";
3869 case STAMUNIT_PP1K: return "pp1k";
3870 case STAMUNIT_PP10K: return "pp10k";
3871 case STAMUNIT_PPM: return "ppm";
3872 case STAMUNIT_PPB: return "ppb";
3873 case STAMUNIT_HZ: return "Hz";
3874 case STAMUNIT_INSTR: return "instr";
3875 case STAMUNIT_INSTR_PER_TB: return "instr";
3876 case STAMUNIT_BYTES_PER_TB: return "bytes";
3877
3878 default:
3879 AssertMsgFailed(("Unknown unit %d\n", enmUnit));
3880 return "(?unit?)";
3881 }
3882}
3883
3884
3885/**
3886 * For something per something-else unit, get the something-else.
3887 *
3888 * @returns Pointer to read only unit string.
3889 * @param enmUnit The unit.
3890 */
3891VMMR3DECL(const char *) STAMR3GetUnit2(STAMUNIT enmUnit)
3892{
3893 switch (enmUnit)
3894 {
3895 case STAMUNIT_TICKS_PER_CALL: return "calls";
3896 case STAMUNIT_NS_PER_CALL: return "calls";
3897 case STAMUNIT_BYTES_PER_CALL: return "calls";
3898 case STAMUNIT_TICKS_PER_OCCURENCE: return "times";
3899 case STAMUNIT_NS_PER_OCCURENCE: return "times";
3900 case STAMUNIT_NONE: return "times";
3901 case STAMUNIT_GOOD_BAD: return "bad";
3902 case STAMUNIT_CALLS_PER_TB: return "tbs";
3903 case STAMUNIT_INSTR_PER_TB: return "tbs";
3904 case STAMUNIT_BYTES_PER_TB: return "tbs";
3905 default:
3906 AssertMsgFailed(("Wrong unit %d\n", enmUnit));
3907 return "times";
3908 }
3909}
3910
3911#ifdef VBOX_WITH_DEBUGGER
3912
3913/**
3914 * @callback_method_impl{FNDBGCCMD, The '.stats' command.}
3915 */
3916static DECLCALLBACK(int) stamR3CmdStats(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
3917{
3918 /*
3919 * Validate input.
3920 */
3921 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
3922 if (RTListIsEmpty(&pUVM->stam.s.List))
3923 return DBGCCmdHlpFail(pCmdHlp, pCmd, "No statistics present");
3924
3925 /*
3926 * Do the printing.
3927 */
3928 STAMR3PRINTONEARGS Args;
3929 Args.pUVM = pUVM;
3930 Args.pvArg = pCmdHlp;
3931 Args.pfnPrintf = stamR3EnumDbgfPrintf;
3932
3933 return stamR3EnumU(pUVM, cArgs ? paArgs[0].u.pszString : NULL, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
3934}
3935
3936
3937/**
3938 * Display one sample in the debugger.
3939 *
3940 * @param pArgs Pointer to the print one argument structure.
3941 * @param pszFormat Format string.
3942 * @param ... Format arguments.
3943 */
3944static DECLCALLBACK(void) stamR3EnumDbgfPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...)
3945{
3946 PDBGCCMDHLP pCmdHlp = (PDBGCCMDHLP)pArgs->pvArg;
3947
3948 va_list va;
3949 va_start(va, pszFormat);
3950 pCmdHlp->pfnPrintfV(pCmdHlp, NULL, pszFormat, va);
3951 va_end(va);
3952 NOREF(pArgs);
3953}
3954
3955
3956/**
3957 * @callback_method_impl{FNDBGCCMD, The '.statsreset' command.}
3958 */
3959static DECLCALLBACK(int) stamR3CmdStatsReset(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
3960{
3961 /*
3962 * Validate input.
3963 */
3964 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
3965 if (RTListIsEmpty(&pUVM->stam.s.List))
3966 return DBGCCmdHlpFail(pCmdHlp, pCmd, "No statistics present");
3967
3968 /*
3969 * Execute reset.
3970 */
3971 int rc = STAMR3Reset(pUVM, cArgs ? paArgs[0].u.pszString : NULL);
3972 if (RT_SUCCESS(rc))
3973 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "STAMR3ResetU");
3974 return DBGCCmdHlpPrintf(pCmdHlp, "Statistics have been reset.\n");
3975}
3976
3977#endif /* VBOX_WITH_DEBUGGER */
3978
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