VirtualBox

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

Last change on this file since 80651 was 80651, checked in by vboxsync, 5 years ago

STAM: STAM_WITH_LOOKUP_TREE isn't optional, so drop the #ifdefs.

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