Zth (libzth)
Loading...
Searching...
No Matches
perf.h
Go to the documentation of this file.
1#ifndef ZTH_PERF_H
2#define ZTH_PERF_H
3/*
4 * SPDX-FileCopyrightText: 2019-2026 Jochem Rutgers
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 */
8
23#include <libzth/macros.h>
24
25#ifdef __cplusplus
26
27# include <libzth/allocator.h>
28# include <libzth/config.h>
29# include <libzth/time.h>
30# include <libzth/util.h>
31
32# include <cstdio>
33# include <cstdlib>
34# include <cstring>
35
36namespace zth {
37
38int perf_init();
39void perf_deinit();
40
41class Fiber;
42
43UniqueID<Fiber> const& currentFiberID() noexcept;
44
49class Backtrace {
51public:
53
54 explicit Backtrace(size_t skip = 0, size_t maxDepth = 128);
55 Fiber* fiber() const noexcept
56 {
57 return m_fiber;
58 }
59
60 uint64_t fiberId() const noexcept
61 {
62 return m_fiberId;
63 }
64
65 bt_type const& bt() const noexcept
66 {
67 return m_bt;
68 }
69
70 bool truncated() const noexcept
71 {
72 return m_truncated;
73 }
74
75 Timestamp const& t0() const noexcept
76 {
77 return m_t0;
78 }
79
80 Timestamp const& t1() const noexcept
81 {
82 return m_t1;
83 }
84
85 void printPartial(size_t start, ssize_t end = -1, int color = -1) const;
86 void print(int color = -1) const;
87 void printDelta(Backtrace const& other, int color = -1) const;
88
89private:
90 Timestamp m_t0;
91 Timestamp m_t1;
92 Fiber* m_fiber;
93 uint64_t m_fiberId;
94 bt_type m_bt;
95 bool m_truncated;
96};
97
102template <bool Enable = Config::EnablePerfEvent>
103struct PerfEvent {
105
106 constexpr PerfEvent() noexcept
107 : fiber()
108 , type(Nothing)
109 , unused()
110 {}
111
112 explicit PerfEvent(UniqueID<Fiber> const& fiber_)
113 : fiber(fiber_.id())
114 , type(FiberName)
115 , str(strdup(fiber_.name().c_str()))
116 {}
117
119 UniqueID<Fiber> const& fiber_, int state,
120 Timestamp const& t_ = Timestamp::now()) noexcept
121 : t(t_)
122 , fiber(fiber_.id())
124 , fiberState(state)
125 {}
126
128 UniqueID<Fiber> const& fiber_, string const& str_,
129 Timestamp const& t_ = Timestamp::now())
130 : t(t_)
131 , fiber(fiber_.id())
132 , type(Log)
133 , str(strdup(str_.c_str()))
134 {}
135
137 UniqueID<Fiber> const& fiber_, char const* marker,
138 Timestamp const& t_ = Timestamp::now()) noexcept
139 : t(t_)
140 , fiber(fiber_.id())
141 , type(Marker)
142 , c_str(marker)
143 {}
144
145 __attribute__((format(ZTH_ATTR_PRINTF, 4, 5)))
146 PerfEvent(UniqueID<Fiber> const& fiber_, Timestamp const& t_, char const* fmt, ...)
147 : t(t_)
148 , fiber(fiber_.id())
149 , type(Log)
150 {
151 va_list args;
152 va_start(args, fmt);
153 if(vasprintf(&str, fmt, args) == -1)
154 str = nullptr;
155 va_end(args);
156 }
157
158 __attribute__((format(ZTH_ATTR_PRINTF, 4, 0)))
159 PerfEvent(UniqueID<Fiber> const& fiber_, Timestamp const& t_, char const* fmt, va_list args)
160 : t(t_)
161 , fiber(fiber_.id())
162 , type(Log)
163 {
164 if(vasprintf(&str, fmt, args) == -1)
165 str = nullptr;
166 }
167
168 // Use default copy/move-ctor and copy/move-assignment, which
169 // duplicate the allocated string pointers. In that case, only
170 // one of the copies must be release()d later on.
171
172 // release() is called by PerfFiber (see perf.cpp), not by the dtor of PerfEvent.
173 void release() const
174 {
175 switch(type) {
176 case FiberName:
177 case Log:
178 free(str);
179 break;
180 case Nothing:
181 case FiberState:
182 case Marker:
183 default:;
184 }
185 }
186
188 uint64_t fiber;
190
191 union {
192 void* unused;
193 // cppcheck-suppress unsafeClassCanLeak
194 char* str;
195 char const* c_str;
197 };
198};
199
200# pragma GCC diagnostic push
201# pragma GCC diagnostic ignored "-Wunused-parameter"
202template <>
203struct PerfEvent<false> {
205
206 constexpr PerfEvent() noexcept
207 : unused()
208 {}
209
210 constexpr explicit PerfEvent(UniqueID<Fiber> const& fiber_) noexcept
211 : unused()
212 {}
213
214 constexpr PerfEvent(
215 UniqueID<Fiber> const& fiber_, int state,
216 Timestamp const& t_ = Timestamp()) noexcept
217 : unused()
218 {}
219
220 constexpr PerfEvent(
221 UniqueID<Fiber> const& fiber_, string const& str_,
222 Timestamp const& t_ = Timestamp()) noexcept
223 : unused()
224 {}
225
226 constexpr PerfEvent(
227 UniqueID<Fiber> const& fiber_, char const* marker,
228 Timestamp const& t_ = Timestamp()) noexcept
229 : unused()
230 {}
231
232 __attribute__((format(ZTH_ATTR_PRINTF, 4, 5))) constexpr PerfEvent(
233 UniqueID<Fiber> const& fiber_, Timestamp const& t_, char const* fmt, ...) noexcept
234 : unused()
235 {}
236
237 __attribute__((format(ZTH_ATTR_PRINTF, 4, 0))) PerfEvent(
238 UniqueID<Fiber> const& fiber_, Timestamp const& t_, char const* fmt,
239 va_list args) noexcept
240 : unused()
241 {}
242
243 void release() const noexcept {}
244
245 union {
246 void* unused;
247 struct timespec t;
248 uint64_t fiber;
250 char* str;
251 char const* c_str;
253 };
254};
255# pragma GCC diagnostic pop
256
258ZTH_TLS_DECLARE(perf_eventBuffer_type*, perf_eventBuffer)
259
260void perf_flushEventBuffer() noexcept;
261
271# if __cplusplus >= 201103L
272// Construct PerfEvent in-place in buffer.
273template <typename... Args>
274inline void perf_event(Args&&... args) noexcept
275{
276 if(!Config::EnablePerfEvent || unlikely(!perf_eventBuffer)) {
277 // Drop, as there is no event buffer...
278 return;
279 }
280
281 try {
282 perf_eventBuffer->emplace_back(std::forward<Args>(args)...);
283 } catch(...) {
284 // Cannot allocate buffer. Drop.
285 return;
286 }
287
288 zth_assert(perf_eventBuffer->size() <= Config::PerfEventBufferSize);
289
290 if(unlikely(perf_eventBuffer->size() >= Config::PerfEventBufferThresholdToTriggerVCDWrite))
292}
293
294# define zth_perf_event(...) zth::perf_event(__VA_ARGS__)
295# else
296// Copy PerfEvent into buffer.
297inline void perf_event(PerfEvent<> const& event) noexcept
298{
299 if(!Config::EnablePerfEvent || unlikely(!perf_eventBuffer)) {
300 // Release now, as there is no event buffer...
301 event.release();
302 return;
303 }
304
305 try {
306 perf_eventBuffer->push_back(event);
307 } catch(...) {
308 // Cannot allocate buffer. Release now and drop.
309 event.release();
310 return;
311 }
312
313 zth_assert(perf_eventBuffer->size() <= Config::PerfEventBufferSize);
314
315 if(unlikely(perf_eventBuffer->size() >= Config::PerfEventBufferThresholdToTriggerVCDWrite))
316 perf_flushEventBuffer();
317}
318
319# define zth_perf_event(...) zth::perf_event(PerfEvent<>(__VA_ARGS__))
320# endif
321
326ZTH_EXPORT inline void perf_mark(char const* marker)
327{
328 if(Config::EnablePerfEvent)
330}
331
336ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 2))) inline void perf_log(char const* fmt, ...)
337{
338 if(!Config::EnablePerfEvent)
339 return;
340
341 va_list args;
342 va_start(args, fmt);
343 zth_perf_event(currentFiberID(), Timestamp::now(), fmt, args);
344 va_end(args);
345}
346
351ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 0))) inline void
352perf_logv(char const* fmt, va_list args)
353{
354 if(Config::EnablePerfEvent)
355 zth_perf_event(currentFiberID(), Timestamp::now(), fmt, args);
356}
357
361inline void perf_syscall(char const* syscall, Timestamp const& t = Timestamp())
362{
363 if(Config::EnablePerfEvent && zth_config(PerfSyscall))
364 zth_perf_event(currentFiberID(), syscall, t.isNull() ? Timestamp::now() : t);
365}
366
377template <typename T = float>
378class Load {
380public:
381 typedef T type;
382
383 explicit Load(type rc = 1) noexcept
384 : m_rc(rc)
385 , m_active()
386 , m_current(Timestamp::now())
387 , m_load()
388 {}
389
390 type rc() const noexcept
391 {
392 return m_rc;
393 }
394
395 void setRc(type rc) noexcept
396 {
397 m_rc = rc;
398 }
399
400 void start(Timestamp const& now = Timestamp::now()) noexcept
401 {
402 if(isActive())
403 return;
404
405 idle((type)(now - m_current).s());
406 m_current = now;
407 m_active = true;
408 }
409
410 void stop(Timestamp const& now = Timestamp::now()) noexcept
411 {
412 if(!isActive())
413 return;
414
415 active((type)(now - m_current).s());
416 m_current = now;
417 m_active = false;
418 }
419
420 bool isActive() const noexcept
421 {
422 return m_active;
423 }
424
425 type load() const noexcept
426 {
427 return m_load;
428 }
429
430 type load(Timestamp const& now) const noexcept
431 {
432 type dt_s = (type)(now - m_current).s();
433 if(isActive())
434 return active(load(), dt_s);
435 else
436 return idle(load(), dt_s);
437 }
438
439 void idle(type dt_s) noexcept
440 {
441 m_load = idle(load(), dt_s);
442 }
443
444 void active(type dt_s) noexcept
445 {
446 m_load = active(load(), dt_s);
447 }
448
449protected:
450 type idle(type load, type dt_s) const noexcept
451 {
452 if(dt_s <= 0)
453 return load;
454
455 return ((type)1 - alpha(dt_s)) * load;
456 }
457
458 type active(type load, type dt_s) const noexcept
459 {
460 if(dt_s <= 0)
461 return load;
462
463 type a = alpha(dt_s);
464 return a + ((type)1 - a) * load;
465 }
466
467 type alpha(type dt_s) const noexcept
468 {
469 return dt_s / (rc() + dt_s);
470 }
471
472private:
473 type m_rc;
474 bool m_active;
475 Timestamp m_current;
476 type m_load;
477};
478
492template <typename T = float, size_t Bins = 2, typename Count = uint_fast32_t>
495public:
496 typedef T type;
497 typedef Count count_type;
498
499 explicit EventRate(type window = 1) noexcept
500 : m_window_s(window)
501 , m_window(window)
502 , m_binStart()
503 , m_bins()
504 , m_current()
505 {
506 static_assert(std::numeric_limits<decltype(m_current)>::max() >= Bins, "");
507 }
508
509 void event(Timestamp const& now = Timestamp::now()) noexcept
510 {
511 size_t b = 0;
512 for(; m_binStart + m_window < now && b < Bins; b++) {
513 // Proceed to next bin.
514 m_current = (m_current + 1U) % Bins;
515 m_bins[m_current] = 0;
516 m_binStart += m_window;
517 }
518
519 if(unlikely(b == Bins)) {
520 // Cleared all bins. Apparently, m_binStart was a long time ago.
521 m_binStart = now;
522 }
523
524 m_bins[m_current]++;
525 }
526
527 void operator()(Timestamp const& now = Timestamp::now()) noexcept
528 {
529 event(now);
530 }
531
532 type rate(Timestamp const& now = Timestamp::now()) const noexcept
533 {
534 type w = (type)(now - m_binStart).s() / m_window_s;
535
536 if(unlikely(w > 1)) {
537 if(w > 2)
538 return 0; // We are really late.
539
540 w = 1;
541 }
542
543 // Scale the first bin, such that it is gradually removed from the average.
544 type first_bin = ((type)1 - w) * (type)m_bins[(m_current + 1U) % Bins];
545 // Last bin only holds events from [m_binStart,now[, no scaling required.
546 type last_bin = (type)m_bins[m_current];
547
548 // Sum all intermediate bins.
549 type sum = first_bin + last_bin;
550 for(decltype(m_current + 0) i = 2; i < Bins; i++)
551 sum += (type)m_bins[(m_current + i) % Bins];
552
553 // As the first and last one are both partial, divide by Bins - 1.
554 return sum * ((type)1 / (type)(Bins - 1)) / m_window_s;
555 }
556
557 operator type() const noexcept
558 {
559 return rate();
560 }
561
562private:
563 type m_window_s;
564 TimeInterval m_window;
565 Timestamp m_binStart;
566 count_type m_bins[Bins];
567 uint8_t m_current;
568};
569
570} // namespace zth
571
572EXTERN_C ZTH_EXPORT ZTH_INLINE void zth_perf_mark_(char const* marker)
573{
574 zth::perf_mark(marker);
575}
576
582EXTERN_C ZTH_EXPORT ZTH_INLINE __attribute__((format(ZTH_ATTR_PRINTF, 1, 2))) void
583zth_perf_log(char const* fmt, ...)
584{
585 va_list args;
586 va_start(args, fmt);
587 zth::perf_logv(fmt, args);
588 va_end(args);
589}
590
596EXTERN_C ZTH_EXPORT ZTH_INLINE __attribute__((format(ZTH_ATTR_PRINTF, 1, 0))) void
597zth_perf_logv(char const* fmt, va_list args)
598{
599 zth::perf_logv(fmt, args);
600}
601
602#else // !__cplusplus
603
604# include <stdarg.h>
605
606ZTH_EXPORT void zth_perf_mark_(char const* marker);
607ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 2))) void zth_perf_log(char const* fmt, ...);
608ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 0))) void
609zth_perf_logv(char const* fmt, va_list args);
610
611#endif // __cplusplus
612
619#define zth_perf_mark(marker) zth_perf_mark_("" marker)
620
621#endif // ZTH_PERF_H
Save a backtrace.
Definition perf.h:49
Timestamp const & t0() const noexcept
Definition perf.h:75
bool truncated() const noexcept
Definition perf.h:70
vector_type< void * >::type bt_type
Definition perf.h:52
bt_type const & bt() const noexcept
Definition perf.h:65
Timestamp const & t1() const noexcept
Definition perf.h:80
uint64_t fiberId() const noexcept
Definition perf.h:60
Fiber * fiber() const noexcept
Definition perf.h:55
Measure the rate of some event in Hz.
Definition perf.h:493
Count count_type
Definition perf.h:497
void operator()(Timestamp const &now=Timestamp::now()) noexcept
Definition perf.h:527
void event(Timestamp const &now=Timestamp::now()) noexcept
Definition perf.h:509
type rate(Timestamp const &now=Timestamp::now()) const noexcept
Definition perf.h:532
EventRate(type window=1) noexcept
Definition perf.h:499
The fiber.
Definition fiber.h:49
Measure the load of some activity.
Definition perf.h:378
void setRc(type rc) noexcept
Definition perf.h:395
Load(type rc=1) noexcept
Definition perf.h:383
type idle(type load, type dt_s) const noexcept
Definition perf.h:450
type active(type load, type dt_s) const noexcept
Definition perf.h:458
type load(Timestamp const &now) const noexcept
Definition perf.h:430
void idle(type dt_s) noexcept
Definition perf.h:439
void stop(Timestamp const &now=Timestamp::now()) noexcept
Definition perf.h:410
void start(Timestamp const &now=Timestamp::now()) noexcept
Definition perf.h:400
type load() const noexcept
Definition perf.h:425
void active(type dt_s) noexcept
Definition perf.h:444
type rc() const noexcept
Definition perf.h:390
type alpha(type dt_s) const noexcept
Definition perf.h:467
T type
Definition perf.h:381
bool isActive() const noexcept
Definition perf.h:420
Convenient wrapper around struct timespec that contains a time interval.
Definition time.h:55
Convenient wrapper around struct timespec that contains an absolute timestamp.
Definition time.h:568
static Timestamp now()
Definition time.h:595
Keeps track of a process-wide unique ID within the type T.
Definition util.h:715
void zth_perf_log(char const *fmt,...)
Put a formatted log string into the perf output.
Definition perf.h:583
void zth_perf_logv(char const *fmt, va_list args)
Put a formatted log string into the perf output.
Definition perf.h:597
#define zth_config(name)
Checks if the given zth::Config field is enabled.
Definition config.h:44
void perf_mark(char const *marker)
Put a string marker into the perf output.
Definition perf.h:326
#define zth_perf_event(...)
Construct a zth::PerfEvent with provided parameters, and forward it to the perf buffer for later proc...
Definition perf.h:294
void perf_logv(char const *fmt, va_list args)
Put a formatted log string into the perf output.
Definition perf.h:352
#define ZTH_CLASS_NEW_DELETE(T)
Define new/delete operators for a class, which are allocator-aware.
Definition allocator.h:143
#define ZTH_TLS_DECLARE(type, var)
Definition macros.h:99
#define ZTH_ATTR_PRINTF
Definition macros.h:63
#define ZTH_INLINE
Definition macros.h:129
void perf_deinit()
Definition perf.cpp:492
int perf_init()
Definition perf.cpp:478
perf_eventBuffer_type * perf_eventBuffer
Definition perf.cpp:48
void perf_flushEventBuffer() noexcept
Definition perf.cpp:505
void perf_event(Args &&... args) noexcept
Definition perf.h:274
UniqueID< Fiber > const & currentFiberID() noexcept
Definition worker.h:408
string format(char const *fmt,...)
Format like sprintf(), but save the result in an zth::string.
Definition util.h:478
void perf_syscall(char const *syscall, Timestamp const &t=Timestamp())
Put a syscall into the perf output.
Definition perf.h:361
vector_type< PerfEvent<> >::type perf_eventBuffer_type
Definition perf.h:257
void zth_perf_mark_(char const *marker)
Definition perf.h:572
constexpr PerfEvent() noexcept
Definition perf.h:206
constexpr PerfEvent(UniqueID< Fiber > const &fiber_, string const &str_, Timestamp const &t_=Timestamp()) noexcept
Definition perf.h:220
constexpr PerfEvent(UniqueID< Fiber > const &fiber_, char const *marker, Timestamp const &t_=Timestamp()) noexcept
Definition perf.h:226
char const * c_str
Definition perf.h:251
constexpr PerfEvent(UniqueID< Fiber > const &fiber_) noexcept
Definition perf.h:210
void release() const noexcept
Definition perf.h:243
constexpr PerfEvent(UniqueID< Fiber > const &fiber_, int state, Timestamp const &t_=Timestamp()) noexcept
Definition perf.h:214
An event to be processed by perf_event().
Definition perf.h:103
PerfEvent(UniqueID< Fiber > const &fiber_, char const *marker, Timestamp const &t_=Timestamp::now()) noexcept
Definition perf.h:136
uint64_t fiber
Definition perf.h:188
int fiberState
Definition perf.h:196
char * str
Definition perf.h:194
constexpr PerfEvent() noexcept
Definition perf.h:106
Type type
Definition perf.h:189
void release() const
Definition perf.h:173
PerfEvent(UniqueID< Fiber > const &fiber_, string const &str_, Timestamp const &t_=Timestamp::now())
Definition perf.h:127
void * unused
Definition perf.h:192
Timestamp t
Definition perf.h:187
PerfEvent(UniqueID< Fiber > const &fiber_)
Definition perf.h:112
PerfEvent(UniqueID< Fiber > const &fiber_, int state, Timestamp const &t_=Timestamp::now()) noexcept
Definition perf.h:118
char const * c_str
Definition perf.h:195
std::vector type using Config::Allocator::type.
Definition allocator.h:173
std::vector< T, typename Config::Allocator< T >::type > type
Definition allocator.h:174
#define zth_assert(expr)
assert(), but better integrated in Zth.
Definition util.h:212
#define unlikely(expr)
Marks the given expression to likely be evaluated to true.
Definition util.h:55