VirtualBox

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

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

VMM/VMMR3/STAM.cpp: fix a few long standing typos in the per-VM GMM statistics

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

© 2023 Oracle
ContactPrivacy policyTerms of Use