mirror of https://github.com/xemu-project/xemu.git
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:
parent
0a22777c71
commit
996e8d9a45
|
@ -24,5 +24,6 @@ void qsp_report(FILE *f, fprintf_function cpu_fprintf, size_t max,
|
||||||
bool qsp_is_enabled(void);
|
bool qsp_is_enabled(void);
|
||||||
void qsp_enable(void);
|
void qsp_enable(void);
|
||||||
void qsp_disable(void);
|
void qsp_disable(void);
|
||||||
|
void qsp_reset(void);
|
||||||
|
|
||||||
#endif /* QEMU_QSP_H */
|
#endif /* QEMU_QSP_H */
|
||||||
|
|
94
util/qsp.c
94
util/qsp.c
|
@ -47,6 +47,11 @@
|
||||||
* an intermediate hash table. This would simplify the code only slightly, but
|
* an intermediate hash table. This would simplify the code only slightly, but
|
||||||
* would perform badly if there were many threads and objects to track.
|
* 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:
|
* Related Work:
|
||||||
* - Lennart Poettering's mutrace: http://0pointer.de/blog/projects/mutrace.html
|
* - Lennart Poettering's mutrace: http://0pointer.de/blog/projects/mutrace.html
|
||||||
* - Lozi, David, Thomas, Lawall and Muller. "Remote Core Locking: Migrating
|
* - Lozi, David, Thomas, Lawall and Muller. "Remote Core Locking: Migrating
|
||||||
|
@ -57,6 +62,7 @@
|
||||||
#include "qemu/thread.h"
|
#include "qemu/thread.h"
|
||||||
#include "qemu/timer.h"
|
#include "qemu/timer.h"
|
||||||
#include "qemu/qht.h"
|
#include "qemu/qht.h"
|
||||||
|
#include "qemu/rcu.h"
|
||||||
#include "exec/tb-hash-xx.h"
|
#include "exec/tb-hash-xx.h"
|
||||||
|
|
||||||
enum QSPType {
|
enum QSPType {
|
||||||
|
@ -88,6 +94,12 @@ struct QSPEntry {
|
||||||
};
|
};
|
||||||
typedef struct QSPEntry QSPEntry;
|
typedef struct QSPEntry QSPEntry;
|
||||||
|
|
||||||
|
struct QSPSnapshot {
|
||||||
|
struct rcu_head rcu;
|
||||||
|
struct qht ht;
|
||||||
|
};
|
||||||
|
typedef struct QSPSnapshot QSPSnapshot;
|
||||||
|
|
||||||
/* initial sizing for hash tables */
|
/* initial sizing for hash tables */
|
||||||
#define QSP_INITIAL_SIZE 64
|
#define QSP_INITIAL_SIZE 64
|
||||||
|
|
||||||
|
@ -107,6 +119,7 @@ static __thread int qsp_thread;
|
||||||
static struct qht qsp_callsite_ht;
|
static struct qht qsp_callsite_ht;
|
||||||
|
|
||||||
static struct qht qsp_ht;
|
static struct qht qsp_ht;
|
||||||
|
static QSPSnapshot *qsp_snapshot;
|
||||||
static bool qsp_initialized, qsp_initializing;
|
static bool qsp_initialized, qsp_initializing;
|
||||||
|
|
||||||
static const char * const qsp_typenames[] = {
|
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);
|
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)
|
static void qsp_mktree(GTree *tree)
|
||||||
{
|
{
|
||||||
|
QSPSnapshot *snap;
|
||||||
struct qht ht;
|
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 */
|
/* Aggregate all results from the global hash table into a local one */
|
||||||
qht_init(&ht, qsp_entry_no_thread_cmp, QSP_INITIAL_SIZE,
|
qht_init(&ht, qsp_entry_no_thread_cmp, QSP_INITIAL_SIZE,
|
||||||
QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES);
|
QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES);
|
||||||
qht_iter(&qsp_ht, qsp_aggregate, &ht);
|
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 */
|
/* sort the hash table elements by using a tree */
|
||||||
qht_iter(&ht, qsp_sort, 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);
|
pr_report(&rep, f, cpu_fprintf);
|
||||||
report_destroy(&rep);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue