VirtualBox

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

Last change on this file since 39944 was 39917, checked in by vboxsync, 13 years ago

STAM,GMM,VBoxDbg: Adding GMM statistics (at last). Introduces STAMTYPE_BOOL and STAMTYPE_BOOL_RESET. Fixes a beyond end of string access in VBoxDbg (memcmp).

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