22#if __cplusplus < 201103L
35#if !defined(ZTH_OS_WINDOWS) && !defined(ZTH_OS_BAREMETAL)
41#ifdef ZTH_HAVE_LIBUNWIND
42# include <libunwind.h>
65 if(!processEventBuffer())
92 size_t spareRoom = eventBuffer().capacity() - eventBuffer().size();
93 if(
unlikely(!f || f == m_worker->currentFiber() || spareRoom < 3)) {
99 if(
likely(spareRoom > 3)) {
101 m_worker->resume(*f);
106 Fiber const* currentFiber_ = m_worker->currentFiber();
109 processEventBuffer();
115 e.
t = Timestamp::now();
118 e.
c_str =
"Emerg flush";
119 eventBuffer().push_back(e);
124 eventBuffer().push_back(e);
128 eventBuffer().push_back(e);
130 zth_dbg(perf,
"Emergency processEventBuffer()");
131 processEventBuffer();
134 e.
t = Timestamp::now();
137 eventBuffer().push_back(e);
141 eventBuffer().push_back(e);
154 return Runnable::fiberHook(f);
163 if(processEventBuffer())
166 m_worker->suspend(*
this);
177 size_t eb_size = eb.size();
184 for(
size_t i = 0; i < eb_size; i++) {
187 char const* s =
nullptr;
192 for(
char* p = e.
str; *p; ++p) {
193 if(*p < 33 || *p > 126)
196 string const&
id = vcdId(e.
fiber);
198 "$var wire 1 %s \\#%s_%s $end\n$var real 0 %s! "
199 "\\#%s_%s/log $end\n",
207 string const&
id = vcdId(e.
fiber);
209 "$var wire 1 %s \\#%s_Fiber $end\n$var real 0 "
210 "%s! \\#%s_Fiber_log $end\n",
222 bool doCleanup =
false;
236 case Fiber::Suspended:
247 static_assert(
sizeof(unsigned) >= 4,
"");
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())
257 vcdIdRelease(e.
fiber);
272 static_assert(
sizeof(unsigned) >= 4,
"");
274 if(fprintf(m_vcdd,
"#%s%09u\ns",
str(t.
ts().tv_sec).
c_str(),
275 (unsigned)t.
ts().tv_nsec)
281 char const* chunkStart = s;
282 char const* chunkEnd = chunkStart;
285 if(*chunkEnd < 33 || *chunkEnd > 126) {
286 if(fprintf(m_vcdd,
"%.*s\\x%02x", len, chunkStart,
287 (
unsigned)(
unsigned char)*chunkEnd)
292 chunkStart = chunkEnd = chunkEnd + 1;
300 if(fprintf(m_vcdd,
"%s %s!\n", chunkStart, vcdId(e.
fiber).c_str())
313 for(
size_t i = 0; i < eb_size; i++)
319 fprintf(stderr,
"Cannot write VCD file; %s\n",
err(res).c_str());
328 char vcdFilename_[32];
329 char const* vcdFilename =
330 snprintf(vcdFilename_,
sizeof(vcdFilename_),
"zth.%d.vcd", (
int)getpid())
335 m_vcd = fopen(vcdFilename,
"w");
339 stderr,
"Cannot open VCD file %s; %s\n", vcdFilename,
346 m_vcdd = fopen(
"zth.vcdd",
"w+");
352 stderr,
"Cannot open temporary VCD data file; %s\n",
359 if(time(&now) != -1) {
360#if defined(ZTH_OS_LINUX) || defined(ZTH_OS_MAC)
362 char const* strnow = ctime_r(&now, dateBuf);
365 char const* strnow = ctime(&now);
368 if(fprintf(m_vcd,
"$date %s$end\n", strnow) < 0)
374 "$version %s $end\n$timescale 1 ns $end\n$scope module top $end\n",
379 (void)fprintf(stderr,
"Using %s for perf VCD output\n", vcdFilename);
383 (void)fprintf(stderr,
"Cannot write %s", vcdFilename);
387 (void)fclose(m_vcdd);
394 return res ? res : EIO;
399 if(!m_vcd || !m_vcdd)
403 if(fprintf(m_vcd,
"$upscope $end\n$enddefinitions $end\n") < 0)
406 (void)fseek(m_vcdd, 0, SEEK_SET);
411 while((cnt = fread(buf, 1,
sizeof(buf), m_vcdd)) > 0)
412 if(fwrite(buf, cnt, 1, m_vcd) != 1)
422 (void)fclose(m_vcdd);
429 fprintf(stderr,
"Cannot read temporary VCD data file\n");
430 return res ? res : EIO;
432 fprintf(stderr,
"Cannot write VCD file\n");
433 return res ? res : EIO;
438 std::pair<
decltype(m_vcdIds.begin()),
bool> i =
439 m_vcdIds.insert(std::make_pair(
fiber,
string()));
442 return i.first->second;
449 char* s = &buf[
sizeof(buf) - 1];
454 *--s = (char)(
id % 93 + 34);
458 return i.first->second = s;
463 decltype(m_vcdIds.begin()) it = m_vcdIds.find(
fiber);
464 if(it != m_vcdIds.end())
480 if(!Config::EnablePerfEvent || !
zth_config(DoPerfEvent))
489 return perfFiber->
run();
494 if(!Config::EnablePerfEvent) {
507 if(!Config::EnablePerfEvent)
531 m_fiberId = m_fiber ? m_fiber->
id() : 0;
533#ifdef ZTH_HAVE_LIBUNWIND
536 if(unw_getcontext(&uc))
540 unw_init_local(&cursor, &uc);
542 for(
size_t i = 0; i < skip && unw_step(&cursor) > 0; i++)
547 while(unw_step(&cursor) > 0 && depth < maxDepth) {
551 unw_get_reg(&cursor, UNW_REG_SP, &sp);
555 unw_get_reg(&cursor, UNW_REG_IP, &ip);
556 m_bt.push_back(
reinterpret_cast<void*
>(ip));
558 if(unw_get_proc_info(&cursor, &pip) == 0
559 && pip.start_ip ==
reinterpret_cast<unw_word_t
>(&
context_entry))
565 m_truncated = depth == maxDepth;
566#elif defined(ZTH_OS_MAC) && defined(ZTH_ARCH_ARM64)
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));
573 m_truncated = m_bt.size() == maxDepth;
579void Backtrace::printPartial(
582#if !defined(ZTH_OS_WINDOWS) && !defined(ZTH_OS_BAREMETAL)
586 if(-end > (ssize_t)
bt().size())
588 end = (ssize_t)
bt().size() + end;
590 if(start > (
size_t)end)
594 FILE* atosf =
nullptr;
597 int len = snprintf(atos,
sizeof(atos),
"atos -p %d\n", getpid());
598 if(len > 0 && (
size_t)len <
sizeof(atos)) {
600 atosf = popen(atos,
"w");
609 backtrace_symbols(&
bt()[start], (
int)((
size_t)end - start + 1));
611 for(
size_t i = start; i <= (size_t)end; i++) {
614 fprintf(atosf,
"%p\n",
bt()[i]);
620 if(dladdr(
bt()[i], &info)) {
623 abi::__cxa_demangle(info.dli_sname,
nullptr,
nullptr, &status);
624 if(status == 0 && demangled) {
627 color,
"%s%-3zd 0x%0*" PRIxPTR
" %s + %" PRIuPTR
"\n",
629 reinterpret_cast<uintptr_t
>(
bt()[i]), demangled,
630 reinterpret_cast<uintptr_t
>(
bt()[i])
631 -
reinterpret_cast<uintptr_t
>(info.dli_saddr));
634 color,
"%s%-3zd %s(%s+0x%" PRIxPTR
") [0x%" PRIxPTR
"]\n",
637 reinterpret_cast<uintptr_t
>(
bt()[i])
638 -
reinterpret_cast<uintptr_t
>(info.dli_saddr),
639 reinterpret_cast<uintptr_t
>(
bt()[i]));
653 color,
"%s%-3zd 0x%0*" PRIxPTR
"\n",
655 reinterpret_cast<uintptr_t
>(
bt()[i]));
670#if !defined(ZTH_OS_WINDOWS) && !defined(ZTH_OS_BAREMETAL)
672 color,
"%sBacktrace of fiber %p #%" PRIu64
":\n", color >= 0 ?
ZTH_DBG_PREFIX :
"",
684void Backtrace::printDelta(
Backtrace const& other,
int color)
const
687 if(other.
t0() >
t0()) {
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])
716 color,
"%sExecution from fiber %p #%s:\n", color >= 0 ?
ZTH_DBG_PREFIX :
"",
void printDelta(Backtrace const &other, int color=-1) const
Timestamp const & t0() const noexcept
bool truncated() const noexcept
vector_type< void * >::type bt_type
bt_type const & bt() const noexcept
void print(int color=-1) const
Timestamp const & t1() const noexcept
void printPartial(size_t start, ssize_t end=-1, int color=-1) const
uint64_t fiberId() const noexcept
State state() const noexcept
void setName(string const &name)
virtual int fiberHook(Fiber &f) override
string const & vcdId(uint64_t fiber)
static perf_eventBuffer_type & eventBuffer()
virtual void entry() override
virtual ~PerfFiber() override
void vcdIdRelease(uint64_t fiber)
PerfFiber(Worker *worker)
An abstract class, that can be started as a fiber.
static safe_ptr< singleton_type >::type instance() noexcept
Return the only instance of T within this thread.
Convenient wrapper around struct timespec that contains a time interval.
constexpr struct timespec const & ts() const noexcept
Convenient wrapper around struct timespec that contains an absolute timestamp.
uint64_t id() const noexcept
The class that manages the fibers within this thread.
Fiber * currentFiber() const noexcept
char const * c_str() const
void context_entry(zth::Context *context) noexcept
Entry point of the fiber.
#define zth_config(name)
Checks if the given zth::Config field is enabled.
fiber_type< F >::fiber fiber(F &&f, Args &&... args)
Create and start a new fiber.
#define zth_dbg(group, fmt, a...)
Debug printf()-like function.
void log_color(int color, char const *fmt,...)
Logs a given printf()-like formatted string using an ANSI color code.
char const * banner() noexcept
Prints a banner line with version and configuration information.
#define ZTH_CLASS_NEW_DELETE(T)
Define new/delete operators for a class, which are allocator-aware.
#define ZTH_TLS_STATIC(type, var, init)
#define ZTH_TLS_DEFINE(type, var, init)
void getContext(Worker **worker, Fiber **fiber) noexcept
void context_entry(Context *context)
Timestamp const startTime
cow_string str(T value)
Returns an zth::string representation of the given value.
perf_eventBuffer_type * perf_eventBuffer
void perf_flushEventBuffer() noexcept
string err(int e)
Return a string like strerror() does, but as a zth::string.
vector_type< PerfEvent<> >::type perf_eventBuffer_type
static bool const Debug
This is a debug build when set to true.
An event to be processed by perf_event().
std::map< Key, T, Compare, typename Config::Allocator< std::pair< const Key, T > >::type > type
#define ZTH_DBG_PREFIX
Prefix for every zth_dbg() call.
#define zth_assert(expr)
assert(), but better integrated in Zth.
#define likely(expr)
Marks the given expression to likely be evaluated to true.
#define ZTH_CLASS_NOCOPY(Class)
#define unlikely(expr)
Marks the given expression to likely be evaluated to true.