10 #define UNW_LOCAL_ONLY
28 #include <sys/types.h>
31 #if !defined(ZTH_OS_WINDOWS) && !defined(ZTH_OS_BAREMETAL)
34 # include <execinfo.h>
37 #ifdef ZTH_HAVE_LIBUNWIND
38 # include <libunwind.h>
60 if(!processEventBuffer())
87 size_t spareRoom = eventBuffer().capacity() - eventBuffer().size();
88 if(
unlikely(!f || f == m_worker.currentFiber() || spareRoom < 3)) {
94 if(
likely(spareRoom > 3)) {
101 Fiber* currentFiber_ = m_worker.currentFiber();
104 processEventBuffer();
113 e.
c_str =
"Emerg flush";
114 eventBuffer().push_back(e);
119 eventBuffer().push_back(e);
123 eventBuffer().push_back(e);
125 zth_dbg(perf,
"Emergency processEventBuffer()");
126 processEventBuffer();
132 eventBuffer().push_back(e);
136 eventBuffer().push_back(e);
158 if(processEventBuffer())
161 m_worker.suspend(*
this);
172 size_t eb_size = eb.size();
179 for(
size_t i = 0; i < eb_size; i++) {
182 char const* s =
nullptr;
187 for(
char* p = e.
str; *p; ++p) {
188 if(*p < 33 || *p > 126)
191 string const&
id = vcdId(e.
fiber);
193 "$var wire 1 %s \\#%" PRIu64
194 "_%s $end\n$var real 0 %s! \\#%" PRIu64
203 string const&
id = vcdId(e.
fiber);
205 "$var wire 1 %s %" PRIu64
206 "_Fiber $end\n$var real 0 %s! %" PRIu64
218 bool doCleanup =
false;
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())
252 vcdIdRelease(e.
fiber);
267 if(fprintf(m_vcdd,
"#%" PRIu64
"%09ld\ns", (uint64_t)t.
ts().tv_sec,
274 char const* chunkStart = s;
275 char const* chunkEnd = chunkStart;
278 if(*chunkEnd < 33 || *chunkEnd > 126) {
279 if(fprintf(m_vcdd,
"%.*s\\x%02hhx", len, chunkStart,
280 (
unsigned char)*chunkEnd)
285 chunkStart = chunkEnd = chunkEnd + 1;
293 if(fprintf(m_vcdd,
"%s %s!\n", chunkStart, vcdId(e.
fiber).c_str())
305 for(
size_t i = 0; i < eb_size; i++)
311 fprintf(stderr,
"Cannot write VCD file; %s\n",
err(res).c_str());
320 char vcdFilename_[32];
321 char const* vcdFilename =
322 snprintf(vcdFilename_,
sizeof(vcdFilename_),
"zth.%d.vcd", (
int)getpid())
327 if(!(m_vcd = fopen(vcdFilename,
"w"))) {
329 fprintf(stderr,
"Cannot open VCD file %s; %s\n", vcdFilename,
334 #ifdef ZTH_OS_WINDOWS
336 m_vcdd = fopen(
"zth.vcdd",
"w+");
341 fprintf(stderr,
"Cannot open temporary VCD data file; %s\n",
348 if(time(&now) != -1) {
349 #if defined(ZTH_OS_LINUX) || defined(ZTH_OS_MAC)
351 char* strnow = ctime_r(&now, dateBuf);
354 char* strnow = ctime(&now);
357 if(fprintf(m_vcd,
"$date %s$end\n", strnow) < 0)
363 "$version %s $end\n$timescale 1 ns $end\n$scope module top $end\n",
368 fprintf(stderr,
"Using %s for perf VCD output\n", vcdFilename);
372 fprintf(stderr,
"Cannot write %s", vcdFilename);
383 return res ? res : EIO;
388 if(!m_vcd || !m_vcdd)
392 if(fprintf(m_vcd,
"$upscope $end\n$enddefinitions $end\n") < 0)
400 while((cnt = fread(buf, 1,
sizeof(buf), m_vcdd)) > 0)
401 if(fwrite(buf, cnt, 1, m_vcd) != 1)
418 fprintf(stderr,
"Cannot read temporary VCD data file\n");
419 return res ? res : EIO;
421 fprintf(stderr,
"Cannot write VCD file\n");
422 return res ? res : EIO;
427 std::pair<decltype(m_vcdIds.begin()),
bool> i =
428 m_vcdIds.insert(std::make_pair(
fiber,
string()));
431 return i.first->second;
438 char* s = &buf[
sizeof(buf) - 1];
442 *--s = (char)(
id % 93 + 34);
446 return i.first->second = s;
451 decltype(m_vcdIds.begin()) it = m_vcdIds.find(
fiber);
452 if(it != m_vcdIds.end())
476 return perfFiber->
run();
518 m_fiberId = m_fiber ? m_fiber->
id() : 0;
520 #ifdef ZTH_HAVE_LIBUNWIND
523 if(unw_getcontext(&uc))
527 unw_init_local(&cursor, &uc);
529 for(
size_t i = 0; i < skip && unw_step(&cursor) > 0; i++)
534 while(unw_step(&cursor) > 0 && depth < maxDepth) {
537 unw_get_reg(&cursor, UNW_REG_SP, &sp);
541 unw_get_reg(&cursor, UNW_REG_IP, &ip);
542 m_bt.push_back(
reinterpret_cast<void*
>(ip));
544 if(unw_get_proc_info(&cursor, &pip) == 0
545 && pip.start_ip ==
reinterpret_cast<unw_word_t
>(&
context_entry))
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));
556 m_truncated = m_bt.size() == maxDepth;
565 #if !defined(ZTH_OS_WINDOWS) && !defined(ZTH_OS_BAREMETAL)
569 if(-
end > (ssize_t)
bt().size())
573 if(start > (
size_t)
end)
577 FILE* atosf =
nullptr;
580 int len = snprintf(atos,
sizeof(atos),
"atos -p %d\n", getpid());
581 if(len > 0 && (
size_t)len <
sizeof(atos)) {
583 atosf = popen(atos,
"w");
592 backtrace_symbols(&
bt()[start], (
int)((
size_t)
end - start + 1));
594 for(
size_t i = start; i <= (size_t)
end; i++) {
597 fprintf(atosf,
"%p\n",
bt()[i]);
603 if(dladdr(
bt()[i], &info)) {
606 abi::__cxa_demangle(info.dli_sname,
nullptr,
nullptr, &status);
607 if(status == 0 && demangled) {
610 color,
"%s%-3zd 0x%0*" PRIxPTR
" %s + %" PRIuPTR
"\n",
612 reinterpret_cast<uintptr_t
>(
bt()[i]), demangled,
613 reinterpret_cast<uintptr_t
>(
bt()[i])
614 -
reinterpret_cast<uintptr_t
>(info.dli_saddr));
617 color,
"%s%-3zd %s(%s+0x%" PRIxPTR
") [0x%" PRIxPTR
"]\n",
620 reinterpret_cast<uintptr_t
>(
bt()[i])
621 -
reinterpret_cast<uintptr_t
>(info.dli_saddr),
622 reinterpret_cast<uintptr_t
>(
bt()[i]));
636 color,
"%s%-3zd 0x%0*" PRIxPTR
"\n",
638 reinterpret_cast<uintptr_t
>(
bt()[i]));
653 #if !defined(ZTH_OS_WINDOWS) && !defined(ZTH_OS_BAREMETAL)
655 color,
"%sBacktrace of fiber %p #%" PRIu64
":\n", color >= 0 ?
ZTH_DBG_PREFIX :
"",
670 if(other.
t0() >
t0()) {
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])
699 color,
"%sExecution from fiber %p #%" PRIu64
":\n",
void printDelta(Backtrace const &other, int color=-1) const
bool truncated() const noexcept
vector_type< void * >::type bt_type
void print(int color=-1) const
Timestamp const & t0() const noexcept
Timestamp const & t1() const noexcept
void printPartial(size_t start, ssize_t end=-1, int color=-1) const
Backtrace(size_t skip=0, size_t maxDepth=128)
uint64_t fiberId() const noexcept
bt_type const & bt() const noexcept
State state() const noexcept
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.
virtual int fiberHook(Fiber &f)
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
void setName(string const &name)
The class that manages the fibers within this thread.
Fiber * currentFiber() const noexcept
#define zth_config(name)
Checks if the given zth::Config field is enabled.
fiber_type< F >::factory fiber(F f, char const *name=nullptr)
Create a new fiber.
@ end
End-of-guard-list marker.
#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
__thread perf_eventBuffer_type * perf_eventBuffer
vector_type< PerfEvent<> >::type perf_eventBuffer_type
void perf_flushEventBuffer() noexcept
string err(int e)
Return a string like strerror() does, but as a zth::string.
static bool const EnablePerfEvent
Enable (but not necessarily record) perf.
static bool const Debug
This is a debug build when set to true.
static size_t const PerfEventBufferSize
Buffer size for perf events.
An event to be processed by perf_event().
std::map type using Config::Allocator::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.