xref: /openbmc/qemu/util/qsp.c (revision 996e8d9a)
1fe9959a2SEmilio G. Cota /*
2fe9959a2SEmilio G. Cota  * qsp.c - QEMU Synchronization Profiler
3fe9959a2SEmilio G. Cota  *
4fe9959a2SEmilio G. Cota  * Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
5fe9959a2SEmilio G. Cota  *
6fe9959a2SEmilio G. Cota  * License: GNU GPL, version 2 or later.
7fe9959a2SEmilio G. Cota  *   See the COPYING file in the top-level directory.
8fe9959a2SEmilio G. Cota  *
9fe9959a2SEmilio G. Cota  * QSP profiles the time spent in synchronization primitives, which can
10fe9959a2SEmilio G. Cota  * help diagnose performance problems, e.g. scalability issues when
11fe9959a2SEmilio G. Cota  * contention is high.
12fe9959a2SEmilio G. Cota  *
13fe9959a2SEmilio G. Cota  * The primitives currently supported are mutexes, recursive mutexes and
14fe9959a2SEmilio G. Cota  * condition variables. Note that not all related functions are intercepted;
15fe9959a2SEmilio G. Cota  * instead we profile only those functions that can have a performance impact,
16fe9959a2SEmilio G. Cota  * either due to blocking (e.g. cond_wait, mutex_lock) or cache line
17fe9959a2SEmilio G. Cota  * contention (e.g. mutex_lock, mutex_trylock).
18fe9959a2SEmilio G. Cota  *
19fe9959a2SEmilio G. Cota  * QSP's design focuses on speed and scalability. This is achieved
20fe9959a2SEmilio G. Cota  * by having threads do their profiling entirely on thread-local data.
21fe9959a2SEmilio G. Cota  * The appropriate thread-local data is found via a QHT, i.e. a concurrent hash
22fe9959a2SEmilio G. Cota  * table. To aggregate data in order to generate a report, we iterate over
23fe9959a2SEmilio G. Cota  * all entries in the hash table. Depending on the number of threads and
24fe9959a2SEmilio G. Cota  * synchronization objects this might be expensive, but note that it is
25fe9959a2SEmilio G. Cota  * very rarely called -- reports are generated only when requested by users.
26fe9959a2SEmilio G. Cota  *
27fe9959a2SEmilio G. Cota  * Reports are generated as a table where each row represents a call site. A
28fe9959a2SEmilio G. Cota  * call site is the triplet formed by the __file__ and __LINE__ of the caller
29fe9959a2SEmilio G. Cota  * as well as the address of the "object" (i.e. mutex, rec. mutex or condvar)
30fe9959a2SEmilio G. Cota  * being operated on. Focusing on call sites instead of just on objects might
31fe9959a2SEmilio G. Cota  * seem puzzling. However, it is a sensible choice since otherwise dealing with
32fe9959a2SEmilio G. Cota  * dynamically-allocated objects becomes difficult (e.g. what to do when an
33fe9959a2SEmilio G. Cota  * object is destroyed, or reused?). Furthermore, the call site info is of most
34fe9959a2SEmilio G. Cota  * importance, since it is callers, and not objects, what cause wait time.
35fe9959a2SEmilio G. Cota  *
36fe9959a2SEmilio G. Cota  * Alternative designs considered:
37fe9959a2SEmilio G. Cota  *
38fe9959a2SEmilio G. Cota  * - Use an off-the-shelf profiler such as mutrace. This is not a viable option
39fe9959a2SEmilio G. Cota  *   for us because QEMU has __malloc_hook set (by one of the libraries it
40fe9959a2SEmilio G. Cota  *   uses); leaving this hook unset is required to avoid deadlock in mutrace.
41fe9959a2SEmilio G. Cota  *
42fe9959a2SEmilio G. Cota  * - Use a glib HT for each thread, protecting each HT with its own lock.
43fe9959a2SEmilio G. Cota  *   This isn't simpler than the current design, and is 10% slower in the
44fe9959a2SEmilio G. Cota  *   atomic_add-bench microbenchmark (-m option).
45fe9959a2SEmilio G. Cota  *
46fe9959a2SEmilio G. Cota  * - For reports, just use a binary tree as we aggregate data, instead of having
47fe9959a2SEmilio G. Cota  *   an intermediate hash table. This would simplify the code only slightly, but
48fe9959a2SEmilio G. Cota  *   would perform badly if there were many threads and objects to track.
49fe9959a2SEmilio G. Cota  *
50*996e8d9aSEmilio G. Cota  * - Wrap operations on qsp entries with RCU read-side critical sections, so
51*996e8d9aSEmilio G. Cota  *   that qsp_reset() can delete entries. Unfortunately, the overhead of calling
52*996e8d9aSEmilio G. Cota  *   rcu_read_lock/unlock slows down atomic_add-bench -m by 24%. Having
53*996e8d9aSEmilio G. Cota  *   a snapshot that is updated on qsp_reset() avoids this overhead.
54*996e8d9aSEmilio G. Cota  *
55fe9959a2SEmilio G. Cota  * Related Work:
56fe9959a2SEmilio G. Cota  * - Lennart Poettering's mutrace: http://0pointer.de/blog/projects/mutrace.html
57fe9959a2SEmilio G. Cota  * - Lozi, David, Thomas, Lawall and Muller. "Remote Core Locking: Migrating
58fe9959a2SEmilio G. Cota  *   Critical-Section Execution to Improve the Performance of Multithreaded
59fe9959a2SEmilio G. Cota  *   Applications", USENIX ATC'12.
60fe9959a2SEmilio G. Cota  */
61fe9959a2SEmilio G. Cota #include "qemu/osdep.h"
62fe9959a2SEmilio G. Cota #include "qemu/thread.h"
63fe9959a2SEmilio G. Cota #include "qemu/timer.h"
64fe9959a2SEmilio G. Cota #include "qemu/qht.h"
65*996e8d9aSEmilio G. Cota #include "qemu/rcu.h"
66fe9959a2SEmilio G. Cota #include "exec/tb-hash-xx.h"
67fe9959a2SEmilio G. Cota 
68fe9959a2SEmilio G. Cota enum QSPType {
69fe9959a2SEmilio G. Cota     QSP_MUTEX,
70fe9959a2SEmilio G. Cota     QSP_REC_MUTEX,
71fe9959a2SEmilio G. Cota     QSP_CONDVAR,
72fe9959a2SEmilio G. Cota };
73fe9959a2SEmilio G. Cota 
74fe9959a2SEmilio G. Cota struct QSPCallSite {
75fe9959a2SEmilio G. Cota     const void *obj;
76fe9959a2SEmilio G. Cota     const char *file; /* i.e. __FILE__; shortened later */
77fe9959a2SEmilio G. Cota     int line;
78fe9959a2SEmilio G. Cota     enum QSPType type;
79fe9959a2SEmilio G. Cota };
80fe9959a2SEmilio G. Cota typedef struct QSPCallSite QSPCallSite;
81fe9959a2SEmilio G. Cota 
82fe9959a2SEmilio G. Cota struct QSPEntry {
83fe9959a2SEmilio G. Cota     void *thread_ptr;
84fe9959a2SEmilio G. Cota     const QSPCallSite *callsite;
85fe9959a2SEmilio G. Cota     uint64_t n_acqs;
86fe9959a2SEmilio G. Cota     uint64_t ns;
87fe9959a2SEmilio G. Cota #ifndef CONFIG_ATOMIC64
88fe9959a2SEmilio G. Cota     /*
89fe9959a2SEmilio G. Cota      * If we cannot update the counts atomically, then use a seqlock.
90fe9959a2SEmilio G. Cota      * We don't need an associated lock because the updates are thread-local.
91fe9959a2SEmilio G. Cota      */
92fe9959a2SEmilio G. Cota     QemuSeqLock sequence;
93fe9959a2SEmilio G. Cota #endif
94fe9959a2SEmilio G. Cota };
95fe9959a2SEmilio G. Cota typedef struct QSPEntry QSPEntry;
96fe9959a2SEmilio G. Cota 
97*996e8d9aSEmilio G. Cota struct QSPSnapshot {
98*996e8d9aSEmilio G. Cota     struct rcu_head rcu;
99*996e8d9aSEmilio G. Cota     struct qht ht;
100*996e8d9aSEmilio G. Cota };
101*996e8d9aSEmilio G. Cota typedef struct QSPSnapshot QSPSnapshot;
102*996e8d9aSEmilio G. Cota 
103fe9959a2SEmilio G. Cota /* initial sizing for hash tables */
104fe9959a2SEmilio G. Cota #define QSP_INITIAL_SIZE 64
105fe9959a2SEmilio G. Cota 
106fe9959a2SEmilio G. Cota /* If this file is moved, QSP_REL_PATH should be updated accordingly */
107fe9959a2SEmilio G. Cota #define QSP_REL_PATH "util/qsp.c"
108fe9959a2SEmilio G. Cota 
109fe9959a2SEmilio G. Cota /* this file's full path. Used to present all call sites with relative paths */
110fe9959a2SEmilio G. Cota static size_t qsp_qemu_path_len;
111fe9959a2SEmilio G. Cota 
112fe9959a2SEmilio G. Cota /* the address of qsp_thread gives us a unique 'thread ID' */
113fe9959a2SEmilio G. Cota static __thread int qsp_thread;
114fe9959a2SEmilio G. Cota 
115fe9959a2SEmilio G. Cota /*
116fe9959a2SEmilio G. Cota  * Call sites are the same for all threads, so we track them in a separate hash
117fe9959a2SEmilio G. Cota  * table to save memory.
118fe9959a2SEmilio G. Cota  */
119fe9959a2SEmilio G. Cota static struct qht qsp_callsite_ht;
120fe9959a2SEmilio G. Cota 
121fe9959a2SEmilio G. Cota static struct qht qsp_ht;
122*996e8d9aSEmilio G. Cota static QSPSnapshot *qsp_snapshot;
123fe9959a2SEmilio G. Cota static bool qsp_initialized, qsp_initializing;
124fe9959a2SEmilio G. Cota 
125fe9959a2SEmilio G. Cota static const char * const qsp_typenames[] = {
126fe9959a2SEmilio G. Cota     [QSP_MUTEX]     = "mutex",
127fe9959a2SEmilio G. Cota     [QSP_REC_MUTEX] = "rec_mutex",
128fe9959a2SEmilio G. Cota     [QSP_CONDVAR]   = "condvar",
129fe9959a2SEmilio G. Cota };
130fe9959a2SEmilio G. Cota 
131fe9959a2SEmilio G. Cota QemuMutexLockFunc qemu_mutex_lock_func = qemu_mutex_lock_impl;
132fe9959a2SEmilio G. Cota QemuMutexTrylockFunc qemu_mutex_trylock_func = qemu_mutex_trylock_impl;
133fe9959a2SEmilio G. Cota QemuRecMutexLockFunc qemu_rec_mutex_lock_func = qemu_rec_mutex_lock_impl;
134fe9959a2SEmilio G. Cota QemuRecMutexTrylockFunc qemu_rec_mutex_trylock_func =
135fe9959a2SEmilio G. Cota     qemu_rec_mutex_trylock_impl;
136fe9959a2SEmilio G. Cota QemuCondWaitFunc qemu_cond_wait_func = qemu_cond_wait_impl;
137fe9959a2SEmilio G. Cota 
138fe9959a2SEmilio G. Cota /*
139fe9959a2SEmilio G. Cota  * It pays off to _not_ hash callsite->file; hashing a string is slow, and
140fe9959a2SEmilio G. Cota  * without it we still get a pretty unique hash.
141fe9959a2SEmilio G. Cota  */
142fe9959a2SEmilio G. Cota static inline
143fe9959a2SEmilio G. Cota uint32_t do_qsp_callsite_hash(const QSPCallSite *callsite, uint64_t a)
144fe9959a2SEmilio G. Cota {
145fe9959a2SEmilio G. Cota     uint64_t b = (uint64_t)(uintptr_t)callsite->obj;
146fe9959a2SEmilio G. Cota     uint32_t e = callsite->line;
147fe9959a2SEmilio G. Cota     uint32_t f = callsite->type;
148fe9959a2SEmilio G. Cota 
149fe9959a2SEmilio G. Cota     return tb_hash_func7(a, b, e, f, 0);
150fe9959a2SEmilio G. Cota }
151fe9959a2SEmilio G. Cota 
152fe9959a2SEmilio G. Cota static inline
153fe9959a2SEmilio G. Cota uint32_t qsp_callsite_hash(const QSPCallSite *callsite)
154fe9959a2SEmilio G. Cota {
155fe9959a2SEmilio G. Cota     return do_qsp_callsite_hash(callsite, 0);
156fe9959a2SEmilio G. Cota }
157fe9959a2SEmilio G. Cota 
158fe9959a2SEmilio G. Cota static inline uint32_t do_qsp_entry_hash(const QSPEntry *entry, uint64_t a)
159fe9959a2SEmilio G. Cota {
160fe9959a2SEmilio G. Cota     return do_qsp_callsite_hash(entry->callsite, a);
161fe9959a2SEmilio G. Cota }
162fe9959a2SEmilio G. Cota 
163fe9959a2SEmilio G. Cota static uint32_t qsp_entry_hash(const QSPEntry *entry)
164fe9959a2SEmilio G. Cota {
165fe9959a2SEmilio G. Cota     return do_qsp_entry_hash(entry, (uint64_t)(uintptr_t)entry->thread_ptr);
166fe9959a2SEmilio G. Cota }
167fe9959a2SEmilio G. Cota 
168fe9959a2SEmilio G. Cota static uint32_t qsp_entry_no_thread_hash(const QSPEntry *entry)
169fe9959a2SEmilio G. Cota {
170fe9959a2SEmilio G. Cota     return do_qsp_entry_hash(entry, 0);
171fe9959a2SEmilio G. Cota }
172fe9959a2SEmilio G. Cota 
173fe9959a2SEmilio G. Cota static bool qsp_callsite_cmp(const void *ap, const void *bp)
174fe9959a2SEmilio G. Cota {
175fe9959a2SEmilio G. Cota     const QSPCallSite *a = ap;
176fe9959a2SEmilio G. Cota     const QSPCallSite *b = bp;
177fe9959a2SEmilio G. Cota 
178fe9959a2SEmilio G. Cota     return a == b ||
179fe9959a2SEmilio G. Cota         (a->obj == b->obj &&
180fe9959a2SEmilio G. Cota          a->line == b->line &&
181fe9959a2SEmilio G. Cota          a->type == b->type &&
182fe9959a2SEmilio G. Cota          (a->file == b->file || !strcmp(a->file, b->file)));
183fe9959a2SEmilio G. Cota }
184fe9959a2SEmilio G. Cota 
185fe9959a2SEmilio G. Cota static bool qsp_entry_no_thread_cmp(const void *ap, const void *bp)
186fe9959a2SEmilio G. Cota {
187fe9959a2SEmilio G. Cota     const QSPEntry *a = ap;
188fe9959a2SEmilio G. Cota     const QSPEntry *b = bp;
189fe9959a2SEmilio G. Cota 
190fe9959a2SEmilio G. Cota     return qsp_callsite_cmp(a->callsite, b->callsite);
191fe9959a2SEmilio G. Cota }
192fe9959a2SEmilio G. Cota 
193fe9959a2SEmilio G. Cota static bool qsp_entry_cmp(const void *ap, const void *bp)
194fe9959a2SEmilio G. Cota {
195fe9959a2SEmilio G. Cota     const QSPEntry *a = ap;
196fe9959a2SEmilio G. Cota     const QSPEntry *b = bp;
197fe9959a2SEmilio G. Cota 
198fe9959a2SEmilio G. Cota     return a->thread_ptr == b->thread_ptr &&
199fe9959a2SEmilio G. Cota         qsp_callsite_cmp(a->callsite, b->callsite);
200fe9959a2SEmilio G. Cota }
201fe9959a2SEmilio G. Cota 
202fe9959a2SEmilio G. Cota /*
203fe9959a2SEmilio G. Cota  * Normally we'd call this from a constructor function, but we want it to work
204fe9959a2SEmilio G. Cota  * via libutil as well.
205fe9959a2SEmilio G. Cota  */
206fe9959a2SEmilio G. Cota static void qsp_do_init(void)
207fe9959a2SEmilio G. Cota {
208fe9959a2SEmilio G. Cota     /* make sure this file's path in the tree is up to date with QSP_REL_PATH */
209fe9959a2SEmilio G. Cota     g_assert(strstr(__FILE__, QSP_REL_PATH));
210fe9959a2SEmilio G. Cota     qsp_qemu_path_len = strlen(__FILE__) - strlen(QSP_REL_PATH);
211fe9959a2SEmilio G. Cota 
212fe9959a2SEmilio G. Cota     qht_init(&qsp_ht, qsp_entry_cmp, QSP_INITIAL_SIZE,
213fe9959a2SEmilio G. Cota              QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES);
214fe9959a2SEmilio G. Cota     qht_init(&qsp_callsite_ht, qsp_callsite_cmp, QSP_INITIAL_SIZE,
215fe9959a2SEmilio G. Cota              QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES);
216fe9959a2SEmilio G. Cota }
217fe9959a2SEmilio G. Cota 
218fe9959a2SEmilio G. Cota static __attribute__((noinline)) void qsp_init__slowpath(void)
219fe9959a2SEmilio G. Cota {
220fe9959a2SEmilio G. Cota     if (atomic_cmpxchg(&qsp_initializing, false, true) == false) {
221fe9959a2SEmilio G. Cota         qsp_do_init();
222fe9959a2SEmilio G. Cota         atomic_set(&qsp_initialized, true);
223fe9959a2SEmilio G. Cota     } else {
224fe9959a2SEmilio G. Cota         while (!atomic_read(&qsp_initialized)) {
225fe9959a2SEmilio G. Cota             cpu_relax();
226fe9959a2SEmilio G. Cota         }
227fe9959a2SEmilio G. Cota     }
228fe9959a2SEmilio G. Cota }
229fe9959a2SEmilio G. Cota 
230fe9959a2SEmilio G. Cota /* qsp_init() must be called from _all_ exported functions */
231fe9959a2SEmilio G. Cota static inline void qsp_init(void)
232fe9959a2SEmilio G. Cota {
233fe9959a2SEmilio G. Cota     if (likely(atomic_read(&qsp_initialized))) {
234fe9959a2SEmilio G. Cota         return;
235fe9959a2SEmilio G. Cota     }
236fe9959a2SEmilio G. Cota     qsp_init__slowpath();
237fe9959a2SEmilio G. Cota }
238fe9959a2SEmilio G. Cota 
239fe9959a2SEmilio G. Cota static QSPCallSite *qsp_callsite_find(const QSPCallSite *orig)
240fe9959a2SEmilio G. Cota {
241fe9959a2SEmilio G. Cota     QSPCallSite *callsite;
242fe9959a2SEmilio G. Cota     uint32_t hash;
243fe9959a2SEmilio G. Cota 
244fe9959a2SEmilio G. Cota     hash = qsp_callsite_hash(orig);
245fe9959a2SEmilio G. Cota     callsite = qht_lookup(&qsp_callsite_ht, orig, hash);
246fe9959a2SEmilio G. Cota     if (callsite == NULL) {
247fe9959a2SEmilio G. Cota         void *existing = NULL;
248fe9959a2SEmilio G. Cota 
249fe9959a2SEmilio G. Cota         callsite = g_new(QSPCallSite, 1);
250fe9959a2SEmilio G. Cota         memcpy(callsite, orig, sizeof(*callsite));
251fe9959a2SEmilio G. Cota         qht_insert(&qsp_callsite_ht, callsite, hash, &existing);
252fe9959a2SEmilio G. Cota         if (unlikely(existing)) {
253fe9959a2SEmilio G. Cota             g_free(callsite);
254fe9959a2SEmilio G. Cota             callsite = existing;
255fe9959a2SEmilio G. Cota         }
256fe9959a2SEmilio G. Cota     }
257fe9959a2SEmilio G. Cota     return callsite;
258fe9959a2SEmilio G. Cota }
259fe9959a2SEmilio G. Cota 
260fe9959a2SEmilio G. Cota static QSPEntry *
261fe9959a2SEmilio G. Cota qsp_entry_create(struct qht *ht, const QSPEntry *entry, uint32_t hash)
262fe9959a2SEmilio G. Cota {
263fe9959a2SEmilio G. Cota     QSPEntry *e;
264fe9959a2SEmilio G. Cota     void *existing = NULL;
265fe9959a2SEmilio G. Cota 
266fe9959a2SEmilio G. Cota     e = g_new0(QSPEntry, 1);
267fe9959a2SEmilio G. Cota     e->thread_ptr = entry->thread_ptr;
268fe9959a2SEmilio G. Cota     e->callsite = qsp_callsite_find(entry->callsite);
269fe9959a2SEmilio G. Cota 
270fe9959a2SEmilio G. Cota     qht_insert(ht, e, hash, &existing);
271fe9959a2SEmilio G. Cota     if (unlikely(existing)) {
272fe9959a2SEmilio G. Cota         g_free(e);
273fe9959a2SEmilio G. Cota         e = existing;
274fe9959a2SEmilio G. Cota     }
275fe9959a2SEmilio G. Cota     return e;
276fe9959a2SEmilio G. Cota }
277fe9959a2SEmilio G. Cota 
278fe9959a2SEmilio G. Cota static QSPEntry *
279fe9959a2SEmilio G. Cota qsp_entry_find(struct qht *ht, const QSPEntry *entry, uint32_t hash)
280fe9959a2SEmilio G. Cota {
281fe9959a2SEmilio G. Cota     QSPEntry *e;
282fe9959a2SEmilio G. Cota 
283fe9959a2SEmilio G. Cota     e = qht_lookup(ht, entry, hash);
284fe9959a2SEmilio G. Cota     if (e == NULL) {
285fe9959a2SEmilio G. Cota         e = qsp_entry_create(ht, entry, hash);
286fe9959a2SEmilio G. Cota     }
287fe9959a2SEmilio G. Cota     return e;
288fe9959a2SEmilio G. Cota }
289fe9959a2SEmilio G. Cota 
290fe9959a2SEmilio G. Cota /*
291fe9959a2SEmilio G. Cota  * Note: Entries are never removed, so callers do not have to be in an RCU
292fe9959a2SEmilio G. Cota  * read-side critical section.
293fe9959a2SEmilio G. Cota  */
294fe9959a2SEmilio G. Cota static QSPEntry *qsp_entry_get(const void *obj, const char *file, int line,
295fe9959a2SEmilio G. Cota                                enum QSPType type)
296fe9959a2SEmilio G. Cota {
297fe9959a2SEmilio G. Cota     QSPCallSite callsite = {
298fe9959a2SEmilio G. Cota         .obj = obj,
299fe9959a2SEmilio G. Cota         .file = file,
300fe9959a2SEmilio G. Cota         .line = line,
301fe9959a2SEmilio G. Cota         .type = type,
302fe9959a2SEmilio G. Cota     };
303fe9959a2SEmilio G. Cota     QSPEntry orig;
304fe9959a2SEmilio G. Cota     uint32_t hash;
305fe9959a2SEmilio G. Cota 
306fe9959a2SEmilio G. Cota     qsp_init();
307fe9959a2SEmilio G. Cota 
308fe9959a2SEmilio G. Cota     orig.thread_ptr = &qsp_thread;
309fe9959a2SEmilio G. Cota     orig.callsite = &callsite;
310fe9959a2SEmilio G. Cota 
311fe9959a2SEmilio G. Cota     hash = qsp_entry_hash(&orig);
312fe9959a2SEmilio G. Cota     return qsp_entry_find(&qsp_ht, &orig, hash);
313fe9959a2SEmilio G. Cota }
314fe9959a2SEmilio G. Cota 
315fe9959a2SEmilio G. Cota /*
316fe9959a2SEmilio G. Cota  * @from is in the global hash table; read it atomically if the host
317fe9959a2SEmilio G. Cota  * supports it, otherwise use the seqlock.
318fe9959a2SEmilio G. Cota  */
319fe9959a2SEmilio G. Cota static void qsp_entry_aggregate(QSPEntry *to, const QSPEntry *from)
320fe9959a2SEmilio G. Cota {
321fe9959a2SEmilio G. Cota #ifdef CONFIG_ATOMIC64
322fe9959a2SEmilio G. Cota     to->ns += atomic_read__nocheck(&from->ns);
323fe9959a2SEmilio G. Cota     to->n_acqs += atomic_read__nocheck(&from->n_acqs);
324fe9959a2SEmilio G. Cota #else
325fe9959a2SEmilio G. Cota     unsigned int version;
326fe9959a2SEmilio G. Cota     uint64_t ns, n_acqs;
327fe9959a2SEmilio G. Cota 
328fe9959a2SEmilio G. Cota     do {
329fe9959a2SEmilio G. Cota         version = seqlock_read_begin(&from->sequence);
330fe9959a2SEmilio G. Cota         ns = atomic_read__nocheck(&from->ns);
331fe9959a2SEmilio G. Cota         n_acqs = atomic_read__nocheck(&from->n_acqs);
332fe9959a2SEmilio G. Cota     } while (seqlock_read_retry(&from->sequence, version));
333fe9959a2SEmilio G. Cota 
334fe9959a2SEmilio G. Cota     to->ns += ns;
335fe9959a2SEmilio G. Cota     to->n_acqs += n_acqs;
336fe9959a2SEmilio G. Cota #endif
337fe9959a2SEmilio G. Cota }
338fe9959a2SEmilio G. Cota 
339fe9959a2SEmilio G. Cota /*
340fe9959a2SEmilio G. Cota  * @e is in the global hash table; it is only written to by the current thread,
341fe9959a2SEmilio G. Cota  * so we write to it atomically (as in "write once") to prevent torn reads.
342fe9959a2SEmilio G. Cota  * If the host doesn't support u64 atomics, use the seqlock.
343fe9959a2SEmilio G. Cota  */
344fe9959a2SEmilio G. Cota static inline void do_qsp_entry_record(QSPEntry *e, int64_t delta, bool acq)
345fe9959a2SEmilio G. Cota {
346fe9959a2SEmilio G. Cota #ifndef CONFIG_ATOMIC64
347fe9959a2SEmilio G. Cota     seqlock_write_begin(&e->sequence);
348fe9959a2SEmilio G. Cota #endif
349fe9959a2SEmilio G. Cota     atomic_set__nocheck(&e->ns, e->ns + delta);
350fe9959a2SEmilio G. Cota     if (acq) {
351fe9959a2SEmilio G. Cota         atomic_set__nocheck(&e->n_acqs, e->n_acqs + 1);
352fe9959a2SEmilio G. Cota     }
353fe9959a2SEmilio G. Cota #ifndef CONFIG_ATOMIC64
354fe9959a2SEmilio G. Cota     seqlock_write_end(&e->sequence);
355fe9959a2SEmilio G. Cota #endif
356fe9959a2SEmilio G. Cota }
357fe9959a2SEmilio G. Cota 
358fe9959a2SEmilio G. Cota static inline void qsp_entry_record(QSPEntry *e, int64_t delta)
359fe9959a2SEmilio G. Cota {
360fe9959a2SEmilio G. Cota     do_qsp_entry_record(e, delta, true);
361fe9959a2SEmilio G. Cota }
362fe9959a2SEmilio G. Cota 
363fe9959a2SEmilio G. Cota #define QSP_GEN_VOID(type_, qsp_t_, func_, impl_)                       \
364fe9959a2SEmilio G. Cota     static void func_(type_ *obj, const char *file, int line)           \
365fe9959a2SEmilio G. Cota     {                                                                   \
366fe9959a2SEmilio G. Cota         QSPEntry *e;                                                    \
367fe9959a2SEmilio G. Cota         int64_t t0, t1;                                                 \
368fe9959a2SEmilio G. Cota                                                                         \
369fe9959a2SEmilio G. Cota         t0 = get_clock();                                               \
370fe9959a2SEmilio G. Cota         impl_(obj, file, line);                                         \
371fe9959a2SEmilio G. Cota         t1 = get_clock();                                               \
372fe9959a2SEmilio G. Cota                                                                         \
373fe9959a2SEmilio G. Cota         e = qsp_entry_get(obj, file, line, qsp_t_);                     \
374fe9959a2SEmilio G. Cota         qsp_entry_record(e, t1 - t0);                                   \
375fe9959a2SEmilio G. Cota     }
376fe9959a2SEmilio G. Cota 
377fe9959a2SEmilio G. Cota #define QSP_GEN_RET1(type_, qsp_t_, func_, impl_)                       \
378fe9959a2SEmilio G. Cota     static int func_(type_ *obj, const char *file, int line)            \
379fe9959a2SEmilio G. Cota     {                                                                   \
380fe9959a2SEmilio G. Cota         QSPEntry *e;                                                    \
381fe9959a2SEmilio G. Cota         int64_t t0, t1;                                                 \
382fe9959a2SEmilio G. Cota         int err;                                                        \
383fe9959a2SEmilio G. Cota                                                                         \
384fe9959a2SEmilio G. Cota         t0 = get_clock();                                               \
385fe9959a2SEmilio G. Cota         err = impl_(obj, file, line);                                   \
386fe9959a2SEmilio G. Cota         t1 = get_clock();                                               \
387fe9959a2SEmilio G. Cota                                                                         \
388fe9959a2SEmilio G. Cota         e = qsp_entry_get(obj, file, line, qsp_t_);                     \
389fe9959a2SEmilio G. Cota         do_qsp_entry_record(e, t1 - t0, !err);                          \
390fe9959a2SEmilio G. Cota         return err;                                                     \
391fe9959a2SEmilio G. Cota     }
392fe9959a2SEmilio G. Cota 
393fe9959a2SEmilio G. Cota QSP_GEN_VOID(QemuMutex, QSP_MUTEX, qsp_mutex_lock, qemu_mutex_lock_impl)
394fe9959a2SEmilio G. Cota QSP_GEN_RET1(QemuMutex, QSP_MUTEX, qsp_mutex_trylock, qemu_mutex_trylock_impl)
395fe9959a2SEmilio G. Cota 
396fe9959a2SEmilio G. Cota QSP_GEN_VOID(QemuRecMutex, QSP_REC_MUTEX, qsp_rec_mutex_lock,
397fe9959a2SEmilio G. Cota              qemu_rec_mutex_lock_impl)
398fe9959a2SEmilio G. Cota QSP_GEN_RET1(QemuRecMutex, QSP_REC_MUTEX, qsp_rec_mutex_trylock,
399fe9959a2SEmilio G. Cota              qemu_rec_mutex_trylock_impl)
400fe9959a2SEmilio G. Cota 
401fe9959a2SEmilio G. Cota #undef QSP_GEN_RET1
402fe9959a2SEmilio G. Cota #undef QSP_GEN_VOID
403fe9959a2SEmilio G. Cota 
404fe9959a2SEmilio G. Cota static void
405fe9959a2SEmilio G. Cota qsp_cond_wait(QemuCond *cond, QemuMutex *mutex, const char *file, int line)
406fe9959a2SEmilio G. Cota {
407fe9959a2SEmilio G. Cota     QSPEntry *e;
408fe9959a2SEmilio G. Cota     int64_t t0, t1;
409fe9959a2SEmilio G. Cota 
410fe9959a2SEmilio G. Cota     t0 = get_clock();
411fe9959a2SEmilio G. Cota     qemu_cond_wait_impl(cond, mutex, file, line);
412fe9959a2SEmilio G. Cota     t1 = get_clock();
413fe9959a2SEmilio G. Cota 
414fe9959a2SEmilio G. Cota     e = qsp_entry_get(cond, file, line, QSP_CONDVAR);
415fe9959a2SEmilio G. Cota     qsp_entry_record(e, t1 - t0);
416fe9959a2SEmilio G. Cota }
417fe9959a2SEmilio G. Cota 
418fe9959a2SEmilio G. Cota bool qsp_is_enabled(void)
419fe9959a2SEmilio G. Cota {
420fe9959a2SEmilio G. Cota     return atomic_read(&qemu_mutex_lock_func) == qsp_mutex_lock;
421fe9959a2SEmilio G. Cota }
422fe9959a2SEmilio G. Cota 
423fe9959a2SEmilio G. Cota void qsp_enable(void)
424fe9959a2SEmilio G. Cota {
425fe9959a2SEmilio G. Cota     atomic_set(&qemu_mutex_lock_func, qsp_mutex_lock);
426fe9959a2SEmilio G. Cota     atomic_set(&qemu_mutex_trylock_func, qsp_mutex_trylock);
427fe9959a2SEmilio G. Cota     atomic_set(&qemu_rec_mutex_lock_func, qsp_rec_mutex_lock);
428fe9959a2SEmilio G. Cota     atomic_set(&qemu_rec_mutex_trylock_func, qsp_rec_mutex_trylock);
429fe9959a2SEmilio G. Cota     atomic_set(&qemu_cond_wait_func, qsp_cond_wait);
430fe9959a2SEmilio G. Cota }
431fe9959a2SEmilio G. Cota 
432fe9959a2SEmilio G. Cota void qsp_disable(void)
433fe9959a2SEmilio G. Cota {
434fe9959a2SEmilio G. Cota     atomic_set(&qemu_mutex_lock_func, qemu_mutex_lock_impl);
435fe9959a2SEmilio G. Cota     atomic_set(&qemu_mutex_trylock_func, qemu_mutex_trylock_impl);
436fe9959a2SEmilio G. Cota     atomic_set(&qemu_rec_mutex_lock_func, qemu_rec_mutex_lock_impl);
437fe9959a2SEmilio G. Cota     atomic_set(&qemu_rec_mutex_trylock_func, qemu_rec_mutex_trylock_impl);
438fe9959a2SEmilio G. Cota     atomic_set(&qemu_cond_wait_func, qemu_cond_wait_impl);
439fe9959a2SEmilio G. Cota }
440fe9959a2SEmilio G. Cota 
441fe9959a2SEmilio G. Cota static gint qsp_tree_cmp(gconstpointer ap, gconstpointer bp, gpointer up)
442fe9959a2SEmilio G. Cota {
443fe9959a2SEmilio G. Cota     const QSPEntry *a = ap;
444fe9959a2SEmilio G. Cota     const QSPEntry *b = bp;
4450a22777cSEmilio G. Cota     enum QSPSortBy sort_by = *(enum QSPSortBy *)up;
446fe9959a2SEmilio G. Cota     const QSPCallSite *ca;
447fe9959a2SEmilio G. Cota     const QSPCallSite *cb;
448fe9959a2SEmilio G. Cota 
4490a22777cSEmilio G. Cota     switch (sort_by) {
4500a22777cSEmilio G. Cota     case QSP_SORT_BY_TOTAL_WAIT_TIME:
451fe9959a2SEmilio G. Cota         if (a->ns > b->ns) {
452fe9959a2SEmilio G. Cota             return -1;
453fe9959a2SEmilio G. Cota         } else if (a->ns < b->ns) {
454fe9959a2SEmilio G. Cota             return 1;
455fe9959a2SEmilio G. Cota         }
4560a22777cSEmilio G. Cota         break;
4570a22777cSEmilio G. Cota     case QSP_SORT_BY_AVG_WAIT_TIME:
4580a22777cSEmilio G. Cota     {
4590a22777cSEmilio G. Cota         double avg_a = a->n_acqs ? a->ns / a->n_acqs : 0;
4600a22777cSEmilio G. Cota         double avg_b = b->n_acqs ? b->ns / b->n_acqs : 0;
4610a22777cSEmilio G. Cota 
4620a22777cSEmilio G. Cota         if (avg_a > avg_b) {
4630a22777cSEmilio G. Cota             return -1;
4640a22777cSEmilio G. Cota         } else if (avg_a < avg_b) {
4650a22777cSEmilio G. Cota             return 1;
4660a22777cSEmilio G. Cota         }
4670a22777cSEmilio G. Cota         break;
4680a22777cSEmilio G. Cota     }
4690a22777cSEmilio G. Cota     default:
4700a22777cSEmilio G. Cota         g_assert_not_reached();
4710a22777cSEmilio G. Cota     }
4720a22777cSEmilio G. Cota 
473fe9959a2SEmilio G. Cota     ca = a->callsite;
474fe9959a2SEmilio G. Cota     cb = b->callsite;
475fe9959a2SEmilio G. Cota     /* Break the tie with the object's address */
476fe9959a2SEmilio G. Cota     if (ca->obj < cb->obj) {
477fe9959a2SEmilio G. Cota         return -1;
478fe9959a2SEmilio G. Cota     } else if (ca->obj > cb->obj) {
479fe9959a2SEmilio G. Cota         return 1;
480fe9959a2SEmilio G. Cota     } else {
481fe9959a2SEmilio G. Cota         int cmp;
482fe9959a2SEmilio G. Cota 
483fe9959a2SEmilio G. Cota         /* same obj. Break the tie with the callsite's file */
484fe9959a2SEmilio G. Cota         cmp = strcmp(ca->file, cb->file);
485fe9959a2SEmilio G. Cota         if (cmp) {
486fe9959a2SEmilio G. Cota             return cmp;
487fe9959a2SEmilio G. Cota         }
488fe9959a2SEmilio G. Cota         /* same callsite file. Break the tie with the callsite's line */
489fe9959a2SEmilio G. Cota         g_assert(ca->line != cb->line);
490fe9959a2SEmilio G. Cota         if (ca->line < cb->line) {
491fe9959a2SEmilio G. Cota             return -1;
492fe9959a2SEmilio G. Cota         } else if (ca->line > cb->line) {
493fe9959a2SEmilio G. Cota             return 1;
494fe9959a2SEmilio G. Cota         } else {
495fe9959a2SEmilio G. Cota             /* break the tie with the callsite's type */
496fe9959a2SEmilio G. Cota             return cb->type - ca->type;
497fe9959a2SEmilio G. Cota         }
498fe9959a2SEmilio G. Cota     }
499fe9959a2SEmilio G. Cota }
500fe9959a2SEmilio G. Cota 
501fe9959a2SEmilio G. Cota static void qsp_sort(struct qht *ht, void *p, uint32_t h, void *userp)
502fe9959a2SEmilio G. Cota {
503fe9959a2SEmilio G. Cota     QSPEntry *e = p;
504fe9959a2SEmilio G. Cota     GTree *tree = userp;
505fe9959a2SEmilio G. Cota 
506fe9959a2SEmilio G. Cota     g_tree_insert(tree, e, NULL);
507fe9959a2SEmilio G. Cota }
508fe9959a2SEmilio G. Cota 
509fe9959a2SEmilio G. Cota static void qsp_aggregate(struct qht *global_ht, void *p, uint32_t h, void *up)
510fe9959a2SEmilio G. Cota {
511fe9959a2SEmilio G. Cota     struct qht *ht = up;
512fe9959a2SEmilio G. Cota     const QSPEntry *e = p;
513fe9959a2SEmilio G. Cota     QSPEntry *agg;
514fe9959a2SEmilio G. Cota     uint32_t hash;
515fe9959a2SEmilio G. Cota 
516fe9959a2SEmilio G. Cota     hash = qsp_entry_no_thread_hash(e);
517fe9959a2SEmilio G. Cota     agg = qsp_entry_find(ht, e, hash);
518fe9959a2SEmilio G. Cota     qsp_entry_aggregate(agg, e);
519fe9959a2SEmilio G. Cota }
520fe9959a2SEmilio G. Cota 
521*996e8d9aSEmilio G. Cota static void qsp_iter_diff(struct qht *orig, void *p, uint32_t hash, void *htp)
522*996e8d9aSEmilio G. Cota {
523*996e8d9aSEmilio G. Cota     struct qht *ht = htp;
524*996e8d9aSEmilio G. Cota     QSPEntry *old = p;
525*996e8d9aSEmilio G. Cota     QSPEntry *new;
526*996e8d9aSEmilio G. Cota 
527*996e8d9aSEmilio G. Cota     new = qht_lookup(ht, old, hash);
528*996e8d9aSEmilio G. Cota     /* entries are never deleted, so we must have this one */
529*996e8d9aSEmilio G. Cota     g_assert(new != NULL);
530*996e8d9aSEmilio G. Cota     /* our reading of the stats happened after the snapshot was taken */
531*996e8d9aSEmilio G. Cota     g_assert(new->n_acqs >= old->n_acqs);
532*996e8d9aSEmilio G. Cota     g_assert(new->ns >= old->ns);
533*996e8d9aSEmilio G. Cota 
534*996e8d9aSEmilio G. Cota     new->n_acqs -= old->n_acqs;
535*996e8d9aSEmilio G. Cota     new->ns -= old->ns;
536*996e8d9aSEmilio G. Cota 
537*996e8d9aSEmilio G. Cota     /* No point in reporting an empty entry */
538*996e8d9aSEmilio G. Cota     if (new->n_acqs == 0 && new->ns == 0) {
539*996e8d9aSEmilio G. Cota         bool removed = qht_remove(ht, new, hash);
540*996e8d9aSEmilio G. Cota 
541*996e8d9aSEmilio G. Cota         g_assert(removed);
542*996e8d9aSEmilio G. Cota         g_free(new);
543*996e8d9aSEmilio G. Cota     }
544*996e8d9aSEmilio G. Cota }
545*996e8d9aSEmilio G. Cota 
546*996e8d9aSEmilio G. Cota static void qsp_diff(struct qht *orig, struct qht *new)
547*996e8d9aSEmilio G. Cota {
548*996e8d9aSEmilio G. Cota     qht_iter(orig, qsp_iter_diff, new);
549*996e8d9aSEmilio G. Cota }
550*996e8d9aSEmilio G. Cota 
551*996e8d9aSEmilio G. Cota static void qsp_ht_delete(struct qht *ht, void *p, uint32_t h, void *htp)
552*996e8d9aSEmilio G. Cota {
553*996e8d9aSEmilio G. Cota     g_free(p);
554*996e8d9aSEmilio G. Cota }
555*996e8d9aSEmilio G. Cota 
556fe9959a2SEmilio G. Cota static void qsp_mktree(GTree *tree)
557fe9959a2SEmilio G. Cota {
558*996e8d9aSEmilio G. Cota     QSPSnapshot *snap;
559fe9959a2SEmilio G. Cota     struct qht ht;
560fe9959a2SEmilio G. Cota 
561*996e8d9aSEmilio G. Cota     /*
562*996e8d9aSEmilio G. Cota      * First, see if there's a prior snapshot, so that we read the global hash
563*996e8d9aSEmilio G. Cota      * table _after_ the snapshot has been created, which guarantees that
564*996e8d9aSEmilio G. Cota      * the entries we'll read will be a superset of the snapshot's entries.
565*996e8d9aSEmilio G. Cota      *
566*996e8d9aSEmilio G. Cota      * We must remain in an RCU read-side critical section until we're done
567*996e8d9aSEmilio G. Cota      * with the snapshot.
568*996e8d9aSEmilio G. Cota      */
569*996e8d9aSEmilio G. Cota     rcu_read_lock();
570*996e8d9aSEmilio G. Cota     snap = atomic_rcu_read(&qsp_snapshot);
571*996e8d9aSEmilio G. Cota 
572fe9959a2SEmilio G. Cota     /* Aggregate all results from the global hash table into a local one */
573fe9959a2SEmilio G. Cota     qht_init(&ht, qsp_entry_no_thread_cmp, QSP_INITIAL_SIZE,
574fe9959a2SEmilio G. Cota              QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES);
575fe9959a2SEmilio G. Cota     qht_iter(&qsp_ht, qsp_aggregate, &ht);
576fe9959a2SEmilio G. Cota 
577*996e8d9aSEmilio G. Cota     /* compute the difference wrt the snapshot, if any */
578*996e8d9aSEmilio G. Cota     if (snap) {
579*996e8d9aSEmilio G. Cota         qsp_diff(&snap->ht, &ht);
580*996e8d9aSEmilio G. Cota     }
581*996e8d9aSEmilio G. Cota     /* done with the snapshot; RCU can reclaim it */
582*996e8d9aSEmilio G. Cota     rcu_read_unlock();
583*996e8d9aSEmilio G. Cota 
584fe9959a2SEmilio G. Cota     /* sort the hash table elements by using a tree */
585fe9959a2SEmilio G. Cota     qht_iter(&ht, qsp_sort, tree);
586fe9959a2SEmilio G. Cota 
587fe9959a2SEmilio G. Cota     /* free the hash table, but keep the elements (those are in the tree now) */
588fe9959a2SEmilio G. Cota     qht_destroy(&ht);
589fe9959a2SEmilio G. Cota }
590fe9959a2SEmilio G. Cota 
591fe9959a2SEmilio G. Cota /* free string with g_free */
592fe9959a2SEmilio G. Cota static char *qsp_at(const QSPCallSite *callsite)
593fe9959a2SEmilio G. Cota {
594fe9959a2SEmilio G. Cota     GString *s = g_string_new(NULL);
595fe9959a2SEmilio G. Cota     const char *shortened;
596fe9959a2SEmilio G. Cota 
597fe9959a2SEmilio G. Cota     /* remove the absolute path to qemu */
598fe9959a2SEmilio G. Cota     if (unlikely(strlen(callsite->file) < qsp_qemu_path_len)) {
599fe9959a2SEmilio G. Cota         shortened = callsite->file;
600fe9959a2SEmilio G. Cota     } else {
601fe9959a2SEmilio G. Cota         shortened = callsite->file + qsp_qemu_path_len;
602fe9959a2SEmilio G. Cota     }
603fe9959a2SEmilio G. Cota     g_string_append_printf(s, "%s:%u", shortened, callsite->line);
604fe9959a2SEmilio G. Cota     return g_string_free(s, FALSE);
605fe9959a2SEmilio G. Cota }
606fe9959a2SEmilio G. Cota 
607fe9959a2SEmilio G. Cota struct QSPReportEntry {
608fe9959a2SEmilio G. Cota     const void *obj;
609fe9959a2SEmilio G. Cota     char *callsite_at;
610fe9959a2SEmilio G. Cota     const char *typename;
611fe9959a2SEmilio G. Cota     double time_s;
612fe9959a2SEmilio G. Cota     double ns_avg;
613fe9959a2SEmilio G. Cota     uint64_t n_acqs;
614fe9959a2SEmilio G. Cota };
615fe9959a2SEmilio G. Cota typedef struct QSPReportEntry QSPReportEntry;
616fe9959a2SEmilio G. Cota 
617fe9959a2SEmilio G. Cota struct QSPReport {
618fe9959a2SEmilio G. Cota     QSPReportEntry *entries;
619fe9959a2SEmilio G. Cota     size_t n_entries;
620fe9959a2SEmilio G. Cota     size_t max_n_entries;
621fe9959a2SEmilio G. Cota };
622fe9959a2SEmilio G. Cota typedef struct QSPReport QSPReport;
623fe9959a2SEmilio G. Cota 
624fe9959a2SEmilio G. Cota static gboolean qsp_tree_report(gpointer key, gpointer value, gpointer udata)
625fe9959a2SEmilio G. Cota {
626fe9959a2SEmilio G. Cota     const QSPEntry *e = key;
627fe9959a2SEmilio G. Cota     QSPReport *report = udata;
628fe9959a2SEmilio G. Cota     QSPReportEntry *entry;
629fe9959a2SEmilio G. Cota 
630fe9959a2SEmilio G. Cota     if (report->n_entries == report->max_n_entries) {
631fe9959a2SEmilio G. Cota         return TRUE;
632fe9959a2SEmilio G. Cota     }
633fe9959a2SEmilio G. Cota     entry = &report->entries[report->n_entries];
634fe9959a2SEmilio G. Cota     report->n_entries++;
635fe9959a2SEmilio G. Cota 
636fe9959a2SEmilio G. Cota     entry->obj = e->callsite->obj;
637fe9959a2SEmilio G. Cota     entry->callsite_at = qsp_at(e->callsite);
638fe9959a2SEmilio G. Cota     entry->typename = qsp_typenames[e->callsite->type];
639fe9959a2SEmilio G. Cota     entry->time_s = e->ns * 1e-9;
640fe9959a2SEmilio G. Cota     entry->n_acqs = e->n_acqs;
641fe9959a2SEmilio G. Cota     entry->ns_avg = e->n_acqs ? e->ns / e->n_acqs : 0;
642fe9959a2SEmilio G. Cota     return FALSE;
643fe9959a2SEmilio G. Cota }
644fe9959a2SEmilio G. Cota 
645fe9959a2SEmilio G. Cota static void
646fe9959a2SEmilio G. Cota pr_report(const QSPReport *rep, FILE *f, fprintf_function pr)
647fe9959a2SEmilio G. Cota {
648fe9959a2SEmilio G. Cota     char *dashes;
649fe9959a2SEmilio G. Cota     size_t max_len = 0;
650fe9959a2SEmilio G. Cota     int callsite_len = 0;
651fe9959a2SEmilio G. Cota     int callsite_rspace;
652fe9959a2SEmilio G. Cota     int n_dashes;
653fe9959a2SEmilio G. Cota     size_t i;
654fe9959a2SEmilio G. Cota 
655fe9959a2SEmilio G. Cota     /* find out the maximum length of all 'callsite' fields */
656fe9959a2SEmilio G. Cota     for (i = 0; i < rep->n_entries; i++) {
657fe9959a2SEmilio G. Cota         const QSPReportEntry *e = &rep->entries[i];
658fe9959a2SEmilio G. Cota         size_t len = strlen(e->callsite_at);
659fe9959a2SEmilio G. Cota 
660fe9959a2SEmilio G. Cota         if (len > max_len) {
661fe9959a2SEmilio G. Cota             max_len = len;
662fe9959a2SEmilio G. Cota         }
663fe9959a2SEmilio G. Cota     }
664fe9959a2SEmilio G. Cota 
665fe9959a2SEmilio G. Cota     callsite_len = MAX(max_len, strlen("Call site"));
666fe9959a2SEmilio G. Cota     /* white space to leave to the right of "Call site" */
667fe9959a2SEmilio G. Cota     callsite_rspace = callsite_len - strlen("Call site");
668fe9959a2SEmilio G. Cota 
669fe9959a2SEmilio G. Cota     pr(f, "Type               Object  Call site%*s  Wait Time (s)  "
670fe9959a2SEmilio G. Cota        "       Count  Average (us)\n", callsite_rspace, "");
671fe9959a2SEmilio G. Cota 
672fe9959a2SEmilio G. Cota     /* build a horizontal rule with dashes */
673fe9959a2SEmilio G. Cota     n_dashes = 79 + callsite_rspace;
674fe9959a2SEmilio G. Cota     dashes = g_malloc(n_dashes + 1);
675fe9959a2SEmilio G. Cota     memset(dashes, '-', n_dashes);
676fe9959a2SEmilio G. Cota     dashes[n_dashes] = '\0';
677fe9959a2SEmilio G. Cota     pr(f, "%s\n", dashes);
678fe9959a2SEmilio G. Cota 
679fe9959a2SEmilio G. Cota     for (i = 0; i < rep->n_entries; i++) {
680fe9959a2SEmilio G. Cota         const QSPReportEntry *e = &rep->entries[i];
681fe9959a2SEmilio G. Cota 
682fe9959a2SEmilio G. Cota         pr(f, "%-9s  %14p  %s%*s  %13.5f  %12" PRIu64 "  %12.2f\n", e->typename,
683fe9959a2SEmilio G. Cota            e->obj, e->callsite_at, callsite_len - (int)strlen(e->callsite_at),
684fe9959a2SEmilio G. Cota            "", e->time_s, e->n_acqs, e->ns_avg * 1e-3);
685fe9959a2SEmilio G. Cota     }
686fe9959a2SEmilio G. Cota 
687fe9959a2SEmilio G. Cota     pr(f, "%s\n", dashes);
688fe9959a2SEmilio G. Cota     g_free(dashes);
689fe9959a2SEmilio G. Cota }
690fe9959a2SEmilio G. Cota 
691fe9959a2SEmilio G. Cota static void report_destroy(QSPReport *rep)
692fe9959a2SEmilio G. Cota {
693fe9959a2SEmilio G. Cota     size_t i;
694fe9959a2SEmilio G. Cota 
695fe9959a2SEmilio G. Cota     for (i = 0; i < rep->n_entries; i++) {
696fe9959a2SEmilio G. Cota         QSPReportEntry *e = &rep->entries[i];
697fe9959a2SEmilio G. Cota 
698fe9959a2SEmilio G. Cota         g_free(e->callsite_at);
699fe9959a2SEmilio G. Cota     }
700fe9959a2SEmilio G. Cota     g_free(rep->entries);
701fe9959a2SEmilio G. Cota }
702fe9959a2SEmilio G. Cota 
7030a22777cSEmilio G. Cota void qsp_report(FILE *f, fprintf_function cpu_fprintf, size_t max,
7040a22777cSEmilio G. Cota                 enum QSPSortBy sort_by)
705fe9959a2SEmilio G. Cota {
7060a22777cSEmilio G. Cota     GTree *tree = g_tree_new_full(qsp_tree_cmp, &sort_by, g_free, NULL);
707fe9959a2SEmilio G. Cota     QSPReport rep;
708fe9959a2SEmilio G. Cota 
709fe9959a2SEmilio G. Cota     qsp_init();
710fe9959a2SEmilio G. Cota 
711fe9959a2SEmilio G. Cota     rep.entries = g_new0(QSPReportEntry, max);
712fe9959a2SEmilio G. Cota     rep.n_entries = 0;
713fe9959a2SEmilio G. Cota     rep.max_n_entries = max;
714fe9959a2SEmilio G. Cota 
715fe9959a2SEmilio G. Cota     qsp_mktree(tree);
716fe9959a2SEmilio G. Cota     g_tree_foreach(tree, qsp_tree_report, &rep);
717fe9959a2SEmilio G. Cota     g_tree_destroy(tree);
718fe9959a2SEmilio G. Cota 
719fe9959a2SEmilio G. Cota     pr_report(&rep, f, cpu_fprintf);
720fe9959a2SEmilio G. Cota     report_destroy(&rep);
721fe9959a2SEmilio G. Cota }
722*996e8d9aSEmilio G. Cota 
723*996e8d9aSEmilio G. Cota static void qsp_snapshot_destroy(QSPSnapshot *snap)
724*996e8d9aSEmilio G. Cota {
725*996e8d9aSEmilio G. Cota     qht_iter(&snap->ht, qsp_ht_delete, NULL);
726*996e8d9aSEmilio G. Cota     qht_destroy(&snap->ht);
727*996e8d9aSEmilio G. Cota     g_free(snap);
728*996e8d9aSEmilio G. Cota }
729*996e8d9aSEmilio G. Cota 
730*996e8d9aSEmilio G. Cota void qsp_reset(void)
731*996e8d9aSEmilio G. Cota {
732*996e8d9aSEmilio G. Cota     QSPSnapshot *new = g_new(QSPSnapshot, 1);
733*996e8d9aSEmilio G. Cota     QSPSnapshot *old;
734*996e8d9aSEmilio G. Cota 
735*996e8d9aSEmilio G. Cota     qsp_init();
736*996e8d9aSEmilio G. Cota 
737*996e8d9aSEmilio G. Cota     qht_init(&new->ht, qsp_entry_cmp, QSP_INITIAL_SIZE,
738*996e8d9aSEmilio G. Cota              QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES);
739*996e8d9aSEmilio G. Cota 
740*996e8d9aSEmilio G. Cota     /* take a snapshot of the current state */
741*996e8d9aSEmilio G. Cota     qht_iter(&qsp_ht, qsp_aggregate, &new->ht);
742*996e8d9aSEmilio G. Cota 
743*996e8d9aSEmilio G. Cota     /* replace the previous snapshot, if any */
744*996e8d9aSEmilio G. Cota     old = atomic_xchg(&qsp_snapshot, new);
745*996e8d9aSEmilio G. Cota     if (old) {
746*996e8d9aSEmilio G. Cota         call_rcu(old, qsp_snapshot_destroy, rcu);
747*996e8d9aSEmilio G. Cota     }
748*996e8d9aSEmilio G. Cota }
749