3#if GUANAQO_WITH_TRACING
5#include <batmat/assume.hpp>
25namespace fs = std::filesystem;
27inline auto ns_to_us(std::chrono::nanoseconds ns) {
28 return std::chrono::duration<double, std::micro>(ns).count();
31inline auto gflops(
const guanaqo::TraceLogger::Log &log) {
33 auto duration = std::chrono::duration<double, std::nano>(log.
duration).count();
34 return valid ?
static_cast<double>(log.
flop_count) / duration : 0.0;
37inline bool is_barrier_event(
const guanaqo::TraceLogger::Log &log) {
38 static constexpr std::string_view prefix =
"barrier-";
39 return std::strncmp(log.
name, prefix.data(), prefix.size()) == 0;
42inline bool is_read_event(
const guanaqo::TraceLogger::Log &log) {
43 static constexpr std::string_view prefix =
"r:";
44 return std::strncmp(log.
name, prefix.data(), prefix.size()) == 0;
47inline bool is_write_event(
const guanaqo::TraceLogger::Log &log) {
48 static constexpr std::string_view prefix =
"w:";
49 return std::strncmp(log.
name, prefix.data(), prefix.size()) == 0;
53struct gzfile_deleter {
54 void operator()(gzFile f)
const noexcept { gzclose(f); }
56using gzfile_ptr = std::unique_ptr<std::remove_pointer_t<gzFile>, gzfile_deleter>;
58inline void gzwrite(gzFile f, std::string_view sv) {
59 gzwrite(f, sv.data(),
static_cast<unsigned>(sv.size()));
63inline std::function<void(std::string_view)> get_writer(
const fs::path &path) {
64 if (path.filename().string().ends_with(
".json.gz")) {
66 gzfile_ptr file{gzopen(path.string().c_str(),
"wb")};
68 throw std::runtime_error(
"Failed to open file " + path.string() +
" for writing");
69 return [file = std::shared_ptr{std::move(file)}](std::string_view sv) {
70 gzwrite(file.get(), sv);
73 throw std::runtime_error(
"Cannot write .gz file: zlib support not enabled");
75 }
else if (path.filename().string().ends_with(
".json")) {
76 auto file = std::make_shared<std::ofstream>(path);
77 if (!file || !file->is_open())
78 throw std::runtime_error(
"Failed to open file " + path.string() +
" for writing");
79 return [file = std::move(file)](std::string_view sv) {
80 file->write(sv.data(),
static_cast<ptrdiff_t
>(sv.size()));
83 throw std::runtime_error(
"Unsupported file extension: should be .json or .json.gz");
87void write_chrome_trace(
const fs::path &path, std::span<const guanaqo::TraceLogger::Log> logs,
88 const TracingOptions &opts) {
89 using namespace std::string_view_literals;
90 auto writer = get_writer(path);
91 std::string buf(4095,
'\0');
93 writer(
"{\"traceEvents\":["sv);
94 std::map<std::size_t, std::size_t> thread_ids;
95 std::map<std::size_t, uint64_t> thread_flops;
96 std::size_t num_threads = 0;
97 for (
const auto &log : logs)
99 num_threads = std::max(num_threads,
static_cast<std::size_t
>(log.
instance + 1));
100 for (
const auto &log : logs)
101 if (thread_ids.try_emplace(log.
thread_id, num_threads).second)
103 auto get_thread_id = [&](std::size_t orig_id) {
104 auto it = thread_ids.find(orig_id);
105 return it != thread_ids.end() ? it->second : orig_id;
107 for (
const auto &log : logs) {
108 if (log.
name ==
"thread_id"sv)
110 if (opts.no_barrier && is_barrier_event(log))
114 const auto dur_us = ns_to_us(log.
duration);
117 if (is_read_event(log)) {
118 return std::format_to_n(
119 buf.data(),
static_cast<ptrdiff_t
>(buf.size()),
120 "{}{{\"cat\":\"{}\",\"id\":{},\"ph\":\"f\",\"ts\":{:.3f},"
121 "\"pid\":0,\"tid\":{}}}",
122 first ?
"\n" :
",\n", std::string_view{log.name}.substr(2), log.
instance, ts_us,
124 }
else if (is_write_event(log)) {
125 return std::format_to_n(
126 buf.data(),
static_cast<ptrdiff_t
>(buf.size()),
127 "{}{{\"cat\":\"{}\",\"id\":{},\"ph\":\"s\",\"ts\":{:.3f},"
128 "\"pid\":0,\"tid\":{}}}",
129 first ?
"\n" :
",\n", std::string_view{log.name}.substr(2), log.
instance, ts_us,
132 std::string_view cat = is_barrier_event(log) ?
"barrier" :
"trace";
134 return std::format_to_n(
135 buf.data(),
static_cast<ptrdiff_t
>(buf.size()),
136 "{}{{\"name\":\"{}\",\"cat\":\"{}\",\"ph\":\"X\",\"ts\":{:.3f},"
137 "\"pid\":0,\"tid\":{},\"dur\":{:.3f},"
138 "\"args\":{{\"instance\":{}}}}}",
139 first ?
"\n" :
",\n", log.
name, cat, ts_us, tid, dur_us, log.
instance);
141 return std::format_to_n(
142 buf.data(),
static_cast<ptrdiff_t
>(buf.size()),
143 "{}{{\"name\":\"{}\",\"cat\":\"{}\",\"ph\":\"i\",\"ts\":{:.3f},"
144 "\"pid\":0,\"tid\":{},"
145 "\"args\":{{\"instance\":{}}}}}",
146 first ?
"\n" :
",\n", log.
name, cat, ts_us, tid, log.
instance);
148 uint64_t &total_flops = thread_flops[tid];
149 auto old_flops = 1e-9 *
static_cast<double>(total_flops);
150 auto new_flops = 1e-9 *
static_cast<double>(total_flops += log.
flop_count);
151 return std::format_to_n(
152 buf.data(),
static_cast<ptrdiff_t
>(buf.size()),
153 "{0}{{\"name\":\"{1}\",\"cat\":\"gflops\",\"ph\":\"X\",\"ts\":{2:.3f},"
154 "\"pid\":0,\"tid\":{3},\"dur\":{4:.3f},\"args\":{{\"instance\":{5},"
155 "\"flop_count\":{6},\"gflops\":{7:.6f}}}}},\n"
156 " {{\"name\":\"gflops_{3}\",\"cat\":\"{1}\",\"ph\":\"C\",\"ts\":{2:.3f},"
157 "\"pid\":0,\"tid\":{3},\"args\":{{\"gflop_count\":{8:.9f}}}}},\n"
158 " {{\"name\":\"gflops_{3}\",\"cat\":\"{1}\",\"ph\":\"C\",\"ts\":{9:.3f},"
159 "\"pid\":0,\"tid\":{3},\"args\":{{\"gflop_count\":{10:.9f}}}}}",
160 first ?
"\n" :
",\n",
174 writer(std::string_view{buf.data(),
static_cast<size_t>(entry.size)});
std::chrono::nanoseconds start_time
std::chrono::nanoseconds duration