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