VirtualBox

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

Last change on this file since 94319 was 93468, checked in by vboxsync, 3 years ago

VBoxDbg,VMM/STAM,Main: Converted VBoxDbg to use the VMM function table, extending the STAMR3Enum to include the unit string to avoid needing to call STAMR3GetUnit a lot. The latter would better fit with the XML based viewer, as it only gets the string version of the unit. Had to adjust a user of STAMR3Enum in Main. bugref:10074

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