VirtualBox

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

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

STAM,VM: ring-3 only testing of pdmcritsectrw.h (disabled).

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