1
/* $Id: VBoxManageMetrics.cpp $ */
3
* VBoxManage - The 'metrics' command.
7
* Copyright (C) 2006-2012 Oracle Corporation
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.
18
#ifndef VBOX_ONLY_DOCS
20
/*******************************************************************************
22
*******************************************************************************/
23
#include <VBox/com/com.h>
24
#include <VBox/com/array.h>
25
#include <VBox/com/ErrorInfo.h>
26
#include <VBox/com/errorprint.h>
27
#include <VBox/com/VirtualBox.h>
30
#include <iprt/stream.h>
31
#include <iprt/string.h>
32
#include <iprt/time.h>
33
#include <iprt/thread.h>
39
#include "VBoxManage.h"
44
///////////////////////////////////////////////////////////////////////////////
47
static int parseFilterParameters(int argc, char *argv[],
48
ComPtr<IVirtualBox> aVirtualBox,
49
ComSafeArrayOut(BSTR, outMetrics),
50
ComSafeArrayOut(IUnknown *, outObjects))
53
com::SafeArray<BSTR> retMetrics(1);
54
com::SafeIfaceArray <IUnknown> retObjects;
56
Bstr metricNames, baseNames;
60
metricNames = argv[1];
66
metricNames.cloneTo(&retMetrics[0]);
69
if (argc > 0 && strcmp(argv[0], "*"))
71
if (!strcmp(argv[0], "host"))
74
CHECK_ERROR(aVirtualBox, COMGETTER(Host)(host.asOutParam()));
76
host.queryInterfaceTo(&retObjects[0]);
80
ComPtr<IMachine> machine;
81
rc = aVirtualBox->FindMachine(Bstr(argv[0]).raw(),
82
machine.asOutParam());
86
machine.queryInterfaceTo(&retObjects[0]);
90
errorArgument("Invalid machine name: '%s'", argv[0]);
97
retMetrics.detachTo(ComSafeArrayOutArg(outMetrics));
98
retObjects.detachTo(ComSafeArrayOutArg(outObjects));
103
static Bstr toBaseName(Utf8Str& aFullName)
105
char *pszRaw = aFullName.mutableRaw();
107
* Currently there are two metrics which base name is the same as the
108
* sub-metric name: CPU/MHz and Net/<iface>/LinkSpeed.
110
if (strcmp(pszRaw, "CPU/MHz") && !RTStrSimplePatternMatch("Net/*/LinkSpeed", pszRaw))
112
char *pszSlash = strrchr(pszRaw, '/');
119
return Bstr(aFullName);
122
static Bstr getObjectName(ComPtr<IVirtualBox> aVirtualBox,
123
ComPtr<IUnknown> aObject)
127
ComPtr<IHost> host = aObject;
131
ComPtr<IMachine> machine = aObject;
132
if (!machine.isNull())
135
CHECK_ERROR(machine, COMGETTER(Name)(name.asOutParam()));
139
return Bstr("unknown");
142
static void listAffectedMetrics(ComPtr<IVirtualBox> aVirtualBox,
143
ComSafeArrayIn(IPerformanceMetric*, aMetrics))
146
com::SafeIfaceArray<IPerformanceMetric> metrics(ComSafeArrayInArg(aMetrics));
149
ComPtr<IUnknown> object;
151
RTPrintf("The following metrics were modified:\n\n"
153
"---------- --------------------\n");
154
for (size_t i = 0; i < metrics.size(); i++)
156
CHECK_ERROR(metrics[i], COMGETTER(Object)(object.asOutParam()));
157
CHECK_ERROR(metrics[i], COMGETTER(MetricName)(metricName.asOutParam()));
158
RTPrintf("%-10ls %-20ls\n",
159
getObjectName(aVirtualBox, object).raw(), metricName.raw());
165
RTMsgError("No metrics match the specified filter!");
172
static int handleMetricsList(int argc, char *argv[],
173
ComPtr<IVirtualBox> aVirtualBox,
174
ComPtr<IPerformanceCollector> performanceCollector)
177
com::SafeArray<BSTR> metrics;
178
com::SafeIfaceArray<IUnknown> objects;
180
rc = parseFilterParameters(argc - 1, &argv[1], aVirtualBox,
181
ComSafeArrayAsOutParam(metrics),
182
ComSafeArrayAsOutParam(objects));
186
com::SafeIfaceArray<IPerformanceMetric> metricInfo;
188
CHECK_ERROR(performanceCollector,
189
GetMetrics(ComSafeArrayAsInParam(metrics),
190
ComSafeArrayAsInParam(objects),
191
ComSafeArrayAsOutParam(metricInfo)));
193
ComPtr<IUnknown> object;
194
Bstr metricName, unit, description;
196
LONG minimum, maximum;
198
"Object Metric Unit Minimum Maximum Period Count Description\n"
199
"---------- -------------------- ---- ---------- ---------- ---------- ---------- -----------\n");
200
for (size_t i = 0; i < metricInfo.size(); i++)
202
CHECK_ERROR(metricInfo[i], COMGETTER(Object)(object.asOutParam()));
203
CHECK_ERROR(metricInfo[i], COMGETTER(MetricName)(metricName.asOutParam()));
204
CHECK_ERROR(metricInfo[i], COMGETTER(Period)(&period));
205
CHECK_ERROR(metricInfo[i], COMGETTER(Count)(&count));
206
CHECK_ERROR(metricInfo[i], COMGETTER(MinimumValue)(&minimum));
207
CHECK_ERROR(metricInfo[i], COMGETTER(MaximumValue)(&maximum));
208
CHECK_ERROR(metricInfo[i], COMGETTER(Unit)(unit.asOutParam()));
209
CHECK_ERROR(metricInfo[i], COMGETTER(Description)(description.asOutParam()));
210
RTPrintf("%-10ls %-20ls %-4ls %10d %10d %10u %10u %ls\n",
211
getObjectName(aVirtualBox, object).raw(), metricName.raw(), unit.raw(),
212
minimum, maximum, period, count, description.raw());
221
static int handleMetricsSetup(int argc, char *argv[],
222
ComPtr<IVirtualBox> aVirtualBox,
223
ComPtr<IPerformanceCollector> performanceCollector)
226
com::SafeArray<BSTR> metrics;
227
com::SafeIfaceArray<IUnknown> objects;
228
uint32_t period = 1, samples = 1;
229
bool listMatches = false;
232
for (i = 1; i < argc; i++)
234
if ( !strcmp(argv[i], "--period")
235
|| !strcmp(argv[i], "-period"))
238
return errorArgument("Missing argument to '%s'", argv[i]);
239
if ( VINF_SUCCESS != RTStrToUInt32Full(argv[++i], 10, &period)
241
return errorArgument("Invalid value for 'period' parameter: '%s'", argv[i]);
243
else if ( !strcmp(argv[i], "--samples")
244
|| !strcmp(argv[i], "-samples"))
247
return errorArgument("Missing argument to '%s'", argv[i]);
248
if ( VINF_SUCCESS != RTStrToUInt32Full(argv[++i], 10, &samples)
250
return errorArgument("Invalid value for 'samples' parameter: '%s'", argv[i]);
252
else if ( !strcmp(argv[i], "--list")
253
|| !strcmp(argv[i], "-list"))
256
break; /* The rest of params should define the filter */
259
rc = parseFilterParameters(argc - i, &argv[i], aVirtualBox,
260
ComSafeArrayAsOutParam(metrics),
261
ComSafeArrayAsOutParam(objects));
265
com::SafeIfaceArray<IPerformanceMetric> affectedMetrics;
266
CHECK_ERROR(performanceCollector,
267
SetupMetrics(ComSafeArrayAsInParam(metrics),
268
ComSafeArrayAsInParam(objects), period, samples,
269
ComSafeArrayAsOutParam(affectedMetrics)));
274
listAffectedMetrics(aVirtualBox,
275
ComSafeArrayAsInParam(affectedMetrics));
283
static int handleMetricsQuery(int argc, char *argv[],
284
ComPtr<IVirtualBox> aVirtualBox,
285
ComPtr<IPerformanceCollector> performanceCollector)
288
com::SafeArray<BSTR> metrics;
289
com::SafeIfaceArray<IUnknown> objects;
291
rc = parseFilterParameters(argc - 1, &argv[1], aVirtualBox,
292
ComSafeArrayAsOutParam(metrics),
293
ComSafeArrayAsOutParam(objects));
297
com::SafeArray<BSTR> retNames;
298
com::SafeIfaceArray<IUnknown> retObjects;
299
com::SafeArray<BSTR> retUnits;
300
com::SafeArray<ULONG> retScales;
301
com::SafeArray<ULONG> retSequenceNumbers;
302
com::SafeArray<ULONG> retIndices;
303
com::SafeArray<ULONG> retLengths;
304
com::SafeArray<LONG> retData;
305
CHECK_ERROR (performanceCollector, QueryMetricsData(ComSafeArrayAsInParam(metrics),
306
ComSafeArrayAsInParam(objects),
307
ComSafeArrayAsOutParam(retNames),
308
ComSafeArrayAsOutParam(retObjects),
309
ComSafeArrayAsOutParam(retUnits),
310
ComSafeArrayAsOutParam(retScales),
311
ComSafeArrayAsOutParam(retSequenceNumbers),
312
ComSafeArrayAsOutParam(retIndices),
313
ComSafeArrayAsOutParam(retLengths),
314
ComSafeArrayAsOutParam(retData)) );
316
RTPrintf("Object Metric Values\n"
317
"---------- -------------------- --------------------------------------------\n");
318
for (unsigned i = 0; i < retNames.size(); i++)
320
Bstr metricUnit(retUnits[i]);
321
Bstr metricName(retNames[i]);
322
RTPrintf("%-10ls %-20ls ", getObjectName(aVirtualBox, retObjects[i]).raw(), metricName.raw());
323
const char *separator = "";
324
for (unsigned j = 0; j < retLengths[i]; j++)
326
if (retScales[i] == 1)
327
RTPrintf("%s%d %ls", separator, retData[retIndices[i] + j], metricUnit.raw());
329
RTPrintf("%s%d.%02d%ls", separator, retData[retIndices[i] + j] / retScales[i],
330
(retData[retIndices[i] + j] * 100 / retScales[i]) % 100, metricUnit.raw());
339
static void getTimestamp(char *pts, size_t tsSize)
342
AssertReturnVoid(tsSize >= 13); /* 3+3+3+3+1 */
345
RTTimeExplode(&Time, RTTimeNow(&TimeSpec));
346
pts += RTStrFormatNumber(pts, Time.u8Hour, 10, 2, 0, RTSTR_F_ZEROPAD);
348
pts += RTStrFormatNumber(pts, Time.u8Minute, 10, 2, 0, RTSTR_F_ZEROPAD);
350
pts += RTStrFormatNumber(pts, Time.u8Second, 10, 2, 0, RTSTR_F_ZEROPAD);
352
pts += RTStrFormatNumber(pts, Time.u32Nanosecond / 1000000, 10, 3, 0, RTSTR_F_ZEROPAD);
356
/** Used by the handleMetricsCollect loop. */
357
static bool volatile g_fKeepGoing = true;
361
* Handler routine for catching Ctrl-C, Ctrl-Break and closing of
364
* @returns true if handled, false if not handled.
365
* @param dwCtrlType The type of control signal.
367
* @remarks This is called on a new thread.
369
static BOOL WINAPI ctrlHandler(DWORD dwCtrlType)
373
/* Ctrl-C or Ctrl-Break or Close */
375
case CTRL_BREAK_EVENT:
376
case CTRL_CLOSE_EVENT:
377
/* Let's shut down gracefully. */
378
ASMAtomicWriteBool(&g_fKeepGoing, false);
381
/* Don't care about the rest -- let it die a horrible death. */
384
#endif /* RT_OS_WINDOWS */
389
static int handleMetricsCollect(int argc, char *argv[],
390
ComPtr<IVirtualBox> aVirtualBox,
391
ComPtr<IPerformanceCollector> performanceCollector)
394
com::SafeArray<BSTR> metrics;
395
com::SafeIfaceArray<IUnknown> objects;
396
uint32_t period = 1, samples = 1;
397
bool isDetached = false, listMatches = false;
399
for (i = 1; i < argc; i++)
401
if ( !strcmp(argv[i], "--period")
402
|| !strcmp(argv[i], "-period"))
405
return errorArgument("Missing argument to '%s'", argv[i]);
406
if ( VINF_SUCCESS != RTStrToUInt32Full(argv[++i], 10, &period)
408
return errorArgument("Invalid value for 'period' parameter: '%s'", argv[i]);
410
else if ( !strcmp(argv[i], "--samples")
411
|| !strcmp(argv[i], "-samples"))
414
return errorArgument("Missing argument to '%s'", argv[i]);
415
if ( VINF_SUCCESS != RTStrToUInt32Full(argv[++i], 10, &samples)
417
return errorArgument("Invalid value for 'samples' parameter: '%s'", argv[i]);
419
else if ( !strcmp(argv[i], "--list")
420
|| !strcmp(argv[i], "-list"))
422
else if ( !strcmp(argv[i], "--detach")
423
|| !strcmp(argv[i], "-detach"))
426
break; /* The rest of params should define the filter */
429
rc = parseFilterParameters(argc - i, &argv[i], aVirtualBox,
430
ComSafeArrayAsOutParam(metrics),
431
ComSafeArrayAsOutParam(objects));
435
com::SafeIfaceArray<IPerformanceMetric> metricInfo;
437
CHECK_ERROR(performanceCollector,
438
GetMetrics(ComSafeArrayAsInParam(metrics),
439
ComSafeArrayAsInParam(objects),
440
ComSafeArrayAsOutParam(metricInfo)));
442
std::set<std::pair<ComPtr<IUnknown>,Bstr> > baseMetrics;
443
ComPtr<IUnknown> objectFiltered;
444
Bstr metricNameFiltered;
445
for (i = 0; i < (int)metricInfo.size(); i++)
447
CHECK_ERROR(metricInfo[i], COMGETTER(Object)(objectFiltered.asOutParam()));
448
CHECK_ERROR(metricInfo[i], COMGETTER(MetricName)(metricNameFiltered.asOutParam()));
449
Utf8Str baseMetricName(metricNameFiltered);
450
baseMetrics.insert(std::make_pair(objectFiltered, toBaseName(baseMetricName)));
452
com::SafeArray<BSTR> baseMetricsFiltered(baseMetrics.size());
453
com::SafeIfaceArray<IUnknown> objectsFiltered(baseMetrics.size());
454
std::set<std::pair<ComPtr<IUnknown>,Bstr> >::iterator it;
456
for (it = baseMetrics.begin(); it != baseMetrics.end(); ++it)
458
it->first.queryInterfaceTo(&objectsFiltered[i]);
459
Bstr(it->second).detachTo(&baseMetricsFiltered[i++]);
461
com::SafeIfaceArray<IPerformanceMetric> affectedMetrics;
462
CHECK_ERROR(performanceCollector,
463
SetupMetrics(ComSafeArrayAsInParam(baseMetricsFiltered),
464
ComSafeArrayAsInParam(objectsFiltered), period, samples,
465
ComSafeArrayAsOutParam(affectedMetrics)));
470
listAffectedMetrics(aVirtualBox,
471
ComSafeArrayAsInParam(affectedMetrics));
472
if (!affectedMetrics.size())
477
RTMsgWarning("The background process holding collected metrics will shutdown\n"
478
"in few seconds, discarding all collected data and parameters.");
483
SetConsoleCtrlHandler(ctrlHandler, true);
484
#endif /* RT_OS_WINDOWS */
486
RTPrintf("Time stamp Object Metric Value\n");
490
RTPrintf("------------ ---------- -------------------- --------------------\n");
491
RTThreadSleep(period * 1000); // Sleep for 'period' seconds
494
getTimestamp(ts, sizeof(ts));
495
com::SafeArray<BSTR> retNames;
496
com::SafeIfaceArray<IUnknown> retObjects;
497
com::SafeArray<BSTR> retUnits;
498
com::SafeArray<ULONG> retScales;
499
com::SafeArray<ULONG> retSequenceNumbers;
500
com::SafeArray<ULONG> retIndices;
501
com::SafeArray<ULONG> retLengths;
502
com::SafeArray<LONG> retData;
503
CHECK_ERROR (performanceCollector, QueryMetricsData(ComSafeArrayAsInParam(metrics),
504
ComSafeArrayAsInParam(objects),
505
ComSafeArrayAsOutParam(retNames),
506
ComSafeArrayAsOutParam(retObjects),
507
ComSafeArrayAsOutParam(retUnits),
508
ComSafeArrayAsOutParam(retScales),
509
ComSafeArrayAsOutParam(retSequenceNumbers),
510
ComSafeArrayAsOutParam(retIndices),
511
ComSafeArrayAsOutParam(retLengths),
512
ComSafeArrayAsOutParam(retData)) );
513
for (unsigned j = 0; j < retNames.size(); j++)
515
Bstr metricUnit(retUnits[j]);
516
Bstr metricName(retNames[j]);
517
RTPrintf("%-12s %-10ls %-20ls ", ts, getObjectName(aVirtualBox, retObjects[j]).raw(), metricName.raw());
518
const char *separator = "";
519
for (unsigned k = 0; k < retLengths[j]; k++)
521
if (retScales[j] == 1)
522
RTPrintf("%s%d %ls", separator, retData[retIndices[j] + k], metricUnit.raw());
524
RTPrintf("%s%d.%02d%ls", separator, retData[retIndices[j] + k] / retScales[j],
525
(retData[retIndices[j] + k] * 100 / retScales[j]) % 100, metricUnit.raw());
530
RTStrmFlush(g_pStdOut);
534
SetConsoleCtrlHandler(ctrlHandler, false);
535
#endif /* RT_OS_WINDOWS */
543
static int handleMetricsEnable(int argc, char *argv[],
544
ComPtr<IVirtualBox> aVirtualBox,
545
ComPtr<IPerformanceCollector> performanceCollector)
548
com::SafeArray<BSTR> metrics;
549
com::SafeIfaceArray<IUnknown> objects;
550
bool listMatches = false;
553
for (i = 1; i < argc; i++)
555
if ( !strcmp(argv[i], "--list")
556
|| !strcmp(argv[i], "-list"))
559
break; /* The rest of params should define the filter */
562
rc = parseFilterParameters(argc - i, &argv[i], aVirtualBox,
563
ComSafeArrayAsOutParam(metrics),
564
ComSafeArrayAsOutParam(objects));
568
com::SafeIfaceArray<IPerformanceMetric> affectedMetrics;
569
CHECK_ERROR(performanceCollector,
570
EnableMetrics(ComSafeArrayAsInParam(metrics),
571
ComSafeArrayAsInParam(objects),
572
ComSafeArrayAsOutParam(affectedMetrics)));
577
listAffectedMetrics(aVirtualBox,
578
ComSafeArrayAsInParam(affectedMetrics));
586
static int handleMetricsDisable(int argc, char *argv[],
587
ComPtr<IVirtualBox> aVirtualBox,
588
ComPtr<IPerformanceCollector> performanceCollector)
591
com::SafeArray<BSTR> metrics;
592
com::SafeIfaceArray<IUnknown> objects;
593
bool listMatches = false;
596
for (i = 1; i < argc; i++)
598
if ( !strcmp(argv[i], "--list")
599
|| !strcmp(argv[i], "-list"))
602
break; /* The rest of params should define the filter */
605
rc = parseFilterParameters(argc - i, &argv[i], aVirtualBox,
606
ComSafeArrayAsOutParam(metrics),
607
ComSafeArrayAsOutParam(objects));
611
com::SafeIfaceArray<IPerformanceMetric> affectedMetrics;
612
CHECK_ERROR(performanceCollector,
613
DisableMetrics(ComSafeArrayAsInParam(metrics),
614
ComSafeArrayAsInParam(objects),
615
ComSafeArrayAsOutParam(affectedMetrics)));
620
listAffectedMetrics(aVirtualBox,
621
ComSafeArrayAsInParam(affectedMetrics));
627
int handleMetrics(HandlerArg *a)
631
/* at least one option: subcommand name */
633
return errorSyntax(USAGE_METRICS, "Subcommand missing");
635
ComPtr<IPerformanceCollector> performanceCollector;
636
CHECK_ERROR(a->virtualBox, COMGETTER(PerformanceCollector)(performanceCollector.asOutParam()));
638
if (!strcmp(a->argv[0], "list"))
639
rc = handleMetricsList(a->argc, a->argv, a->virtualBox, performanceCollector);
640
else if (!strcmp(a->argv[0], "setup"))
641
rc = handleMetricsSetup(a->argc, a->argv, a->virtualBox, performanceCollector);
642
else if (!strcmp(a->argv[0], "query"))
643
rc = handleMetricsQuery(a->argc, a->argv, a->virtualBox, performanceCollector);
644
else if (!strcmp(a->argv[0], "collect"))
645
rc = handleMetricsCollect(a->argc, a->argv, a->virtualBox, performanceCollector);
646
else if (!strcmp(a->argv[0], "enable"))
647
rc = handleMetricsEnable(a->argc, a->argv, a->virtualBox, performanceCollector);
648
else if (!strcmp(a->argv[0], "disable"))
649
rc = handleMetricsDisable(a->argc, a->argv, a->virtualBox, performanceCollector);
651
return errorSyntax(USAGE_METRICS, "Invalid subcommand '%s'", a->argv[0]);
656
#endif /* VBOX_ONLY_DOCS */