VirtualBox

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

Last change on this file since 77758 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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