VirtualBox

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

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

STAM: Use SUPCallVMMR0Ex.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use