Zth (libzth)
perf.cpp
Go to the documentation of this file.
1 /*
2  * Zth (libzth), a cooperative userspace multitasking library.
3  * Copyright (C) 2019-2022 Jochem Rutgers
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
8  */
9 
10 #define UNW_LOCAL_ONLY
11 
12 #include <libzth/macros.h>
13 #include <libzth/allocator.h>
14 
15 #ifdef ZTH_OS_MAC
16 # ifndef _BSD_SOURCE
17 # define _BSD_SOURCE
18 # endif
19 #endif
20 
21 #include <libzth/perf.h>
22 #include <libzth/worker.h>
23 
24 #include <cstdlib>
25 #include <fcntl.h>
26 #include <map>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 
31 #if !defined(ZTH_OS_WINDOWS) && !defined(ZTH_OS_BAREMETAL)
32 # include <cxxabi.h>
33 # include <dlfcn.h>
34 # include <execinfo.h>
35 #endif
36 
37 #ifdef ZTH_HAVE_LIBUNWIND
38 # include <libunwind.h>
39 #endif
40 
41 namespace zth {
42 
44 
45 class PerfFiber : public Runnable {
48 public:
49  explicit PerfFiber(Worker* worker)
50  : m_worker(*worker)
51  , m_vcd()
52  , m_vcdd()
53  {
54  zth_assert(worker);
55  startVCD();
56  }
57 
58  virtual ~PerfFiber() override
59  {
60  if(!processEventBuffer())
61  finalizeVCD();
62 
63  if(m_vcdd) {
64  fclose(m_vcdd);
65  m_vcdd = nullptr;
66  }
67 
68  if(m_vcd) {
69  fclose(m_vcd);
70  m_vcd = nullptr;
71  }
72 
73  if(perf_eventBuffer) {
74  for(size_t i = 0; i < perf_eventBuffer->size(); i++)
75  (*perf_eventBuffer)[i].release();
76 
77  delete_alloc(perf_eventBuffer);
78  perf_eventBuffer = nullptr;
79  }
80  }
81 
83  {
85 
86  Fiber* f = fiber();
87  size_t spareRoom = eventBuffer().capacity() - eventBuffer().size();
88  if(unlikely(!f || f == m_worker.currentFiber() || spareRoom < 3)) {
89  // Do it right here right now.
90  processEventBuffer();
91  return;
92  }
93 
94  if(likely(spareRoom > 3)) {
95  // Wakeup and do the processing later on.
96  m_worker.resume(*f);
97  return;
98  }
99 
100  // Almost full, but enough room to signal that we are going to process the buffer.
101  Fiber* currentFiber_ = m_worker.currentFiber();
102  if(unlikely(!currentFiber_)) {
103  // Huh? Do it here anyway.
104  processEventBuffer();
105  return;
106  }
107 
108  // Record a 'context switch' to f...
109  PerfEvent<> e;
110  e.t = Timestamp::now();
111  e.fiber = f->id();
113  e.c_str = "Emerg flush";
114  eventBuffer().push_back(e);
115 
116  e.fiber = currentFiber_->id();
119  eventBuffer().push_back(e);
120 
121  e.fiber = f->id();
123  eventBuffer().push_back(e);
124 
125  zth_dbg(perf, "Emergency processEventBuffer()");
126  processEventBuffer();
127 
128  // ...and switch back.
129  e.t = Timestamp::now();
130  e.fiber = f->id();
131  e.fiberState = f->state();
132  eventBuffer().push_back(e);
133 
134  e.fiber = currentFiber_->id();
135  e.fiberState = currentFiber_->state();
136  eventBuffer().push_back(e);
137  }
138 
140  {
142  return *perf_eventBuffer;
143  }
144 
145 protected:
146  virtual int fiberHook(Fiber& f) override
147  {
148  f.setName("zth::PerfFiber");
149  return Runnable::fiberHook(f);
150  }
151 
152  virtual void entry() override
153  {
154  if(!m_vcd)
155  return;
156 
157  while(true) {
158  if(processEventBuffer())
159  return;
160 
161  m_worker.suspend(*this);
162  }
163 
164  // Will never get here. The fiber is never properly stopped, but just killed when
165  // the Worker terminates.
166  }
167 
169  {
170  int res = 0;
171  perf_eventBuffer_type& eb = eventBuffer();
172  size_t eb_size = eb.size();
173 
174  if(unlikely(!m_vcdd || !m_vcd)) {
175  res = EAGAIN;
176  goto done;
177  }
178 
179  for(size_t i = 0; i < eb_size; i++) {
180  PerfEvent<> const& e = eb[i];
182  char const* s = nullptr;
183 
184  switch(e.type) {
186  if(e.str) {
187  for(char* p = e.str; *p; ++p) {
188  if(*p < 33 || *p > 126)
189  *p = '_';
190  }
191  string const& id = vcdId(e.fiber);
192  if(fprintf(m_vcd,
193  "$var wire 1 %s \\#%" PRIu64
194  "_%s $end\n$var real 0 %s! \\#%" PRIu64
195  "_%s/log $end\n",
196  id.c_str(), e.fiber, e.str, id.c_str(), e.fiber,
197  e.str)
198  <= 0) {
199  res = errno;
200  goto write_error;
201  }
202  } else {
203  string const& id = vcdId(e.fiber);
204  if(fprintf(m_vcd,
205  "$var wire 1 %s %" PRIu64
206  "_Fiber $end\n$var real 0 %s! %" PRIu64
207  "_Fiber_log $end\n",
208  id.c_str(), e.fiber, id.c_str(), e.fiber)
209  <= 0) {
210  res = errno;
211  goto write_error;
212  }
213  }
214  break;
215 
217  char x = '-';
218  bool doCleanup = false;
219  switch(e.fiberState) {
220  case Fiber::New:
221  x = 'L';
222  break;
223  case Fiber::Ready:
224  x = '0';
225  break;
226  case Fiber::Running:
227  x = '1';
228  break;
229  case Fiber::Waiting:
230  x = 'W';
231  break;
232  case Fiber::Suspended:
233  x = 'Z';
234  break;
235  case Fiber::Dead:
236  x = 'X';
237  break;
238  default:
239  x = '-';
240  doCleanup = true;
241  }
242 
243  if(fprintf(m_vcdd, "#%" PRIu64 "%09ld\n%c%s\n",
244  (uint64_t)t.ts().tv_sec, t.ts().tv_nsec, x,
245  vcdId(e.fiber).c_str())
246  <= 0) {
247  res = errno;
248  goto write_error;
249  }
250 
251  if(doCleanup)
252  vcdIdRelease(e.fiber);
253 
254  break;
255  }
256 
257  case PerfEvent<>::Log: {
258  if(true) { // NOLINT
259  s = e.c_str;
260  } else {
261  case PerfEvent<>::Marker:
262  s = e.str;
263  }
264  if(!s)
265  break;
266 
267  if(fprintf(m_vcdd, "#%" PRIu64 "%09ld\ns", (uint64_t)t.ts().tv_sec,
268  t.ts().tv_nsec)
269  <= 0) {
270  res = errno;
271  goto write_error;
272  }
273 
274  char const* chunkStart = s;
275  char const* chunkEnd = chunkStart;
276  int len = 0;
277  while(chunkEnd[1]) {
278  if(*chunkEnd < 33 || *chunkEnd > 126) {
279  if(fprintf(m_vcdd, "%.*s\\x%02hhx", len, chunkStart,
280  (unsigned char)*chunkEnd)
281  < 0) {
282  res = errno;
283  goto write_error;
284  }
285  chunkStart = chunkEnd = chunkEnd + 1;
286  len = 0;
287  } else {
288  chunkEnd++;
289  len++;
290  }
291  }
292 
293  if(fprintf(m_vcdd, "%s %s!\n", chunkStart, vcdId(e.fiber).c_str())
294  <= 0) {
295  res = errno;
296  goto write_error;
297  }
298  }
299 
300  default:; // ignore
301  }
302  }
303 
304 done:
305  for(size_t i = 0; i < eb_size; i++)
306  eb[i].release();
307  eb.clear();
308  return res;
309 
310 write_error:
311  fprintf(stderr, "Cannot write VCD file; %s\n", err(res).c_str());
312  if(!res)
313  res = EIO;
314  goto done;
315  }
316 
317  int startVCD()
318  {
319  int res = 0;
320  char vcdFilename_[32];
321  char const* vcdFilename =
322  snprintf(vcdFilename_, sizeof(vcdFilename_), "zth.%d.vcd", (int)getpid())
323  <= 0
324  ? vcdFilename_
325  : "zth.vcd";
326 
327  if(!(m_vcd = fopen(vcdFilename, "w"))) {
328  res = errno;
329  fprintf(stderr, "Cannot open VCD file %s; %s\n", vcdFilename,
330  err(res).c_str());
331  goto rollback;
332  }
333 
334 #ifdef ZTH_OS_WINDOWS
335  // tmpfile() on WIN32 is broken
336  m_vcdd = fopen("zth.vcdd", "w+");
337 #else
338  m_vcdd = tmpfile();
339 #endif
340  if(!(m_vcdd)) {
341  fprintf(stderr, "Cannot open temporary VCD data file; %s\n",
342  err(errno).c_str());
343  goto rollback;
344  }
345 
346  {
347  time_t now = -1;
348  if(time(&now) != -1) {
349 #if defined(ZTH_OS_LINUX) || defined(ZTH_OS_MAC)
350  char dateBuf[128];
351  char* strnow = ctime_r(&now, dateBuf);
352 #else
353  // Possibly not thread-safe.
354  char* strnow = ctime(&now);
355 #endif
356  if(strnow)
357  if(fprintf(m_vcd, "$date %s$end\n", strnow) < 0)
358  goto write_error;
359  }
360  }
361 
362  if(fprintf(m_vcd,
363  "$version %s $end\n$timescale 1 ns $end\n$scope module top $end\n",
364  banner())
365  < 0)
366  goto write_error;
367 
368  fprintf(stderr, "Using %s for perf VCD output\n", vcdFilename);
369  return 0;
370 
371 write_error:
372  fprintf(stderr, "Cannot write %s", vcdFilename);
373  res = EIO;
374 rollback:
375  if(m_vcdd) {
376  fclose(m_vcdd);
377  m_vcdd = nullptr;
378  }
379  if(m_vcd) {
380  fclose(m_vcd);
381  m_vcd = nullptr;
382  }
383  return res ? res : EIO;
384  }
385 
387  {
388  if(!m_vcd || !m_vcdd)
389  return 0;
390 
391  int res = 0;
392  if(fprintf(m_vcd, "$upscope $end\n$enddefinitions $end\n") < 0)
393  goto write_error;
394 
395  rewind(m_vcdd);
396 
397  {
398  char buf[1024];
399  size_t cnt = 0;
400  while((cnt = fread(buf, 1, sizeof(buf), m_vcdd)) > 0)
401  if(fwrite(buf, cnt, 1, m_vcd) != 1)
402  goto write_error;
403  }
404 
405  if(ferror(m_vcdd))
406  goto read_error;
407 
408  zth_assert(feof(m_vcdd));
409  zth_assert(!ferror(m_vcd));
410 
411  fclose(m_vcdd);
412  m_vcdd = nullptr;
413  fclose(m_vcd);
414  m_vcd = nullptr;
415  return 0;
416 
417 read_error:
418  fprintf(stderr, "Cannot read temporary VCD data file\n");
419  return res ? res : EIO;
420 write_error:
421  fprintf(stderr, "Cannot write VCD file\n");
422  return res ? res : EIO;
423  }
424 
425  string const& vcdId(uint64_t fiber)
426  {
427  std::pair<decltype(m_vcdIds.begin()), bool> i =
428  m_vcdIds.insert(std::make_pair(fiber, string()));
429  if(likely(!i.second))
430  // Already in the map. Return the value.
431  return i.first->second;
432 
433  // Just added with empty string. Generate a proper VCD identifier.
434  // Identifier is a string of ASCII 34-126. Use 33 (!) as special character.
435  // Do a radix-93 convert.
436  uint64_t id = fiber;
437  char buf[16]; // the string cannot be longer than 10 chars, as 93^10 > 2^64.
438  char* s = &buf[sizeof(buf) - 1];
439  *s = 0;
440 
441  do {
442  *--s = (char)(id % 93 + 34);
443  id /= 93;
444  } while(id);
445 
446  return i.first->second = s;
447  }
448 
449  void vcdIdRelease(uint64_t fiber)
450  {
451  decltype(m_vcdIds.begin()) it = m_vcdIds.find(fiber);
452  if(it != m_vcdIds.end())
453  m_vcdIds.erase(it);
454  }
455 
456 private:
457  Worker& m_worker;
458  FILE* m_vcd;
459  FILE* m_vcdd;
461 };
462 
463 ZTH_TLS_STATIC(PerfFiber*, perfFiber, nullptr);
464 
466 {
467  if(!Config::EnablePerfEvent || !zth_config(DoPerfEvent))
468  return 0;
469 
470  perf_eventBuffer = new_alloc<perf_eventBuffer_type>();
472 
473  Worker* worker = nullptr;
474  getContext(&worker, nullptr);
475  perfFiber = new PerfFiber(worker);
476  return perfFiber->run();
477 }
478 
480 {
482  zth_assert(!perfFiber);
483  return;
484  }
485 
486  if(perfFiber) {
487  delete perfFiber;
488  perfFiber = nullptr;
489  }
490 }
491 
492 void perf_flushEventBuffer() noexcept
493 {
495  return;
496 
497  try {
498  if(likely(perfFiber))
499  perfFiber->flushEventBuffer();
500  } catch(...) {
501  // Something is wrong. Disable perf.
502  perf_deinit();
503  }
504 }
505 
506 extern "C" void context_entry(Context* context);
507 
508 // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
509 Backtrace::Backtrace(size_t UNUSED_PAR(skip), size_t UNUSED_PAR(maxDepth))
510  : m_t0(Timestamp::now())
511  , m_fiber()
512  , m_fiberId()
513  , m_truncated(true)
514 {
515  Worker* worker = Worker::instance();
516  m_fiber = worker ? worker->currentFiber() : nullptr;
517  // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
518  m_fiberId = m_fiber ? m_fiber->id() : 0;
519 
520 #ifdef ZTH_HAVE_LIBUNWIND
521  unw_context_t uc;
522 
523  if(unw_getcontext(&uc))
524  return;
525 
526  unw_cursor_t cursor;
527  unw_init_local(&cursor, &uc);
528  size_t depth = 0;
529  for(size_t i = 0; i < skip && unw_step(&cursor) > 0; i++)
530  ;
531 
532  unw_proc_info_t pip;
533 
534  while(unw_step(&cursor) > 0 && depth < maxDepth) {
535  if(depth == 0) {
536  unw_word_t sp = 0;
537  unw_get_reg(&cursor, UNW_REG_SP, &sp);
538  }
539 
540  unw_word_t ip = 0;
541  unw_get_reg(&cursor, UNW_REG_IP, &ip);
542  m_bt.push_back(reinterpret_cast<void*>(ip));
543 
544  if(unw_get_proc_info(&cursor, &pip) == 0
545  && pip.start_ip == reinterpret_cast<unw_word_t>(&context_entry))
546  // Stop here, as we might get segfaults when passing the context_entry
547  // functions.
548  break;
549  }
550 
551  m_truncated = depth == maxDepth;
552 #elif !defined(ZTH_OS_WINDOWS) && !defined(ZTH_OS_BAREMETAL)
553  m_bt.resize(maxDepth);
554  m_bt.resize((size_t)backtrace(m_bt.data(), (int)maxDepth));
555  // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
556  m_truncated = m_bt.size() == maxDepth;
557 #endif
558 
559  m_t1 = Timestamp::now();
560 }
561 
563  size_t UNUSED_PAR(start), ssize_t UNUSED_PAR(end), int UNUSED_PAR(color)) const
564 {
565 #if !defined(ZTH_OS_WINDOWS) && !defined(ZTH_OS_BAREMETAL)
566  if(bt().empty())
567  return;
568  if(end < 0) {
569  if(-end > (ssize_t)bt().size())
570  return;
571  end = (ssize_t)bt().size() + end;
572  }
573  if(start > (size_t)end)
574  return;
575 
576 # ifdef ZTH_OS_MAC
577  FILE* atosf = nullptr;
578  if(Config::Debug) {
579  char atos[256];
580  int len = snprintf(atos, sizeof(atos), "atos -p %d\n", getpid());
581  if(len > 0 && (size_t)len < sizeof(atos)) {
582  fflush(nullptr);
583  atosf = popen(atos, "w");
584  }
585  }
586 # endif
587 
588  char** syms =
589 # ifdef ZTH_OS_MAC
590  !atosf ? nullptr :
591 # endif
592  backtrace_symbols(&bt()[start], (int)((size_t)end - start + 1));
593 
594  for(size_t i = start; i <= (size_t)end; i++) {
595 # ifdef ZTH_OS_MAC
596  if(atosf) {
597  fprintf(atosf, "%p\n", bt()[i]);
598  continue;
599  }
600 # endif
601 
602  Dl_info info;
603  if(dladdr(bt()[i], &info)) {
604  int status = -1;
605  char* demangled =
606  abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
607  if(status == 0 && demangled) {
608 # ifdef ZTH_OS_MAC
609  log_color(
610  color, "%s%-3zd 0x%0*" PRIxPTR " %s + %" PRIuPTR "\n",
611  color >= 0 ? ZTH_DBG_PREFIX : "", i, (int)sizeof(void*) * 2,
612  reinterpret_cast<uintptr_t>(bt()[i]), demangled,
613  reinterpret_cast<uintptr_t>(bt()[i])
614  - reinterpret_cast<uintptr_t>(info.dli_saddr));
615 # else
616  log_color(
617  color, "%s%-3zd %s(%s+0x%" PRIxPTR ") [0x%" PRIxPTR "]\n",
618  color >= 0 ? ZTH_DBG_PREFIX : "", i, info.dli_fname,
619  demangled,
620  reinterpret_cast<uintptr_t>(bt()[i])
621  - reinterpret_cast<uintptr_t>(info.dli_saddr),
622  reinterpret_cast<uintptr_t>(bt()[i]));
623 # endif
624 
625  free(demangled);
626  continue;
627  }
628  }
629 
630  if(syms)
631  log_color(
632  color, "%s%-3zd %s\n", color >= 0 ? ZTH_DBG_PREFIX : "", i,
633  syms[i - start]);
634  else
635  log_color(
636  color, "%s%-3zd 0x%0*" PRIxPTR "\n",
637  color >= 0 ? ZTH_DBG_PREFIX : "", i, (int)sizeof(void*) * 2,
638  reinterpret_cast<uintptr_t>(bt()[i]));
639  }
640 
641  if(syms)
642  free(syms);
643 
644 # ifdef ZTH_OS_MAC
645  if(atosf)
646  pclose(atosf);
647 # endif
648 #endif
649 }
650 
651 void Backtrace::print(int UNUSED_PAR(color)) const
652 {
653 #if !defined(ZTH_OS_WINDOWS) && !defined(ZTH_OS_BAREMETAL)
654  log_color(
655  color, "%sBacktrace of fiber %p #%" PRIu64 ":\n", color >= 0 ? ZTH_DBG_PREFIX : "",
656  m_fiber, m_fiberId);
657  if(!bt().empty())
658  printPartial(0, (ssize_t)bt().size() - 1, color);
659 
660  if(truncated())
661  log_color(color, "%s<truncated>\n", color >= 0 ? ZTH_DBG_PREFIX : "");
662  else
663  log_color(color, "%s<end>\n", color >= 0 ? ZTH_DBG_PREFIX : "");
664 #endif
665 }
666 
667 void Backtrace::printDelta(Backtrace const& other, int color) const
668 {
669  // Make sure other was first.
670  if(other.t0() > t0()) {
671  other.printDelta(*this, color);
672  return;
673  }
674 
675  TimeInterval dt = t0() - other.t1();
676 
677  if(other.fiberId() != fiberId() || other.truncated() || truncated()) {
678  log_color(color, "%sExecuted from:\n", color >= 0 ? ZTH_DBG_PREFIX : "");
679  other.print(color);
680  log_color(color, "%sto:\n", color >= 0 ? ZTH_DBG_PREFIX : "");
681  print(color);
682  log_color(color, "%stook %s\n", color >= 0 ? ZTH_DBG_PREFIX : "", dt.str().c_str());
683  return;
684  }
685 
686  // Find common base
687  ssize_t common = 0;
688  {
689  Backtrace::bt_type const& this_bt = bt();
690  Backtrace::bt_type const& other_bt = other.bt();
691  size_t max_common = std::min(this_bt.size(), other_bt.size());
692  while((size_t)common < max_common
693  && this_bt[this_bt.size() - 1 - (size_t)common]
694  == other_bt[other_bt.size() - 1 - (size_t)common])
695  common++;
696  }
697 
698  log_color(
699  color, "%sExecution from fiber %p #%" PRIu64 ":\n",
700  color >= 0 ? ZTH_DBG_PREFIX : "", m_fiber, m_fiberId);
701  other.printPartial(0, -common - 1, color);
702  log_color(color, "%sto:\n", color >= 0 ? ZTH_DBG_PREFIX : "");
703  printPartial(0, -common - 1, color);
704  if(common > 0) {
705  log_color(color, "%shaving in common:\n", color >= 0 ? ZTH_DBG_PREFIX : "");
706  other.printPartial(other.bt().size() - (size_t)common, color);
707  }
708  log_color(color, "%stook %s\n", color >= 0 ? ZTH_DBG_PREFIX : "", dt.str().c_str());
709 }
710 
711 } // namespace zth
Save a backtrace.
Definition: perf.h:52
void printDelta(Backtrace const &other, int color=-1) const
Definition: perf.cpp:667
bool truncated() const noexcept
Definition: perf.h:73
vector_type< void * >::type bt_type
Definition: perf.h:55
void print(int color=-1) const
Definition: perf.cpp:651
Timestamp const & t0() const noexcept
Definition: perf.h:78
Timestamp const & t1() const noexcept
Definition: perf.h:83
void printPartial(size_t start, ssize_t end=-1, int color=-1) const
Definition: perf.cpp:562
Backtrace(size_t skip=0, size_t maxDepth=128)
Definition: perf.cpp:509
uint64_t fiberId() const noexcept
Definition: perf.h:63
bt_type const & bt() const noexcept
Definition: perf.h:68
The fiber.
Definition: fiber.h:52
State state() const noexcept
Definition: fiber.h:143
@ Suspended
Definition: fiber.h:71
@ Dead
Definition: fiber.h:71
@ Waiting
Definition: fiber.h:71
@ New
Definition: fiber.h:71
@ Ready
Definition: fiber.h:71
@ Running
Definition: fiber.h:71
virtual int fiberHook(Fiber &f) override
Definition: perf.cpp:146
int processEventBuffer()
Definition: perf.cpp:168
string const & vcdId(uint64_t fiber)
Definition: perf.cpp:425
void flushEventBuffer()
Definition: perf.cpp:82
int startVCD()
Definition: perf.cpp:317
static perf_eventBuffer_type & eventBuffer()
Definition: perf.cpp:139
virtual void entry() override
Definition: perf.cpp:152
int finalizeVCD()
Definition: perf.cpp:386
virtual ~PerfFiber() override
Definition: perf.cpp:58
void vcdIdRelease(uint64_t fiber)
Definition: perf.cpp:449
PerfFiber(Worker *worker)
Definition: perf.cpp:49
An abstract class, that can be started as a fiber.
Definition: fiber.h:466
int run()
Definition: fiber.cpp:18
virtual int fiberHook(Fiber &f)
Definition: fiber.h:496
static safe_ptr< singleton_type >::type instance() noexcept
Return the only instance of T within this thread.
Definition: util.h:989
Convenient wrapper around struct timespec that contains a time interval.
Definition: time.h:52
string str() const
Definition: time.h:404
constexpr struct timespec const & ts() const noexcept
Definition: time.h:244
Convenient wrapper around struct timespec that contains an absolute timestamp.
Definition: time.h:527
static Timestamp now()
Definition: time.h:554
uint64_t id() const noexcept
Definition: util.h:720
void setName(string const &name)
Definition: util.h:730
The class that manages the fibers within this thread.
Definition: worker.h:38
Fiber * currentFiber() const noexcept
Definition: worker.h:96
#define zth_config(name)
Checks if the given zth::Config field is enabled.
Definition: config.h:46
fiber_type< F >::factory fiber(F f, char const *name=nullptr)
Create a new fiber.
Definition: async.h:713
@ end
End-of-guard-list marker.
Definition: fsm.h:45
#define zth_dbg(group, fmt, a...)
Debug printf()-like function.
Definition: util.h:210
void log_color(int color, char const *fmt,...)
Logs a given printf()-like formatted string using an ANSI color code.
Definition: util.h:297
char const * banner() noexcept
Prints a banner line with version and configuration information.
Definition: util.cpp:34
#define ZTH_CLASS_NEW_DELETE(T)
Define new/delete operators for a class, which are allocator-aware.
Definition: allocator.h:114
#define ZTH_TLS_STATIC(type, var, init)
Definition: macros.h:57
#define ZTH_TLS_DEFINE(type, var, init)
Definition: macros.h:56
#define UNUSED_PAR(name)
Definition: macros.h:79
Definition: allocator.h:23
void getContext(Worker **worker, Fiber **fiber) noexcept
Definition: worker.h:412
void context_entry(Context *context)
Timestamp const startTime
Definition: time.cpp:191
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
string err(int e)
Return a string like strerror() does, but as a zth::string.
Definition: util.h:617
static bool const EnablePerfEvent
Enable (but not necessarily record) perf.
Definition: config.h:163
static bool const Debug
This is a debug build when set to true.
Definition: config.h:50
static size_t const PerfEventBufferSize
Buffer size for perf events.
Definition: config.h:154
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
Type type
Definition: perf.h:189
Timestamp t
Definition: perf.h:187
char const * c_str
Definition: perf.h:194
std::map type using Config::Allocator::type.
Definition: allocator.h:142
#define ZTH_DBG_PREFIX
Prefix for every zth_dbg() call.
Definition: util.h:198
#define zth_assert(expr)
assert(), but better integrated in Zth.
Definition: util.h:236
#define likely(expr)
Marks the given expression to likely be evaluated to true.
Definition: util.h:42
#define ZTH_CLASS_NOCOPY(Class)
Definition: util.h:254
#define unlikely(expr)
Marks the given expression to likely be evaluated to true.
Definition: util.h:56