VirtualBox

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

Last change on this file since 53403 was 52862, checked in by vboxsync, 10 years ago

stamR3LookupMaybeFree: Fixed bug causing assertions in stamR3LookupDestroyTree.

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