VirtualBox

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

Last change on this file since 92473 was 92387, checked in by vboxsync, 3 years ago

VMM/STAM: New STAMUNIT_BYTES_PER_CALL unit. Improved the formatting of profile stats - may cause incorrect statistics to be discovered.

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