VirtualBox

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

Last change on this file since 46452 was 46452, checked in by vboxsync, 11 years ago

build fix

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