All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Alex Bennée" <alex.bennee@linaro.org>
To: qemu-devel@nongnu.org
Cc: Pavel.Dovgaluk@ispras.ru, vilanova@ac.upc.edu, cota@braap.org,
	"Alex Bennée" <alex.bennee@linaro.org>,
	"Stefan Hajnoczi" <stefanha@redhat.com>
Subject: [Qemu-devel] [RFC PATCH  21/21] plugin: add tlbflush stats plugin
Date: Fri,  5 Oct 2018 16:49:10 +0100	[thread overview]
Message-ID: <20181005154910.3099-22-alex.bennee@linaro.org> (raw)
In-Reply-To: <20181005154910.3099-1-alex.bennee@linaro.org>

This plugin attempts to give an idea of how long it take QEMU to
process the various types of TLB flush operation. Although this is
generally of interest to QEMU developers the plugin is only using the
trace point information to work this out. For example launching a
system emulation with -plugin file=./trace/plugins/tlbflush.so allows
the following to be reported on the monitor:

  (qemu) info trace-plugins
  ./trace/plugins/tlbflush.so: tlbflush report:
  cpu 0 101244 self events: avg time 103, max 18313
  cpu 0 2 async events: avg time 14076146, max 27536460
  cpu 0 122328 synced events: avg time 5441, max 29080526
  cpu 0 162671 work events: avg time 5380, max 47305
  cpu 1 101742 self events: avg time 110, max 29708
  cpu 1 2 async events: avg time 14112140, max 27573506
  cpu 1 122669 synced events: avg time 5762, max 1175419
  cpu 1 163401 work events: avg time 5427, max 942563
  cpu 2 116726 self events: avg time 107, max 18277
  cpu 2 2 async events: avg time 14126146, max 27615150
  cpu 2 120119 synced events: avg time 6318, max 928542
  cpu 2 177175 work events: avg time 5772, max 699626
  cpu 3 94551 self events: avg time 107, max 23313
  cpu 3 1 async events: avg time 27947822, max 27947822
  cpu 3 123203 synced events: avg time 7877, max 3913260
  cpu 3 156481 work events: avg time 5126, max 54107

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
 trace/plugins/tlbflush/tlbflush.c | 283 ++++++++++++++++++++++++++++++
 1 file changed, 283 insertions(+)
 create mode 100644 trace/plugins/tlbflush/tlbflush.c

