VirtualBox

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

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

VMM/doxygen: More links.

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