VirtualBox

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

Last change on this file since 69153 was 69111, checked in by vboxsync, 7 years ago

(C) year

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette