VirtualBox

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

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

VMM: Made STAM assert on missaligned statistics. Fixed a bunch of mislignments on 32-bit darwin. (burns are expected)

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

© 2023 Oracle
ContactPrivacy policyTerms of Use