guanaqo main
Utilities for scientific software
Loading...
Searching...
No Matches
trace.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file
4/// @ingroup trace_core
5/// Tracing logger and macros (ITT, Perfetto, or fallback).
6
7#include <guanaqo/export.h>
10#include <guanaqo/stringify.h>
11
12#include <algorithm>
13#include <span>
14#include <utility>
15
16#if GUANAQO_WITH_ITT
17#include <ittnotify.h>
18#include <iosfwd>
19#else
20#include <atomic>
21#include <chrono>
22#include <cstddef>
23#include <cstdint>
24#include <iomanip>
25#include <ostream>
26#include <thread>
27#endif
28
29namespace guanaqo {
30
31#if GUANAQO_WITH_ITT
32struct TraceLogger {
33 struct Log {
34 friend std::ostream &operator<<(std::ostream &os, Log) { return os; }
35 };
36
37 __itt_domain *domain = __itt_domain_create("guanaqo");
38 static constexpr int64_t max_instance_num = 255;
39 TraceLogger(size_t = 0) {
40 for (int64_t i = 0; i <= max_instance_num; ++i)
41 __itt_id_create(
42 domain,
43 __itt_id_make(nullptr, static_cast<unsigned long long>(i)));
44 }
45
46 TraceLogger(const TraceLogger &) = delete;
47 TraceLogger &operator=(const TraceLogger &) = delete;
48 TraceLogger(TraceLogger &&other) noexcept
49 : domain{std::exchange(other.domain, nullptr)} {}
50 TraceLogger &operator=(TraceLogger &&) = delete;
51 ~TraceLogger() {
52 if (!domain)
53 return;
54 for (int64_t i = 0; i <= max_instance_num; ++i)
55 __itt_id_destroy(
56 domain,
57 __itt_id_make(nullptr, static_cast<unsigned long long>(i)));
58 }
59
60 struct ScopedLog {
61 __itt_domain *domain;
62 ScopedLog(__itt_domain *domain, __itt_string_handle *name,
63 int64_t instance)
64 : domain{domain} {
65 if (!domain)
66 return;
67 instance = std::clamp<int64_t>(instance, 0, max_instance_num);
68 auto id = __itt_id_make(nullptr,
69 static_cast<unsigned long long>(instance));
70 __itt_task_begin(domain, id, __itt_null, name);
71 }
72 ScopedLog(const ScopedLog &) = delete;
73 ScopedLog &operator=(const ScopedLog &) = delete;
74 ScopedLog(ScopedLog &&other) noexcept
75 : domain{std::exchange(other.domain, nullptr)} {}
76 ScopedLog &operator=(ScopedLog &&) = delete;
77 ~ScopedLog() {
78 if (!domain)
79 return;
80 __itt_task_end(domain);
81 }
82 };
83
84 [[nodiscard]] ScopedLog trace(__itt_string_handle *name,
85 int64_t instance) const {
86 return ScopedLog{domain, name, instance};
87 }
88
89 void trace_instant(__itt_string_handle *name, int64_t instance) const {
90 if (!domain)
91 return;
92 instance = std::clamp<int64_t>(instance, 0, max_instance_num);
93 auto id =
94 __itt_id_make(nullptr, static_cast<unsigned long long>(instance));
95 __itt_marker(domain, id, name, __itt_scope_task);
96 }
97
98 [[nodiscard]] std::span<const Log> get_logs() const { return {}; }
99
100 void reset() {}
101};
102
103GUANAQO_EXPORT TraceLogger &get_trace_logger();
104
105#if !GUANAQO_WITH_PERFETTO
106#define GUANAQO_TRACE_IMPL(var_name, name, instance) \
107 static auto GUANAQO_CAT(var_name, _name) = \
108 __itt_string_handle_create(name); \
109 const auto var_name = ::guanaqo::get_trace_logger().trace( \
110 GUANAQO_CAT(var_name, _name), instance)
111#define GUANAQO_TRACE_INSTANT_IMPL(var_name, name, instance) \
112 do { \
113 static auto GUANAQO_CAT(var_name, _name) = \
114 __itt_string_handle_create(name); \
115 ::guanaqo::get_trace_logger().trace_instant( \
116 GUANAQO_CAT(var_name, _name), instance); \
117 } while (0)
118#define GUANAQO_TRACE(name, instance, ...) \
119 GUANAQO_TRACE_IMPL(GUANAQO_CAT(trace_log_, __COUNTER__), name, instance)
120#define GUANAQO_TRACE_INSTANT(name, instance) \
121 GUANAQO_TRACE_INSTANT_IMPL(GUANAQO_CAT(trace_instant_, __COUNTER__), name, \
122 instance)
123#define GUANAQO_TRACE_LINALG(name, gflops) \
124 GUANAQO_TRACE(name, 0) /* TODO: record gflops? */
125#define GUANAQO_TRACE_REGION(name, instance) GUANAQO_TRACE(name, instance)
126#define GUANAQO_TRACE_STATIC_STR(s) s
127#endif
128
129#else
130
131/// Class for recording trace logs, used when ITT or Perfetto tracing is not enabled.
132/// @ingroup trace_core
134 struct Log {
135 const char *name = "";
136 int64_t instance{0};
137 std::chrono::nanoseconds start_time{0};
138 std::chrono::nanoseconds duration{0};
139 std::size_t thread_id{0};
140 int64_t flop_count{0};
141
142 friend std::ostream &operator<<(std::ostream &os, const Log &log) {
143 return os << std::quoted(log.name) << ',' << log.instance << ','
144 << log.start_time.count() << ',' << log.duration.count()
145 << ',' << log.thread_id << ',' << log.flop_count;
146 }
147 };
148
149 static std::ostream &write_column_headings(std::ostream &os) {
150 return os << "name" << ',' << "instance" << ',' << "start_time" << ','
151 << "duration" << ',' << "thread_id" << ',' << "flop_count";
152 }
153
154 using clock = std::chrono::steady_clock;
155
156 clock::time_point t0 = clock::now();
157 std::vector<Log> logs;
158 std::atomic_size_t count{0};
159
160 struct ScopedLog {
161 Log *log = nullptr;
162 clock::time_point start_time_point;
163
164 ScopedLog() = default;
167 ScopedLog(const ScopedLog &) = delete;
168 ScopedLog &operator=(const ScopedLog &) = delete;
169 ScopedLog(ScopedLog &&other) noexcept
170 : log{std::exchange(other.log, nullptr)},
171 start_time_point{other.start_time_point} {}
174 if (log)
175 log->duration = clock::now() - start_time_point;
176 }
177 };
178
179 TraceLogger(size_t capacity) { logs.resize(capacity); }
180
181 [[nodiscard]] ScopedLog trace(const char *name, int64_t instance,
182 int64_t flop_count = -1) {
183 size_t index = count.fetch_add(1, std::memory_order_relaxed);
184 if (index >= logs.size())
185 return ScopedLog{nullptr, {}};
186 static constexpr std::hash<std::thread::id> hasher;
187 auto &log = logs[index];
188 auto t1 = clock::now();
189 log.name = name;
190 log.instance = instance;
191 log.flop_count = flop_count;
192 log.start_time = t1 - t0;
193 log.thread_id = hasher(std::this_thread::get_id());
194 return ScopedLog{&log, t1};
195 }
196
197 void trace_instant(const char *name, int64_t instance,
198 int64_t flop_count = -1) {
199 auto instant_log = trace(name, instance, flop_count);
200 instant_log.log = nullptr;
201 }
202
203 [[nodiscard]] std::span<const Log> get_logs() const {
204 auto n = std::min(logs.size(), count.load(std::memory_order_relaxed));
205 return std::span{logs}.first(n);
206 }
207
208 void reset() { count.store(0, std::memory_order_relaxed); }
209};
210
211#if GUANAQO_WITH_TRACING
212/// Get a reference to the global trace logger instance.
213/// @note Tracing is thread-safe, but for performance reasons, it may be desirable to use a separate
214/// logger for each thread.
215/// @ingroup trace_core
216GUANAQO_EXPORT TraceLogger &get_trace_logger();
217#endif
218
219#endif
220
221#if !GUANAQO_WITH_PERFETTO && !GUANAQO_WITH_ITT
222#if GUANAQO_WITH_TRACING
223#define GUANAQO_TRACE(name, ...) \
224 const auto GUANAQO_CAT(trace_log_, __COUNTER__) = \
225 ::guanaqo::get_trace_logger().trace(name, __VA_ARGS__)
226#define GUANAQO_TRACE_INSTANT(name, instance) \
227 do { \
228 ::guanaqo::get_trace_logger().trace_instant(name, instance); \
229 } while (0)
230#define GUANAQO_TRACE_LINALG(name, gflops) GUANAQO_TRACE(name, 0, gflops)
231#define GUANAQO_TRACE_REGION(name, instance) GUANAQO_TRACE(name, instance)
232#define GUANAQO_TRACE_STATIC_STR(s) s
233#else
234#define GUANAQO_TRACE(name, ...) GUANAQO_NOOP()
235#define GUANAQO_TRACE_INSTANT(name, instance) GUANAQO_NOOP()
236#define GUANAQO_TRACE_LINALG(name, gflops) GUANAQO_NOOP()
237#define GUANAQO_TRACE_REGION(name, instance) GUANAQO_NOOP()
238#define GUANAQO_TRACE_STATIC_STR(s) s
239#endif
240#endif
241
242#if GUANAQO_WITH_ITT
243#define GUANAQO_IF_ITT(...) __VA_ARGS__
244#else
245#define GUANAQO_IF_ITT(...)
246#endif
247
248} // namespace guanaqo
TraceLogger & get_trace_logger()
Get a reference to the global trace logger instance.
Definition trace.cpp:5
Perfetto tracing macros and session helpers.
Token concatenation and argument-counting helpers.
Stringify and token concatenation helpers.
std::chrono::nanoseconds start_time
Definition trace.hpp:137
std::chrono::nanoseconds duration
Definition trace.hpp:138
friend std::ostream & operator<<(std::ostream &os, const Log &log)
Definition trace.hpp:142
ScopedLog(Log *log, clock::time_point start_time_point)
Definition trace.hpp:165
ScopedLog(const ScopedLog &)=delete
ScopedLog & operator=(ScopedLog &&)=delete
ScopedLog(ScopedLog &&other) noexcept
Definition trace.hpp:169
clock::time_point start_time_point
Definition trace.hpp:162
ScopedLog & operator=(const ScopedLog &)=delete
Class for recording trace logs, used when ITT or Perfetto tracing is not enabled.
Definition trace.hpp:133
clock::time_point t0
Definition trace.hpp:156
void trace_instant(const char *name, int64_t instance, int64_t flop_count=-1)
Definition trace.hpp:197
std::vector< Log > logs
Definition trace.hpp:157
TraceLogger(size_t capacity)
Definition trace.hpp:179
std::chrono::steady_clock clock
Definition trace.hpp:154
ScopedLog trace(const char *name, int64_t instance, int64_t flop_count=-1)
Definition trace.hpp:181
static std::ostream & write_column_headings(std::ostream &os)
Definition trace.hpp:149
std::atomic_size_t count
Definition trace.hpp:158
std::span< const Log > get_logs() const
Definition trace.hpp:203