qsp: add qsp_reset

I first implemented this by deleting all entries in the global
hash table. But doing that safely slows down profiling, since
we'd need to introduce rcu_read_lock/unlock in the fast path.

What's implemented here avoids messing with the thread-local
data in the global hash table. It achieves this by taking a snapshot
of the current state, so that subsequent reports present the delta
wrt to the snapshot.

Signed-off-by: Emilio G. Cota <cota@braap.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Emilio G. Cota 2018-08-16 23:29:49 -04:00 committed by Paolo Bonzini
parent 0a22777c71
commit 996e8d9a45
2 changed files with 95 additions and 0 deletions

View File

@ -24,5 +24,6 @@ void qsp_report(FILE *f, fprintf_function cpu_fprintf, size_t max,
bool qsp_is_enabled(void);
void qsp_enable(void);
void qsp_disable(void);
void qsp_reset(void);
#endif /* QEMU_QSP_H */

View File

@ -47,6 +47,11 @@
* an intermediate hash table. This would simplify the code only slightly, but
* would perform badly if there were many threads and objects to track.
*
* - Wrap operations on qsp entries with RCU read-side critical sections, so
* that qsp_reset() can delete entries. Unfortunately, the overhead of calling
* rcu_read_lock/unlock slows down atomic_add-bench -m by 24%. Having
* a snapshot that is updated on qsp_reset() avoids this overhead.
*
* Related Work:
* - Lennart Poettering's mutrace: http://0pointer.de/blog/projects/mutrace.html
* - Lozi, David, Thomas, Lawall and Muller. "Remote Core Locking: Migrating
@ -57,6 +62,7 @@
#include "qemu/thread.h"
#include "qemu/timer.h"
#include "qemu/qht.h"
#include "qemu/rcu.h"
#include "exec/tb-hash-xx.h"
enum QSPType {
@ -88,6 +94,12 @@ struct QSPEntry {
};
typedef struct QSPEntry QSPEntry;
struct QSPSnapshot {
struct rcu_head rcu;
struct qht ht;
};
typedef struct QSPSnapshot QSPSnapshot;
/* initial sizing for hash tables */
#define QSP_INITIAL_SIZE 64
@ -107,6 +119,7 @@ static __thread int qsp_thread;
static struct qht qsp_callsite_ht;
static struct qht qsp_ht;
static QSPSnapshot *qsp_snapshot;
static bool qsp_initialized, qsp_initializing;
static const char * const qsp_typenames[] = {
@ -505,15 +518,69 @@ static void qsp_aggregate(struct qht *global_ht, void *p, uint32_t h, void *up)
qsp_entry_aggregate(agg, e);
}
static void qsp_iter_diff(struct qht *orig, void *p, uint32_t hash, void *htp)
{
struct qht *ht = htp;
QSPEntry *old = p;
QSPEntry *new;
new = qht_lookup(ht, old, hash);
/* entries are never deleted, so we must have this one */
g_assert(new != NULL);
/* our reading of the stats happened after the snapshot was taken */
g_assert(new->n_acqs >= old->n_acqs);
g_assert(new->ns >= old->ns);
new->n_acqs -= old->n_acqs;
new->ns -= old->ns;
/* No point in reporting an empty entry */
if (new->n_acqs == 0 && new->ns == 0) {
bool removed = qht_remove(ht, new, hash);
g_assert(removed);
g_free(new);
}
}
static void qsp_diff(struct qht *orig, struct qht *new)
{
qht_iter(orig, qsp_iter_diff, new);
}
static void qsp_ht_delete(struct qht *ht, void *p, uint32_t h, void *htp)
{
g_free(p);
}
static void qsp_mktree(GTree *tree)
{
QSPSnapshot *snap;
struct qht ht;
/*
* First, see if there's a prior snapshot, so that we read the global hash
* table _after_ the snapshot has been created, which guarantees that
* the entries we'll read will be a superset of the snapshot's entries.
*
* We must remain in an RCU read-side critical section until we're done
* with the snapshot.
*/
rcu_read_lock();
snap = atomic_rcu_read(&qsp_snapshot);
/* Aggregate all results from the global hash table into a local one */
qht_init(&ht, qsp_entry_no_thread_cmp, QSP_INITIAL_SIZE,
QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES);
qht_iter(&qsp_ht, qsp_aggregate, &ht);
/* compute the difference wrt the snapshot, if any */
if (snap) {
qsp_diff(&snap->ht, &ht);
}
/* done with the snapshot; RCU can reclaim it */
rcu_read_unlock();
/* sort the hash table elements by using a tree */
qht_iter(&ht, qsp_sort, tree);
@ -652,3 +719,30 @@ void qsp_report(FILE *f, fprintf_function cpu_fprintf, size_t max,
pr_report(&rep, f, cpu_fprintf);
report_destroy(&rep);
}
static void qsp_snapshot_destroy(QSPSnapshot *snap)
{
qht_iter(&snap->ht, qsp_ht_delete, NULL);
qht_destroy(&snap->ht);
g_free(snap);
}
void qsp_reset(void)
{
QSPSnapshot *new = g_new(QSPSnapshot, 1);
QSPSnapshot *old;
qsp_init();
qht_init(&new->ht, qsp_entry_cmp, QSP_INITIAL_SIZE,
QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES);
/* take a snapshot of the current state */
qht_iter(&qsp_ht, qsp_aggregate, &new->ht);
/* replace the previous snapshot, if any */
old = atomic_xchg(&qsp_snapshot, new);
if (old) {
call_rcu(old, qsp_snapshot_destroy, rcu);
}
}