VirtualBox

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

Last change on this file since 41965 was 41965, checked in by vboxsync, 12 years ago

VMM: ran scm. Mostly svn:keywords changes (adding Revision).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 79.8 KB
Line 
1/* $Id: STAM.cpp 41965 2012-06-29 02:52:49Z vboxsync $ */
2/** @file
3 * STAM - The Statistics Manager.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/** @page pg_stam STAM - The Statistics Manager
19 *
20 * The purpose for the statistics manager is to present the rest of the system
21 * with a somewhat uniform way of accessing VMM statistics. STAM sports a
22 * couple of different APIs for accessing them: STAMR3EnumU, STAMR3SnapshotU,
23 * STAMR3DumpU, STAMR3DumpToReleaseLogU and the debugger. Main is exposing the
24 * XML based one, STAMR3SnapshotU.
25 *
26 * The rest of the VMM together with the devices and drivers registers their
27 * statistics with STAM giving them a name. The name is hierarchical, the
28 * components separated by slashes ('/') and must start with a slash.
29 *
30 * Each item registered with STAM - also, half incorrectly, called a sample -
31 * has a type, unit, visibility, data pointer and description associated with it
32 * in addition to the name (described above). The type tells STAM what kind of
33 * structure the pointer is pointing to. The visibility allows unused
34 * statistics from cluttering the output or showing up in the GUI. All the bits
35 * together makes STAM able to present the items in a sensible way to the user.
36 * Some types also allows STAM to reset the data, which is very convenient when
37 * digging into specific operations and such.
38 *
39 * PS. The VirtualBox Debugger GUI has a viewer for inspecting the statistics
40 * STAM provides. You will also find statistics in the release and debug logs.
41 * And as mentioned in the introduction, the debugger console features a couple
42 * of command: .stats and .statsreset.
43 *
44 * @see grp_stam
45 */
46
47/*******************************************************************************
48* Header Files *
49*******************************************************************************/
50#define LOG_GROUP LOG_GROUP_STAM
51#include <VBox/vmm/stam.h>
52#include "STAMInternal.h"
53#include <VBox/vmm/vm.h>
54#include <VBox/vmm/uvm.h>
55#include <VBox/err.h>
56#include <VBox/dbg.h>
57#include <VBox/log.h>
58
59#include <iprt/assert.h>
60#include <iprt/asm.h>
61#include <iprt/alloc.h>
62#include <iprt/stream.h>
63#include <iprt/string.h>
64
65
66/*******************************************************************************
67* Structures and Typedefs *
68*******************************************************************************/
69/**
70 * Argument structure for stamR3PrintOne().
71 */
72typedef struct STAMR3PRINTONEARGS
73{
74 PVM pVM;
75 void *pvArg;
76 DECLCALLBACKMEMBER(void, pfnPrintf)(struct STAMR3PRINTONEARGS *pvArg, const char *pszFormat, ...);
77} STAMR3PRINTONEARGS, *PSTAMR3PRINTONEARGS;
78
79
80/**
81 * Argument structure to stamR3EnumOne().
82 */
83typedef struct STAMR3ENUMONEARGS
84{
85 PVM pVM;
86 PFNSTAMR3ENUM pfnEnum;
87 void *pvUser;
88} STAMR3ENUMONEARGS, *PSTAMR3ENUMONEARGS;
89
90
91/**
92 * The snapshot status structure.
93 * Argument package passed to stamR3SnapshotOne, stamR3SnapshotPrintf and stamR3SnapshotOutput.
94 */
95typedef struct STAMR3SNAPSHOTONE
96{
97 /** Pointer to the buffer start. */
98 char *pszStart;
99 /** Pointer to the buffer end. */
100 char *pszEnd;
101 /** Pointer to the current buffer position. */
102 char *psz;
103 /** Pointer to the VM. */
104 PVM pVM;
105 /** The number of bytes allocated. */
106 size_t cbAllocated;
107 /** The status code. */
108 int rc;
109 /** Whether to include the description strings. */
110 bool fWithDesc;
111} STAMR3SNAPSHOTONE, *PSTAMR3SNAPSHOTONE;
112
113
114/**
115 * Init record for a ring-0 statistic sample.
116 */
117typedef struct STAMR0SAMPLE
118{
119 /** The GVMMSTATS structure offset of the variable. */
120 unsigned offVar;
121 /** The type. */
122 STAMTYPE enmType;
123 /** The unit. */
124 STAMUNIT enmUnit;
125 /** The name. */
126 const char *pszName;
127 /** The description. */
128 const char *pszDesc;
129} STAMR0SAMPLE;
130
131
132/*******************************************************************************
133* Internal Functions *
134*******************************************************************************/
135static int stamR3RegisterU(PUVM pUVM, void *pvSample, PFNSTAMR3CALLBACKRESET pfnReset, PFNSTAMR3CALLBACKPRINT pfnPrint,
136 STAMTYPE enmType, STAMVISIBILITY enmVisibility, const char *pszName, STAMUNIT enmUnit, const char *pszDesc);
137static int stamR3ResetOne(PSTAMDESC pDesc, void *pvArg);
138static DECLCALLBACK(void) stamR3EnumLogPrintf(PSTAMR3PRINTONEARGS pvArg, const char *pszFormat, ...);
139static DECLCALLBACK(void) stamR3EnumRelLogPrintf(PSTAMR3PRINTONEARGS pvArg, const char *pszFormat, ...);
140static DECLCALLBACK(void) stamR3EnumPrintf(PSTAMR3PRINTONEARGS pvArg, const char *pszFormat, ...);
141static int stamR3SnapshotOne(PSTAMDESC pDesc, void *pvArg);
142static int stamR3SnapshotPrintf(PSTAMR3SNAPSHOTONE pThis, const char *pszFormat, ...);
143static int stamR3PrintOne(PSTAMDESC pDesc, void *pvArg);
144static int stamR3EnumOne(PSTAMDESC pDesc, void *pvArg);
145static bool stamR3MultiMatch(const char * const *papszExpressions, unsigned cExpressions, unsigned *piExpression, const char *pszName);
146static char ** stamR3SplitPattern(const char *pszPat, unsigned *pcExpressions, char **ppszCopy);
147static int stamR3EnumU(PUVM pUVM, const char *pszPat, bool fUpdateRing0, int (pfnCallback)(PSTAMDESC pDesc, void *pvArg), void *pvArg);
148static void stamR3Ring0StatsRegisterU(PUVM pUVM);
149static void stamR3Ring0StatsUpdateU(PUVM pUVM, const char *pszPat);
150static void stamR3Ring0StatsUpdateMultiU(PUVM pUVM, const char * const *papszExpressions, unsigned cExpressions);
151
152#ifdef VBOX_WITH_DEBUGGER
153static DECLCALLBACK(int) stamR3CmdStats(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs);
154static DECLCALLBACK(void) stamR3EnumDbgfPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...);
155static DECLCALLBACK(int) stamR3CmdStatsReset(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs);
156#endif
157
158
159/*******************************************************************************
160* Global Variables *
161*******************************************************************************/
162#ifdef VBOX_WITH_DEBUGGER
163/** Pattern argument. */
164static const DBGCVARDESC g_aArgPat[] =
165{
166 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
167 { 0, 1, DBGCVAR_CAT_STRING, 0, "pattern", "Which samples the command shall be applied to. Use '*' as wildcard. Use ';' to separate expression." }
168};
169
170/** Command descriptors. */
171static const DBGCCMD g_aCmds[] =
172{
173 /* pszCmd, cArgsMin, cArgsMax, paArgDesc, cArgDescs, fFlags, pfnHandler pszSyntax, ....pszDescription */
174 { "stats", 0, 1, &g_aArgPat[0], RT_ELEMENTS(g_aArgPat), 0, stamR3CmdStats, "[pattern]", "Display statistics." },
175 { "statsreset", 0, 1, &g_aArgPat[0], RT_ELEMENTS(g_aArgPat), 0, stamR3CmdStatsReset,"[pattern]", "Resets statistics." }
176};
177#endif
178
179
180/**
181 * The GVMM mapping records - sans the host cpus.
182 */
183static const STAMR0SAMPLE g_aGVMMStats[] =
184{
185 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cHaltCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/HaltCalls", "The number of calls to GVMMR0SchedHalt." },
186 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cHaltBlocking), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/HaltBlocking", "The number of times we did go to sleep in GVMMR0SchedHalt." },
187 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cHaltTimeouts), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/HaltTimeouts", "The number of times we timed out in GVMMR0SchedHalt." },
188 { 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." },
189 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cHaltWakeUps), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/HaltWakeUps", "The number of wake ups done during GVMMR0SchedHalt." },
190 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cWakeUpCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/WakeUpCalls", "The number of calls to GVMMR0WakeUp." },
191 { 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." },
192 { 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)." },
193 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cPokeCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/PokeCalls", "The number of calls to GVMMR0Poke." },
194 { 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." },
195 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cPollCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/PollCalls", "The number of calls to GVMMR0SchedPoll." },
196 { 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." },
197 { RT_UOFFSETOF(GVMMSTATS, SchedVM.cPollWakeUps), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/VM/PollWakeUps", "The number of wake ups done during GVMMR0SchedPoll." },
198
199 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cHaltCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/HaltCalls", "The number of calls to GVMMR0SchedHalt." },
200 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cHaltBlocking), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/HaltBlocking", "The number of times we did go to sleep in GVMMR0SchedHalt." },
201 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cHaltTimeouts), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/HaltTimeouts", "The number of times we timed out in GVMMR0SchedHalt." },
202 { 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." },
203 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cHaltWakeUps), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/HaltWakeUps", "The number of wake ups done during GVMMR0SchedHalt." },
204 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cWakeUpCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/WakeUpCalls", "The number of calls to GVMMR0WakeUp." },
205 { 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." },
206 { 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)." },
207 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cPokeCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/PokeCalls", "The number of calls to GVMMR0Poke." },
208 { 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." },
209 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cPollCalls), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/PollCalls", "The number of calls to GVMMR0SchedPoll." },
210 { 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." },
211 { RT_UOFFSETOF(GVMMSTATS, SchedSum.cPollWakeUps), STAMTYPE_U64_RESET, STAMUNIT_CALLS, "/GVMM/Sum/PollWakeUps", "The number of wake ups done during GVMMR0SchedPoll." },
212
213 { RT_UOFFSETOF(GVMMSTATS, cVMs), STAMTYPE_U32, STAMUNIT_CALLS, "/GVMM/VMs", "The number of VMs accessible to the caller." },
214 { RT_UOFFSETOF(GVMMSTATS, cEMTs), STAMTYPE_U32, STAMUNIT_CALLS, "/GVMM/EMTs", "The number of emulation threads." },
215 { RT_UOFFSETOF(GVMMSTATS, cHostCpus), STAMTYPE_U32, STAMUNIT_CALLS, "/GVMM/HostCPUs", "The number of host CPUs." },
216};
217
218
219/**
220 * The GMM mapping records.
221 */
222static const STAMR0SAMPLE g_aGMMStats[] =
223{
224 { RT_UOFFSETOF(GMMSTATS, cMaxPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/cMaxPages", "The maximum number of pages GMM is allowed to allocate." },
225 { RT_UOFFSETOF(GMMSTATS, cReservedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/cReservedPages", "The number of pages that has been reserved." },
226 { RT_UOFFSETOF(GMMSTATS, cOverCommittedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/cOverCommittedPages", "The number of pages that we have over-committed in reservations." },
227 { RT_UOFFSETOF(GMMSTATS, cAllocatedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/cAllocatedPages", "The number of actually allocated (committed if you like) pages." },
228 { RT_UOFFSETOF(GMMSTATS, cSharedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/cSharedPages", "The number of pages that are shared. A subset of cAllocatedPages." },
229 { RT_UOFFSETOF(GMMSTATS, cDuplicatePages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/cDuplicatePages", "The number of pages that are actually shared between VMs." },
230 { 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." },
231 { RT_UOFFSETOF(GMMSTATS, cBalloonedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/cBalloonedPages", "The number of current ballooned pages." },
232 { RT_UOFFSETOF(GMMSTATS, cChunks), STAMTYPE_U32, STAMUNIT_COUNT, "/GMM/cChunks", "The number of allocation chunks." },
233 { RT_UOFFSETOF(GMMSTATS, cFreedChunks), STAMTYPE_U32, STAMUNIT_COUNT, "/GMM/cFreedChunks", "The number of freed chunks ever." },
234 { RT_UOFFSETOF(GMMSTATS, cShareableModules), STAMTYPE_U32, STAMUNIT_COUNT, "/GMM/cShareableModules", "The number of shareable modules." },
235 { 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." },
236 { RT_UOFFSETOF(GMMSTATS, VMStats.Reserved.cShadowPages), STAMTYPE_U32, STAMUNIT_PAGES, "/GMM/VM/Reserved/cShadowPages", "The amount of memory reserved for shadow/nested page tables." },
237 { 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." },
238 { 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." },
239 { RT_UOFFSETOF(GMMSTATS, VMStats.Allocated.cShadowPages), STAMTYPE_U32, STAMUNIT_PAGES, "/GMM/VM/Allocated/cShadowPages", "The amount of memory allocated for shadow/nested page tables." },
240 { 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." },
241 { RT_UOFFSETOF(GMMSTATS, VMStats.cPrivatePages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/VM/cPrivatePages", "The current number of private pages." },
242 { RT_UOFFSETOF(GMMSTATS, VMStats.cSharedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/VM/cSharedPages", "The current number of shared pages." },
243 { RT_UOFFSETOF(GMMSTATS, VMStats.cBalloonedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/VM/cBalloonedPages", "The current number of ballooned pages." },
244 { RT_UOFFSETOF(GMMSTATS, VMStats.cMaxBalloonedPages), STAMTYPE_U64, STAMUNIT_PAGES, "/GMM/VM/cMaxBalloonedPages", "The max number of pages that can be ballooned." },
245 { 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." },
246 { 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." },
247 { 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." },
248 { RT_UOFFSETOF(GMMSTATS, VMStats.cShareableModules), STAMTYPE_U32, STAMUNIT_COUNT, "/GMM/VM/cShareableModules", "The number of shareable modules traced by the VM." },
249 { RT_UOFFSETOF(GMMSTATS, VMStats.enmPolicy), STAMTYPE_U32, STAMUNIT_NONE, "/GMM/VM/enmPolicy", "The current over-commit policy." },
250 { 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." },
251 { RT_UOFFSETOF(GMMSTATS, VMStats.fBallooningEnabled), STAMTYPE_BOOL, STAMUNIT_NONE, "/GMM/VM/fBallooningEnabled", "Whether ballooning is enabled or not." },
252 { RT_UOFFSETOF(GMMSTATS, VMStats.fBallooningEnabled), STAMTYPE_BOOL, STAMUNIT_NONE, "/GMM/VM/fSharedPagingEnabled", "Whether shared paging is enabled or not." },
253 { RT_UOFFSETOF(GMMSTATS, VMStats.fBallooningEnabled), STAMTYPE_BOOL, STAMUNIT_NONE, "/GMM/VM/fMayAllocate", "Whether the VM is allowed to allocate memory or not." },
254};
255
256
257/**
258 * Initializes the STAM.
259 *
260 * @returns VBox status code.
261 * @param pVM Pointer to the VM.
262 */
263VMMR3DECL(int) STAMR3InitUVM(PUVM pUVM)
264{
265 LogFlow(("STAMR3Init\n"));
266
267 /*
268 * Assert alignment and sizes.
269 */
270 AssertCompile(sizeof(pUVM->stam.s) <= sizeof(pUVM->stam.padding));
271 AssertRelease(sizeof(pUVM->stam.s) <= sizeof(pUVM->stam.padding));
272
273 /*
274 * Setup any fixed pointers and offsets.
275 */
276 int rc = RTSemRWCreate(&pUVM->stam.s.RWSem);
277 AssertRCReturn(rc, rc);
278
279 /*
280 * Register the ring-0 statistics (GVMM/GMM).
281 */
282 stamR3Ring0StatsRegisterU(pUVM);
283
284#ifdef VBOX_WITH_DEBUGGER
285 /*
286 * Register debugger commands.
287 */
288 static bool fRegisteredCmds = false;
289 if (!fRegisteredCmds)
290 {
291 rc = DBGCRegisterCommands(&g_aCmds[0], RT_ELEMENTS(g_aCmds));
292 if (RT_SUCCESS(rc))
293 fRegisteredCmds = true;
294 }
295#endif
296
297 return VINF_SUCCESS;
298}
299
300
301/**
302 * Terminates the STAM.
303 *
304 * @param pUVM Pointer to the user mode VM structure.
305 */
306VMMR3DECL(void) STAMR3TermUVM(PUVM pUVM)
307{
308 /*
309 * Free used memory and the RWLock.
310 */
311 PSTAMDESC pCur = pUVM->stam.s.pHead;
312 while (pCur)
313 {
314 void *pvFree = pCur;
315 pCur = pCur->pNext;
316 RTMemFree(pvFree);
317 }
318 pUVM->stam.s.pHead = NULL;
319
320 Assert(pUVM->stam.s.RWSem != NIL_RTSEMRW);
321 RTSemRWDestroy(pUVM->stam.s.RWSem);
322 pUVM->stam.s.RWSem = NIL_RTSEMRW;
323}
324
325
326/**
327 * Registers a sample with the statistics manager.
328 *
329 * Statistics are maintained on a per VM basis and is normally registered
330 * during the VM init stage, but there is nothing preventing you from
331 * register them at runtime.
332 *
333 * Use STAMR3Deregister() to deregister statistics at runtime, however do
334 * not bother calling at termination time.
335 *
336 * It is not possible to register the same sample twice.
337 *
338 * @returns VBox status.
339 * @param pUVM Pointer to the user mode VM structure.
340 * @param pvSample Pointer to the sample.
341 * @param enmType Sample type. This indicates what pvSample is pointing at.
342 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
343 * @param pszName Sample name. The name is on this form "/<component>/<sample>".
344 * Further nesting is possible.
345 * @param enmUnit Sample unit.
346 * @param pszDesc Sample description.
347 */
348VMMR3DECL(int) STAMR3RegisterU(PUVM pUVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, const char *pszName, STAMUNIT enmUnit, const char *pszDesc)
349{
350 AssertReturn(enmType != STAMTYPE_CALLBACK, VERR_INVALID_PARAMETER);
351 return stamR3RegisterU(pUVM, pvSample, NULL, NULL, enmType, enmVisibility, pszName, enmUnit, pszDesc);
352}
353
354
355/**
356 * Registers a sample with the statistics manager.
357 *
358 * Statistics are maintained on a per VM basis and is normally registered
359 * during the VM init stage, but there is nothing preventing you from
360 * register them at runtime.
361 *
362 * Use STAMR3Deregister() to deregister statistics at runtime, however do
363 * not bother calling at termination time.
364 *
365 * It is not possible to register the same sample twice.
366 *
367 * @returns VBox status.
368 * @param pVM Pointer to the VM.
369 * @param pvSample Pointer to the sample.
370 * @param enmType Sample type. This indicates what pvSample is pointing at.
371 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
372 * @param pszName Sample name. The name is on this form "/<component>/<sample>".
373 * Further nesting is possible.
374 * @param enmUnit Sample unit.
375 * @param pszDesc Sample description.
376 */
377VMMR3DECL(int) STAMR3Register(PVM pVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, const char *pszName, STAMUNIT enmUnit, const char *pszDesc)
378{
379 AssertReturn(enmType != STAMTYPE_CALLBACK, VERR_INVALID_PARAMETER);
380 return stamR3RegisterU(pVM->pUVM, pvSample, NULL, NULL, enmType, enmVisibility, pszName, enmUnit, pszDesc);
381}
382
383
384/**
385 * Same as STAMR3RegisterU except that the name is specified in a
386 * RTStrPrintf like fashion.
387 *
388 * @returns VBox status.
389 * @param pUVM Pointer to the user mode VM structure.
390 * @param pvSample Pointer to the sample.
391 * @param enmType Sample type. This indicates what pvSample is pointing at.
392 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
393 * @param enmUnit Sample unit.
394 * @param pszDesc Sample description.
395 * @param pszName The sample name format string.
396 * @param ... Arguments to the format string.
397 */
398VMMR3DECL(int) STAMR3RegisterFU(PUVM pUVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
399 const char *pszDesc, const char *pszName, ...)
400{
401 va_list args;
402 va_start(args, pszName);
403 int rc = STAMR3RegisterVU(pUVM, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName, args);
404 va_end(args);
405 return rc;
406}
407
408
409/**
410 * Same as STAMR3Register except that the name is specified in a
411 * RTStrPrintf like fashion.
412 *
413 * @returns VBox status.
414 * @param pVM Pointer to the VM.
415 * @param pvSample Pointer to the sample.
416 * @param enmType Sample type. This indicates what pvSample is pointing at.
417 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
418 * @param enmUnit Sample unit.
419 * @param pszDesc Sample description.
420 * @param pszName The sample name format string.
421 * @param ... Arguments to the format string.
422 */
423VMMR3DECL(int) STAMR3RegisterF(PVM pVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
424 const char *pszDesc, const char *pszName, ...)
425{
426 va_list args;
427 va_start(args, pszName);
428 int rc = STAMR3RegisterVU(pVM->pUVM, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName, args);
429 va_end(args);
430 return rc;
431}
432
433
434/**
435 * Same as STAMR3Register except that the name is specified in a
436 * RTStrPrintfV like fashion.
437 *
438 * @returns VBox status.
439 * @param pVM Pointer to the VM.
440 * @param pvSample Pointer to the sample.
441 * @param enmType Sample type. This indicates what pvSample is pointing at.
442 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
443 * @param enmUnit Sample unit.
444 * @param pszDesc Sample description.
445 * @param pszName The sample name format string.
446 * @param args Arguments to the format string.
447 */
448VMMR3DECL(int) STAMR3RegisterVU(PUVM pUVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
449 const char *pszDesc, const char *pszName, va_list args)
450{
451 AssertReturn(enmType != STAMTYPE_CALLBACK, VERR_INVALID_PARAMETER);
452
453 char *pszFormattedName;
454 RTStrAPrintfV(&pszFormattedName, pszName, args);
455 if (!pszFormattedName)
456 return VERR_NO_MEMORY;
457
458 int rc = STAMR3RegisterU(pUVM, pvSample, enmType, enmVisibility, pszFormattedName, enmUnit, pszDesc);
459 RTStrFree(pszFormattedName);
460 return rc;
461}
462
463
464/**
465 * Same as STAMR3Register except that the name is specified in a
466 * RTStrPrintfV like fashion.
467 *
468 * @returns VBox status.
469 * @param pVM Pointer to the VM.
470 * @param pvSample Pointer to the sample.
471 * @param enmType Sample type. This indicates what pvSample is pointing at.
472 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
473 * @param enmUnit Sample unit.
474 * @param pszDesc Sample description.
475 * @param pszName The sample name format string.
476 * @param args Arguments to the format string.
477 */
478VMMR3DECL(int) STAMR3RegisterV(PVM pVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
479 const char *pszDesc, const char *pszName, va_list args)
480{
481 return STAMR3RegisterVU(pVM->pUVM, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName, args);
482}
483
484
485/**
486 * Similar to STAMR3Register except for the two callbacks, the implied type (STAMTYPE_CALLBACK),
487 * and name given in an RTStrPrintf like fashion.
488 *
489 * @returns VBox status.
490 * @param pVM Pointer to the VM.
491 * @param pvSample Pointer to the sample.
492 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
493 * @param enmUnit Sample unit.
494 * @param pfnReset Callback for resetting the sample. NULL should be used if the sample can't be reset.
495 * @param pfnPrint Print the sample.
496 * @param pszDesc Sample description.
497 * @param pszName The sample name format string.
498 * @param ... Arguments to the format string.
499 * @remark There is currently no device or driver variant of this API. Add one if it should become necessary!
500 */
501VMMR3DECL(int) STAMR3RegisterCallback(PVM pVM, void *pvSample, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
502 PFNSTAMR3CALLBACKRESET pfnReset, PFNSTAMR3CALLBACKPRINT pfnPrint,
503 const char *pszDesc, const char *pszName, ...)
504{
505 va_list args;
506 va_start(args, pszName);
507 int rc = STAMR3RegisterCallbackV(pVM, pvSample, enmVisibility, enmUnit, pfnReset, pfnPrint, pszDesc, pszName, args);
508 va_end(args);
509 return rc;
510}
511
512
513/**
514 * Same as STAMR3RegisterCallback() except for the ellipsis which is a va_list here.
515 *
516 * @returns VBox status.
517 * @param pVM Pointer to the VM.
518 * @param pvSample Pointer to the sample.
519 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
520 * @param enmUnit Sample unit.
521 * @param pfnReset Callback for resetting the sample. NULL should be used if the sample can't be reset.
522 * @param pfnPrint Print the sample.
523 * @param pszDesc Sample description.
524 * @param pszName The sample name format string.
525 * @param args Arguments to the format string.
526 * @remark There is currently no device or driver variant of this API. Add one if it should become necessary!
527 */
528VMMR3DECL(int) STAMR3RegisterCallbackV(PVM pVM, void *pvSample, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
529 PFNSTAMR3CALLBACKRESET pfnReset, PFNSTAMR3CALLBACKPRINT pfnPrint,
530 const char *pszDesc, const char *pszName, va_list args)
531{
532 char *pszFormattedName;
533 RTStrAPrintfV(&pszFormattedName, pszName, args);
534 if (!pszFormattedName)
535 return VERR_NO_MEMORY;
536
537 int rc = stamR3RegisterU(pVM->pUVM, pvSample, pfnReset, pfnPrint, STAMTYPE_CALLBACK, enmVisibility, pszFormattedName, enmUnit, pszDesc);
538 RTStrFree(pszFormattedName);
539 return rc;
540}
541
542
543#ifdef VBOX_STRICT
544/**
545 * Divide the strings into sub-strings using '/' as delimiter
546 * and then compare them in strcmp fashion.
547 *
548 * @returns Difference.
549 * @retval 0 if equal.
550 * @retval < 0 if psz1 is less than psz2.
551 * @retval > 0 if psz1 greater than psz2.
552 *
553 * @param psz1 The first string.
554 * @param psz2 The second string.
555 */
556static int stamR3SlashCompare(const char *psz1, const char *psz2)
557{
558 for (;;)
559 {
560 unsigned int ch1 = *psz1++;
561 unsigned int ch2 = *psz2++;
562 if (ch1 != ch2)
563 {
564 /* slash is end-of-sub-string, so it trumps everything but '\0'. */
565 if (ch1 == '/')
566 return ch2 ? -1 : 1;
567 if (ch2 == '/')
568 return ch1 ? 1 : -1;
569 return ch1 - ch2;
570 }
571
572 /* done? */
573 if (ch1 == '\0')
574 return 0;
575 }
576}
577#endif /* VBOX_STRICT */
578
579
580/**
581 * Internal worker for the different register calls.
582 *
583 * @returns VBox status.
584 * @param pUVM Pointer to the user mode VM structure.
585 * @param pvSample Pointer to the sample.
586 * @param pfnReset Callback for resetting the sample. NULL should be used if the sample can't be reset.
587 * @param pfnPrint Print the sample.
588 * @param enmType Sample type. This indicates what pvSample is pointing at.
589 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
590 * @param enmUnit Sample unit.
591 * @param pszDesc Sample description.
592 * @param pszName The sample name format string.
593 * @param args Arguments to the format string.
594 * @remark There is currently no device or driver variant of this API. Add one if it should become necessary!
595 */
596static int stamR3RegisterU(PUVM pUVM, void *pvSample, PFNSTAMR3CALLBACKRESET pfnReset, PFNSTAMR3CALLBACKPRINT pfnPrint,
597 STAMTYPE enmType, STAMVISIBILITY enmVisibility, const char *pszName, STAMUNIT enmUnit, const char *pszDesc)
598{
599 STAM_LOCK_WR(pUVM);
600
601 /*
602 * Check if exists.
603 */
604 PSTAMDESC pPrev = NULL;
605 PSTAMDESC pCur = pUVM->stam.s.pHead;
606 while (pCur)
607 {
608 int iDiff = strcmp(pCur->pszName, pszName);
609 /* passed it */
610 if (iDiff > 0)
611 break;
612 /* found it. */
613 if (!iDiff)
614 {
615 STAM_UNLOCK_WR(pUVM);
616 AssertMsgFailed(("Duplicate sample name: %s\n", pszName));
617 return VERR_ALREADY_EXISTS;
618 }
619
620 /* next */
621 pPrev = pCur;
622 pCur = pCur->pNext;
623 }
624
625 /*
626 * Check that the name doesn't screw up sorting order when taking
627 * slashes into account. The QT4 GUI makes some assumptions.
628 * Problematic chars are: !"#$%&'()*+,-.
629 */
630 Assert(pszName[0] == '/');
631 if (pPrev)
632 Assert(stamR3SlashCompare(pPrev->pszName, pszName) < 0);
633 if (pCur)
634 Assert(stamR3SlashCompare(pCur->pszName, pszName) > 0);
635
636#ifdef VBOX_STRICT
637 /*
638 * Check alignment requirements.
639 */
640 switch (enmType)
641 {
642 /* 8 byte / 64-bit */
643 case STAMTYPE_U64:
644 case STAMTYPE_U64_RESET:
645 case STAMTYPE_X64:
646 case STAMTYPE_X64_RESET:
647 case STAMTYPE_COUNTER:
648 case STAMTYPE_PROFILE:
649 case STAMTYPE_PROFILE_ADV:
650 AssertMsg(!((uintptr_t)pvSample & 7), ("%p - %s\n", pvSample, pszName));
651 break;
652
653 /* 4 byte / 32-bit */
654 case STAMTYPE_RATIO_U32:
655 case STAMTYPE_RATIO_U32_RESET:
656 case STAMTYPE_U32:
657 case STAMTYPE_U32_RESET:
658 case STAMTYPE_X32:
659 case STAMTYPE_X32_RESET:
660 AssertMsg(!((uintptr_t)pvSample & 3), ("%p - %s\n", pvSample, pszName));
661 break;
662
663 /* 2 byte / 32-bit */
664 case STAMTYPE_U16:
665 case STAMTYPE_U16_RESET:
666 case STAMTYPE_X16:
667 case STAMTYPE_X16_RESET:
668 AssertMsg(!((uintptr_t)pvSample & 1), ("%p - %s\n", pvSample, pszName));
669 break;
670
671 /* 1 byte / 8-bit / unaligned */
672 case STAMTYPE_U8:
673 case STAMTYPE_U8_RESET:
674 case STAMTYPE_X8:
675 case STAMTYPE_X8_RESET:
676 case STAMTYPE_BOOL:
677 case STAMTYPE_BOOL_RESET:
678 case STAMTYPE_CALLBACK:
679 break;
680
681 default:
682 AssertMsgFailed(("%d\n", enmType));
683 break;
684 }
685#endif /* VBOX_STRICT */
686
687 /*
688 * Create a new node and insert it at the current location.
689 */
690 int rc;
691 size_t cchName = strlen(pszName) + 1;
692 size_t cchDesc = pszDesc ? strlen(pszDesc) + 1 : 0;
693 PSTAMDESC pNew = (PSTAMDESC)RTMemAlloc(sizeof(*pNew) + cchName + cchDesc);
694 if (pNew)
695 {
696 pNew->pszName = (char *)memcpy((char *)(pNew + 1), pszName, cchName);
697 pNew->enmType = enmType;
698 pNew->enmVisibility = enmVisibility;
699 if (enmType != STAMTYPE_CALLBACK)
700 pNew->u.pv = pvSample;
701 else
702 {
703 pNew->u.Callback.pvSample = pvSample;
704 pNew->u.Callback.pfnReset = pfnReset;
705 pNew->u.Callback.pfnPrint = pfnPrint;
706 }
707 pNew->enmUnit = enmUnit;
708 pNew->pszDesc = NULL;
709 if (pszDesc)
710 pNew->pszDesc = (char *)memcpy((char *)(pNew + 1) + cchName, pszDesc, cchDesc);
711
712 pNew->pNext = pCur;
713 if (pPrev)
714 pPrev->pNext = pNew;
715 else
716 pUVM->stam.s.pHead = pNew;
717
718 stamR3ResetOne(pNew, pUVM->pVM);
719 rc = VINF_SUCCESS;
720 }
721 else
722 rc = VERR_NO_MEMORY;
723
724 STAM_UNLOCK_WR(pUVM);
725 return rc;
726}
727
728
729/**
730 * Deregisters a sample previously registered by STAR3Register().
731 *
732 * This is intended used for devices which can be unplugged and for
733 * temporary samples.
734 *
735 * @returns VBox status.
736 * @param pUVM Pointer to the user mode VM structure.
737 * @param pvSample Pointer to the sample registered with STAMR3Register().
738 */
739VMMR3DECL(int) STAMR3DeregisterU(PUVM pUVM, void *pvSample)
740{
741 STAM_LOCK_WR(pUVM);
742
743 /*
744 * Search for it.
745 */
746 int rc = VERR_INVALID_HANDLE;
747 PSTAMDESC pPrev = NULL;
748 PSTAMDESC pCur = pUVM->stam.s.pHead;
749 while (pCur)
750 {
751 if (pCur->u.pv == pvSample)
752 {
753 void *pvFree = pCur;
754 pCur = pCur->pNext;
755 if (pPrev)
756 pPrev->pNext = pCur;
757 else
758 pUVM->stam.s.pHead = pCur;
759
760 RTMemFree(pvFree);
761 rc = VINF_SUCCESS;
762 continue;
763 }
764
765 /* next */
766 pPrev = pCur;
767 pCur = pCur->pNext;
768 }
769
770 STAM_UNLOCK_WR(pUVM);
771 return rc;
772}
773
774
775/**
776 * Deregisters a sample previously registered by STAR3Register().
777 *
778 * This is intended used for devices which can be unplugged and for
779 * temporary samples.
780 *
781 * @returns VBox status.
782 * @param pVM Pointer to the VM.
783 * @param pvSample Pointer to the sample registered with STAMR3Register().
784 */
785VMMR3DECL(int) STAMR3Deregister(PVM pVM, void *pvSample)
786{
787 return STAMR3DeregisterU(pVM->pUVM, pvSample);
788}
789
790
791/**
792 * Resets statistics for the specified VM.
793 * It's possible to select a subset of the samples.
794 *
795 * @returns VBox status. (Basically, it cannot fail.)
796 * @param pVM Pointer to the VM.
797 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
798 * If NULL all samples are reset.
799 * @remarks Don't confuse this with the other 'XYZR3Reset' methods, it's not called at VM reset.
800 */
801VMMR3DECL(int) STAMR3ResetU(PUVM pUVM, const char *pszPat)
802{
803 int rc = VINF_SUCCESS;
804
805 /* ring-0 */
806 GVMMRESETSTATISTICSSREQ GVMMReq;
807 GMMRESETSTATISTICSSREQ GMMReq;
808 bool fGVMMMatched = !pszPat || !*pszPat;
809 bool fGMMMatched = fGVMMMatched;
810 if (fGVMMMatched)
811 {
812 memset(&GVMMReq.Stats, 0xff, sizeof(GVMMReq.Stats));
813 memset(&GMMReq.Stats, 0xff, sizeof(GMMReq.Stats));
814 }
815 else
816 {
817 char *pszCopy;
818 unsigned cExpressions;
819 char **papszExpressions = stamR3SplitPattern(pszPat, &cExpressions, &pszCopy);
820 if (!papszExpressions)
821 return VERR_NO_MEMORY;
822
823 /* GVMM */
824 RT_ZERO(GVMMReq.Stats);
825 for (unsigned i = 0; i < RT_ELEMENTS(g_aGVMMStats); i++)
826 if (stamR3MultiMatch(papszExpressions, cExpressions, NULL, g_aGVMMStats[i].pszName))
827 {
828 *((uint8_t *)&GVMMReq.Stats + g_aGVMMStats[i].offVar) = 0xff;
829 fGVMMMatched = true;
830 }
831 if (!fGVMMMatched)
832 {
833 /** @todo match cpu leaves some rainy day. */
834 }
835
836 /* GMM */
837 RT_ZERO(GMMReq.Stats);
838 for (unsigned i = 0; i < RT_ELEMENTS(g_aGMMStats); i++)
839 if (stamR3MultiMatch(papszExpressions, cExpressions, NULL, g_aGMMStats[i].pszName))
840 {
841 *((uint8_t *)&GMMReq.Stats + g_aGMMStats[i].offVar) = 0xff;
842 fGMMMatched = true;
843 }
844
845 RTMemTmpFree(papszExpressions);
846 RTStrFree(pszCopy);
847 }
848
849 STAM_LOCK_WR(pUVM);
850
851 if (fGVMMMatched)
852 {
853 PVM pVM = pUVM->pVM;
854 GVMMReq.Hdr.cbReq = sizeof(GVMMReq);
855 GVMMReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
856 GVMMReq.pSession = pVM->pSession;
857 rc = SUPR3CallVMMR0Ex(pVM->pVMR0, NIL_VMCPUID, VMMR0_DO_GVMM_RESET_STATISTICS, 0, &GVMMReq.Hdr);
858 }
859
860 if (fGMMMatched)
861 {
862 PVM pVM = pUVM->pVM;
863 GMMReq.Hdr.cbReq = sizeof(GMMReq);
864 GMMReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
865 GMMReq.pSession = pVM->pSession;
866 rc = SUPR3CallVMMR0Ex(pVM->pVMR0, NIL_VMCPUID, VMMR0_DO_GMM_RESET_STATISTICS, 0, &GMMReq.Hdr);
867 }
868
869 /* and the reset */
870 stamR3EnumU(pUVM, pszPat, false /* fUpdateRing0 */, stamR3ResetOne, pUVM->pVM);
871
872 STAM_UNLOCK_WR(pUVM);
873 return rc;
874}
875
876
877/**
878 * Resets statistics for the specified VM.
879 * It's possible to select a subset of the samples.
880 *
881 * @returns VBox status. (Basically, it cannot fail.)
882 * @param pVM Pointer to the VM.
883 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
884 * If NULL all samples are reset.
885 * @remarks Don't confuse this with the other 'XYZR3Reset' methods, it's not called at VM reset.
886 */
887VMMR3DECL(int) STAMR3Reset(PVM pVM, const char *pszPat)
888{
889 return STAMR3ResetU(pVM->pUVM, pszPat);
890}
891
892
893/**
894 * Resets one statistics sample.
895 * Callback for stamR3EnumU().
896 *
897 * @returns VINF_SUCCESS
898 * @param pDesc Pointer to the current descriptor.
899 * @param pvArg User argument - Pointer to the VM.
900 */
901static int stamR3ResetOne(PSTAMDESC pDesc, void *pvArg)
902{
903 switch (pDesc->enmType)
904 {
905 case STAMTYPE_COUNTER:
906 ASMAtomicXchgU64(&pDesc->u.pCounter->c, 0);
907 break;
908
909 case STAMTYPE_PROFILE:
910 case STAMTYPE_PROFILE_ADV:
911 ASMAtomicXchgU64(&pDesc->u.pProfile->cPeriods, 0);
912 ASMAtomicXchgU64(&pDesc->u.pProfile->cTicks, 0);
913 ASMAtomicXchgU64(&pDesc->u.pProfile->cTicksMax, 0);
914 ASMAtomicXchgU64(&pDesc->u.pProfile->cTicksMin, ~0);
915 break;
916
917 case STAMTYPE_RATIO_U32_RESET:
918 ASMAtomicXchgU32(&pDesc->u.pRatioU32->u32A, 0);
919 ASMAtomicXchgU32(&pDesc->u.pRatioU32->u32B, 0);
920 break;
921
922 case STAMTYPE_CALLBACK:
923 if (pDesc->u.Callback.pfnReset)
924 pDesc->u.Callback.pfnReset((PVM)pvArg, pDesc->u.Callback.pvSample);
925 break;
926
927 case STAMTYPE_U8_RESET:
928 case STAMTYPE_X8_RESET:
929 ASMAtomicXchgU8(pDesc->u.pu8, 0);
930 break;
931
932 case STAMTYPE_U16_RESET:
933 case STAMTYPE_X16_RESET:
934 ASMAtomicXchgU16(pDesc->u.pu16, 0);
935 break;
936
937 case STAMTYPE_U32_RESET:
938 case STAMTYPE_X32_RESET:
939 ASMAtomicXchgU32(pDesc->u.pu32, 0);
940 break;
941
942 case STAMTYPE_U64_RESET:
943 case STAMTYPE_X64_RESET:
944 ASMAtomicXchgU64(pDesc->u.pu64, 0);
945 break;
946
947 case STAMTYPE_BOOL_RESET:
948 ASMAtomicXchgBool(pDesc->u.pf, false);
949 break;
950
951 /* These are custom and will not be touched. */
952 case STAMTYPE_U8:
953 case STAMTYPE_X8:
954 case STAMTYPE_U16:
955 case STAMTYPE_X16:
956 case STAMTYPE_U32:
957 case STAMTYPE_X32:
958 case STAMTYPE_U64:
959 case STAMTYPE_X64:
960 case STAMTYPE_RATIO_U32:
961 case STAMTYPE_BOOL:
962 break;
963
964 default:
965 AssertMsgFailed(("enmType=%d\n", pDesc->enmType));
966 break;
967 }
968 NOREF(pvArg);
969 return VINF_SUCCESS;
970}
971
972
973/**
974 * Get a snapshot of the statistics.
975 * It's possible to select a subset of the samples.
976 *
977 * @returns VBox status. (Basically, it cannot fail.)
978 * @param pUVM Pointer to the user mode VM structure.
979 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
980 * If NULL all samples are reset.
981 * @param fWithDesc Whether to include the descriptions.
982 * @param ppszSnapshot Where to store the pointer to the snapshot data.
983 * The format of the snapshot should be XML, but that will have to be discussed
984 * when this function is implemented.
985 * The returned pointer must be freed by calling STAMR3SnapshotFree().
986 * @param pcchSnapshot Where to store the size of the snapshot data. (Excluding the trailing '\0')
987 */
988VMMR3DECL(int) STAMR3SnapshotU(PUVM pUVM, const char *pszPat, char **ppszSnapshot, size_t *pcchSnapshot, bool fWithDesc)
989{
990 STAMR3SNAPSHOTONE State = { NULL, NULL, NULL, pUVM->pVM, 0, VINF_SUCCESS, fWithDesc };
991
992 /*
993 * Write the XML header.
994 */
995 /** @todo Make this proper & valid XML. */
996 stamR3SnapshotPrintf(&State, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
997
998 /*
999 * Write the content.
1000 */
1001 stamR3SnapshotPrintf(&State, "<Statistics>\n");
1002 int rc = stamR3EnumU(pUVM, pszPat, true /* fUpdateRing0 */, stamR3SnapshotOne, &State);
1003 stamR3SnapshotPrintf(&State, "</Statistics>\n");
1004
1005 if (RT_SUCCESS(rc))
1006 rc = State.rc;
1007 else
1008 {
1009 RTMemFree(State.pszStart);
1010 State.pszStart = State.pszEnd = State.psz = NULL;
1011 State.cbAllocated = 0;
1012 }
1013
1014 /*
1015 * Done.
1016 */
1017 *ppszSnapshot = State.pszStart;
1018 if (pcchSnapshot)
1019 *pcchSnapshot = State.psz - State.pszStart;
1020 return rc;
1021}
1022
1023
1024/**
1025 * Get a snapshot of the statistics.
1026 * It's possible to select a subset of the samples.
1027 *
1028 * @returns VBox status. (Basically, it cannot fail.)
1029 * @param pVM Pointer to the VM.
1030 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
1031 * If NULL all samples are reset.
1032 * @param fWithDesc Whether to include the descriptions.
1033 * @param ppszSnapshot Where to store the pointer to the snapshot data.
1034 * The format of the snapshot should be XML, but that will have to be discussed
1035 * when this function is implemented.
1036 * The returned pointer must be freed by calling STAMR3SnapshotFree().
1037 * @param pcchSnapshot Where to store the size of the snapshot data.
1038 * (Excluding the trailing '\\0')
1039 */
1040VMMR3DECL(int) STAMR3Snapshot(PVM pVM, const char *pszPat, char **ppszSnapshot, size_t *pcchSnapshot, bool fWithDesc)
1041{
1042 return STAMR3SnapshotU(pVM->pUVM, pszPat, ppszSnapshot, pcchSnapshot, fWithDesc);
1043}
1044
1045
1046/**
1047 * stamR3EnumU callback employed by STAMR3Snapshot.
1048 *
1049 * @returns VBox status code, but it's interpreted as 0 == success / !0 == failure by enmR3Enum.
1050 * @param pDesc The sample.
1051 * @param pvArg The snapshot status structure.
1052 */
1053static int stamR3SnapshotOne(PSTAMDESC pDesc, void *pvArg)
1054{
1055 PSTAMR3SNAPSHOTONE pThis = (PSTAMR3SNAPSHOTONE)pvArg;
1056
1057 switch (pDesc->enmType)
1058 {
1059 case STAMTYPE_COUNTER:
1060 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pDesc->u.pCounter->c == 0)
1061 return VINF_SUCCESS;
1062 stamR3SnapshotPrintf(pThis, "<Counter c=\"%lld\"", pDesc->u.pCounter->c);
1063 break;
1064
1065 case STAMTYPE_PROFILE:
1066 case STAMTYPE_PROFILE_ADV:
1067 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pDesc->u.pProfile->cPeriods == 0)
1068 return VINF_SUCCESS;
1069 stamR3SnapshotPrintf(pThis, "<Profile cPeriods=\"%lld\" cTicks=\"%lld\" cTicksMin=\"%lld\" cTicksMax=\"%lld\"",
1070 pDesc->u.pProfile->cPeriods, pDesc->u.pProfile->cTicks, pDesc->u.pProfile->cTicksMin,
1071 pDesc->u.pProfile->cTicksMax);
1072 break;
1073
1074 case STAMTYPE_RATIO_U32:
1075 case STAMTYPE_RATIO_U32_RESET:
1076 if (pDesc->enmVisibility == STAMVISIBILITY_USED && !pDesc->u.pRatioU32->u32A && !pDesc->u.pRatioU32->u32B)
1077 return VINF_SUCCESS;
1078 stamR3SnapshotPrintf(pThis, "<Ratio32 u32A=\"%lld\" u32B=\"%lld\"",
1079 pDesc->u.pRatioU32->u32A, pDesc->u.pRatioU32->u32B);
1080 break;
1081
1082 case STAMTYPE_CALLBACK:
1083 {
1084 char szBuf[512];
1085 pDesc->u.Callback.pfnPrint(pThis->pVM, pDesc->u.Callback.pvSample, szBuf, sizeof(szBuf));
1086 stamR3SnapshotPrintf(pThis, "<Callback val=\"%s\"", szBuf);
1087 break;
1088 }
1089
1090 case STAMTYPE_U8:
1091 case STAMTYPE_U8_RESET:
1092 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu8 == 0)
1093 return VINF_SUCCESS;
1094 stamR3SnapshotPrintf(pThis, "<U8 val=\"%u\"", *pDesc->u.pu8);
1095 break;
1096
1097 case STAMTYPE_X8:
1098 case STAMTYPE_X8_RESET:
1099 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu8 == 0)
1100 return VINF_SUCCESS;
1101 stamR3SnapshotPrintf(pThis, "<X8 val=\"%#x\"", *pDesc->u.pu8);
1102 break;
1103
1104 case STAMTYPE_U16:
1105 case STAMTYPE_U16_RESET:
1106 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu16 == 0)
1107 return VINF_SUCCESS;
1108 stamR3SnapshotPrintf(pThis, "<U16 val=\"%u\"", *pDesc->u.pu16);
1109 break;
1110
1111 case STAMTYPE_X16:
1112 case STAMTYPE_X16_RESET:
1113 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu16 == 0)
1114 return VINF_SUCCESS;
1115 stamR3SnapshotPrintf(pThis, "<X16 val=\"%#x\"", *pDesc->u.pu16);
1116 break;
1117
1118 case STAMTYPE_U32:
1119 case STAMTYPE_U32_RESET:
1120 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu32 == 0)
1121 return VINF_SUCCESS;
1122 stamR3SnapshotPrintf(pThis, "<U32 val=\"%u\"", *pDesc->u.pu32);
1123 break;
1124
1125 case STAMTYPE_X32:
1126 case STAMTYPE_X32_RESET:
1127 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu32 == 0)
1128 return VINF_SUCCESS;
1129 stamR3SnapshotPrintf(pThis, "<X32 val=\"%#x\"", *pDesc->u.pu32);
1130 break;
1131
1132 case STAMTYPE_U64:
1133 case STAMTYPE_U64_RESET:
1134 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu64 == 0)
1135 return VINF_SUCCESS;
1136 stamR3SnapshotPrintf(pThis, "<U64 val=\"%llu\"", *pDesc->u.pu64);
1137 break;
1138
1139 case STAMTYPE_X64:
1140 case STAMTYPE_X64_RESET:
1141 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu64 == 0)
1142 return VINF_SUCCESS;
1143 stamR3SnapshotPrintf(pThis, "<X64 val=\"%#llx\"", *pDesc->u.pu64);
1144 break;
1145
1146 case STAMTYPE_BOOL:
1147 case STAMTYPE_BOOL_RESET:
1148 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pf == false)
1149 return VINF_SUCCESS;
1150 stamR3SnapshotPrintf(pThis, "<BOOL val=\"%RTbool\"", *pDesc->u.pf);
1151 break;
1152
1153 default:
1154 AssertMsgFailed(("%d\n", pDesc->enmType));
1155 return 0;
1156 }
1157
1158 stamR3SnapshotPrintf(pThis, " unit=\"%s\"", STAMR3GetUnit(pDesc->enmUnit));
1159
1160 switch (pDesc->enmVisibility)
1161 {
1162 default:
1163 case STAMVISIBILITY_ALWAYS:
1164 break;
1165 case STAMVISIBILITY_USED:
1166 stamR3SnapshotPrintf(pThis, " vis=\"used\"");
1167 break;
1168 case STAMVISIBILITY_NOT_GUI:
1169 stamR3SnapshotPrintf(pThis, " vis=\"not-gui\"");
1170 break;
1171 }
1172
1173 stamR3SnapshotPrintf(pThis, " name=\"%s\"", pDesc->pszName);
1174
1175 if (pThis->fWithDesc && pDesc->pszDesc)
1176 {
1177 /*
1178 * The description is a bit tricky as it may include chars that
1179 * xml requires to be escaped.
1180 */
1181 const char *pszBadChar = strpbrk(pDesc->pszDesc, "&<>\"'");
1182 if (!pszBadChar)
1183 return stamR3SnapshotPrintf(pThis, " desc=\"%s\"/>\n", pDesc->pszDesc);
1184
1185 stamR3SnapshotPrintf(pThis, " desc=\"");
1186 const char *pszCur = pDesc->pszDesc;
1187 do
1188 {
1189 stamR3SnapshotPrintf(pThis, "%.*s", pszBadChar - pszCur, pszCur);
1190 switch (*pszBadChar)
1191 {
1192 case '&': stamR3SnapshotPrintf(pThis, "&amp;"); break;
1193 case '<': stamR3SnapshotPrintf(pThis, "&lt;"); break;
1194 case '>': stamR3SnapshotPrintf(pThis, "&gt;"); break;
1195 case '"': stamR3SnapshotPrintf(pThis, "&quot;"); break;
1196 case '\'': stamR3SnapshotPrintf(pThis, "&apos;"); break;
1197 default: AssertMsgFailed(("%c", *pszBadChar)); break;
1198 }
1199 pszCur = pszBadChar + 1;
1200 pszBadChar = strpbrk(pszCur, "&<>\"'");
1201 } while (pszBadChar);
1202 return stamR3SnapshotPrintf(pThis, "%s\"/>\n", pszCur);
1203 }
1204 return stamR3SnapshotPrintf(pThis, "/>\n");
1205}
1206
1207
1208/**
1209 * Output callback for stamR3SnapshotPrintf.
1210 *
1211 * @returns number of bytes written.
1212 * @param pvArg The snapshot status structure.
1213 * @param pach Pointer to an array of characters (bytes).
1214 * @param cch The number or chars (bytes) to write from the array.
1215 */
1216static DECLCALLBACK(size_t) stamR3SnapshotOutput(void *pvArg, const char *pach, size_t cch)
1217{
1218 PSTAMR3SNAPSHOTONE pThis = (PSTAMR3SNAPSHOTONE)pvArg;
1219
1220 /*
1221 * Make sure we've got space for it.
1222 */
1223 if (RT_UNLIKELY((uintptr_t)pThis->pszEnd - (uintptr_t)pThis->psz < cch + 1))
1224 {
1225 if (RT_FAILURE(pThis->rc))
1226 return 0;
1227
1228 size_t cbNewSize = pThis->cbAllocated;
1229 if (cbNewSize > cch)
1230 cbNewSize *= 2;
1231 else
1232 cbNewSize += RT_ALIGN(cch + 1, 0x1000);
1233 char *pszNew = (char *)RTMemRealloc(pThis->pszStart, cbNewSize);
1234 if (!pszNew)
1235 {
1236 /*
1237 * Free up immediately, out-of-memory is bad news and this
1238 * isn't an important allocations / API.
1239 */
1240 pThis->rc = VERR_NO_MEMORY;
1241 RTMemFree(pThis->pszStart);
1242 pThis->pszStart = pThis->pszEnd = pThis->psz = NULL;
1243 pThis->cbAllocated = 0;
1244 return 0;
1245 }
1246
1247 pThis->psz = pszNew + (pThis->psz - pThis->pszStart);
1248 pThis->pszStart = pszNew;
1249 pThis->pszEnd = pszNew + cbNewSize;
1250 pThis->cbAllocated = cbNewSize;
1251 }
1252
1253 /*
1254 * Copy the chars to the buffer and terminate it.
1255 */
1256 memcpy(pThis->psz, pach, cch);
1257 pThis->psz += cch;
1258 *pThis->psz = '\0';
1259 return cch;
1260}
1261
1262
1263/**
1264 * Wrapper around RTStrFormatV for use by the snapshot API.
1265 *
1266 * @returns VBox status code.
1267 * @param pThis The snapshot status structure.
1268 * @param pszFormat The format string.
1269 * @param ... Optional arguments.
1270 */
1271static int stamR3SnapshotPrintf(PSTAMR3SNAPSHOTONE pThis, const char *pszFormat, ...)
1272{
1273 va_list va;
1274 va_start(va, pszFormat);
1275 RTStrFormatV(stamR3SnapshotOutput, pThis, NULL, NULL, pszFormat, va);
1276 va_end(va);
1277 return pThis->rc;
1278}
1279
1280
1281/**
1282 * Releases a statistics snapshot returned by STAMR3Snapshot().
1283 *
1284 * @returns VBox status.
1285 * @param pUVM Pointer to the user mode VM structure.
1286 * @param pszSnapshot The snapshot data pointer returned by STAMR3Snapshot().
1287 * NULL is allowed.
1288 */
1289VMMR3DECL(int) STAMR3SnapshotFreeU(PUVM pUVM, char *pszSnapshot)
1290{
1291 if (!pszSnapshot)
1292 RTMemFree(pszSnapshot);
1293 NOREF(pUVM);
1294 return VINF_SUCCESS;
1295}
1296
1297
1298/**
1299 * Releases a statistics snapshot returned by STAMR3Snapshot().
1300 *
1301 * @returns VBox status.
1302 * @param pVM Pointer to the VM.
1303 * @param pszSnapshot The snapshot data pointer returned by STAMR3Snapshot().
1304 * NULL is allowed.
1305 */
1306VMMR3DECL(int) STAMR3SnapshotFree(PVM pVM, char *pszSnapshot)
1307{
1308 return STAMR3SnapshotFreeU(pVM->pUVM, pszSnapshot);
1309}
1310
1311
1312/**
1313 * Dumps the selected statistics to the log.
1314 *
1315 * @returns VBox status.
1316 * @param pUVM Pointer to the user mode VM structure.
1317 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
1318 * If NULL all samples are written to the log.
1319 */
1320VMMR3DECL(int) STAMR3DumpU(PUVM pUVM, const char *pszPat)
1321{
1322 STAMR3PRINTONEARGS Args;
1323 Args.pVM = pUVM->pVM;
1324 Args.pvArg = NULL;
1325 Args.pfnPrintf = stamR3EnumLogPrintf;
1326
1327 stamR3EnumU(pUVM, pszPat, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
1328 return VINF_SUCCESS;
1329}
1330
1331
1332/**
1333 * Dumps the selected statistics to the log.
1334 *
1335 * @returns VBox status.
1336 * @param pVM Pointer to the VM.
1337 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
1338 * If NULL all samples are written to the log.
1339 */
1340VMMR3DECL(int) STAMR3Dump(PVM pVM, const char *pszPat)
1341{
1342 return STAMR3DumpU(pVM->pUVM, pszPat);
1343}
1344
1345
1346/**
1347 * Prints to the log.
1348 *
1349 * @param pArgs Pointer to the print one argument structure.
1350 * @param pszFormat Format string.
1351 * @param ... Format arguments.
1352 */
1353static DECLCALLBACK(void) stamR3EnumLogPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...)
1354{
1355 va_list va;
1356 va_start(va, pszFormat);
1357 RTLogPrintfV(pszFormat, va);
1358 va_end(va);
1359 NOREF(pArgs);
1360}
1361
1362
1363/**
1364 * Dumps the selected statistics to the release log.
1365 *
1366 * @returns VBox status.
1367 * @param pUVM Pointer to the user mode VM structure.
1368 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
1369 * If NULL all samples are written to the log.
1370 */
1371VMMR3DECL(int) STAMR3DumpToReleaseLogU(PUVM pUVM, const char *pszPat)
1372{
1373 STAMR3PRINTONEARGS Args;
1374 Args.pVM = pUVM->pVM;
1375 Args.pvArg = NULL;
1376 Args.pfnPrintf = stamR3EnumRelLogPrintf;
1377
1378 stamR3EnumU(pUVM, pszPat, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
1379 return VINF_SUCCESS;
1380}
1381
1382
1383/**
1384 * Dumps the selected statistics to the release log.
1385 *
1386 * @returns VBox status.
1387 * @param pVM Pointer to the VM.
1388 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
1389 * If NULL all samples are written to the log.
1390 */
1391VMMR3DECL(int) STAMR3DumpToReleaseLog(PVM pVM, const char *pszPat)
1392{
1393 return STAMR3DumpToReleaseLogU(pVM->pUVM, pszPat);
1394}
1395
1396
1397/**
1398 * Prints to the release log.
1399 *
1400 * @param pArgs Pointer to the print one argument structure.
1401 * @param pszFormat Format string.
1402 * @param ... Format arguments.
1403 */
1404static DECLCALLBACK(void) stamR3EnumRelLogPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...)
1405{
1406 va_list va;
1407 va_start(va, pszFormat);
1408 RTLogRelPrintfV(pszFormat, va);
1409 va_end(va);
1410 NOREF(pArgs);
1411}
1412
1413
1414/**
1415 * Prints the selected statistics to standard out.
1416 *
1417 * @returns VBox status.
1418 * @param pVM Pointer to the VM.
1419 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
1420 * If NULL all samples are reset.
1421 */
1422VMMR3DECL(int) STAMR3PrintU(PUVM pUVM, const char *pszPat)
1423{
1424 STAMR3PRINTONEARGS Args;
1425 Args.pVM = pUVM->pVM;
1426 Args.pvArg = NULL;
1427 Args.pfnPrintf = stamR3EnumPrintf;
1428
1429 stamR3EnumU(pUVM, pszPat, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
1430 return VINF_SUCCESS;
1431}
1432
1433
1434/**
1435 * Prints the selected statistics to standard out.
1436 *
1437 * @returns VBox status.
1438 * @param pVM Pointer to the VM.
1439 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
1440 * If NULL all samples are reset.
1441 */
1442VMMR3DECL(int) STAMR3Print(PVM pVM, const char *pszPat)
1443{
1444 return STAMR3PrintU(pVM->pUVM, pszPat);
1445}
1446
1447
1448/**
1449 * Prints to stdout.
1450 *
1451 * @param pArgs Pointer to the print one argument structure.
1452 * @param pszFormat Format string.
1453 * @param ... Format arguments.
1454 */
1455static DECLCALLBACK(void) stamR3EnumPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...)
1456{
1457 va_list va;
1458 va_start(va, pszFormat);
1459 RTPrintfV(pszFormat, va);
1460 va_end(va);
1461 NOREF(pArgs);
1462}
1463
1464
1465/**
1466 * Prints one sample.
1467 * Callback for stamR3EnumU().
1468 *
1469 * @returns VINF_SUCCESS
1470 * @param pDesc Pointer to the current descriptor.
1471 * @param pvArg User argument - STAMR3PRINTONEARGS.
1472 */
1473static int stamR3PrintOne(PSTAMDESC pDesc, void *pvArg)
1474{
1475 PSTAMR3PRINTONEARGS pArgs = (PSTAMR3PRINTONEARGS)pvArg;
1476
1477 switch (pDesc->enmType)
1478 {
1479 case STAMTYPE_COUNTER:
1480 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pDesc->u.pCounter->c == 0)
1481 return VINF_SUCCESS;
1482
1483 pArgs->pfnPrintf(pArgs, "%-32s %8llu %s\n", pDesc->pszName, pDesc->u.pCounter->c, STAMR3GetUnit(pDesc->enmUnit));
1484 break;
1485
1486 case STAMTYPE_PROFILE:
1487 case STAMTYPE_PROFILE_ADV:
1488 {
1489 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pDesc->u.pProfile->cPeriods == 0)
1490 return VINF_SUCCESS;
1491
1492 uint64_t u64 = pDesc->u.pProfile->cPeriods ? pDesc->u.pProfile->cPeriods : 1;
1493 pArgs->pfnPrintf(pArgs, "%-32s %8llu %s (%12llu ticks, %7llu times, max %9llu, min %7lld)\n", pDesc->pszName,
1494 pDesc->u.pProfile->cTicks / u64, STAMR3GetUnit(pDesc->enmUnit),
1495 pDesc->u.pProfile->cTicks, pDesc->u.pProfile->cPeriods, pDesc->u.pProfile->cTicksMax, pDesc->u.pProfile->cTicksMin);
1496 break;
1497 }
1498
1499 case STAMTYPE_RATIO_U32:
1500 case STAMTYPE_RATIO_U32_RESET:
1501 if (pDesc->enmVisibility == STAMVISIBILITY_USED && !pDesc->u.pRatioU32->u32A && !pDesc->u.pRatioU32->u32B)
1502 return VINF_SUCCESS;
1503 pArgs->pfnPrintf(pArgs, "%-32s %8u:%-8u %s\n", pDesc->pszName,
1504 pDesc->u.pRatioU32->u32A, pDesc->u.pRatioU32->u32B, STAMR3GetUnit(pDesc->enmUnit));
1505 break;
1506
1507 case STAMTYPE_CALLBACK:
1508 {
1509 char szBuf[512];
1510 pDesc->u.Callback.pfnPrint(pArgs->pVM, pDesc->u.Callback.pvSample, szBuf, sizeof(szBuf));
1511 pArgs->pfnPrintf(pArgs, "%-32s %s %s\n", pDesc->pszName, szBuf, STAMR3GetUnit(pDesc->enmUnit));
1512 break;
1513 }
1514
1515 case STAMTYPE_U8:
1516 case STAMTYPE_U8_RESET:
1517 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu8 == 0)
1518 return VINF_SUCCESS;
1519 pArgs->pfnPrintf(pArgs, "%-32s %8u %s\n", pDesc->pszName, *pDesc->u.pu8, STAMR3GetUnit(pDesc->enmUnit));
1520 break;
1521
1522 case STAMTYPE_X8:
1523 case STAMTYPE_X8_RESET:
1524 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu8 == 0)
1525 return VINF_SUCCESS;
1526 pArgs->pfnPrintf(pArgs, "%-32s %8x %s\n", pDesc->pszName, *pDesc->u.pu8, STAMR3GetUnit(pDesc->enmUnit));
1527 break;
1528
1529 case STAMTYPE_U16:
1530 case STAMTYPE_U16_RESET:
1531 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu16 == 0)
1532 return VINF_SUCCESS;
1533 pArgs->pfnPrintf(pArgs, "%-32s %8u %s\n", pDesc->pszName, *pDesc->u.pu16, STAMR3GetUnit(pDesc->enmUnit));
1534 break;
1535
1536 case STAMTYPE_X16:
1537 case STAMTYPE_X16_RESET:
1538 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu16 == 0)
1539 return VINF_SUCCESS;
1540 pArgs->pfnPrintf(pArgs, "%-32s %8x %s\n", pDesc->pszName, *pDesc->u.pu16, STAMR3GetUnit(pDesc->enmUnit));
1541 break;
1542
1543 case STAMTYPE_U32:
1544 case STAMTYPE_U32_RESET:
1545 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu32 == 0)
1546 return VINF_SUCCESS;
1547 pArgs->pfnPrintf(pArgs, "%-32s %8u %s\n", pDesc->pszName, *pDesc->u.pu32, STAMR3GetUnit(pDesc->enmUnit));
1548 break;
1549
1550 case STAMTYPE_X32:
1551 case STAMTYPE_X32_RESET:
1552 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu32 == 0)
1553 return VINF_SUCCESS;
1554 pArgs->pfnPrintf(pArgs, "%-32s %8x %s\n", pDesc->pszName, *pDesc->u.pu32, STAMR3GetUnit(pDesc->enmUnit));
1555 break;
1556
1557 case STAMTYPE_U64:
1558 case STAMTYPE_U64_RESET:
1559 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu64 == 0)
1560 return VINF_SUCCESS;
1561 pArgs->pfnPrintf(pArgs, "%-32s %8llu %s\n", pDesc->pszName, *pDesc->u.pu64, STAMR3GetUnit(pDesc->enmUnit));
1562 break;
1563
1564 case STAMTYPE_X64:
1565 case STAMTYPE_X64_RESET:
1566 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu64 == 0)
1567 return VINF_SUCCESS;
1568 pArgs->pfnPrintf(pArgs, "%-32s %8llx %s\n", pDesc->pszName, *pDesc->u.pu64, STAMR3GetUnit(pDesc->enmUnit));
1569 break;
1570
1571 case STAMTYPE_BOOL:
1572 case STAMTYPE_BOOL_RESET:
1573 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pf == false)
1574 return VINF_SUCCESS;
1575 pArgs->pfnPrintf(pArgs, "%-32s %s %s\n", pDesc->pszName, *pDesc->u.pf ? "true " : "false ", STAMR3GetUnit(pDesc->enmUnit));
1576 break;
1577
1578 default:
1579 AssertMsgFailed(("enmType=%d\n", pDesc->enmType));
1580 break;
1581 }
1582 NOREF(pvArg);
1583 return VINF_SUCCESS;
1584}
1585
1586
1587/**
1588 * Enumerate the statistics by the means of a callback function.
1589 *
1590 * @returns Whatever the callback returns.
1591 *
1592 * @param pUVM Pointer to the user mode VM structure.
1593 * @param pszPat The pattern to match samples.
1594 * @param pfnEnum The callback function.
1595 * @param pvUser The pvUser argument of the callback function.
1596 */
1597VMMR3DECL(int) STAMR3EnumU(PUVM pUVM, const char *pszPat, PFNSTAMR3ENUM pfnEnum, void *pvUser)
1598{
1599 STAMR3ENUMONEARGS Args;
1600 Args.pVM = pUVM->pVM;
1601 Args.pfnEnum = pfnEnum;
1602 Args.pvUser = pvUser;
1603
1604 return stamR3EnumU(pUVM, pszPat, true /* fUpdateRing0 */, stamR3EnumOne, &Args);
1605}
1606
1607
1608/**
1609 * Enumerate the statistics by the means of a callback function.
1610 *
1611 * @returns Whatever the callback returns.
1612 *
1613 * @param pVM Pointer to the VM.
1614 * @param pszPat The pattern to match samples.
1615 * @param pfnEnum The callback function.
1616 * @param pvUser The pvUser argument of the callback function.
1617 */
1618VMMR3DECL(int) STAMR3Enum(PVM pVM, const char *pszPat, PFNSTAMR3ENUM pfnEnum, void *pvUser)
1619{
1620 return STAMR3EnumU(pVM->pUVM, pszPat, pfnEnum, pvUser);
1621}
1622
1623
1624/**
1625 * Callback function for STARTR3Enum().
1626 *
1627 * @returns whatever the callback returns.
1628 * @param pDesc Pointer to the current descriptor.
1629 * @param pvArg Points to a STAMR3ENUMONEARGS structure.
1630 */
1631static int stamR3EnumOne(PSTAMDESC pDesc, void *pvArg)
1632{
1633 PSTAMR3ENUMONEARGS pArgs = (PSTAMR3ENUMONEARGS)pvArg;
1634 int rc;
1635 if (pDesc->enmType == STAMTYPE_CALLBACK)
1636 {
1637 /* Give the enumerator something useful. */
1638 char szBuf[512];
1639 pDesc->u.Callback.pfnPrint(pArgs->pVM, pDesc->u.Callback.pvSample, szBuf, sizeof(szBuf));
1640 rc = pArgs->pfnEnum(pDesc->pszName, pDesc->enmType, szBuf, pDesc->enmUnit,
1641 pDesc->enmVisibility, pDesc->pszDesc, pArgs->pvUser);
1642 }
1643 else
1644 rc = pArgs->pfnEnum(pDesc->pszName, pDesc->enmType, pDesc->u.pv, pDesc->enmUnit,
1645 pDesc->enmVisibility, pDesc->pszDesc, pArgs->pvUser);
1646 return rc;
1647}
1648
1649
1650/**
1651 * Match a name against an array of patterns.
1652 *
1653 * @returns true if it matches, false if it doesn't match.
1654 * @param papszExpressions The array of pattern expressions.
1655 * @param cExpressions The number of array entries.
1656 * @param piExpression Where to read/store the current skip index. Optional.
1657 * @param pszName The name to match.
1658 */
1659static bool stamR3MultiMatch(const char * const *papszExpressions, unsigned cExpressions,
1660 unsigned *piExpression, const char *pszName)
1661{
1662 for (unsigned i = piExpression ? *piExpression : 0; i < cExpressions; i++)
1663 {
1664 const char *pszPat = papszExpressions[i];
1665 if (RTStrSimplePatternMatch(pszPat, pszName))
1666 {
1667 /* later:
1668 if (piExpression && i > *piExpression)
1669 {
1670 check if we can skip some expressions
1671 }*/
1672 return true;
1673 }
1674 }
1675 return false;
1676}
1677
1678
1679/**
1680 * Splits a multi pattern into single ones.
1681 *
1682 * @returns Pointer to an array of single patterns. Free it with RTMemTmpFree.
1683 * @param pszPat The pattern to split.
1684 * @param pcExpressions The number of array elements.
1685 * @param pszCopy The pattern copy to free using RTStrFree.
1686 */
1687static char **stamR3SplitPattern(const char *pszPat, unsigned *pcExpressions, char **ppszCopy)
1688{
1689 Assert(pszPat && *pszPat);
1690
1691 char *pszCopy = RTStrDup(pszPat);
1692 if (!pszCopy)
1693 return NULL;
1694
1695 /* count them & allocate array. */
1696 char *psz = pszCopy;
1697 unsigned cExpressions = 1;
1698 while ((psz = strchr(psz, '|')) != NULL)
1699 cExpressions++, psz++;
1700
1701 char **papszExpressions = (char **)RTMemTmpAllocZ((cExpressions + 1) * sizeof(char *));
1702 if (!papszExpressions)
1703 {
1704 RTStrFree(pszCopy);
1705 return NULL;
1706 }
1707
1708 /* split */
1709 psz = pszCopy;
1710 for (unsigned i = 0;;)
1711 {
1712 papszExpressions[i] = psz;
1713 if (++i >= cExpressions)
1714 break;
1715 psz = strchr(psz, '|');
1716 *psz++ = '\0';
1717 }
1718
1719 /* sort the array, putting '*' last. */
1720 /** @todo sort it... */
1721
1722 *pcExpressions = cExpressions;
1723 *ppszCopy = pszCopy;
1724 return papszExpressions;
1725}
1726
1727
1728/**
1729 * Enumerates the nodes selected by a pattern or all nodes if no pattern
1730 * is specified.
1731 *
1732 * The call may lock STAM for writing before calling this function, however do
1733 * not lock it for reading as this function may need to write lock STAM.
1734 *
1735 * @returns The rc from the callback.
1736 * @param pUVM Pointer to the user mode VM structure.
1737 * @param pszPat Pattern.
1738 * @param fUpdateRing0 Update the ring-0 .
1739 * @param pfnCallback Callback function which shall be called for matching nodes.
1740 * If it returns anything but VINF_SUCCESS the enumeration is
1741 * terminated and the status code returned to the caller.
1742 * @param pvArg User parameter for the callback.
1743 */
1744static int stamR3EnumU(PUVM pUVM, const char *pszPat, bool fUpdateRing0,
1745 int (*pfnCallback)(PSTAMDESC pDesc, void *pvArg), void *pvArg)
1746{
1747 int rc = VINF_SUCCESS;
1748
1749 /*
1750 * All
1751 */
1752 if (!pszPat || !*pszPat || !strcmp(pszPat, "*"))
1753 {
1754 if (fUpdateRing0)
1755 stamR3Ring0StatsUpdateU(pUVM, "*");
1756
1757 STAM_LOCK_RD(pUVM);
1758 PSTAMDESC pCur = pUVM->stam.s.pHead;
1759 while (pCur)
1760 {
1761 rc = pfnCallback(pCur, pvArg);
1762 if (rc)
1763 break;
1764
1765 /* next */
1766 pCur = pCur->pNext;
1767 }
1768 STAM_UNLOCK_RD(pUVM);
1769 }
1770
1771 /*
1772 * Single expression pattern.
1773 */
1774 else if (!strchr(pszPat, '|'))
1775 {
1776 if (fUpdateRing0)
1777 stamR3Ring0StatsUpdateU(pUVM, pszPat);
1778
1779 STAM_LOCK_RD(pUVM);
1780 /** @todo This needs to be optimized since the GUI is using this path for the VM info dialog.
1781 * Note that it's doing exact matching. Organizing the samples in a tree would speed up thing
1782 * no end (at least for debug and profile builds). */
1783 for (PSTAMDESC pCur = pUVM->stam.s.pHead; pCur; pCur = pCur->pNext)
1784 if (RTStrSimplePatternMatch(pszPat, pCur->pszName))
1785 {
1786 rc = pfnCallback(pCur, pvArg);
1787 if (rc)
1788 break;
1789 }
1790 STAM_UNLOCK_RD(pUVM);
1791 }
1792
1793 /*
1794 * Multi expression pattern.
1795 */
1796 else
1797 {
1798 /*
1799 * Split up the pattern first.
1800 */
1801 char *pszCopy;
1802 unsigned cExpressions;
1803 char **papszExpressions = stamR3SplitPattern(pszPat, &cExpressions, &pszCopy);
1804 if (!papszExpressions)
1805 return VERR_NO_MEMORY;
1806
1807 /*
1808 * Perform the enumeration.
1809 */
1810 if (fUpdateRing0)
1811 stamR3Ring0StatsUpdateMultiU(pUVM, papszExpressions, cExpressions);
1812
1813 STAM_LOCK_RD(pUVM);
1814 unsigned iExpression = 0;
1815 for (PSTAMDESC pCur = pUVM->stam.s.pHead; pCur; pCur = pCur->pNext)
1816 if (stamR3MultiMatch(papszExpressions, cExpressions, &iExpression, pCur->pszName))
1817 {
1818 rc = pfnCallback(pCur, pvArg);
1819 if (rc)
1820 break;
1821 }
1822 STAM_UNLOCK_RD(pUVM);
1823
1824 RTMemTmpFree(papszExpressions);
1825 RTStrFree(pszCopy);
1826 }
1827
1828 return rc;
1829}
1830
1831
1832/**
1833 * Registers the ring-0 statistics.
1834 *
1835 * @param pUVM Pointer to the user mode VM structure.
1836 */
1837static void stamR3Ring0StatsRegisterU(PUVM pUVM)
1838{
1839 /* GVMM */
1840 for (unsigned i = 0; i < RT_ELEMENTS(g_aGVMMStats); i++)
1841 stamR3RegisterU(pUVM, (uint8_t *)&pUVM->stam.s.GVMMStats + g_aGVMMStats[i].offVar, NULL, NULL,
1842 g_aGVMMStats[i].enmType, STAMVISIBILITY_ALWAYS, g_aGVMMStats[i].pszName,
1843 g_aGVMMStats[i].enmUnit, g_aGVMMStats[i].pszDesc);
1844 pUVM->stam.s.cRegisteredHostCpus = 0;
1845
1846 /* GMM */
1847 for (unsigned i = 0; i < RT_ELEMENTS(g_aGMMStats); i++)
1848 stamR3RegisterU(pUVM, (uint8_t *)&pUVM->stam.s.GMMStats + g_aGMMStats[i].offVar, NULL, NULL,
1849 g_aGMMStats[i].enmType, STAMVISIBILITY_ALWAYS, g_aGMMStats[i].pszName,
1850 g_aGMMStats[i].enmUnit, g_aGMMStats[i].pszDesc);
1851}
1852
1853
1854/**
1855 * Updates the ring-0 statistics (the copy).
1856 *
1857 * @param pUVM Pointer to the user mode VM structure.
1858 * @param pszPat The pattern.
1859 */
1860static void stamR3Ring0StatsUpdateU(PUVM pUVM, const char *pszPat)
1861{
1862 stamR3Ring0StatsUpdateMultiU(pUVM, &pszPat, 1);
1863}
1864
1865
1866/**
1867 * Updates the ring-0 statistics.
1868 *
1869 * The ring-0 statistics aren't directly addressable from ring-3 and must be
1870 * copied when needed.
1871 *
1872 * @param pUVM Pointer to the user mode VM structure.
1873 * @param pszPat The pattern (for knowing when to skip).
1874 */
1875static void stamR3Ring0StatsUpdateMultiU(PUVM pUVM, const char * const *papszExpressions, unsigned cExpressions)
1876{
1877 PVM pVM = pUVM->pVM;
1878 if (!pVM || !pVM->pSession)
1879 return;
1880
1881 /*
1882 * GVMM
1883 */
1884 bool fUpdate = false;
1885 for (unsigned i = 0; i < RT_ELEMENTS(g_aGVMMStats); i++)
1886 if (stamR3MultiMatch(papszExpressions, cExpressions, NULL, g_aGVMMStats[i].pszName))
1887 {
1888 fUpdate = true;
1889 break;
1890 }
1891 if (!fUpdate)
1892 {
1893 /** @todo check the cpu leaves - rainy day. */
1894 }
1895 if (fUpdate)
1896 {
1897 GVMMQUERYSTATISTICSSREQ Req;
1898 Req.Hdr.cbReq = sizeof(Req);
1899 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
1900 Req.pSession = pVM->pSession;
1901 int rc = SUPR3CallVMMR0Ex(pVM->pVMR0, NIL_VMCPUID, VMMR0_DO_GVMM_QUERY_STATISTICS, 0, &Req.Hdr);
1902 if (RT_SUCCESS(rc))
1903 {
1904 pUVM->stam.s.GVMMStats = Req.Stats;
1905
1906 /*
1907 * Check if the number of host CPUs has changed (it will the first
1908 * time around and normally never again).
1909 */
1910 if (RT_UNLIKELY(pUVM->stam.s.GVMMStats.cHostCpus > pUVM->stam.s.cRegisteredHostCpus))
1911 {
1912 STAM_LOCK_WR(pUVM);
1913 if (RT_UNLIKELY(pUVM->stam.s.GVMMStats.cHostCpus > pUVM->stam.s.cRegisteredHostCpus))
1914 {
1915 uint32_t cCpus = pUVM->stam.s.GVMMStats.cHostCpus;
1916 for (uint32_t iCpu = pUVM->stam.s.cRegisteredHostCpus; iCpu < cCpus; iCpu++)
1917 {
1918 char szName[120];
1919 size_t cchBase = RTStrPrintf(szName, sizeof(szName), "/GVMM/HostCpus/%u", iCpu);
1920 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].idCpu, NULL, NULL,
1921 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_NONE, "Host CPU ID");
1922 strcpy(&szName[cchBase], "/idxCpuSet");
1923 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].idxCpuSet, NULL, NULL,
1924 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_NONE, "CPU Set index");
1925 strcpy(&szName[cchBase], "/DesiredHz");
1926 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].uDesiredHz, NULL, NULL,
1927 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_HZ, "The desired frequency");
1928 strcpy(&szName[cchBase], "/CurTimerHz");
1929 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].uTimerHz, NULL, NULL,
1930 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_HZ, "The current timer frequency");
1931 strcpy(&szName[cchBase], "/PPTChanges");
1932 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].cChanges, NULL, NULL,
1933 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_OCCURENCES, "RTTimerChangeInterval calls");
1934 strcpy(&szName[cchBase], "/PPTStarts");
1935 stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].cStarts, NULL, NULL,
1936 STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_OCCURENCES, "RTTimerStart calls");
1937 }
1938 pUVM->stam.s.cRegisteredHostCpus = cCpus;
1939 }
1940 STAM_UNLOCK_WR(pUVM);
1941 }
1942 }
1943 }
1944
1945 /*
1946 * GMM
1947 */
1948 fUpdate = false;
1949 for (unsigned i = 0; i < RT_ELEMENTS(g_aGMMStats); i++)
1950 if (stamR3MultiMatch(papszExpressions, cExpressions, NULL, g_aGMMStats[i].pszName))
1951 {
1952 fUpdate = true;
1953 break;
1954 }
1955 if (fUpdate)
1956 {
1957 GMMQUERYSTATISTICSSREQ Req;
1958 Req.Hdr.cbReq = sizeof(Req);
1959 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
1960 Req.pSession = pVM->pSession;
1961 int rc = SUPR3CallVMMR0Ex(pVM->pVMR0, NIL_VMCPUID, VMMR0_DO_GMM_QUERY_STATISTICS, 0, &Req.Hdr);
1962 if (RT_SUCCESS(rc))
1963 pUVM->stam.s.GMMStats = Req.Stats;
1964 }
1965}
1966
1967
1968/**
1969 * Get the unit string.
1970 *
1971 * @returns Pointer to read only unit string.
1972 * @param enmUnit The unit.
1973 */
1974VMMR3DECL(const char *) STAMR3GetUnit(STAMUNIT enmUnit)
1975{
1976 switch (enmUnit)
1977 {
1978 case STAMUNIT_NONE: return "";
1979 case STAMUNIT_CALLS: return "calls";
1980 case STAMUNIT_COUNT: return "count";
1981 case STAMUNIT_BYTES: return "bytes";
1982 case STAMUNIT_PAGES: return "pages";
1983 case STAMUNIT_ERRORS: return "errors";
1984 case STAMUNIT_OCCURENCES: return "times";
1985 case STAMUNIT_TICKS: return "ticks";
1986 case STAMUNIT_TICKS_PER_CALL: return "ticks/call";
1987 case STAMUNIT_TICKS_PER_OCCURENCE: return "ticks/time";
1988 case STAMUNIT_GOOD_BAD: return "good:bad";
1989 case STAMUNIT_MEGABYTES: return "megabytes";
1990 case STAMUNIT_KILOBYTES: return "kilobytes";
1991 case STAMUNIT_NS: return "ns";
1992 case STAMUNIT_NS_PER_CALL: return "ns/call";
1993 case STAMUNIT_NS_PER_OCCURENCE: return "ns/time";
1994 case STAMUNIT_PCT: return "%";
1995 case STAMUNIT_HZ: return "Hz";
1996
1997 default:
1998 AssertMsgFailed(("Unknown unit %d\n", enmUnit));
1999 return "(?unit?)";
2000 }
2001}
2002
2003#ifdef VBOX_WITH_DEBUGGER
2004
2005/**
2006 * The '.stats' command.
2007 *
2008 * @returns VBox status.
2009 * @param pCmd Pointer to the command descriptor (as registered).
2010 * @param pCmdHlp Pointer to command helper functions.
2011 * @param pVM Pointer to the current VM (if any).
2012 * @param paArgs Pointer to (readonly) array of arguments.
2013 * @param cArgs Number of arguments in the array.
2014 */
2015static DECLCALLBACK(int) stamR3CmdStats(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs)
2016{
2017 /*
2018 * Validate input.
2019 */
2020 DBGC_CMDHLP_REQ_VM_RET(pCmdHlp, pCmd, pVM);
2021 PUVM pUVM = pVM->pUVM;
2022 if (!pUVM->stam.s.pHead)
2023 return DBGCCmdHlpFail(pCmdHlp, pCmd, "No statistics present");
2024
2025 /*
2026 * Do the printing.
2027 */
2028 STAMR3PRINTONEARGS Args;
2029 Args.pVM = pVM;
2030 Args.pvArg = pCmdHlp;
2031 Args.pfnPrintf = stamR3EnumDbgfPrintf;
2032
2033 return stamR3EnumU(pUVM, cArgs ? paArgs[0].u.pszString : NULL, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
2034}
2035
2036
2037/**
2038 * Display one sample in the debugger.
2039 *
2040 * @param pArgs Pointer to the print one argument structure.
2041 * @param pszFormat Format string.
2042 * @param ... Format arguments.
2043 */
2044static DECLCALLBACK(void) stamR3EnumDbgfPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...)
2045{
2046 PDBGCCMDHLP pCmdHlp = (PDBGCCMDHLP)pArgs->pvArg;
2047
2048 va_list va;
2049 va_start(va, pszFormat);
2050 pCmdHlp->pfnPrintfV(pCmdHlp, NULL, pszFormat, va);
2051 va_end(va);
2052 NOREF(pArgs);
2053}
2054
2055
2056/**
2057 * The '.statsreset' command.
2058 *
2059 * @returns VBox status.
2060 * @param pCmd Pointer to the command descriptor (as registered).
2061 * @param pCmdHlp Pointer to command helper functions.
2062 * @param pVM Pointer to the current VM (if any).
2063 * @param paArgs Pointer to (readonly) array of arguments.
2064 * @param cArgs Number of arguments in the array.
2065 */
2066static DECLCALLBACK(int) stamR3CmdStatsReset(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs)
2067{
2068 /*
2069 * Validate input.
2070 */
2071 DBGC_CMDHLP_REQ_VM_RET(pCmdHlp, pCmd, pVM);
2072 PUVM pUVM = pVM->pUVM;
2073 if (!pUVM->stam.s.pHead)
2074 return DBGCCmdHlpFail(pCmdHlp, pCmd, "No statistics present");
2075
2076 /*
2077 * Execute reset.
2078 */
2079 int rc = STAMR3ResetU(pUVM, cArgs ? paArgs[0].u.pszString : NULL);
2080 if (RT_SUCCESS(rc))
2081 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "STAMR3ResetU");
2082 return DBGCCmdHlpPrintf(pCmdHlp, "Statistics have been reset.\n");
2083}
2084
2085#endif /* VBOX_WITH_DEBUGGER */
2086
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