VirtualBox

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

Last change on this file since 33000 was 32796, checked in by vboxsync, 14 years ago

GVMMR0,TM,STAM: Periodic preemption timer fixes, adjustments and statistics. (still disabled)

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

© 2023 Oracle
ContactPrivacy policyTerms of Use