16a5fcf6cSMarkus Armbruster /*
26a5fcf6cSMarkus Armbruster * HMP commands related to stats
36a5fcf6cSMarkus Armbruster *
46a5fcf6cSMarkus Armbruster * This work is licensed under the terms of the GNU GPL, version 2 or
56a5fcf6cSMarkus Armbruster * (at your option) any later version.
66a5fcf6cSMarkus Armbruster */
76a5fcf6cSMarkus Armbruster
86a5fcf6cSMarkus Armbruster #include "qemu/osdep.h"
96a5fcf6cSMarkus Armbruster #include "qapi/qapi-commands-stats.h"
106a5fcf6cSMarkus Armbruster #include "monitor/hmp.h"
116a5fcf6cSMarkus Armbruster #include "monitor/monitor.h"
126a5fcf6cSMarkus Armbruster #include "qemu/cutils.h"
136a5fcf6cSMarkus Armbruster #include "hw/core/cpu.h"
146a5fcf6cSMarkus Armbruster #include "qapi/qmp/qdict.h"
156a5fcf6cSMarkus Armbruster #include "qapi/error.h"
166a5fcf6cSMarkus Armbruster
print_stats_schema_value(Monitor * mon,StatsSchemaValue * value)176a5fcf6cSMarkus Armbruster static void print_stats_schema_value(Monitor *mon, StatsSchemaValue *value)
186a5fcf6cSMarkus Armbruster {
196a5fcf6cSMarkus Armbruster const char *unit = NULL;
206a5fcf6cSMarkus Armbruster monitor_printf(mon, " %s (%s%s", value->name, StatsType_str(value->type),
216a5fcf6cSMarkus Armbruster value->has_unit || value->exponent ? ", " : "");
226a5fcf6cSMarkus Armbruster
236a5fcf6cSMarkus Armbruster if (value->has_unit) {
246a5fcf6cSMarkus Armbruster if (value->unit == STATS_UNIT_SECONDS) {
256a5fcf6cSMarkus Armbruster unit = "s";
266a5fcf6cSMarkus Armbruster } else if (value->unit == STATS_UNIT_BYTES) {
276a5fcf6cSMarkus Armbruster unit = "B";
286a5fcf6cSMarkus Armbruster }
296a5fcf6cSMarkus Armbruster }
306a5fcf6cSMarkus Armbruster
316a5fcf6cSMarkus Armbruster if (unit && value->base == 10 &&
326a5fcf6cSMarkus Armbruster value->exponent >= -18 && value->exponent <= 18 &&
336a5fcf6cSMarkus Armbruster value->exponent % 3 == 0) {
346a5fcf6cSMarkus Armbruster monitor_puts(mon, si_prefix(value->exponent));
356a5fcf6cSMarkus Armbruster } else if (unit && value->base == 2 &&
366a5fcf6cSMarkus Armbruster value->exponent >= 0 && value->exponent <= 60 &&
376a5fcf6cSMarkus Armbruster value->exponent % 10 == 0) {
386a5fcf6cSMarkus Armbruster
396a5fcf6cSMarkus Armbruster monitor_puts(mon, iec_binary_prefix(value->exponent));
406a5fcf6cSMarkus Armbruster } else if (value->exponent) {
416a5fcf6cSMarkus Armbruster /* Use exponential notation and write the unit's English name */
426a5fcf6cSMarkus Armbruster monitor_printf(mon, "* %d^%d%s",
436a5fcf6cSMarkus Armbruster value->base, value->exponent,
446a5fcf6cSMarkus Armbruster value->has_unit ? " " : "");
456a5fcf6cSMarkus Armbruster unit = NULL;
466a5fcf6cSMarkus Armbruster }
476a5fcf6cSMarkus Armbruster
486a5fcf6cSMarkus Armbruster if (value->has_unit) {
496a5fcf6cSMarkus Armbruster monitor_puts(mon, unit ? unit : StatsUnit_str(value->unit));
506a5fcf6cSMarkus Armbruster }
516a5fcf6cSMarkus Armbruster
526a5fcf6cSMarkus Armbruster /* Print bucket size for linear histograms */
536a5fcf6cSMarkus Armbruster if (value->type == STATS_TYPE_LINEAR_HISTOGRAM && value->has_bucket_size) {
546a5fcf6cSMarkus Armbruster monitor_printf(mon, ", bucket size=%d", value->bucket_size);
556a5fcf6cSMarkus Armbruster }
566a5fcf6cSMarkus Armbruster monitor_printf(mon, ")");
576a5fcf6cSMarkus Armbruster }
586a5fcf6cSMarkus Armbruster
find_schema_value_list(StatsSchemaList * list,StatsProvider provider,StatsTarget target)596a5fcf6cSMarkus Armbruster static StatsSchemaValueList *find_schema_value_list(
606a5fcf6cSMarkus Armbruster StatsSchemaList *list, StatsProvider provider,
616a5fcf6cSMarkus Armbruster StatsTarget target)
626a5fcf6cSMarkus Armbruster {
636a5fcf6cSMarkus Armbruster StatsSchemaList *node;
646a5fcf6cSMarkus Armbruster
656a5fcf6cSMarkus Armbruster for (node = list; node; node = node->next) {
666a5fcf6cSMarkus Armbruster if (node->value->provider == provider &&
676a5fcf6cSMarkus Armbruster node->value->target == target) {
686a5fcf6cSMarkus Armbruster return node->value->stats;
696a5fcf6cSMarkus Armbruster }
706a5fcf6cSMarkus Armbruster }
716a5fcf6cSMarkus Armbruster return NULL;
726a5fcf6cSMarkus Armbruster }
736a5fcf6cSMarkus Armbruster
print_stats_results(Monitor * mon,StatsTarget target,bool show_provider,StatsResult * result,StatsSchemaList * schema)746a5fcf6cSMarkus Armbruster static void print_stats_results(Monitor *mon, StatsTarget target,
756a5fcf6cSMarkus Armbruster bool show_provider,
766a5fcf6cSMarkus Armbruster StatsResult *result,
776a5fcf6cSMarkus Armbruster StatsSchemaList *schema)
786a5fcf6cSMarkus Armbruster {
796a5fcf6cSMarkus Armbruster /* Find provider schema */
806a5fcf6cSMarkus Armbruster StatsSchemaValueList *schema_value_list =
816a5fcf6cSMarkus Armbruster find_schema_value_list(schema, result->provider, target);
826a5fcf6cSMarkus Armbruster StatsList *stats_list;
836a5fcf6cSMarkus Armbruster
846a5fcf6cSMarkus Armbruster if (!schema_value_list) {
856a5fcf6cSMarkus Armbruster monitor_printf(mon, "failed to find schema list for %s\n",
866a5fcf6cSMarkus Armbruster StatsProvider_str(result->provider));
876a5fcf6cSMarkus Armbruster return;
886a5fcf6cSMarkus Armbruster }
896a5fcf6cSMarkus Armbruster
906a5fcf6cSMarkus Armbruster if (show_provider) {
916a5fcf6cSMarkus Armbruster monitor_printf(mon, "provider: %s\n",
926a5fcf6cSMarkus Armbruster StatsProvider_str(result->provider));
936a5fcf6cSMarkus Armbruster }
946a5fcf6cSMarkus Armbruster
956a5fcf6cSMarkus Armbruster for (stats_list = result->stats; stats_list;
966a5fcf6cSMarkus Armbruster stats_list = stats_list->next,
976a5fcf6cSMarkus Armbruster schema_value_list = schema_value_list->next) {
986a5fcf6cSMarkus Armbruster
996a5fcf6cSMarkus Armbruster Stats *stats = stats_list->value;
1006a5fcf6cSMarkus Armbruster StatsValue *stats_value = stats->value;
1016a5fcf6cSMarkus Armbruster StatsSchemaValue *schema_value = schema_value_list->value;
1026a5fcf6cSMarkus Armbruster
1036a5fcf6cSMarkus Armbruster /* Find schema entry */
1046a5fcf6cSMarkus Armbruster while (!g_str_equal(stats->name, schema_value->name)) {
1056a5fcf6cSMarkus Armbruster if (!schema_value_list->next) {
1066a5fcf6cSMarkus Armbruster monitor_printf(mon, "failed to find schema entry for %s\n",
1076a5fcf6cSMarkus Armbruster stats->name);
1086a5fcf6cSMarkus Armbruster return;
1096a5fcf6cSMarkus Armbruster }
1106a5fcf6cSMarkus Armbruster schema_value_list = schema_value_list->next;
1116a5fcf6cSMarkus Armbruster schema_value = schema_value_list->value;
1126a5fcf6cSMarkus Armbruster }
1136a5fcf6cSMarkus Armbruster
1146a5fcf6cSMarkus Armbruster print_stats_schema_value(mon, schema_value);
1156a5fcf6cSMarkus Armbruster
1166a5fcf6cSMarkus Armbruster if (stats_value->type == QTYPE_QNUM) {
1176a5fcf6cSMarkus Armbruster monitor_printf(mon, ": %" PRId64 "\n", stats_value->u.scalar);
1186a5fcf6cSMarkus Armbruster } else if (stats_value->type == QTYPE_QBOOL) {
1196a5fcf6cSMarkus Armbruster monitor_printf(mon, ": %s\n", stats_value->u.boolean ? "yes" : "no");
1206a5fcf6cSMarkus Armbruster } else if (stats_value->type == QTYPE_QLIST) {
1216a5fcf6cSMarkus Armbruster uint64List *list;
1226a5fcf6cSMarkus Armbruster int i;
1236a5fcf6cSMarkus Armbruster
1246a5fcf6cSMarkus Armbruster monitor_printf(mon, ": ");
1256a5fcf6cSMarkus Armbruster for (list = stats_value->u.list, i = 1;
1266a5fcf6cSMarkus Armbruster list;
1276a5fcf6cSMarkus Armbruster list = list->next, i++) {
1286a5fcf6cSMarkus Armbruster monitor_printf(mon, "[%d]=%" PRId64 " ", i, list->value);
1296a5fcf6cSMarkus Armbruster }
1306a5fcf6cSMarkus Armbruster monitor_printf(mon, "\n");
1316a5fcf6cSMarkus Armbruster }
1326a5fcf6cSMarkus Armbruster }
1336a5fcf6cSMarkus Armbruster }
1346a5fcf6cSMarkus Armbruster
1356a5fcf6cSMarkus Armbruster /* Create the StatsFilter that is needed for an "info stats" invocation. */
stats_filter(StatsTarget target,const char * names,int cpu_index,StatsProvider provider)1366a5fcf6cSMarkus Armbruster static StatsFilter *stats_filter(StatsTarget target, const char *names,
1376a5fcf6cSMarkus Armbruster int cpu_index, StatsProvider provider)
1386a5fcf6cSMarkus Armbruster {
1396a5fcf6cSMarkus Armbruster StatsFilter *filter = g_malloc0(sizeof(*filter));
1406a5fcf6cSMarkus Armbruster StatsProvider provider_idx;
1416a5fcf6cSMarkus Armbruster StatsRequestList *request_list = NULL;
1426a5fcf6cSMarkus Armbruster
1436a5fcf6cSMarkus Armbruster filter->target = target;
1446a5fcf6cSMarkus Armbruster switch (target) {
1456a5fcf6cSMarkus Armbruster case STATS_TARGET_VM:
1466a5fcf6cSMarkus Armbruster break;
1476a5fcf6cSMarkus Armbruster case STATS_TARGET_VCPU:
1486a5fcf6cSMarkus Armbruster {
1496a5fcf6cSMarkus Armbruster strList *vcpu_list = NULL;
1506a5fcf6cSMarkus Armbruster CPUState *cpu = qemu_get_cpu(cpu_index);
1516a5fcf6cSMarkus Armbruster char *canonical_path = object_get_canonical_path(OBJECT(cpu));
1526a5fcf6cSMarkus Armbruster
1536a5fcf6cSMarkus Armbruster QAPI_LIST_PREPEND(vcpu_list, canonical_path);
1546a5fcf6cSMarkus Armbruster filter->u.vcpu.has_vcpus = true;
1556a5fcf6cSMarkus Armbruster filter->u.vcpu.vcpus = vcpu_list;
1566a5fcf6cSMarkus Armbruster break;
1576a5fcf6cSMarkus Armbruster }
158*f2b90109Szhenwei pi case STATS_TARGET_CRYPTODEV:
159*f2b90109Szhenwei pi break;
1606a5fcf6cSMarkus Armbruster default:
1616a5fcf6cSMarkus Armbruster break;
1626a5fcf6cSMarkus Armbruster }
1636a5fcf6cSMarkus Armbruster
1646a5fcf6cSMarkus Armbruster if (!names && provider == STATS_PROVIDER__MAX) {
1656a5fcf6cSMarkus Armbruster return filter;
1666a5fcf6cSMarkus Armbruster }
1676a5fcf6cSMarkus Armbruster
1686a5fcf6cSMarkus Armbruster /*
1696a5fcf6cSMarkus Armbruster * "info stats" can only query either one or all the providers. Querying
1706a5fcf6cSMarkus Armbruster * by name, but not by provider, requires the creation of one filter per
1716a5fcf6cSMarkus Armbruster * provider.
1726a5fcf6cSMarkus Armbruster */
1736a5fcf6cSMarkus Armbruster for (provider_idx = 0; provider_idx < STATS_PROVIDER__MAX; provider_idx++) {
1746a5fcf6cSMarkus Armbruster if (provider == STATS_PROVIDER__MAX || provider == provider_idx) {
1756a5fcf6cSMarkus Armbruster StatsRequest *request = g_new0(StatsRequest, 1);
1766a5fcf6cSMarkus Armbruster request->provider = provider_idx;
1776a5fcf6cSMarkus Armbruster if (names && !g_str_equal(names, "*")) {
1786a5fcf6cSMarkus Armbruster request->has_names = true;
1796a5fcf6cSMarkus Armbruster request->names = hmp_split_at_comma(names);
1806a5fcf6cSMarkus Armbruster }
1816a5fcf6cSMarkus Armbruster QAPI_LIST_PREPEND(request_list, request);
1826a5fcf6cSMarkus Armbruster }
1836a5fcf6cSMarkus Armbruster }
1846a5fcf6cSMarkus Armbruster
1856a5fcf6cSMarkus Armbruster filter->has_providers = true;
1866a5fcf6cSMarkus Armbruster filter->providers = request_list;
1876a5fcf6cSMarkus Armbruster return filter;
1886a5fcf6cSMarkus Armbruster }
1896a5fcf6cSMarkus Armbruster
hmp_info_stats(Monitor * mon,const QDict * qdict)1906a5fcf6cSMarkus Armbruster void hmp_info_stats(Monitor *mon, const QDict *qdict)
1916a5fcf6cSMarkus Armbruster {
1926a5fcf6cSMarkus Armbruster const char *target_str = qdict_get_str(qdict, "target");
1936a5fcf6cSMarkus Armbruster const char *provider_str = qdict_get_try_str(qdict, "provider");
1946a5fcf6cSMarkus Armbruster const char *names = qdict_get_try_str(qdict, "names");
1956a5fcf6cSMarkus Armbruster
1966a5fcf6cSMarkus Armbruster StatsProvider provider = STATS_PROVIDER__MAX;
1976a5fcf6cSMarkus Armbruster StatsTarget target;
1986a5fcf6cSMarkus Armbruster Error *err = NULL;
1996a5fcf6cSMarkus Armbruster g_autoptr(StatsSchemaList) schema = NULL;
2006a5fcf6cSMarkus Armbruster g_autoptr(StatsResultList) stats = NULL;
2016a5fcf6cSMarkus Armbruster g_autoptr(StatsFilter) filter = NULL;
2026a5fcf6cSMarkus Armbruster StatsResultList *entry;
2036a5fcf6cSMarkus Armbruster
2046a5fcf6cSMarkus Armbruster target = qapi_enum_parse(&StatsTarget_lookup, target_str, -1, &err);
2056a5fcf6cSMarkus Armbruster if (err) {
2066a5fcf6cSMarkus Armbruster monitor_printf(mon, "invalid stats target %s\n", target_str);
2076a5fcf6cSMarkus Armbruster goto exit_no_print;
2086a5fcf6cSMarkus Armbruster }
2096a5fcf6cSMarkus Armbruster if (provider_str) {
2106a5fcf6cSMarkus Armbruster provider = qapi_enum_parse(&StatsProvider_lookup, provider_str, -1, &err);
2116a5fcf6cSMarkus Armbruster if (err) {
2126a5fcf6cSMarkus Armbruster monitor_printf(mon, "invalid stats provider %s\n", provider_str);
2136a5fcf6cSMarkus Armbruster goto exit_no_print;
2146a5fcf6cSMarkus Armbruster }
2156a5fcf6cSMarkus Armbruster }
2166a5fcf6cSMarkus Armbruster
2176a5fcf6cSMarkus Armbruster schema = qmp_query_stats_schemas(provider_str ? true : false,
2186a5fcf6cSMarkus Armbruster provider, &err);
2196a5fcf6cSMarkus Armbruster if (err) {
2206a5fcf6cSMarkus Armbruster goto exit;
2216a5fcf6cSMarkus Armbruster }
2226a5fcf6cSMarkus Armbruster
2236a5fcf6cSMarkus Armbruster switch (target) {
2246a5fcf6cSMarkus Armbruster case STATS_TARGET_VM:
2256a5fcf6cSMarkus Armbruster filter = stats_filter(target, names, -1, provider);
2266a5fcf6cSMarkus Armbruster break;
2276a5fcf6cSMarkus Armbruster case STATS_TARGET_VCPU: {}
2286a5fcf6cSMarkus Armbruster int cpu_index = monitor_get_cpu_index(mon);
2296a5fcf6cSMarkus Armbruster filter = stats_filter(target, names, cpu_index, provider);
2306a5fcf6cSMarkus Armbruster break;
231*f2b90109Szhenwei pi case STATS_TARGET_CRYPTODEV:
232*f2b90109Szhenwei pi filter = stats_filter(target, names, -1, provider);
233*f2b90109Szhenwei pi break;
2346a5fcf6cSMarkus Armbruster default:
2356a5fcf6cSMarkus Armbruster abort();
2366a5fcf6cSMarkus Armbruster }
2376a5fcf6cSMarkus Armbruster
2386a5fcf6cSMarkus Armbruster stats = qmp_query_stats(filter, &err);
2396a5fcf6cSMarkus Armbruster if (err) {
2406a5fcf6cSMarkus Armbruster goto exit;
2416a5fcf6cSMarkus Armbruster }
2426a5fcf6cSMarkus Armbruster for (entry = stats; entry; entry = entry->next) {
2436a5fcf6cSMarkus Armbruster print_stats_results(mon, target, provider_str == NULL, entry->value, schema);
2446a5fcf6cSMarkus Armbruster }
2456a5fcf6cSMarkus Armbruster
2466a5fcf6cSMarkus Armbruster exit:
2476a5fcf6cSMarkus Armbruster if (err) {
2486a5fcf6cSMarkus Armbruster monitor_printf(mon, "%s\n", error_get_pretty(err));
2496a5fcf6cSMarkus Armbruster }
2506a5fcf6cSMarkus Armbruster exit_no_print:
2516a5fcf6cSMarkus Armbruster error_free(err);
2526a5fcf6cSMarkus Armbruster }
253