qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v4 1/3] qmp: Support for querying stats


From: Mark Kanda
Subject: [PATCH v4 1/3] qmp: Support for querying stats
Date: Tue, 15 Feb 2022 09:04:31 -0600

Introduce QMP support for querying stats. Provide a framework for adding new
stats and support for the following commands:

- query-stats
Returns a list of all stats per target type (only VM and vCPU to start), with
additional options for specifying stat names, vCPU qom paths, and providers.

- query-stats-schemas
Returns a list of stats included in each target type, with an option for
specifying the provider.

The framework provides a method to register callbacks for these QMP commands.

The first use-case will be for fd-based KVM stats (in an upcoming patch).

Examples (with fd-based KVM stats):

- Query all VM stats:

{ "execute": "query-stats", "arguments" : { "target": "vm" } }

{ "return": {
  "vm": [
     { "provider": "kvm",
       "stats": [
          { "name": "max_mmu_page_hash_collisions", "value": 0 },
          { "name": "max_mmu_rmap_size", "value": 0 },
          { "name": "nx_lpage_splits", "value": 148 },
          ...
     { "provider": "xyz",
       "stats": [ ...
     ...
] } }

- Query all vCPU stats:

{ "execute": "query-stats", "arguments" : { "target": "vcpu" } }

{ "return": {
    "vcpus": [
      { "path": "/machine/unattached/device[0]"
        "providers": [
          { "provider": "kvm",
            "stats": [
              { "name": "guest_mode", "value": 0 },
              { "name": "directed_yield_successful", "value": 0 },
              { "name": "directed_yield_attempted", "value": 106 },
              ...
          { "provider": "xyz",
            "stats": [ ...
           ...
      { "path": "/machine/unattached/device[1]"
        "providers": [
          { "provider": "kvm",
            "stats": [...
          ...
} ] } }

- Query 'exits' and 'l1d_flush' KVM stats, and 'somestat' from provider 'xyz'
for vCPUs '/machine/unattached/device[2]' and '/machine/unattached/device[4]':

{ "execute": "query-stats",
  "arguments": {
    "target": "vcpu",
    "vcpus": [ "/machine/unattached/device[2]",
               "/machine/unattached/device[4]" ],
    "filters": [
      { "provider": "kvm",
        "fields": [ "l1d_flush", "exits" ] },
      { "provider": "xyz",
        "fields": [ "somestat" ] } ] } }

{ "return": {
    "vcpus": [
      { "path": "/machine/unattached/device[2]"
        "providers": [
          { "provider": "kvm",
            "stats": [ { "name": "l1d_flush", "value": 41213 },
                       { "name": "exits", "value": 74291 } ] },
          { "provider": "xyz",
            "stats": [ ... ] } ] },
      { "path": "/machine/unattached/device[4]"
        "providers": [
          { "provider": "kvm",
            "stats": [ { "name": "l1d_flush", "value": 16132 },
                       { "name": "exits", "value": 57922 } ] },
          { "provider": "xyz",
            "stats": [ ... ] } ] } ] } }

- Query stats schemas:

{ "execute": "query-stats-schemas" }

{ "return": {
    "vcpu": [
      { "provider": "kvm",
        "stats": [
           { "name": "guest_mode",
             "unit": "none",
             "base": 10,
             "exponent": 0,
             "type": "instant" },
          { "name": "directed_yield_successful",
             "unit": "none",
             "base": 10,
             "exponent": 0,
             "type": "cumulative" },
             ...
      { "provider": "xyz",
        ...
   "vm": [
      { "provider": "kvm",
        "stats": [
           { "name": "max_mmu_page_hash_collisions",
             "unit": "none",
             "base": 10,
             "exponent": 0,
             "type": "peak" },
      { "provider": "xyz",
      ...

Signed-off-by: Mark Kanda <mark.kanda@oracle.com>
---
 include/monitor/stats.h |  51 ++++++++
 monitor/qmp-cmds.c      | 219 +++++++++++++++++++++++++++++++++
 qapi/meson.build        |   1 +
 qapi/qapi-schema.json   |   1 +
 qapi/stats.json         | 259 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 531 insertions(+)
 create mode 100644 include/monitor/stats.h
 create mode 100644 qapi/stats.json

diff --git a/include/monitor/stats.h b/include/monitor/stats.h
new file mode 100644
index 0000000000..172dc01a4d
--- /dev/null
+++ b/include/monitor/stats.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef STATS_H
+#define STATS_H
+
+#include "qapi/qapi-types-stats.h"
+
+/*
+ * Add QMP stats callbacks to the stats_callbacks list.
+ *
+ * @provider: stats provider
+ *
+ * @stats_fn: routine to query stats:
+ *    void (*stats_fn)(StatsResults *results, StatsFilter *filter, Error 
**errp)
+ *
+ * @schema_fn: routine to query stat schemas:
+ *    void (*schemas_fn)(StatsSchemaResult *results, Error **errp)
+ */
+void add_stats_callbacks(StatsProvider provider,
+                         void (*stats_fn)(StatsResults *, StatsFilter *,
+                                          Error **),
+                         void (*schemas_fn)(StatsSchemaResults *, Error **));
+
+/*
+ * Helper routines for adding stats entries to the results lists.
+ */
+void add_vm_stats_entry(StatsList *, StatsResults *, StatsProvider);
+void add_vcpu_stats_entry(StatsList *, StatsResults *, StatsProvider, char *);
+void add_vm_stats_schema(StatsSchemaValueList *, StatsSchemaResults *,
+                         StatsProvider);
+void add_vcpu_stats_schema(StatsSchemaValueList *, StatsSchemaResults *,
+                           StatsProvider);
+
+/*
+ * True if a stat name and provider match a filter or if no corresponding
+ * filters are defined. False otherwise.
+ */
+bool stats_requested_name(const char *, StatsProvider, StatsFilter *);
+
+/*
+ * True if a vcpu qom path and provider match a filter or if no corresponding
+ * filters are defined. False otherwise.
+ */
+bool stats_requested_vcpu(const char *, StatsProvider, StatsFilter *);
+
+#endif /* STATS_H */
diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c
index db4d186448..07f1e683be 100644
--- a/monitor/qmp-cmds.c
+++ b/monitor/qmp-cmds.c
@@ -36,6 +36,7 @@
 #include "qapi/qapi-commands-control.h"
 #include "qapi/qapi-commands-machine.h"
 #include "qapi/qapi-commands-misc.h"
+#include "qapi/qapi-commands-stats.h"
 #include "qapi/qapi-commands-ui.h"
 #include "qapi/type-helpers.h"
 #include "qapi/qmp/qerror.h"
@@ -44,6 +45,7 @@
 #include "hw/acpi/acpi_dev_interface.h"
 #include "hw/intc/intc.h"
 #include "hw/rdma/rdma.h"
+#include "monitor/stats.h"
 
 NameInfo *qmp_query_name(Error **errp)
 {
@@ -448,3 +450,220 @@ HumanReadableText *qmp_x_query_irq(Error **errp)
 
     return human_readable_text_from_str(buf);
 }
+
+typedef struct StatsCallbacks {
+    StatsProvider provider;
+    void (*stats_cb)(StatsResults *, StatsFilter *, Error **);
+    void (*schemas_cb)(StatsSchemaResults *, Error **);
+    QTAILQ_ENTRY(StatsCallbacks) next;
+} StatsCallbacks;
+
+static QTAILQ_HEAD(, StatsCallbacks) stats_callbacks =
+    QTAILQ_HEAD_INITIALIZER(stats_callbacks);
+
+void add_stats_callbacks(StatsProvider provider,
+                         void (*stats_fn)(StatsResults *, StatsFilter*,
+                                          Error **),
+                         void (*schemas_fn)(StatsSchemaResults *, Error **))
+{
+    StatsCallbacks *entry = g_malloc0(sizeof(*entry));
+    entry->provider = provider;
+    entry->stats_cb = stats_fn;
+    entry->schemas_cb = schemas_fn;
+
+    QTAILQ_INSERT_TAIL(&stats_callbacks, entry, next);
+}
+
+static StatsRequestList *stats_target_filter(StatsFilter *filter)
+{
+    switch (filter->target) {
+    case STATS_TARGET_VM:
+        if (!filter->u.vm.has_filters) {
+            return NULL;
+        }
+        return filter->u.vm.filters;
+    case STATS_TARGET_VCPU:
+        if (!filter->u.vcpu.has_filters) {
+            return NULL;
+        }
+        return filter->u.vcpu.filters;
+        break;
+    default:
+        return NULL;
+    }
+}
+
+static bool stats_provider_match(StatsProvider provider,
+                                 StatsRequestList *request)
+{
+    return (!request->value->has_provider ||
+            (request->value->provider == provider));
+}
+
+static bool stats_requested_provider(StatsProvider provider,
+                                     StatsFilter *filter)
+{
+    StatsRequestList *request = stats_target_filter(filter);
+
+    if (!request) {
+        return true;
+    }
+    for (; request; request = request->next) {
+        if (stats_provider_match(provider, request)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+StatsResults *qmp_query_stats(StatsFilter *filter, Error **errp)
+{
+    StatsResults *stats_results = g_malloc0(sizeof(*stats_results));
+    StatsCallbacks *entry;
+
+    QTAILQ_FOREACH(entry, &stats_callbacks, next) {
+        if (stats_requested_provider(entry->provider, filter)) {
+            entry->stats_cb(stats_results, filter, errp);
+        }
+    }
+
+    return stats_results;
+}
+
+StatsSchemaResults *qmp_query_stats_schemas(bool has_provider,
+                                           StatsProvider provider,
+                                           Error **errp)
+{
+    StatsSchemaResults *stats_results = g_malloc0(sizeof(*stats_results));
+    StatsCallbacks *entry;
+
+    QTAILQ_FOREACH(entry, &stats_callbacks, next) {
+        if (has_provider && (provider != entry->provider)) {
+            continue;
+        }
+        entry->schemas_cb(stats_results, errp);
+    }
+
+    return stats_results;
+}
+
+void add_vm_stats_entry(StatsList *stats_list, StatsResults *stats_results,
+                        StatsProvider provider)
+{
+    StatsResultsEntry *entry = g_malloc0(sizeof(*entry));
+    entry->provider = provider;
+    entry->stats = stats_list;
+
+    QAPI_LIST_PREPEND(stats_results->vm, entry);
+    stats_results->has_vm = true;
+}
+
+void add_vcpu_stats_entry(StatsList *stats_list, StatsResults *stats_results,
+                          StatsProvider provider, char *path)
+{
+    StatsResultsVCPUEntry *value;
+    StatsResultsEntry *entry;
+    StatsResultsVCPUEntryList **tailp, *tail;
+
+    entry = g_malloc0(sizeof(*entry));
+    entry->provider = provider;
+    entry->stats = stats_list;
+
+    /* Find the vCPU entry and add to its list; else create it */
+    tailp = &stats_results->vcpus;
+
+    for (tail = *tailp; tail; tail = tail->next) {
+        if (g_str_equal(tail->value->path, path)) {
+            /* Add to the existing vCPU list */
+            QAPI_LIST_PREPEND(tail->value->providers, entry);
+            return;
+        }
+        tailp = &tail->next;
+    }
+
+    /* Create and populate a new vCPU entry */
+    value = g_malloc0(sizeof(*value));
+    value->path = g_strdup(path);
+    value->providers = g_malloc0(sizeof(*value->providers));
+    value->providers->value = entry;
+    QAPI_LIST_APPEND(tailp, value);
+    stats_results->has_vcpus = true;
+}
+
+void add_vm_stats_schema(StatsSchemaValueList *stats_list,
+                         StatsSchemaResults *schema_results,
+                         StatsProvider provider)
+{
+    StatsSchemaProvider *entry = g_malloc0(sizeof(*entry));
+
+    entry->provider = provider;
+    entry->stats = stats_list;
+    QAPI_LIST_PREPEND(schema_results->vm, entry);
+    schema_results->has_vm = true;
+}
+
+void add_vcpu_stats_schema(StatsSchemaValueList *stats_list,
+                           StatsSchemaResults *schema_results,
+                           StatsProvider provider)
+{
+    StatsSchemaProvider *entry = g_malloc0(sizeof(*entry));
+
+    entry->provider = provider;
+    entry->stats = stats_list;
+    QAPI_LIST_PREPEND(schema_results->vcpu, entry);
+    schema_results->has_vcpu = true;
+}
+
+static bool str_in_list(const char *name, strList *list)
+{
+    strList *str_list = NULL;
+
+    for (str_list = list; str_list; str_list = str_list->next) {
+        if (g_str_equal(name, str_list->value)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool stats_requested_name(const char *name, StatsProvider provider,
+                          StatsFilter *filter)
+{
+    StatsRequestList *request = stats_target_filter(filter);
+
+    if (!request) {
+        return true;
+    }
+    for (; request; request = request->next) {
+        if (!stats_provider_match(provider, request)) {
+            continue;
+        }
+        if (!request->value->has_fields ||
+            str_in_list(name, request->value->fields)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool stats_requested_vcpu(const char *path, StatsProvider provider,
+                          StatsFilter *filter)
+{
+    StatsRequestList *request = stats_target_filter(filter);
+
+    if (!request) {
+        return true;
+    }
+    if (!filter->u.vcpu.has_filters) {
+        return true;
+    }
+    if (filter->u.vcpu.has_vcpus && !str_in_list(path, filter->u.vcpu.vcpus)) {
+        return false;
+    }
+    for (; request; request = request->next) {
+        if (stats_provider_match(provider, request)) {
+            return true;
+        }
+    }
+    return false;
+}
diff --git a/qapi/meson.build b/qapi/meson.build
index 656ef0e039..fd5c93d643 100644
--- a/qapi/meson.build
+++ b/qapi/meson.build
@@ -46,6 +46,7 @@ qapi_all_modules = [
   'replay',
   'run-state',
   'sockets',
+  'stats',
   'trace',
   'transaction',
   'yank',
diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json
index 4912b9744e..92d7ecc52c 100644
--- a/qapi/qapi-schema.json
+++ b/qapi/qapi-schema.json
@@ -93,3 +93,4 @@
 { 'include': 'audio.json' }
 { 'include': 'acpi.json' }
 { 'include': 'pci.json' }
+{ 'include': 'stats.json' }
diff --git a/qapi/stats.json b/qapi/stats.json
new file mode 100644
index 0000000000..ae5dc3ee2c
--- /dev/null
+++ b/qapi/stats.json
@@ -0,0 +1,259 @@
+# -*- Mode: Python -*-
+# vim: filetype=python
+#
+# Copyright (c) 2022 Oracle and/or its affiliates.
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+##
+# = Stats
+##
+
+##
+# @StatsType:
+#
+# Enumeration of stats types
+# @cumulative: stat is cumulative; value can only increase.
+# @instant: stat is instantaneous; value can increase or decrease.
+# @peak: stat is the peak value; value can only increase.
+# @linear-hist: stat is a linear histogram.
+# @log-hist: stat is a logarithmic histogram.
+#
+# Since: 7.0
+##
+{ 'enum' : 'StatsType',
+  'data' : [ 'cumulative', 'instant', 'peak', 'linear-hist', 'log-hist' ] }
+
+##
+# @StatsUnit:
+#
+# Enumeration of stats units
+# @bytes: stat reported in bytes.
+# @seconds: stat reported in seconds.
+# @cycles: stat reported in clock cycles.
+# @none: no unit for this stat.
+#
+# Since: 7.0
+##
+{ 'enum' : 'StatsUnit',
+  'data' : [ 'bytes', 'seconds', 'cycles', 'none' ] }
+
+##
+# @StatsBase:
+#
+# Enumeration of stats base
+# @pow10: scale is based on power of 10.
+# @pow2: scale is based on power of 2.
+#
+# Since: 7.0
+##
+{ 'enum' : 'StatsBase',
+  'data' : [ 'pow10', 'pow2' ] }
+
+##
+# @StatsProvider:
+#
+# Enumeration of stats providers.
+#
+# Since: 7.0
+##
+{ 'enum': 'StatsProvider',
+  'data': [ ] }
+
+##
+# @StatsTarget:
+#
+# Enumeration of stats targets.
+# @vm: stat is per vm.
+# @vcpu: stat is per vcpu.
+#
+# Since: 7.0
+##
+{ 'enum': 'StatsTarget',
+  'data': [ 'vm', 'vcpu' ] }
+
+##
+# @StatsRequest:
+#
+# Stats filter element.
+# @provider: stat provider.
+# @fields: list of stat names.
+#
+# Since: 7.0
+##
+{ 'struct': 'StatsRequest',
+  'data': { '*provider': 'StatsProvider',
+            '*fields': [ 'str' ] } }
+
+##
+# @StatsRequestArray:
+#
+# @filters: filters for this request.
+##
+{ 'struct': 'StatsRequestArray',
+  'data': { '*filters': [ 'StatsRequest' ] } }
+
+##
+# @StatsVCPURequestArray:
+#
+# @vcpus: list of qom paths.
+##
+{ 'struct': 'StatsVCPURequestArray',
+  'base': 'StatsRequestArray',
+  'data': { '*vcpus': [ 'str' ] } }
+
+##
+# @StatsFilter:
+#
+# Target specific filter.
+#
+# Since: 7.0
+##
+{ 'union': 'StatsFilter',
+  'base': { 'target': 'StatsTarget' },
+  'discriminator': 'target',
+  'data': { 'vcpu': 'StatsVCPURequestArray',
+            'vm': 'StatsRequestArray' } }
+
+##
+# @StatsValueArray:
+#
+# @values: uint64 list.
+#
+# Since: 7.0
+##
+{ 'struct': 'StatsValueArray',
+  'data': { 'values' : [ 'uint64' ] } }
+
+##
+# @StatsValue:
+#
+# @scalar: single uint64.
+# @list: list of uint64.
+#
+# Since: 7.0
+##
+{ 'alternate': 'StatsValue',
+  'data': { 'scalar': 'uint64',
+            'list': 'StatsValueArray' } }
+
+##
+# @Stats:
+#
+# @name: name of stat.
+# @value: stat value.
+#
+# Since: 7.0
+##
+{ 'struct': 'Stats',
+  'data': { 'name': 'str',
+            'value' : 'StatsValue' } }
+
+##
+# @StatsResultsEntry:
+#
+# @provider: stat provider.
+# @stats: list of stats.
+#
+# Since: 7.0
+##
+{ 'struct': 'StatsResultsEntry',
+  'data': { 'provider': 'StatsProvider',
+            'stats': [ 'Stats' ] } }
+
+##
+# @StatsResultsVCPUEntry:
+#
+# @path: vCPU qom path.
+# @providers: per provider results.
+#
+# Since: 7.0
+##
+{ 'struct': 'StatsResultsVCPUEntry',
+  'data': { 'path': 'str',
+            'providers': [ 'StatsResultsEntry' ] } }
+
+##
+# @StatsResults:
+#
+# Target specific results.
+#
+# Since: 7.0
+##
+{ 'struct': 'StatsResults',
+  'data': {
+      '*vcpus': [ 'StatsResultsVCPUEntry' ],
+      '*vm': [ 'StatsResultsEntry' ] } }
+
+##
+# @query-stats:
+#
+# data: @StatsFilter.
+# Returns: @StatsResults.
+#
+# Since: 7.0
+##
+{ 'command': 'query-stats',
+  'data': 'StatsFilter',
+  'boxed': true,
+  'returns': 'StatsResults' }
+
+##
+# @StatsSchemaValue:
+#
+# Individual stat schema.
+# @name: stat name.
+# @type: @StatType.
+# @unit: @StatUnit.
+# @base: @StatBase.
+# @exponent: Used together with @base.
+# @bucket-size: Used with linear-hist to report bucket size
+#
+# Since: 7.0
+##
+{ 'struct': 'StatsSchemaValue',
+  'data': { 'name': 'str',
+            'type': 'StatsType',
+            'unit': 'StatsUnit',
+            'base': 'StatsBase',
+            'exponent': 'int16',
+            '*bucket-size': 'uint32' } }
+
+##
+# @StatsSchemaProvider:
+#
+# @provider: @StatsProvider.
+# @stats: list of stats.
+#
+# Since: 7.0
+##
+{ 'struct': 'StatsSchemaProvider',
+  'data': { 'provider': 'StatsProvider',
+            'stats': [ 'StatsSchemaValue' ] } }
+
+##
+# @StatsSchemaResults:
+#
+# @vm: vm stats schemas.
+# @vcpu: vcpu stats schemas.
+#
+# Since: 7.0
+##
+{ 'struct': 'StatsSchemaResults',
+  'data': { '*vm': [ 'StatsSchemaProvider' ],
+            '*vcpu': [ 'StatsSchemaProvider' ] } }
+
+##
+# @query-stats-schemas:
+#
+# Query Stats schemas.
+# Returns @StatsSchemaResult.
+#
+# Since: 7.0
+##
+{ 'command': 'query-stats-schemas',
+  'data': { '*provider': 'StatsProvider' },
+  'returns': 'StatsSchemaResults' }
-- 
2.27.0




reply via email to

[Prev in Thread] Current Thread [Next in Thread]