VirtualBox

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

Last change on this file since 30037 was 28800, checked in by vboxsync, 14 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

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

© 2023 Oracle
ContactPrivacy policyTerms of Use