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