VirtualBox

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

Last change on this file since 19454 was 19454, checked in by vboxsync, 15 years ago

VMM++: More on poking. Fixed broken R0 stats (wrong way of calling into VMMR0), use NIL_VMCPUID instead of 0 to VMMR0EntryEx when it is supposed to be irrellevant. Use VMCPUID. Allow for and check NIL_VMCPUID. Fixed a few missing/wrong idCpu checks (paranoia mostly).

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