VirtualBox

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

Last change on this file since 12818 was 12818, checked in by vboxsync, 16 years ago

STAM,IOM: force sort order compatability with file system hierarchy to fix some assumptions in the qt4 stats viewer.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette