VirtualBox

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

Last change on this file since 5232 was 5211, checked in by vboxsync, 17 years ago

GVMM statistics.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 54.2 KB
Line 
1/* $Id: STAM.cpp 5211 2007-10-09 18:16:08Z vboxsync $ */
2/** @file
3 * STAM - The Statistics Manager.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
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 as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_STAM
23#include <VBox/stam.h>
24#include "STAMInternal.h"
25#include <VBox/vm.h>
26#include <VBox/err.h>
27#include <VBox/dbg.h>
28#include <VBox/log.h>
29
30#include <iprt/assert.h>
31#include <iprt/asm.h>
32#include <iprt/alloc.h>
33#include <iprt/stream.h>
34#include <iprt/string.h>
35
36
37/*******************************************************************************
38* Structures and Typedefs *
39*******************************************************************************/
40/**
41 * Argument structure for stamR3PrintOne().
42 */
43typedef struct STAMR3PRINTONEARGS
44{
45 PVM pVM;
46 void *pvArg;
47 DECLCALLBACKMEMBER(void, pfnPrintf)(struct STAMR3PRINTONEARGS *pvArg, const char *pszFormat, ...);
48} STAMR3PRINTONEARGS, *PSTAMR3PRINTONEARGS;
49
50
51/**
52 * Argument structure to stamR3EnumOne().
53 */
54typedef struct STAMR3ENUMONEARGS
55{
56 PVM pVM;
57 PFNSTAMR3ENUM pfnEnum;
58 void *pvUser;
59} STAMR3ENUMONEARGS, *PSTAMR3ENUMONEARGS;
60
61
62/**
63 * The snapshot status structure.
64 * Argument package passed to stamR3SnapshotOne, stamR3SnapshotPrintf and stamR3SnapshotOutput.
65 */
66typedef struct STAMR3SNAPSHOTONE
67{
68 /** Pointer to the buffer start. */
69 char *pszStart;
70 /** Pointer to the buffer end. */
71 char *pszEnd;
72 /** Pointer to the current buffer position. */
73 char *psz;
74 /** The VM handle (just in case). */
75 PVM pVM;
76 /** The number of bytes allocated. */
77 size_t cbAllocated;
78 /** The status code. */
79 int rc;
80 /** Whether to include the description strings. */
81 bool fWithDesc;
82} STAMR3SNAPSHOTONE, *PSTAMR3SNAPSHOTONE;
83
84
85/**
86 * Init record for a ring-0 statistic sample.
87 */
88typedef struct STAMINITR0SAMPLE
89{
90 /** The VM structure offset of the variable. */
91 unsigned offVar;
92 /** The type. */
93 STAMTYPE enmType;
94 /** The unit. */
95 STAMUNIT enmUnit;
96 /** The name. */
97 const char *pszName;
98 /** The description. */
99 const char *pszDesc;
100} STAMINITR0SAMPLE;
101
102
103/*******************************************************************************
104* Internal Functions *
105*******************************************************************************/
106static int stamR3Register(PVM pVM, void *pvSample, PFNSTAMR3CALLBACKRESET pfnReset, PFNSTAMR3CALLBACKPRINT pfnPrint,
107 STAMTYPE enmType, STAMVISIBILITY enmVisibility, const char *pszName, STAMUNIT enmUnit, const char *pszDesc);
108static int stamR3ResetOne(PSTAMDESC pDesc, void *pvArg);
109static DECLCALLBACK(void) stamR3EnumLogPrintf(PSTAMR3PRINTONEARGS pvArg, const char *pszFormat, ...);
110static DECLCALLBACK(void) stamR3EnumRelLogPrintf(PSTAMR3PRINTONEARGS pvArg, const char *pszFormat, ...);
111static DECLCALLBACK(void) stamR3EnumPrintf(PSTAMR3PRINTONEARGS pvArg, const char *pszFormat, ...);
112static int stamR3SnapshotOne(PSTAMDESC pDesc, void *pvArg);
113static int stamR3SnapshotPrintf(PSTAMR3SNAPSHOTONE pThis, const char *pszFormat, ...);
114static int stamR3PrintOne(PSTAMDESC pDesc, void *pvArg);
115static int stamR3EnumOne(PSTAMDESC pDesc, void *pvArg);
116static int stamR3Enum(PVM pVM, const char *pszPat, bool fUpdateRing0, int (pfnCallback)(PSTAMDESC pDesc, void *pvArg), void *pvArg);
117static void stamR3Ring0StatsRegister(PVM pVM);
118static void stamR3Ring0StatsUpdate(PVM pVM, const char *pszPat);
119static void stamR3Ring0StatsUpdateMulti(PVM pVM, const char * const *papszExpressions, unsigned cExpressions);
120
121#ifdef VBOX_WITH_DEBUGGER
122static DECLCALLBACK(int) stamR3CmdStats(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
123static DECLCALLBACK(void) stamR3EnumDbgfPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...);
124static DECLCALLBACK(int) stamR3CmdStatsReset(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
125#endif
126
127
128/*******************************************************************************
129* Global Variables *
130*******************************************************************************/
131#ifdef VBOX_WITH_DEBUGGER
132/** Pattern argument. */
133static const DBGCVARDESC g_aArgPat[] =
134{
135 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
136 { 0, 1, DBGCVAR_CAT_STRING, 0, "pattern", "Which samples the command shall be applied to. Use '*' as wildcard. Use ';' to separate expression." }
137};
138
139/** Command descriptors. */
140static const DBGCCMD g_aCmds[] =
141{
142 /* pszCmd, cArgsMin, cArgsMax, paArgDesc, cArgDescs, pResultDesc, fFlags, pfnHandler pszSyntax, ....pszDescription */
143 { "stats", 0, 1, &g_aArgPat[0], ELEMENTS(g_aArgPat), NULL, 0, stamR3CmdStats, "[pattern]", "Display statistics." },
144 { "statsreset", 0, 1, &g_aArgPat[0], ELEMENTS(g_aArgPat), NULL, 0, stamR3CmdStatsReset,"[pattern]", "Resets statistics." }
145};
146#endif
147
148
149/**
150 * The GVMM init records.
151 */
152static const STAMINITR0SAMPLE g_aGVMMStats[] =
153{
154 { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedVM.cHaltCalls), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/VM/HaltCalls", "The number of calls to GVMMR0SchedHalt." },
155 { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedVM.cHaltBlocking), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/VM/HaltBlocking", "The number of times we did go to sleep in GVMMR0SchedHalt." },
156 { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedVM.cHaltTimeouts), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/VM/HaltTimeouts", "The number of times we timed out in GVMMR0SchedHalt." },
157 { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedVM.cHaltNotBlocking), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/VM/HaltNotBlocking", "The number of times we didn't go to sleep in GVMMR0SchedHalt." },
158 { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedVM.cHaltWakeUps), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/VM/HaltWakeUps", "The number of wake ups done during GVMMR0SchedHalt." },
159 { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedVM.cWakeUpCalls), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/VM/WakeUpCalls", "The number of calls to GVMMR0WakeUp." },
160 { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedVM.cWakeUpNotHalted), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/VM/WakeUpNotHalted", "The number of times the EMT thread wasn't actually halted when GVMMR0WakeUp was called." },
161 { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedVM.cWakeUpWakeUps), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/VM/WakeUpWakeUps", "The number of wake ups done during GVMMR0WakeUp (not counting the explicit one)." },
162 { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedVM.cPollCalls), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/VM/PollCalls", "The number of calls to GVMMR0SchedPoll." },
163 { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedVM.cPollHalts), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/VM/PollHalts", "The number of times the EMT has halted in a GVMMR0SchedPoll call." },
164 { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedVM.cPollWakeUps), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/VM/PollWakeUps", "The number of wake ups done during GVMMR0SchedPoll." },
165
166 { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedSum.cHaltCalls), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/Sum/HaltCalls", "The number of calls to GVMMR0SchedHalt." },
167 { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedSum.cHaltBlocking), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/Sum/HaltBlocking", "The number of times we did go to sleep in GVMMR0SchedHalt." },
168 { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedSum.cHaltTimeouts), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/Sum/HaltTimeouts", "The number of times we timed out in GVMMR0SchedHalt." },
169 { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedSum.cHaltNotBlocking), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/Sum/HaltNotBlocking", "The number of times we didn't go to sleep in GVMMR0SchedHalt." },
170 { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedSum.cHaltWakeUps), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/Sum/HaltWakeUps", "The number of wake ups done during GVMMR0SchedHalt." },
171 { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedSum.cWakeUpCalls), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/Sum/WakeUpCalls", "The number of calls to GVMMR0WakeUp." },
172 { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedSum.cWakeUpNotHalted), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/Sum/WakeUpNotHalted", "The number of times the EMT thread wasn't actually halted when GVMMR0WakeUp was called." },
173 { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedSum.cWakeUpWakeUps), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/Sum/WakeUpWakeUps", "The number of wake ups done during GVMMR0WakeUp (not counting the explicit one)." },
174 { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedSum.cPollCalls), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/Sum/PollCalls", "The number of calls to GVMMR0SchedPoll." },
175 { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedSum.cPollHalts), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/Sum/PollHalts", "The number of times the EMT has halted in a GVMMR0SchedPoll call." },
176 { RT_UOFFSETOF(VM, stam.s.GVMMStats.SchedSum.cPollWakeUps), STAMTYPE_U64, STAMUNIT_CALLS, "/GVMM/Sum/PollWakeUps", "The number of wake ups done during GVMMR0SchedPoll." },
177
178 { RT_UOFFSETOF(VM, stam.s.GVMMStats.cVMs), STAMTYPE_U32, STAMUNIT_CALLS, "/GVMM/VMs", "The number of VMs accessible to the caller." },
179};
180
181
182/**
183 * Initializes the STAM.
184 *
185 * @returns VBox status code.
186 * @param pVM The VM to operate on.
187 */
188STAMR3DECL(int) STAMR3Init(PVM pVM)
189{
190 LogFlow(("STAMR3Init\n"));
191
192 /*
193 * Assert alignment and sizes.
194 */
195 AssertRelease(!(RT_OFFSETOF(VM, stam.s) & 31));
196 AssertRelease(sizeof(pVM->stam.s) <= sizeof(pVM->stam.padding));
197
198 /*
199 * Setup any fixed pointers and offsets.
200 */
201 pVM->stam.s.offVM = RT_OFFSETOF(VM, stam);
202 int rc = RTSemRWCreate(&pVM->stam.s.RWSem);
203 AssertRC(rc);
204 if (VBOX_FAILURE(rc))
205 return rc;
206
207 /*
208 * Register the ring-0 statistics (GVMM/GMM).
209 */
210 stamR3Ring0StatsRegister(pVM);
211
212#ifdef VBOX_WITH_DEBUGGER
213 /*
214 * Register debugger commands.
215 */
216 static bool fRegisteredCmds = false;
217 if (!fRegisteredCmds)
218 {
219 int rc = DBGCRegisterCommands(&g_aCmds[0], ELEMENTS(g_aCmds));
220 if (VBOX_SUCCESS(rc))
221 fRegisteredCmds = true;
222 }
223#endif
224
225 return VINF_SUCCESS;
226}
227
228
229/**
230 * Applies relocations to data and code managed by this
231 * component. This function will be called at init and
232 * whenever the VMM need to relocate it self inside the GC.
233 *
234 * @param pVM The VM.
235 */
236STAMR3DECL(void) STAMR3Relocate(PVM pVM)
237{
238 LogFlow(("STAMR3Relocate\n"));
239 NOREF(pVM);
240}
241
242
243/**
244 * Terminates the STAM.
245 *
246 * Termination means cleaning up and freeing all resources,
247 * the VM it self is at this point powered off or suspended.
248 *
249 * @returns VBox status code.
250 * @param pVM The VM to operate on.
251 */
252STAMR3DECL(int) STAMR3Term(PVM pVM)
253{
254 /*
255 * Free used memory and the RWLock.
256 */
257 PSTAMDESC pCur = pVM->stam.s.pHead;
258 while (pCur)
259 {
260 void *pvFree = pCur;
261 pCur = pCur->pNext;
262 RTMemFree(pvFree);
263 }
264 pVM->stam.s.pHead = NULL;
265
266 /* careful here as we might be called twice in on some failure paths (?) */
267 if (pVM->stam.s.RWSem != NIL_RTSEMRW)
268 RTSemRWDestroy(pVM->stam.s.RWSem);
269 pVM->stam.s.RWSem = NIL_RTSEMRW;
270 return VINF_SUCCESS;
271}
272
273
274
275
276/**
277 * Registers a sample with the statistics mamanger.
278 *
279 * Statistics are maintained on a per VM basis and should therefore
280 * be registered during the VM init stage. However, there is not problem
281 * registering temporary samples or samples for hotpluggable devices. Samples
282 * can be deregisterd using the STAMR3Deregister() function, but note that
283 * this is only necessary for temporary samples or hotpluggable devices.
284 *
285 * It is not possible to register the same sample twice.
286 *
287 * @returns VBox status.
288 * @param pVM The VM handle.
289 * @param pvSample Pointer to the sample.
290 * @param enmType Sample type. This indicates what pvSample is pointing at.
291 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
292 * @param pszName Sample name. The name is on this form "/<component>/<sample>".
293 * Further nesting is possible.
294 * @param enmUnit Sample unit.
295 * @param pszDesc Sample description.
296 */
297STAMR3DECL(int) STAMR3Register(PVM pVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, const char *pszName, STAMUNIT enmUnit, const char *pszDesc)
298{
299 AssertReturn(enmType != STAMTYPE_CALLBACK, VERR_INVALID_PARAMETER);
300 return stamR3Register(pVM, pvSample, NULL, NULL, enmType, enmVisibility, pszName, enmUnit, pszDesc);
301}
302
303
304/**
305 * Same as STAMR3Register except that the name is specified in a
306 * RTStrPrintf like fashion.
307 *
308 * @returns VBox status.
309 * @param pVM The VM handle.
310 * @param pvSample Pointer to the sample.
311 * @param enmType Sample type. This indicates what pvSample is pointing at.
312 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
313 * @param enmUnit Sample unit.
314 * @param pszDesc Sample description.
315 * @param pszName The sample name format string.
316 * @param ... Arguments to the format string.
317 */
318STAMR3DECL(int) STAMR3RegisterF(PVM pVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
319 const char *pszDesc, const char *pszName, ...)
320{
321 va_list args;
322 va_start(args, pszName);
323 int rc = STAMR3RegisterV(pVM, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName, args);
324 va_end(args);
325 return rc;
326}
327
328
329/**
330 * Same as STAMR3Register except that the name is specified in a
331 * RTStrPrintfV like fashion.
332 *
333 * @returns VBox status.
334 * @param pVM The VM handle.
335 * @param pvSample Pointer to the sample.
336 * @param enmType Sample type. This indicates what pvSample is pointing at.
337 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
338 * @param enmUnit Sample unit.
339 * @param pszDesc Sample description.
340 * @param pszName The sample name format string.
341 * @param args Arguments to the format string.
342 */
343STAMR3DECL(int) STAMR3RegisterV(PVM pVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
344 const char *pszDesc, const char *pszName, va_list args)
345{
346 AssertReturn(enmType != STAMTYPE_CALLBACK, VERR_INVALID_PARAMETER);
347
348 char *pszFormattedName;
349 RTStrAPrintfV(&pszFormattedName, pszName, args);
350 if (!pszFormattedName)
351 return VERR_NO_MEMORY;
352
353 int rc = STAMR3Register(pVM, pvSample, enmType, enmVisibility, pszFormattedName, enmUnit, pszDesc);
354 RTStrFree(pszFormattedName);
355 return rc;
356}
357
358
359/**
360 * Similar to STAMR3Register except for the two callbacks, the implied type (STAMTYPE_CALLBACK),
361 * and name given in an RTStrPrintf like fashion.
362 *
363 * @returns VBox status.
364 * @param pVM The VM handle.
365 * @param pvSample Pointer to the sample.
366 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
367 * @param enmUnit Sample unit.
368 * @param pfnReset Callback for resetting the sample. NULL should be used if the sample can't be reset.
369 * @param pfnPrint Print the sample.
370 * @param pszDesc Sample description.
371 * @param pszName The sample name format string.
372 * @param ... Arguments to the format string.
373 * @remark There is currently no device or driver variant of this API. Add one if it should become necessary!
374 */
375STAMR3DECL(int) STAMR3RegisterCallback(PVM pVM, void *pvSample, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
376 PFNSTAMR3CALLBACKRESET pfnReset, PFNSTAMR3CALLBACKPRINT pfnPrint,
377 const char *pszDesc, const char *pszName, ...)
378{
379 va_list args;
380 va_start(args, pszName);
381 int rc = STAMR3RegisterCallbackV(pVM, pvSample, enmVisibility, enmUnit, pfnReset, pfnPrint, pszDesc, pszName, args);
382 va_end(args);
383 return rc;
384}
385
386
387/**
388 * Same as STAMR3RegisterCallback() except for the ellipsis which is a va_list here.
389 *
390 * @returns VBox status.
391 * @param pVM The VM handle.
392 * @param pvSample Pointer to the sample.
393 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
394 * @param enmUnit Sample unit.
395 * @param pfnReset Callback for resetting the sample. NULL should be used if the sample can't be reset.
396 * @param pfnPrint Print the sample.
397 * @param pszDesc Sample description.
398 * @param pszName The sample name format string.
399 * @param args Arguments to the format string.
400 * @remark There is currently no device or driver variant of this API. Add one if it should become necessary!
401 */
402STAMR3DECL(int) STAMR3RegisterCallbackV(PVM pVM, void *pvSample, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
403 PFNSTAMR3CALLBACKRESET pfnReset, PFNSTAMR3CALLBACKPRINT pfnPrint,
404 const char *pszDesc, const char *pszName, va_list args)
405{
406 char *pszFormattedName;
407 RTStrAPrintfV(&pszFormattedName, pszName, args);
408 if (!pszFormattedName)
409 return VERR_NO_MEMORY;
410
411 int rc = stamR3Register(pVM, pvSample, pfnReset, pfnPrint, STAMTYPE_CALLBACK, enmVisibility, pszFormattedName, enmUnit, pszDesc);
412 RTStrFree(pszFormattedName);
413 return rc;
414}
415
416
417/**
418 * Internal worker for the different register calls.
419 *
420 * @returns VBox status.
421 * @param pVM The VM handle.
422 * @param pvSample Pointer to the sample.
423 * @param pfnReset Callback for resetting the sample. NULL should be used if the sample can't be reset.
424 * @param pfnPrint Print the sample.
425 * @param enmType Sample type. This indicates what pvSample is pointing at.
426 * @param enmVisibility Visibility type specifying whether unused statistics should be visible or not.
427 * @param enmUnit Sample unit.
428 * @param pszDesc Sample description.
429 * @param pszName The sample name format string.
430 * @param args Arguments to the format string.
431 * @remark There is currently no device or driver variant of this API. Add one if it should become necessary!
432 */
433static int stamR3Register(PVM pVM, void *pvSample, PFNSTAMR3CALLBACKRESET pfnReset, PFNSTAMR3CALLBACKPRINT pfnPrint,
434 STAMTYPE enmType, STAMVISIBILITY enmVisibility, const char *pszName, STAMUNIT enmUnit, const char *pszDesc)
435{
436 STAM_LOCK_WR(pVM);
437
438 /*
439 * Check if exists.
440 */
441 PSTAMDESC pPrev = NULL;
442 PSTAMDESC pCur = pVM->stam.s.pHead;
443 while (pCur)
444 {
445 int iDiff = strcmp(pCur->pszName, pszName);
446 /* passed it */
447 if (iDiff > 0)
448 break;
449 /* found it. */
450 if (!iDiff)
451 {
452 STAM_UNLOCK_WR(pVM);
453 AssertMsgFailed(("Duplicate sample name: %s\n", pszName));
454 return VERR_ALREADY_EXISTS;
455 }
456
457 /* next */
458 pPrev = pCur;
459 pCur = pCur->pNext;
460 }
461
462 /*
463 * Create a new node and insert it at the current location.
464 */
465 int rc;
466 int cchName = strlen(pszName) + 1;
467 int cchDesc = pszDesc ? strlen(pszDesc) + 1 : 0;
468 PSTAMDESC pNew = (PSTAMDESC)RTMemAlloc(sizeof(*pNew) + cchName + cchDesc);
469 if (pNew)
470 {
471 pNew->pszName = (char *)memcpy((char *)(pNew + 1), pszName, cchName);
472 pNew->enmType = enmType;
473 pNew->enmVisibility = enmVisibility;
474 if (enmType != STAMTYPE_CALLBACK)
475 pNew->u.pv = pvSample;
476 else
477 {
478 pNew->u.Callback.pvSample = pvSample;
479 pNew->u.Callback.pfnReset = pfnReset;
480 pNew->u.Callback.pfnPrint = pfnPrint;
481 }
482 pNew->enmUnit = enmUnit;
483 pNew->pszDesc = NULL;
484 if (pszDesc)
485 pNew->pszDesc = (char *)memcpy((char *)(pNew + 1) + cchName, pszDesc, cchDesc);
486
487 pNew->pNext = pCur;
488 if (pPrev)
489 pPrev->pNext = pNew;
490 else
491 pVM->stam.s.pHead = pNew;
492
493 stamR3ResetOne(pNew, pVM);
494 rc = VINF_SUCCESS;
495 }
496 else
497 rc = VERR_NO_MEMORY;
498
499 STAM_UNLOCK_WR(pVM);
500 return rc;
501}
502
503
504/**
505 * Deregisters a sample previously registered by STAR3Register().
506 *
507 * This is intended used for devices which can be unplugged and for
508 * temporary samples.
509 *
510 * @returns VBox status.
511 * @param pVM The VM handle.
512 * @param pvSample Pointer to the sample registered with STAMR3Register().
513 */
514STAMR3DECL(int) STAMR3Deregister(PVM pVM, void *pvSample)
515{
516 STAM_LOCK_WR(pVM);
517
518 /*
519 * Search for it.
520 */
521 int rc = VERR_INVALID_HANDLE;
522 PSTAMDESC pPrev = NULL;
523 PSTAMDESC pCur = pVM->stam.s.pHead;
524 while (pCur)
525 {
526 if (pCur->u.pv == pvSample)
527 {
528 void *pvFree = pCur;
529 pCur = pCur->pNext;
530 if (pPrev)
531 pPrev->pNext = pCur;
532 else
533 pVM->stam.s.pHead = pCur;
534
535 RTMemFree(pvFree);
536 rc = VINF_SUCCESS;
537 continue;
538 }
539
540 /* next */
541 pPrev = pCur;
542 pCur = pCur->pNext;
543 }
544
545 STAM_UNLOCK_WR(pVM);
546 return rc;
547}
548
549
550/**
551 * Resets statistics for the specified VM.
552 * It's possible to select a subset of the samples.
553 *
554 * @returns VBox status. (Basically, it cannot fail.)
555 * @param pVM The VM handle.
556 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
557 * If NULL all samples are reset.
558 */
559STAMR3DECL(int) STAMR3Reset(PVM pVM, const char *pszPat)
560{
561 STAM_LOCK_WR(pVM);
562 stamR3Enum(pVM, pszPat, false /* fUpdateRing0 */, stamR3ResetOne, pVM);
563 STAM_UNLOCK_WR(pVM);
564 return VINF_SUCCESS;
565}
566
567
568/**
569 * Resets one statistics sample.
570 * Callback for stamR3Enum().
571 *
572 * @returns VINF_SUCCESS
573 * @param pDesc Pointer to the current descriptor.
574 * @param pvArg User argument - The VM handle.
575 */
576static int stamR3ResetOne(PSTAMDESC pDesc, void *pvArg)
577{
578 switch (pDesc->enmType)
579 {
580 case STAMTYPE_COUNTER:
581 ASMAtomicXchgU64(&pDesc->u.pCounter->c, 0);
582 break;
583
584 case STAMTYPE_PROFILE:
585 case STAMTYPE_PROFILE_ADV:
586 ASMAtomicXchgU64(&pDesc->u.pProfile->cPeriods, 0);
587 ASMAtomicXchgU64(&pDesc->u.pProfile->cTicks, 0);
588 ASMAtomicXchgU64(&pDesc->u.pProfile->cTicksMax, 0);
589 ASMAtomicXchgU64(&pDesc->u.pProfile->cTicksMin, ~0);
590 break;
591
592 case STAMTYPE_RATIO_U32_RESET:
593 ASMAtomicXchgU32(&pDesc->u.pRatioU32->u32A, 0);
594 ASMAtomicXchgU32(&pDesc->u.pRatioU32->u32B, 0);
595 break;
596
597 case STAMTYPE_CALLBACK:
598 if (pDesc->u.Callback.pfnReset)
599 pDesc->u.Callback.pfnReset((PVM)pvArg, pDesc->u.Callback.pvSample);
600 break;
601
602 case STAMTYPE_U8_RESET:
603 case STAMTYPE_X8_RESET:
604 ASMAtomicXchgU8(pDesc->u.pu8, 0);
605 break;
606
607 case STAMTYPE_U16_RESET:
608 case STAMTYPE_X16_RESET:
609 ASMAtomicXchgU16(pDesc->u.pu16, 0);
610 break;
611
612 case STAMTYPE_U32_RESET:
613 case STAMTYPE_X32_RESET:
614 ASMAtomicXchgU32(pDesc->u.pu32, 0);
615 break;
616
617 case STAMTYPE_U64_RESET:
618 case STAMTYPE_X64_RESET:
619 ASMAtomicXchgU64(pDesc->u.pu64, 0);
620 break;
621
622 /* These are custom and will not be touched. */
623 case STAMTYPE_U8:
624 case STAMTYPE_X8:
625 case STAMTYPE_U16:
626 case STAMTYPE_X16:
627 case STAMTYPE_U32:
628 case STAMTYPE_X32:
629 case STAMTYPE_U64:
630 case STAMTYPE_X64:
631 case STAMTYPE_RATIO_U32:
632 break;
633
634 default:
635 AssertMsgFailed(("enmType=%d\n", pDesc->enmType));
636 break;
637 }
638 NOREF(pvArg);
639 return VINF_SUCCESS;
640}
641
642
643/**
644 * Get a snapshot of the statistics.
645 * It's possible to select a subset of the samples.
646 *
647 * @returns VBox status. (Basically, it cannot fail.)
648 * @param pVM The VM handle.
649 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
650 * If NULL all samples are reset.
651 * @param fWithDesc Whether to include the descriptions.
652 * @param ppszSnapshot Where to store the pointer to the snapshot data.
653 * The format of the snapshot should be XML, but that will have to be discussed
654 * when this function is implemented.
655 * The returned pointer must be freed by calling STAMR3SnapshotFree().
656 * @param pcchSnapshot Where to store the size of the snapshot data. (Excluding the trailing '\0')
657 */
658STAMR3DECL(int) STAMR3Snapshot(PVM pVM, const char *pszPat, char **ppszSnapshot, size_t *pcchSnapshot, bool fWithDesc)
659{
660 STAMR3SNAPSHOTONE State = { NULL, NULL, NULL, pVM, 0, VINF_SUCCESS, fWithDesc };
661
662 /*
663 * Write the XML header.
664 */
665 /** @todo Make this proper & valid XML. */
666 stamR3SnapshotPrintf(&State, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
667
668 /*
669 * Write the content.
670 */
671 stamR3SnapshotPrintf(&State, "<Statistics>\n");
672 STAM_LOCK_RD(pVM);
673 int rc = stamR3Enum(pVM, pszPat, true /* fUpdateRing0 */, stamR3SnapshotOne, &State);
674 STAM_UNLOCK_RD(pVM);
675 stamR3SnapshotPrintf(&State, "</Statistics>\n");
676
677 if (VBOX_SUCCESS(rc))
678 rc = State.rc;
679 else
680 {
681 RTMemFree(State.pszStart);
682 State.pszStart = State.pszEnd = State.psz = NULL;
683 State.cbAllocated = 0;
684 }
685
686 /*
687 * Done.
688 */
689 *ppszSnapshot = State.pszStart;
690 if (pcchSnapshot)
691 *pcchSnapshot = State.psz - State.pszStart;
692 return rc;
693}
694
695
696/**
697 * stamR3Enum callback employed by STAMR3Snapshot.
698 *
699 * @returns VBox status code, but it's interpreted as 0 == success / !0 == failure by enmR3Enum.
700 * @param pDesc The sample.
701 * @param pvArg The snapshot status structure.
702 */
703static int stamR3SnapshotOne(PSTAMDESC pDesc, void *pvArg)
704{
705 PSTAMR3SNAPSHOTONE pThis = (PSTAMR3SNAPSHOTONE)pvArg;
706
707 switch (pDesc->enmType)
708 {
709 case STAMTYPE_COUNTER:
710 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pDesc->u.pCounter->c == 0)
711 return VINF_SUCCESS;
712 stamR3SnapshotPrintf(pThis, "<Counter c=\"%lld\"", pDesc->u.pCounter->c);
713 break;
714
715 case STAMTYPE_PROFILE:
716 case STAMTYPE_PROFILE_ADV:
717 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pDesc->u.pProfile->cPeriods == 0)
718 return VINF_SUCCESS;
719 stamR3SnapshotPrintf(pThis, "<Profile cPeriods=\"%lld\" cTicks=\"%lld\" cTicksMin=\"%lld\" cTicksMax=\"%lld\"",
720 pDesc->u.pProfile->cPeriods, pDesc->u.pProfile->cTicks, pDesc->u.pProfile->cTicksMin,
721 pDesc->u.pProfile->cTicksMax);
722 break;
723
724 case STAMTYPE_RATIO_U32:
725 case STAMTYPE_RATIO_U32_RESET:
726 if (pDesc->enmVisibility == STAMVISIBILITY_USED && !pDesc->u.pRatioU32->u32A && !pDesc->u.pRatioU32->u32B)
727 return VINF_SUCCESS;
728 stamR3SnapshotPrintf(pThis, "<Ratio32 u32A=\"%lld\" u32B=\"%lld\"",
729 pDesc->u.pRatioU32->u32A, pDesc->u.pRatioU32->u32B);
730 break;
731
732 case STAMTYPE_CALLBACK:
733 {
734 char szBuf[512];
735 pDesc->u.Callback.pfnPrint(pThis->pVM, pDesc->u.Callback.pvSample, szBuf, sizeof(szBuf));
736 stamR3SnapshotPrintf(pThis, "<Callback val=\"%s\"", szBuf);
737 break;
738 }
739
740 case STAMTYPE_U8:
741 case STAMTYPE_U8_RESET:
742 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu8 == 0)
743 return VINF_SUCCESS;
744 stamR3SnapshotPrintf(pThis, "<U8 val=\"%u\"", *pDesc->u.pu8);
745 break;
746
747 case STAMTYPE_X8:
748 case STAMTYPE_X8_RESET:
749 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu8 == 0)
750 return VINF_SUCCESS;
751 stamR3SnapshotPrintf(pThis, "<X8 val=\"%#x\"", *pDesc->u.pu8);
752 break;
753
754 case STAMTYPE_U16:
755 case STAMTYPE_U16_RESET:
756 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu16 == 0)
757 return VINF_SUCCESS;
758 stamR3SnapshotPrintf(pThis, "<U16 val=\"%u\"", *pDesc->u.pu16);
759 break;
760
761 case STAMTYPE_X16:
762 case STAMTYPE_X16_RESET:
763 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu16 == 0)
764 return VINF_SUCCESS;
765 stamR3SnapshotPrintf(pThis, "<X16 val=\"%#x\"", *pDesc->u.pu16);
766 break;
767
768 case STAMTYPE_U32:
769 case STAMTYPE_U32_RESET:
770 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu32 == 0)
771 return VINF_SUCCESS;
772 stamR3SnapshotPrintf(pThis, "<U32 val=\"%u\"", *pDesc->u.pu32);
773 break;
774
775 case STAMTYPE_X32:
776 case STAMTYPE_X32_RESET:
777 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu32 == 0)
778 return VINF_SUCCESS;
779 stamR3SnapshotPrintf(pThis, "<X32 val=\"%#x\"", *pDesc->u.pu32);
780 break;
781
782 case STAMTYPE_U64:
783 case STAMTYPE_U64_RESET:
784 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu64 == 0)
785 return VINF_SUCCESS;
786 stamR3SnapshotPrintf(pThis, "<U64 val=\"%llu\"", *pDesc->u.pu64);
787 break;
788
789 case STAMTYPE_X64:
790 case STAMTYPE_X64_RESET:
791 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu64 == 0)
792 return VINF_SUCCESS;
793 stamR3SnapshotPrintf(pThis, "<X64 val=\"%#llx\"", *pDesc->u.pu64);
794 break;
795
796 default:
797 AssertMsgFailed(("%d\n", pDesc->enmType));
798 return 0;
799 }
800
801 stamR3SnapshotPrintf(pThis, " unit=\"%s\"", STAMR3GetUnit(pDesc->enmUnit));
802
803 switch (pDesc->enmVisibility)
804 {
805 default:
806 case STAMVISIBILITY_ALWAYS:
807 break;
808 case STAMVISIBILITY_USED:
809 stamR3SnapshotPrintf(pThis, " vis=\"used\"");
810 break;
811 case STAMVISIBILITY_NOT_GUI:
812 stamR3SnapshotPrintf(pThis, " vis=\"not-gui\"");
813 break;
814 }
815
816 stamR3SnapshotPrintf(pThis, " name=\"%s\"", pDesc->pszName);
817
818 if (pThis->fWithDesc && pDesc->pszDesc)
819 return stamR3SnapshotPrintf(pThis, " desc=\"%s\"/>\n", pDesc->pszDesc);
820 return stamR3SnapshotPrintf(pThis, "/>\n");
821}
822
823
824/**
825 * Output callback for stamR3SnapshotPrintf.
826 *
827 * @returns number of bytes written.
828 * @param pvArg The snapshot status structure.
829 * @param pach Pointer to an array of characters (bytes).
830 * @param cch The number or chars (bytes) to write from the array.
831 */
832static DECLCALLBACK(size_t) stamR3SnapshotOutput(void *pvArg, const char *pach, size_t cch)
833{
834 PSTAMR3SNAPSHOTONE pThis = (PSTAMR3SNAPSHOTONE)pvArg;
835
836 /*
837 * Make sure we've got space for it.
838 */
839 if (RT_UNLIKELY((uintptr_t)pThis->pszEnd - (uintptr_t)pThis->psz < cch + 1))
840 {
841 if (RT_FAILURE(pThis->rc))
842 return 0;
843
844 size_t cbNewSize = pThis->cbAllocated;
845 if (cbNewSize > cch)
846 cbNewSize *= 2;
847 else
848 cbNewSize += RT_ALIGN(cch + 1, 0x1000);
849 char *pszNew = (char *)RTMemRealloc(pThis->pszStart, cbNewSize);
850 if (!pszNew)
851 {
852 /*
853 * Free up immediately, out-of-memory is bad news and this
854 * isn't an important allocations / API.
855 */
856 pThis->rc = VERR_NO_MEMORY;
857 RTMemFree(pThis->pszStart);
858 pThis->pszStart = pThis->pszEnd = pThis->psz = NULL;
859 pThis->cbAllocated = 0;
860 return 0;
861 }
862
863 pThis->psz = pszNew + (pThis->psz - pThis->pszStart);
864 pThis->pszStart = pszNew;
865 pThis->pszEnd = pszNew + cbNewSize;
866 pThis->cbAllocated = cbNewSize;
867 }
868
869 /*
870 * Copy the chars to the buffer and terminate it.
871 */
872 memcpy(pThis->psz, pach, cch);
873 pThis->psz += cch;
874 *pThis->psz = '\0';
875 return cch;
876}
877
878
879/**
880 * Wrapper around RTStrFormatV for use by the snapshot API.
881 *
882 * @returns VBox status code.
883 * @param pThis The snapshot status structure.
884 * @param pszFormat The format string.
885 * @param ... Optional arguments.
886 */
887static int stamR3SnapshotPrintf(PSTAMR3SNAPSHOTONE pThis, const char *pszFormat, ...)
888{
889 va_list va;
890 va_start(va, pszFormat);
891 RTStrFormatV(stamR3SnapshotOutput, pThis, NULL, NULL, pszFormat, va);
892 va_end(va);
893 return pThis->rc;
894}
895
896
897/**
898 * Releases a statistics snapshot returned by STAMR3Snapshot().
899 *
900 * @returns VBox status.
901 * @param pVM The VM handle.
902 * @param pszSnapshot The snapshot data pointer returned by STAMR3Snapshot().
903 * NULL is allowed.
904 */
905STAMR3DECL(int) STAMR3SnapshotFree(PVM pVM, char *pszSnapshot)
906{
907 if (!pszSnapshot)
908 RTMemFree(pszSnapshot);
909 return VINF_SUCCESS;
910}
911
912
913/**
914 * Dumps the selected statistics to the log.
915 *
916 * @returns VBox status.
917 * @param pVM The VM handle.
918 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
919 * If NULL all samples are written to the log.
920 */
921STAMR3DECL(int) STAMR3Dump(PVM pVM, const char *pszPat)
922{
923 STAMR3PRINTONEARGS Args;
924 Args.pVM = pVM;
925 Args.pvArg = NULL;
926 Args.pfnPrintf = stamR3EnumLogPrintf;
927
928 STAM_LOCK_RD(pVM);
929 stamR3Enum(pVM, pszPat, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
930 STAM_UNLOCK_RD(pVM);
931 return VINF_SUCCESS;
932}
933
934
935/**
936 * Prints to the log.
937 *
938 * @param pArgs Pointer to the print one argument structure.
939 * @param pszFormat Format string.
940 * @param ... Format arguments.
941 */
942static DECLCALLBACK(void) stamR3EnumLogPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...)
943{
944 va_list va;
945 va_start(va, pszFormat);
946 RTLogPrintfV(pszFormat, va);
947 va_end(va);
948 NOREF(pArgs);
949}
950
951
952/**
953 * Dumps the selected statistics to the release log.
954 *
955 * @returns VBox status.
956 * @param pVM The VM handle.
957 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
958 * If NULL all samples are written to the log.
959 */
960STAMR3DECL(int) STAMR3DumpToReleaseLog(PVM pVM, const char *pszPat)
961{
962 STAMR3PRINTONEARGS Args;
963 Args.pVM = pVM;
964 Args.pvArg = NULL;
965 Args.pfnPrintf = stamR3EnumRelLogPrintf;
966
967 STAM_LOCK_RD(pVM);
968 stamR3Enum(pVM, pszPat, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
969 STAM_UNLOCK_RD(pVM);
970 return VINF_SUCCESS;
971}
972
973
974/**
975 * Prints to the release log.
976 *
977 * @param pArgs Pointer to the print one argument structure.
978 * @param pszFormat Format string.
979 * @param ... Format arguments.
980 */
981static DECLCALLBACK(void) stamR3EnumRelLogPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...)
982{
983 va_list va;
984 va_start(va, pszFormat);
985 RTLogRelPrintfV(pszFormat, va);
986 va_end(va);
987 NOREF(pArgs);
988}
989
990
991/**
992 * Prints the selected statistics to standard out.
993 *
994 * @returns VBox status.
995 * @param pVM The VM handle.
996 * @param pszPat The name matching pattern. See somewhere_where_this_is_described_in_detail.
997 * If NULL all samples are reset.
998 */
999STAMR3DECL(int) STAMR3Print(PVM pVM, const char *pszPat)
1000{
1001 STAMR3PRINTONEARGS Args;
1002 Args.pVM = pVM;
1003 Args.pvArg = NULL;
1004 Args.pfnPrintf = stamR3EnumPrintf;
1005
1006 STAM_LOCK_RD(pVM);
1007 stamR3Enum(pVM, pszPat, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
1008 STAM_UNLOCK_RD(pVM);
1009 return VINF_SUCCESS;
1010}
1011
1012
1013/**
1014 * Prints to stdout.
1015 *
1016 * @param pArgs Pointer to the print one argument structure.
1017 * @param pszFormat Format string.
1018 * @param ... Format arguments.
1019 */
1020static DECLCALLBACK(void) stamR3EnumPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...)
1021{
1022 va_list va;
1023 va_start(va, pszFormat);
1024 RTPrintfV(pszFormat, va);
1025 va_end(va);
1026 NOREF(pArgs);
1027}
1028
1029
1030/**
1031 * Prints one sample.
1032 * Callback for stamR3Enum().
1033 *
1034 * @returns VINF_SUCCESS
1035 * @param pDesc Pointer to the current descriptor.
1036 * @param pvArg User argument - STAMR3PRINTONEARGS.
1037 */
1038static int stamR3PrintOne(PSTAMDESC pDesc, void *pvArg)
1039{
1040 PSTAMR3PRINTONEARGS pArgs = (PSTAMR3PRINTONEARGS)pvArg;
1041
1042 switch (pDesc->enmType)
1043 {
1044 case STAMTYPE_COUNTER:
1045 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pDesc->u.pCounter->c == 0)
1046 return VINF_SUCCESS;
1047
1048 pArgs->pfnPrintf(pArgs, "%-32s %8llu %s\n", pDesc->pszName, pDesc->u.pCounter->c, STAMR3GetUnit(pDesc->enmUnit));
1049 break;
1050
1051 case STAMTYPE_PROFILE:
1052 case STAMTYPE_PROFILE_ADV:
1053 {
1054 if (pDesc->enmVisibility == STAMVISIBILITY_USED && pDesc->u.pProfile->cPeriods == 0)
1055 return VINF_SUCCESS;
1056
1057 uint64_t u64 = pDesc->u.pProfile->cPeriods ? pDesc->u.pProfile->cPeriods : 1;
1058 pArgs->pfnPrintf(pArgs, "%-32s %8llu %s (%12llu ticks, %7llu times, max %9llu, min %7lld)\n", pDesc->pszName,
1059 pDesc->u.pProfile->cTicks / u64, STAMR3GetUnit(pDesc->enmUnit),
1060 pDesc->u.pProfile->cTicks, pDesc->u.pProfile->cPeriods, pDesc->u.pProfile->cTicksMax, pDesc->u.pProfile->cTicksMin);
1061 break;
1062 }
1063
1064 case STAMTYPE_RATIO_U32:
1065 case STAMTYPE_RATIO_U32_RESET:
1066 if (pDesc->enmVisibility == STAMVISIBILITY_USED && !pDesc->u.pRatioU32->u32A && !pDesc->u.pRatioU32->u32B)
1067 return VINF_SUCCESS;
1068 pArgs->pfnPrintf(pArgs, "%-32s %8u:%-8u %s\n", pDesc->pszName,
1069 pDesc->u.pRatioU32->u32A, pDesc->u.pRatioU32->u32B, STAMR3GetUnit(pDesc->enmUnit));
1070 break;
1071
1072 case STAMTYPE_CALLBACK:
1073 {
1074 char szBuf[512];
1075 pDesc->u.Callback.pfnPrint(pArgs->pVM, pDesc->u.Callback.pvSample, szBuf, sizeof(szBuf));
1076 pArgs->pfnPrintf(pArgs, "%-32s %s %s\n", pDesc->pszName, szBuf, STAMR3GetUnit(pDesc->enmUnit));
1077 break;
1078 }
1079
1080 case STAMTYPE_U8:
1081 case STAMTYPE_U8_RESET:
1082 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu8 == 0)
1083 return VINF_SUCCESS;
1084 pArgs->pfnPrintf(pArgs, "%-32s %8u %s\n", pDesc->pszName, *pDesc->u.pu8, STAMR3GetUnit(pDesc->enmUnit));
1085 break;
1086
1087 case STAMTYPE_X8:
1088 case STAMTYPE_X8_RESET:
1089 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu8 == 0)
1090 return VINF_SUCCESS;
1091 pArgs->pfnPrintf(pArgs, "%-32s %8x %s\n", pDesc->pszName, *pDesc->u.pu8, STAMR3GetUnit(pDesc->enmUnit));
1092 break;
1093
1094 case STAMTYPE_U16:
1095 case STAMTYPE_U16_RESET:
1096 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu16 == 0)
1097 return VINF_SUCCESS;
1098 pArgs->pfnPrintf(pArgs, "%-32s %8u %s\n", pDesc->pszName, *pDesc->u.pu16, STAMR3GetUnit(pDesc->enmUnit));
1099 break;
1100
1101 case STAMTYPE_X16:
1102 case STAMTYPE_X16_RESET:
1103 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu16 == 0)
1104 return VINF_SUCCESS;
1105 pArgs->pfnPrintf(pArgs, "%-32s %8x %s\n", pDesc->pszName, *pDesc->u.pu16, STAMR3GetUnit(pDesc->enmUnit));
1106 break;
1107
1108 case STAMTYPE_U32:
1109 case STAMTYPE_U32_RESET:
1110 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu32 == 0)
1111 return VINF_SUCCESS;
1112 pArgs->pfnPrintf(pArgs, "%-32s %8u %s\n", pDesc->pszName, *pDesc->u.pu32, STAMR3GetUnit(pDesc->enmUnit));
1113 break;
1114
1115 case STAMTYPE_X32:
1116 case STAMTYPE_X32_RESET:
1117 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu32 == 0)
1118 return VINF_SUCCESS;
1119 pArgs->pfnPrintf(pArgs, "%-32s %8x %s\n", pDesc->pszName, *pDesc->u.pu32, STAMR3GetUnit(pDesc->enmUnit));
1120 break;
1121
1122 case STAMTYPE_U64:
1123 case STAMTYPE_U64_RESET:
1124 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu64 == 0)
1125 return VINF_SUCCESS;
1126 pArgs->pfnPrintf(pArgs, "%-32s %8llu %s\n", pDesc->pszName, *pDesc->u.pu64, STAMR3GetUnit(pDesc->enmUnit));
1127 break;
1128
1129 case STAMTYPE_X64:
1130 case STAMTYPE_X64_RESET:
1131 if (pDesc->enmVisibility == STAMVISIBILITY_USED && *pDesc->u.pu64 == 0)
1132 return VINF_SUCCESS;
1133 pArgs->pfnPrintf(pArgs, "%-32s %8llx %s\n", pDesc->pszName, *pDesc->u.pu64, STAMR3GetUnit(pDesc->enmUnit));
1134 break;
1135
1136 default:
1137 AssertMsgFailed(("enmType=%d\n", pDesc->enmType));
1138 break;
1139 }
1140 NOREF(pvArg);
1141 return VINF_SUCCESS;
1142}
1143
1144
1145/**
1146 * Enumerate the statistics by the means of a callback function.
1147 *
1148 * @returns Whatever the callback returns.
1149 *
1150 * @param pVM The VM handle.
1151 * @param pszPat The pattern to match samples.
1152 * @param pfnEnum The callback function.
1153 * @param pvUser The pvUser argument of the callback function.
1154 */
1155STAMR3DECL(int) STAMR3Enum(PVM pVM, const char *pszPat, PFNSTAMR3ENUM pfnEnum, void *pvUser)
1156{
1157 STAMR3ENUMONEARGS Args;
1158 Args.pVM = pVM;
1159 Args.pfnEnum = pfnEnum;
1160 Args.pvUser = pvUser;
1161
1162 STAM_LOCK_RD(pVM);
1163 int rc = stamR3Enum(pVM, pszPat, true /* fUpdateRing0 */, stamR3EnumOne, &Args);
1164 STAM_UNLOCK_RD(pVM);
1165 return rc;
1166}
1167
1168
1169/**
1170 * Callback function for STARTR3Enum().
1171 *
1172 * @returns whatever the callback returns.
1173 * @param pDesc Pointer to the current descriptor.
1174 * @param pvArg Points to a STAMR3ENUMONEARGS structure.
1175 */
1176static int stamR3EnumOne(PSTAMDESC pDesc, void *pvArg)
1177{
1178 PSTAMR3ENUMONEARGS pArgs = (PSTAMR3ENUMONEARGS)pvArg;
1179 int rc;
1180 if (pDesc->enmType == STAMTYPE_CALLBACK)
1181 {
1182 /* Give the enumerator something useful. */
1183 char szBuf[512];
1184 pDesc->u.Callback.pfnPrint(pArgs->pVM, pDesc->u.Callback.pvSample, szBuf, sizeof(szBuf));
1185 rc = pArgs->pfnEnum(pDesc->pszName, pDesc->enmType, szBuf, pDesc->enmUnit,
1186 pDesc->enmVisibility, pDesc->pszDesc, pArgs->pvUser);
1187 }
1188 else
1189 rc = pArgs->pfnEnum(pDesc->pszName, pDesc->enmType, pDesc->u.pv, pDesc->enmUnit,
1190 pDesc->enmVisibility, pDesc->pszDesc, pArgs->pvUser);
1191 return rc;
1192}
1193
1194
1195/**
1196 * Matches a sample name against a pattern.
1197 *
1198 * @returns True if matches, false if not.
1199 * @param pszPat Pattern.
1200 * @param pszName Name to match against the pattern.
1201 */
1202static bool stamR3Match(const char *pszPat, const char *pszName)
1203{
1204 /* ASSUMES ASCII */
1205 for (;;)
1206 {
1207 char chPat = *pszPat;
1208 switch (chPat)
1209 {
1210 default:
1211 if (*pszName != chPat)
1212 return false;
1213 break;
1214
1215 case '*':
1216 {
1217 while ((chPat = *++pszPat) == '*' || chPat == '?')
1218 /* nothing */;
1219
1220 for (;;)
1221 {
1222 char ch = *pszName++;
1223 if ( ch == chPat
1224 && ( !chPat
1225 || stamR3Match(pszPat + 1, pszName)))
1226 return true;
1227 if (!ch)
1228 return false;
1229 }
1230 /* won't ever get here */
1231 break;
1232 }
1233
1234 case '?':
1235 if (!*pszName)
1236 return false;
1237 break;
1238
1239 case '\0':
1240 return !*pszName;
1241 }
1242 pszName++;
1243 pszPat++;
1244 }
1245 return true;
1246}
1247
1248
1249/**
1250 * Match a name against an array of patterns.
1251 *
1252 * @returns true if it matches, false if it doesn't match.
1253 * @param papszExpressions The array of pattern expressions.
1254 * @param cExpressions The number of array entries.
1255 * @param piExpression Where to read/store the current skip index. Optional.
1256 * @param pszName The name to match.
1257 */
1258static bool stamR3MultiMatch(const char * const *papszExpressions, unsigned cExpressions,
1259 unsigned *piExpression, const char *pszName)
1260{
1261 for (unsigned i = piExpression ? *piExpression : 0; i < cExpressions; i++)
1262 {
1263 const char *pszPat = papszExpressions[i];
1264 if (stamR3Match(pszPat, pszName))
1265 {
1266 /* later:
1267 if (piExpression && i > *piExpression)
1268 {
1269 check if we can skip some expressions
1270 }*/
1271 return true;
1272 }
1273 }
1274 return false;
1275}
1276
1277
1278
1279/**
1280 * Enumerates the nodes selected by a pattern or all nodes if no pattern
1281 * is specified.
1282 *
1283 * The call must own at least a read lock to the STAM data.
1284 *
1285 * @returns The rc from the callback.
1286 * @param pVM VM handle
1287 * @param pszPat Pattern.
1288 * @param fUpdateRing0 Update the ring-0 .
1289 * @param pfnCallback Callback function which shall be called for matching nodes.
1290 * If it returns anything but VINF_SUCCESS the enumeration is
1291 * terminated and the status code returned to the caller.
1292 * @param pvArg User parameter for the callback.
1293 */
1294static int stamR3Enum(PVM pVM, const char *pszPat, bool fUpdateRing0, int (*pfnCallback)(PSTAMDESC pDesc, void *pvArg), void *pvArg)
1295{
1296 int rc = VINF_SUCCESS;
1297
1298 /*
1299 * All
1300 */
1301 if (!pszPat || !*pszPat || !strcmp(pszPat, "*"))
1302 {
1303 if (fUpdateRing0)
1304 stamR3Ring0StatsUpdate(pVM, "*");
1305
1306 PSTAMDESC pCur = pVM->stam.s.pHead;
1307 while (pCur)
1308 {
1309 rc = pfnCallback(pCur, pvArg);
1310 if (rc)
1311 break;
1312
1313 /* next */
1314 pCur = pCur->pNext;
1315 }
1316 }
1317
1318 /*
1319 * Single expression pattern.
1320 */
1321 else if (!strchr(pszPat, '|'))
1322 {
1323 if (fUpdateRing0)
1324 stamR3Ring0StatsUpdate(pVM, pszPat);
1325
1326 for (PSTAMDESC pCur = pVM->stam.s.pHead; pCur; pCur = pCur->pNext)
1327 if (stamR3Match(pszPat, pCur->pszName))
1328 {
1329 rc = pfnCallback(pCur, pvArg);
1330 if (rc)
1331 break;
1332 }
1333 }
1334
1335 /*
1336 * Multi expression pattern.
1337 */
1338 else
1339 {
1340 /*
1341 * Split up the pattern first.
1342 */
1343 char *pszCopy = RTStrDup(pszPat);
1344 if (!pszCopy)
1345 return VERR_NO_MEMORY;
1346
1347 /* count them & allocate array. */
1348 char *psz = pszCopy;
1349 unsigned cExpressions = 1;
1350 while ((psz = strchr(psz, '|')) != NULL)
1351 cExpressions++, psz++;
1352
1353 char **papszExpressions = (char **)RTMemTmpAlloc((cExpressions + 1) * sizeof(char *));
1354 if (!papszExpressions)
1355 {
1356 RTStrFree(pszCopy);
1357 return VERR_NO_TMP_MEMORY;
1358 }
1359
1360 /* split */
1361 psz = pszCopy;
1362 for (unsigned i = 0;;)
1363 {
1364 papszExpressions[i] = psz;
1365 if (++i >= cExpressions)
1366 break;
1367 psz = strchr(psz, '|');
1368 *psz++ = '\0';
1369 }
1370
1371 /* sort the array, putting '*' last. */
1372 /** @todo sort it... */
1373
1374 /*
1375 * Perform the enumeration.
1376 */
1377 if (fUpdateRing0)
1378 stamR3Ring0StatsUpdateMulti(pVM, papszExpressions, cExpressions);
1379
1380 unsigned iExpression = 0;
1381 for (PSTAMDESC pCur = pVM->stam.s.pHead; pCur; pCur = pCur->pNext)
1382 if (stamR3MultiMatch(papszExpressions, cExpressions, &iExpression, pCur->pszName))
1383 {
1384 rc = pfnCallback(pCur, pvArg);
1385 if (rc)
1386 break;
1387 }
1388
1389 RTMemTmpFree(papszExpressions);
1390 RTStrFree(pszCopy);
1391 }
1392
1393 return rc;
1394}
1395
1396
1397/**
1398 * Registers the ring-0 statistics.
1399 *
1400 * @param pVM Pointer to the shared VM structure.
1401 */
1402static void stamR3Ring0StatsRegister(PVM pVM)
1403{
1404 /* GVMM */
1405 for (unsigned i = 0; i < RT_ELEMENTS(g_aGVMMStats); i++)
1406 stamR3Register(pVM, (uint8_t *)pVM + g_aGVMMStats[i].offVar, NULL, NULL,
1407 g_aGVMMStats[i].enmType, STAMVISIBILITY_ALWAYS, g_aGVMMStats[i].pszName,
1408 g_aGVMMStats[i].enmUnit, g_aGVMMStats[i].pszDesc);
1409}
1410
1411
1412/**
1413 * Updates the ring-0 statistics (the copy).
1414 *
1415 * @param pVM Pointer to the shared VM structure.
1416 * @param pszPat The pattern.
1417 */
1418static void stamR3Ring0StatsUpdate(PVM pVM, const char *pszPat)
1419{
1420 stamR3Ring0StatsUpdateMulti(pVM, &pszPat, 1);
1421}
1422
1423
1424/**
1425 * Updates the ring-0 statistics.
1426 *
1427 * The ring-0 statistics aren't directly addressable from ring-3 and
1428 * must be copied when needed.
1429 *
1430 * @param pVM Pointer to the shared VM structure.
1431 * @param pszPat The pattern (for knowing when to skip).
1432 */
1433static void stamR3Ring0StatsUpdateMulti(PVM pVM, const char * const *papszExpressions, unsigned cExpressions)
1434{
1435 if (!pVM->pSession)
1436 return;
1437
1438 /* GVMM */
1439 bool fUpdate = false;
1440 for (unsigned i = 0; i < RT_ELEMENTS(g_aGVMMStats); i++)
1441 if (stamR3MultiMatch(papszExpressions, cExpressions, NULL, g_aGVMMStats[i].pszName))
1442 {
1443 fUpdate = true;
1444 break;
1445 }
1446 if (fUpdate)
1447 {
1448 GVMMQUERYSTATISTICSSREQ Req;
1449 Req.Hdr.cbReq = sizeof(Req);
1450 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
1451 Req.pSession = pVM->pSession;
1452 int rc = SUPCallVMMR0Ex(pVM->pVMR0, VMMR0_DO_GVMM_QUERY_STATISTICS, 0, &Req.Hdr);
1453 if (RT_SUCCESS(rc))
1454 pVM->stam.s.GVMMStats = Req.Stats;
1455 }
1456}
1457
1458
1459/**
1460 * Get the unit string.
1461 *
1462 * @returns Pointer to read only unit string.
1463 * @param enmUnit The unit.
1464 */
1465STAMR3DECL(const char *) STAMR3GetUnit(STAMUNIT enmUnit)
1466{
1467 switch (enmUnit)
1468 {
1469 case STAMUNIT_NONE: return "";
1470 case STAMUNIT_CALLS: return "calls";
1471 case STAMUNIT_COUNT: return "count";
1472 case STAMUNIT_BYTES: return "bytes";
1473 case STAMUNIT_PAGES: return "pages";
1474 case STAMUNIT_ERRORS: return "errors";
1475 case STAMUNIT_OCCURENCES: return "times";
1476 case STAMUNIT_TICKS_PER_CALL: return "ticks/call";
1477 case STAMUNIT_TICKS_PER_OCCURENCE: return "ticks/time";
1478 case STAMUNIT_GOOD_BAD: return "good:bad";
1479 case STAMUNIT_MEGABYTES: return "megabytes";
1480 case STAMUNIT_KILOBYTES: return "kilobytes";
1481 case STAMUNIT_NS: return "ns";
1482 case STAMUNIT_NS_PER_CALL: return "ns/call";
1483 case STAMUNIT_NS_PER_OCCURENCE: return "ns/time";
1484 case STAMUNIT_PCT: return "%";
1485
1486 default:
1487 AssertMsgFailed(("Unknown unit %d\n", enmUnit));
1488 return "(?unit?)";
1489 }
1490}
1491
1492
1493#ifdef VBOX_WITH_DEBUGGER
1494/**
1495 * The '.stats' command.
1496 *
1497 * @returns VBox status.
1498 * @param pCmd Pointer to the command descriptor (as registered).
1499 * @param pCmdHlp Pointer to command helper functions.
1500 * @param pVM Pointer to the current VM (if any).
1501 * @param paArgs Pointer to (readonly) array of arguments.
1502 * @param cArgs Number of arguments in the array.
1503 */
1504static DECLCALLBACK(int) stamR3CmdStats(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
1505{
1506 /*
1507 * Validate input.
1508 */
1509 if (!pVM)
1510 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: The command requires VM to be selected.\n");
1511 if (!pVM->stam.s.pHead)
1512 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Sorry, no statistics present.\n");
1513
1514 /*
1515 * Do the printing.
1516 */
1517 STAMR3PRINTONEARGS Args;
1518 Args.pVM = pVM;
1519 Args.pvArg = pCmdHlp;
1520 Args.pfnPrintf = stamR3EnumDbgfPrintf;
1521
1522 STAM_LOCK_RD(pVM);
1523 int rc = stamR3Enum(pVM, cArgs ? paArgs[0].u.pszString : NULL, true /* fUpdateRing0 */, stamR3PrintOne, &Args);
1524 STAM_UNLOCK_RD(pVM);
1525
1526 return rc;
1527}
1528
1529
1530/**
1531 * Display one sample in the debugger.
1532 *
1533 * @param pArgs Pointer to the print one argument structure.
1534 * @param pszFormat Format string.
1535 * @param ... Format arguments.
1536 */
1537static DECLCALLBACK(void) stamR3EnumDbgfPrintf(PSTAMR3PRINTONEARGS pArgs, const char *pszFormat, ...)
1538{
1539 PDBGCCMDHLP pCmdHlp = (PDBGCCMDHLP)pArgs->pvArg;
1540
1541 va_list va;
1542 va_start(va, pszFormat);
1543 pCmdHlp->pfnPrintfV(pCmdHlp, NULL, pszFormat, va);
1544 va_end(va);
1545 NOREF(pArgs);
1546}
1547
1548
1549/**
1550 * The '.statsreset' command.
1551 *
1552 * @returns VBox status.
1553 * @param pCmd Pointer to the command descriptor (as registered).
1554 * @param pCmdHlp Pointer to command helper functions.
1555 * @param pVM Pointer to the current VM (if any).
1556 * @param paArgs Pointer to (readonly) array of arguments.
1557 * @param cArgs Number of arguments in the array.
1558 */
1559static DECLCALLBACK(int) stamR3CmdStatsReset(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
1560{
1561 /*
1562 * Validate input.
1563 */
1564 if (!pVM)
1565 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: The command requires VM to be selected.\n");
1566 if (!pVM->stam.s.pHead)
1567 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Sorry, no statistics present.\n");
1568
1569 /*
1570 * Execute reset.
1571 */
1572 int rc = STAMR3Reset(pVM, cArgs ? paArgs[0].u.pszString : NULL);
1573 if (VBOX_SUCCESS(rc))
1574 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "info: Statistics reset.\n");
1575
1576 return pCmdHlp->pfnVBoxError(pCmdHlp, rc, "Restting statistics.\n");
1577}
1578#endif
1579
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