diff --git a/trace/plugins/tlbflush/tlbflush.c b/trace/plugins/tlbflush/tlbflush.c
new file mode 100644
index 0000000000..0e6ab82978
--- /dev/null
+++ b/trace/plugins/tlbflush/tlbflush.c
@@ -0,0 +1,283 @@
+/*
+ * Execution Hotblocks Plugin
+ *
+ * Copyright (c) 2018
+ * Written by Alex Bennée <alex.bennee@linaro.org>
+ *
+ * This code is licensed under the GNU GPL v2.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <glib.h>
+#include <time.h>
+#include <inttypes.h>
+#include "plugins.h"
+
+#define MAX_CPU  16
+
+#ifndef __ATOMIC_RELAXED
+#error "this plugin relies on atomics"
+#endif
+
+/*
+ * We are not going to try and track coalesced requests so the
+ * measurement will start from the point of the first request and be
+ * cleared from the first work complete.
+ */
+
+typedef enum FlushType { SELF, ASYNC, SYNCED } FlushType;
+
+typedef struct {
+    unsigned long count;
+    unsigned long total;
+    unsigned long max;
+} EventCounts;
+
+typedef struct {
+    struct timespec start;
+    FlushType type;
+
+    EventCounts self;
+    EventCounts async;
+    EventCounts synced;
+    EventCounts work;
+} TLBStats;
+
+static TLBStats stats[MAX_CPU];
+
+bool plugin_init(const char *args)
+{
+    return true;
+}
+
+static void add_stats(GString *report, int cpu, char *name, EventCounts *event)
+{
+    if (event->count) {
+        g_string_append_printf(report,
+                               "cpu %d %ld %s events: avg time %lu, max %lu\n",
+                               cpu, event->count, name,
+                               event->total / event->count, event->max);
+    }
+}
+
+char *plugin_status(void)
+{
+    GString *report = g_string_new("tlbflush report:\n");
+    char *r;
+    int i;
+
+    for (i = 0; i < MAX_CPU; i++) {
+        TLBStats *cpu_stats = &stats[i];
+
+        add_stats(report, i, "self", &cpu_stats->self);
+        add_stats(report, i, "async", &cpu_stats->async);
+        add_stats(report, i, "synced", &cpu_stats->synced);
+        add_stats(report, i, "work", &cpu_stats->work);
+    }
+
+    r = report->str;
+    g_string_free(report, FALSE);
+    return r;
+}
+
+
+/*
+ * Maybe start an event. This is imprecise as we don't catch all
+ * events. We will skip events when:
+ *   - we already started for an event that hasn't completed
+ *   - events that happen to start when tv_nsec == 0
+ */
+static bool maybe_start_timer(TLBStats *cpu, struct timespec now)
+{
+    struct timespec zero = { 0, 0 };
+    bool started;
+
+    started = __atomic_compare_exchange_n(&cpu->start.tv_nsec,
+                                          &zero.tv_nsec,
+                                          now.tv_nsec, false,
+                                          __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
+    if (started) {
+        __atomic_store_n(&cpu->start.tv_sec, now.tv_sec, __ATOMIC_SEQ_CST);
+    }
+
+    return started;
+}
+
+bool tlb_flush_self(int line, int vcpu)
+{
+    struct timespec current;
+    TLBStats *this = &stats[vcpu];
+
+    clock_gettime(CLOCK_MONOTONIC, &current);
+
+    if (maybe_start_timer(this, current)) {
+        this->type = SELF;
+    }
+
+    return false;
+}
+
+static void start_one_cpu(TLBStats *cpu, FlushType type, struct timespec now)
+{
+    if (maybe_start_timer(cpu, now)) {
+        cpu->type = type;
+    }
+}
+
+bool tlb_flush_async_schedule(int line, int from, int to)
+{
+    struct timespec current;
+    TLBStats *this;
+
+    clock_gettime(CLOCK_MONOTONIC, &current);
+
+    if (to < 0) {
+        int i;
+        for (i = 0; i < MAX_CPU; i++) {
+            if (i != from) {
+                this = &stats[i];
+                start_one_cpu(this, ASYNC, current);
+            }
+        }
+    } else {
+        this = &stats[to];
+        start_one_cpu(this, ASYNC, current);
+    }
+}
+
+bool tlb_flush_synced_schedule(int line, int from, int to)
+{
+    struct timespec current;
+    TLBStats *this;
+
+    clock_gettime(CLOCK_MONOTONIC, &current);
+
+    if (to < 0) {
+        int i;
+        for (i = 0; i < MAX_CPU; i++) {
+            if (i != from) {
+                this = &stats[i];
+                start_one_cpu(this, SYNCED, current);
+            }
+        }
+    } else {
+        this = &stats[to];
+        start_one_cpu(this, SYNCED, current);
+    }
+}
+
+
+static EventCounts *get_counts(FlushType type, TLBStats *stats)
+{
+    switch (type) {
+    case SELF:
+        return &stats->self;
+    case ASYNC:
+        return &stats->async;
+    case SYNCED:
+        return &stats->synced;
+    }
+}
+
+/* return elapsed ns */
+static unsigned long get_elapsed(struct timespec start, struct timespec end)
+{
+    struct timespec temp;
+
+    if ((end.tv_nsec - start.tv_nsec) < 0) {
+        temp.tv_sec = end.tv_sec - start.tv_sec - 1;
+        temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;
+    } else {
+        temp.tv_sec = end.tv_sec - start.tv_sec;
+        temp.tv_nsec = end.tv_nsec - start.tv_nsec;
+    }
+
+    return (temp.tv_sec * 1000000000) + temp.tv_nsec;
+}
+
+/* Record the duration of a single event based on when it started */
+static struct timespec record_event(struct timespec start, EventCounts *event)
+{
+    struct timespec current;
+    unsigned long elapsed ;
+
+    clock_gettime(CLOCK_MONOTONIC, &current);
+    elapsed = get_elapsed(start, current);
+    event->count++;
+    event->total += elapsed;
+    if (elapsed > event->max) {
+        event->max = elapsed;
+    }
+
+    return current;
+}
+
+/* Update the timer for work. As we are re-reading in the same thread
+ * we don't need to be totally sequential - it doesn't matter if the
+ * outer thread reads this or zero.
+ */
+static void update_timer(TLBStats *cpu, struct timespec new_time)
+{
+    /* we are using 0 to indicate no current event */
+    if (new_time.tv_nsec == 0) {
+        new_time.tv_nsec = 1;
+    }
+    __atomic_store_n(&cpu->start.tv_sec, new_time.tv_sec, __ATOMIC_RELAXED);
+    __atomic_store_n(&cpu->start.tv_nsec, new_time.tv_nsec, __ATOMIC_RELAXED);
+}
+
+/* ??? Maybe we should drop the start of work trace points but create
+ * a new one for processing and queued work ??? */
+
+bool tlb_flush_all_work(int vcpu)
+{
+    struct timespec start;
+    TLBStats *this = &stats[vcpu];
+
+    start.tv_nsec = __atomic_load_n(&this->start.tv_nsec, __ATOMIC_SEQ_CST);
+    start.tv_sec = __atomic_load_n(&this->start.tv_sec, __ATOMIC_SEQ_CST);
+
+    if (start.tv_nsec) {
+        EventCounts *event = get_counts(this->type, this);
+        struct timespec start_work = record_event(start, event);
+        update_timer(this, start_work);
+    }
+
+    return false;
+}
+
+bool tlb_flush_work(int line, int vcpu, unsigned long data)
+{
+    struct timespec start;
+    TLBStats *this = &stats[vcpu];
+
+    start.tv_nsec = __atomic_load_n(&this->start.tv_nsec, __ATOMIC_SEQ_CST);
+    start.tv_sec = __atomic_load_n(&this->start.tv_sec, __ATOMIC_SEQ_CST);
+
+    if (start.tv_nsec) {
+        EventCounts *event = get_counts(this->type, this);
+        struct timespec start_work = record_event(start, event);
+        update_timer(this, start_work);
+    }
+
+    return false;
+}
+
+bool tlb_flush_work_complete(int line, int vcpu)
+{
+    struct timespec start;
+    TLBStats *this = &stats[vcpu];
+
+    start.tv_sec = __atomic_load_n(&this->start.tv_sec, __ATOMIC_RELAXED);
+    start.tv_nsec = __atomic_load_n(&this->start.tv_nsec, __ATOMIC_RELAXED);
+
+    if (start.tv_nsec) {
+        EventCounts *event = &this->work;
+        record_event(start, event);
+        /* reset for next event */
+        __atomic_store_n(&this->start.tv_nsec, 0, __ATOMIC_SEQ_CST);
+    }
+
+    return false;
+}
-- 
2.17.1

  parent reply	other threads:[~2018-10-05 15:55 UTC|newest]

Thread overview: 70+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-10-05 15:48 [Qemu-devel] [RFC PATCH 00/21] Trace updates and plugin RFC Alex Bennée
2018-10-05 15:48 ` [Qemu-devel] [RFC PATCH 01/21] util/log: allow -dfilter to stack Alex Bennée
2018-10-06 16:57   ` Richard Henderson
2018-10-05 15:48 ` [Qemu-devel] [RFC PATCH 02/21] util/log: add qemu_dfilter_append_range() Alex Bennée
2018-10-06 17:00   ` Richard Henderson
2018-10-05 15:48 ` [Qemu-devel] [RFC PATCH 03/21] linux-user: add -dfilter progtext shortcut Alex Bennée
2018-10-06 17:12   ` Richard Henderson
2018-10-05 15:48 ` [Qemu-devel] [RFC PATCH 04/21] trace: enable the exec_tb trace events Alex Bennée
2018-10-06 17:15   ` Richard Henderson
2018-10-07  1:42   ` Emilio G. Cota
2018-10-08  9:41     ` Alex Bennée
2018-10-05 15:48 ` [Qemu-devel] [RFC PATCH 05/21] trace: keep a count of trace-point hits Alex Bennée
2018-10-06 18:26   ` Richard Henderson
2018-10-05 15:48 ` [Qemu-devel] [RFC PATCH 06/21] trace: show trace point counts in the monitor Alex Bennée
2018-10-06 18:27   ` Richard Henderson
2018-10-08 12:51   ` Markus Armbruster
2018-10-17 11:52   ` Dr. David Alan Gilbert
2018-10-05 15:48 ` [Qemu-devel] [RFC PATCH 07/21] accel/tcg/cputlb: convert tlb_flush debugging into trace events Alex Bennée
2018-10-15 16:33   ` Richard Henderson
2018-10-15 18:23     ` Alex Bennée
2018-10-15 18:37       ` Richard Henderson
2018-10-05 15:48 ` [Qemu-devel] [RFC PATCH 08/21] accel/tcg/cputlb: convert remaining tlb_debug() to " Alex Bennée
2018-10-15 16:38   ` Richard Henderson
2018-10-15 18:17     ` Alex Bennée
2018-10-15 18:35       ` Richard Henderson
2018-10-05 15:48 ` [Qemu-devel] [RFC PATCH 09/21] trace: suppress log output of trace points Alex Bennée
2018-10-15 16:47   ` Richard Henderson
2018-10-05 15:48 ` [Qemu-devel] [RFC PATCH 10/21] qom/cpu: add a cpu_exit trace event Alex Bennée
2018-10-06 18:51   ` Richard Henderson
2018-10-05 15:49 ` [Qemu-devel] [RFC PATCH 11/21] trace: expose a plugin fn pointer in TraceEvent Alex Bennée
2018-10-15 16:52   ` Richard Henderson
2018-10-05 15:49 ` [Qemu-devel] [RFC PATCH 12/21] configure: expose a plugin to the trace-backends Alex Bennée
2018-10-15 17:14   ` Richard Henderson
2018-10-05 15:49 ` [Qemu-devel] [RFC PATCH 13/21] tracetool: generate plugin snippets Alex Bennée
2018-10-15 17:02   ` Richard Henderson
2018-10-05 15:49 ` [Qemu-devel] [RFC PATCH 14/21] trace: add support for plugin infrastructure Alex Bennée
2018-10-07  1:29   ` Emilio G. Cota
2018-10-15 17:11   ` Richard Henderson
2018-10-05 15:49 ` [Qemu-devel] [RFC PATCH 15/21] trace: add linux-user plugin support Alex Bennée
2018-10-15 17:13   ` Richard Henderson
2018-10-05 15:49 ` [Qemu-devel] [RFC PATCH 16/21] trace: add infrastructure for building plugins Alex Bennée
2018-10-15 17:24   ` Richard Henderson
2018-10-15 18:16     ` Alex Bennée
2018-10-05 15:49 ` [Qemu-devel] [RFC PATCH 17/21] hmp: expose status of plugins to the monitor Alex Bennée
2018-10-15 17:27   ` Richard Henderson
2018-10-17 12:04   ` Dr. David Alan Gilbert
2018-10-17 17:36     ` Markus Armbruster
2018-10-05 15:49 ` [Qemu-devel] [RFC PATCH 18/21] linux-user: allow dumping of plugin status at end of run Alex Bennée
2018-10-15 17:28   ` Richard Henderson
2018-10-05 15:49 ` [Qemu-devel] [RFC PATCH 19/21] plugins: add an example hotblocks plugin Alex Bennée
2018-10-08 12:59   ` Pavel Dovgalyuk
2018-10-08 14:05     ` Alex Bennée
2018-10-15 17:33   ` Richard Henderson
2018-10-15 18:15     ` Alex Bennée
2018-10-05 15:49 ` [Qemu-devel] [RFC PATCH 20/21] plugins: add hotness summary to hotblocks Alex Bennée
2018-10-15 17:34   ` Richard Henderson
2018-10-05 15:49 ` Alex Bennée [this message]
2018-10-07  1:23 ` [Qemu-devel] [RFC PATCH 00/21] Trace updates and plugin RFC Emilio G. Cota
2018-10-08 10:28   ` Alex Bennée
2018-10-08 12:51     ` Philippe Mathieu-Daudé
2018-10-08 13:59     ` Emilio G. Cota
2018-10-08 14:15       ` Alex Bennée
2018-10-09  6:28 ` Pavel Dovgalyuk
2018-10-09  8:31   ` Alex Bennée
2018-10-09  8:38     ` Pavel Dovgalyuk
2018-10-09  9:26       ` Alex Bennée
2018-10-29  7:46         ` Pavel Dovgalyuk
2018-10-29 11:30           ` Alex Bennée
2018-10-29 12:24             ` Pavel Dovgalyuk
2018-10-29 15:03               ` Alex Bennée

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20181005154910.3099-22-alex.bennee@linaro.org \
    --to=alex.bennee@linaro.org \
    --cc=Pavel.Dovgaluk@ispras.ru \
    --cc=cota@braap.org \
    --cc=qemu-devel@nongnu.org \
    --cc=stefanha@redhat.com \
    --cc=vilanova@ac.upc.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.