18#ifndef ZTH_OS_BAREMETAL
19# define ZTH_VCD_USE_FILE_IO
30static void perf_time(Timestamp
const& t = Timestamp());
31static void perf_dt(Timestamp
const& t = Timestamp());
75 m_buffer = allocate_noexcept<char>(
capacity());
89 deallocate<char>(
const_cast<char*
>(m_buffer),
capacity());
94 size_t size() const noexcept
96 return std::min(
capacity(), (
size_t)m_size);
121 return (
char const*)m_buffer;
135#if GCC_VERSION < 40802L
136 __sync_add_and_fetch(&m_size, s);
138 __atomic_add_fetch(&m_size, s, __ATOMIC_RELAXED);
141 size_t pstart = pend - s;
142 char volatile* p = &m_buffer[pstart];
166 memset(
const_cast<char*
>(m_buffer), 0,
capacity());
216 void stop() noexcept;
225 return m_known.find(x) != m_known.end();
232 }
catch(std::bad_alloc
const&) {
238 char volatile* m_buffer;
239 size_t volatile m_size;
240 bool volatile m_running;
266 perf_buffer->
start(f);
282void PerfBuffer::stop() noexcept
284 if(perf_buffer !=
this)
300static size_t leb128_encode(leb128_buf_t& buf, uint64_t x)
305 buf[len] = (char)(x & 0x7FU);
309 buf[len] = (char)((
unsigned)buf[len] | 0x80U);
314static size_t leb128_len(
char const* buf)
317 for(; *buf & 0x80; buf++, len++)
327static void perf_time(Timestamp
const& t)
333 Timestamp
const& t_ = t.isNull() ? (t_now = Timestamp::now()) : t;
335 struct timespec const& ts = t_.ts();
337 size_t s_len = leb128_encode(s, (uint64_t)ts.tv_sec);
339 zth_assert(ts.tv_nsec < TimeInterval::BILLION);
341 size_t ns_len = leb128_encode(ns, (uint64_t)ts.tv_nsec);
343 char volatile* p = perf_buffer->
reserve(s_len + ns_len + 1);
347 memcpy(
const_cast<char*
>(p + 1), s, s_len);
348 memcpy(
const_cast<char*
>(p + 1 + s_len), ns, ns_len);
356static void perf_dt(Timestamp
const& t)
362 Timestamp
const& t_ = t.isNull() ? (t_now = Timestamp::now()) : t;
364 Timestamp
const& t0 = perf_buffer->
t();
365 TimeInterval d = t_ - t0;
376 zth_assert(d.ts().tv_nsec < TimeInterval::BILLION);
379 size_t len = leb128_encode(buf, (uint64_t)d.ts().tv_nsec);
381 char volatile* p = perf_buffer->
reserve(len + 1);
385 memcpy(
const_cast<char*
>(p + 1), buf, len);
400 perf_buffer->
check();
413 handle->p =
static_cast<void*
>(perf_buffer);
426 if(!handle || !handle->p)
430 char volatile* p = pb->
reserve(
sizeof(marker) + 1);
435 memcpy(
const_cast<char*
>(p + 1), &marker,
sizeof(marker));
469 if(!perf_buffer || !perf_buffer->
running())
473 va_copy(args2, args);
474 int len = vsnprintf(
nullptr, 0, fmt, args2);
483 char volatile* p = perf_buffer->
reserve((
size_t)len + 2);
487 int len2 = vsnprintf(
const_cast<char*
>(p + 1), (
size_t)len + 1U, fmt, args);
492 perf_buffer->
check();
502 if(!perf_buffer || !perf_buffer->
running())
505 perf_buffer->
know(&f);
508 size_t id_len = leb128_encode(
id, f.id());
510 char const* name = f.name().c_str();
511 size_t name_len = f.name().size() + 1;
513 char volatile* p = perf_buffer->
reserve(id_len + name_len + 1);
517 memcpy(
const_cast<char*
>(p + 1),
id, id_len);
518 memcpy(
const_cast<char*
>(p + 1 + id_len), name, name_len);
522 perf_buffer->
check();
530 if(!perf_buffer || !perf_buffer->
running())
535 if(!perf_buffer->
knows(&f))
539 size_t id_len = leb128_encode(
id, f.id());
541 char volatile* p = perf_buffer->
reserve(id_len + 2);
545 memcpy(
const_cast<char*
>(p + 1),
id, id_len);
548 state = (int)f.state();
550 p[1 + id_len] = (char)state;
552 perf_buffer->
check();
577 char const* p = perf_buffer->
data();
581 char const* end = p + perf_buffer->
size();
591 size_t len = 1 + leb128_len(p + 1);
592 len += leb128_len(p + len);
598 size_t len = leb128_len(p + 1) + 1U;
605 char const*
const __attribute__((aligned(1)))* s =
606 reinterpret_cast<char const*
const __attribute__((aligned(1)))*
>(
608 size_t len = strlen(*s);
612 p +=
sizeof(*s) + 1U;
616 size_t len = strlen(p + 1);
622 size_t len = 1 + leb128_len(p + 1U);
623 len += strlen(p + len) + 1U;
629 size_t len = 1 + leb128_len(p + 1) + 1U;
643static void perf_continue()
645 zth_dbg(perf,
"[%s] Dump", currentWorker().id_str());
652 Timestamp t_now = Timestamp::now();
653 char id = PerfEventTime;
657 f(buf, leb128_encode(buf, (uint64_t)t_now.ts().tv_sec));
658 f(buf, leb128_encode(buf, (uint64_t)t_now.ts().tv_nsec));
667 perf_start(perf_continue);
687 if(!perf_buffer || !perf_buffer->
enabled())
712#ifndef ZTH_VCD_USE_FILE_IO
713void perf_run_dump(
char const* path)
noexcept
723static
void perf_run_dump_callback(
void const* buf,
size_t len)
730 if(fwrite(buf, len, 1, perf_dump_file) != 1) {
731 zth_log(
"Cannot write to perf dump file");
732 (void)fclose(perf_dump_file);
733 perf_dump_file =
nullptr;
748 path = getenv(
"ZTH_PERF_FILE");
752 format(
"%s.%u-%u.perf", path ? path :
"zth", (unsigned)getpid(),
763 (void)fclose(perf_dump_file);
764 perf_dump_file =
nullptr;
767 perf_dump_file = fopen(path,
"w+b");
768 if(!perf_dump_file) {
769 string e =
err(errno);
770 zth_log(
"Cannot open perf dump file %s; %s", path, e.c_str());
784 if(!Config::EnablePerfEvent)
789 perf_buffer = new_alloc<PerfBuffer>();
790 }
catch(std::bad_alloc
const&) {
808 bool running = perf_buffer->
running();
812#ifdef ZTH_VCD_USE_FILE_IO
825 delete_alloc(perf_buffer);
826 perf_buffer =
nullptr;
835#ifndef ZTH_VCD_USE_FILE_IO
838int perf_vcd(
char const* perf,
char const* vcd)
noexcept
844int perf_vcdf(FILE* perf, FILE* vcd)
noexcept
860int perf_vcd(
char const* perf,
char const* vcd)
noexcept
862 FILE* fperf =
nullptr;
863 bool fperf_reuse =
false;
864 FILE* fvcd =
nullptr;
868 fperf = perf_dump_file;
877 fperf = fopen(perf,
"rb");
884 vcd = getenv(
"ZTH_PERF_VCD");
893 size_t len = svcd.size();
895 if(len >= 5 && strcmp(svcd.c_str() + len - 5,
".perf") == 0)
896 svcd.resize(len - 5);
900 Worker const* w = Worker::instance();
903 format(
"zth.%u-%u.vcd", (
unsigned)getpid(),
906 svcd =
format(
"zth.%u.vcd", (
unsigned)getpid());
910 fvcd = fopen(svcd.c_str(),
"w");
911 }
catch(std::bad_alloc
const&) {
919 fvcd = fopen(vcd,
"w");
932 (void)fseek(fperf, 0, SEEK_END);
964 if(fseek(perf, 0, SEEK_SET))
971 if((res = parseFile(perf)))
1009 std::pair<
decltype(m_vcdIds.begin()),
bool> i =
1010 m_vcdIds.insert(std::make_pair(
fiber,
string()));
1013 return i.first->second;
1018 uint64_t
id =
fiber;
1020 char* s = &buf[
sizeof(buf) - 1];
1025 *--s = (char)(
id % 93 + 34);
1029 return i.first->second = s;
1034 decltype(m_vcdIds.begin()) it = m_vcdIds.find(
fiber);
1035 if(it != m_vcdIds.end())
1058 va_start(args, fmt);
1059 int res =
writev(fmt, args);
1066 int res = vfprintf(
vcd(), fmt, args);
1078 static int read(FILE* f,
void* buf,
size_t size)
1080 size_t res = fread(buf, size, 1, f);
1090 int parseFile(FILE* f)
1093 int res = parseEntry(f);
1100 int parseEntry(FILE* f)
1105 if((res = parseType(f, type)))
1106 return feof(f) ? 0 : res;
1110 if(fseek(f, 0, SEEK_END))
1114 if((res = parseTime(f)))
1118 if((res = parseTimeDelta(f)))
1122 if((res = parseLog(f)))
1126 if((res = parseFiber(f)))
1130 if((res = parseFiberState(f)))
1141 static int parseType(FILE* f,
char& x)
1143 return read(f, &x,
sizeof(x));
1146 static int parseLeb128(FILE* f, uint64_t& x)
1150 unsigned char c = 0x80U;
1152 int res = read(f, &c, 1);
1157 x |= (uint64_t)(c & 0x7FU) << i;
1163 int parseTime(FILE* f)
1166 struct timespec ts = {};
1169 if((res = parseLeb128(f, x)))
1171 ts.tv_sec = (time_t)x;
1173 if((res = parseLeb128(f, x)))
1177 ts.tv_nsec = (long)x;
1183 int parseTimeDelta(FILE* f)
1189 if((res = parseLeb128(f, ns)))
1194 m_t += TimeInterval(0, (
long)ns);
1198 static int parseString(FILE* f,
string& s)
1202 int res = read(f, &c, 1);
1211 int parseLog(FILE* f)
1214 int res = parseString(f, s);
1221 int parseFiber(FILE* f)
1224 if((res = parseLeb128(f, m_fiber)))
1228 if((res = parseString(f, name)))
1231 return handleFiber(m_fiber, name.data(), name.size());
1234 int parseFiberState(FILE* f)
1237 if((res = parseLeb128(f, m_fiber)))
1240 unsigned char state = 0;
1241 if((res = read(f, &state, 1)))
1251 map_type<uint64_t, string>::type m_vcdIds;
1270 if(time(&t_now) != -1) {
1271# if defined(ZTH_OS_LINUX) || defined(ZTH_OS_MAC)
1273 char const* strnow = ctime_r(&t_now, dateBuf);
1276 char const* strnow = ctime(&t_now);
1278 size_t len = strnow ? strlen(strnow) : 0;
1279 while(len > 0 && (strnow[len - 1] ==
'\n' || strnow[len - 1] ==
'\r'))
1282 if((res =
write(
"$date %.*s $end\n", (
int)len, strnow)))
1287 "$version %s $end\n$timescale 1 ns $end\n$scope module top $end\n",
1298 for(FibersMap::iterator it = m_fibers.begin(); it != m_fibers.end(); ++it)
1299 if((res = outputFiber(it->first, it->second)))
1302 if((res =
write(
"$upscope $end\n$enddefinitions $end\n")))
1310 FibersMap::iterator it = m_fibers.find(
fiber);
1311 if(it != m_fibers.end())
1312 it->second =
string(name, size);
1314 it = m_fibers.insert(std::make_pair(
fiber,
string(name, size))).first;
1316 string& s = it->second;
1317 for(
size_t i = 0; i < s.size(); ++i) {
1318 if(s[i] < 33 || s[i] > 126)
1326 int outputFiber(uint64_t
fiber,
string const& name)
1332 "$var wire 1 %s \\#%s_%s $end\n$var real 0 %s! "
1333 "\\#%s_%s/log $end\n",
1338 "$var wire 1 %s \\#%s_Fiber $end\n$var real 0 "
1339 "%s! \\#%s_Fiber_log $end\n",
1361 write(
"#%s%09u\ns",
str(
t().ts().tv_sec).c_str(),
1362 (
unsigned)
t().ts().tv_nsec))) {
1366 char const* chunkStart =
log;
1367 char const* chunkEnd = chunkStart;
1369 while(chunkEnd[1]) {
1370 if(*chunkEnd < 33 || *chunkEnd > 126) {
1372 write(
"%.*s\\x%02x", len, chunkStart,
1373 (
unsigned)(
unsigned char)*chunkEnd))) {
1376 chunkStart = chunkEnd = chunkEnd + 1;
1393 bool doCleanup =
false;
1418 static_assert(
sizeof(unsigned) >= 4,
"");
1422 write(
"#%s%09u\n%c%s\n",
str(
t().ts().tv_sec).c_str(),
1423 (
unsigned)
t().ts().tv_nsec, x,
vcdId(
fiber()).c_str()))) {
1442 if(Config::UseLimitedFormatSpecifiers)
1449 if(fgetpos(perf, &perf_pos))
1457 }
catch(std::bad_alloc
const&) {
1463 (void)fsetpos(perf, &perf_pos);
bool knows(void *x) const noexcept
Timestamp const & t() const noexcept
zth_perf_done_callback_t * done_callback() const noexcept
char const * data() noexcept
void dump_callback(zth_perf_dump_callback_t *f) noexcept
zth_perf_dump_callback_t * dump_callback() const noexcept
size_t size() const noexcept
void done_callback(zth_perf_done_callback_t *f) noexcept
char volatile * reserve(size_t s) noexcept
void start(zth_perf_done_callback_t *f=nullptr) noexcept
set_type< Known >::type KnownSet
bool enabled() const noexcept
bool full() const noexcept
void t(Timestamp const &t) noexcept
size_t space() const noexcept
void know(void *x) noexcept
static constexpr size_t capacity() noexcept
bool running() const noexcept
static long const BILLION
Convenient wrapper around struct timespec that contains an absolute timestamp.
uint64_t id() const noexcept
VCDDataGenerator(FILE *vcd)
virtual int handleLog(char const *log) override
virtual ~VCDDataGenerator() noexcept override=default
virtual int handleFiberState(int state) override
virtual ~VCDGenerator() noexcept=default
Timestamp const & t() const
uint64_t const & fiber() const
virtual int handleLog(char const *log)
virtual int handleFiber(uint64_t fiber, char const *name, size_t size)
int write(char const *fmt,...)
virtual int handleFiberState(int state)
int writev(char const *fmt, va_list args)
void vcdIdRelease(uint64_t fiber)
string const & vcdId(uint64_t fiber)
The class that manages the fibers within this thread.
char const * c_str() const
void zth_log(char const *fmt,...)
Logs a given printf()-like formatted string.
#define zth_config(name)
Checks if the given zth::Config field is enabled.
Worker & currentWorker() noexcept
Return the (thread-local) singleton Worker instance.
void perf_stop() noexcept
Stops recording perf events.
void perf_async_handle(zth_perf_async_handle_t *handle) noexcept
Returns the handle of the current thread's perf buffer.
void perf_mark(char const *marker, Timestamp const &t=Timestamp()) noexcept
Put a string marker into the perf output.
void perf_log(char const *fmt,...) noexcept
Put a formatted log string into the perf output.
void perf_abort() noexcept
Abort a zth::perf_run().
void perf_run(zth_perf_dump_callback_t *f) noexcept
Setup the system to automatically perform perf event recording, dumping, and resuming.
void perf_start(zth_perf_done_callback_t *f=nullptr) noexcept
Starts recording perf events.
void perf_dump(zth_perf_dump_callback_t *f) noexcept
Passes collected perf data to f.
void perf_logv(char const *fmt, va_list args, Timestamp const &t=Timestamp()) noexcept
Put a formatted log string into the perf output.
void perf_run_dump(char const *path=nullptr) noexcept
Like zth::perf_run(), but dump the contents to file immediately.
void perf_mark_async(char const *marker, zth_perf_async_handle_t *handle) noexcept
Async-/thread-safe zth::perf_mark().
#define zth_dbg(group, fmt, a...)
Debug printf()-like function.
std::basic_string< char, std::char_traits< char >, Config::Allocator< char >::type > string
std::string type using Config::Allocator::type.
char const * banner() noexcept
Returns a banner line with version and configuration information.
void log(char const *fmt,...)
Logs a given printf()-like formatted string.
#define ZTH_TLS_STATIC(type, var, init)
void perf_fiber_state(Fiber &f, int state=-1, Timestamp const &t=Timestamp()) noexcept
Record the current fiber state.
cow_string str(T value)
Returns an zth::string representation of the given value.
int perf_init()
Initializes the per-thread perf event buffer.
string err(int e)
Return a string like strerror() does, but as a zth::string.
int perf_vcd(char const *perf=nullptr, char const *vcd=nullptr) noexcept
Convert a perf file into VCD.
string format(char const *fmt,...)
Format like sprintf(), but save the result in an zth::string.
void perf_fiber(Fiber &f) noexcept
Write fiber ID/name to the perf buffer.
int perf_vcdf(FILE *perf, FILE *vcd) noexcept
Convert a perf file into VCD.
void() zth_perf_dump_callback_t(void const *, size_t)
void() zth_perf_done_callback_t()
static size_t const PerfEventBufferSize
Buffer size for perf events.
static bool const EnableThreads
Add (Worker) thread support when true.
static size_t const PerfEventBufferSpare
Minimum remaining space before perf event collection is stopped.
std::map< Key, T, Compare, typename Config::Allocator< std::pair< const Key, T > >::type > type
std::set< Key, Compare, typename Config::Allocator< Key >::type > type
#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.