13#include <spdlog/spdlog.h>
14#include <nlohmann/json.hpp>
33NLOHMANN_JSON_SERIALIZE_ENUM(
scope, {
65 template <
typename T>
struct duration_suffix_impl;
66 template <>
struct duration_suffix_impl<
std::chrono::milliseconds> {
67 static constexpr char const * value =
"ms";
69 template <>
struct duration_suffix_impl<
std::chrono::nanoseconds> {
70 static constexpr char const * value =
"ns";
76static constexpr char const *
duration_suffix = detail::duration_suffix_impl<T>::value;
79template <
typename Duration = std::chrono::nanoseconds,
typename Clock = std::chrono::high_resolution_clock>
86 size_t ts = std::chrono::duration_cast<duration>(clock::now().time_since_epoch()).count();
88 size_t tid = std::hash<std::thread::id>{}(std::this_thread::get_id());
90 std::optional<string>
id{};
95template <
typename Duration,
typename Clock>
98 if (not t.
cat) j[
"cat"] = t.
cat;
104 if (t.
id.has_value()) j[
"id"] = *t.
id;
105 if (t.
bp) j[
"bp"] = t.
bp;
106 if (not t.
args.is_null()) j[
"args"] = t.
args;
110 typename Mutex = std::mutex,
111 typename Duration = std::chrono::nanoseconds,
112 typename Clock = std::chrono::high_resolution_clock
139 spdlog::warn(
"Warning: unsaved profile, discarding {} event(s)",
events.size());
154#if defined(__APPLE__)
156 name = getprogname();
157#elif defined(__linux__)
159 name = program_invocation_short_name;
186 log({.name = std::move(name), .cat = std::move(cat), .ph =
event_type::flow_end, .id = std::move(
id), .bp =
"e"_ss});
191 spdlog::info(
"Saving profile: {}",
filename.c_str());
196 template <
typename OStream>
200 o << std::setw(2) << nlohmann::json(p) << std::endl;
207template <
typename Mutex,
typename Duration,
typename Clock>
211 {
"traceEvents", p.events},
214 if (p.metadata.is_object())
215 for (
const auto &e : p.metadata.template get<nlohmann::json::object_t>())
216 j[e.first] = e.second;
219template <
typename Profile>
222 using clock =
typename Profile::clock;
223 using mutex =
typename Profile::mutex;
245template <
typename Profile>
247 using event =
typename Profile::event;
280 typename Mutex = std::mutex,
281 typename Duration = std::chrono::nanoseconds,
282 typename Clock = std::chrono::high_resolution_clock
321template <
typename K,
typename V,
typename ... Args>
322inline void make_args(nlohmann::json & j, K && k, V && v, Args && ... args) {
323 j[std::forward(k)] = std::forward(v);
324 make_args(j,std::forward<Args>(args)...);
353#if defined(EIN_TESTING) || defined(EIN_TESTING_PROFILING)
354TEST_CASE(
"profiling",
"[profiling]") {
355 using namespace nlohmann;
359 using std::filesystem::path;
360 using namespace std::chrono;
362 SECTION(
"scope enum serialization") {
363 json j = scope::global;
373 SECTION(
"event_type enum serialization") {
374 json j = event_type::duration_begin;
377 j = event_type::duration_end;
380 j = event_type::complete;
386TEST_CASE(
"profiling logging (broken)",
"[.profiling]") {
387 using namespace nlohmann;
390 using std::filesystem::path;
391 using namespace std::chrono;
392 SECTION(
"profile_event JSON serialization") {
394 .name =
"test_event"_ss,
395 .cat =
"test_category"_ss,
396 .ph = event_type::instant,
401 CHECK(j[
"name"] ==
"test_event");
402 CHECK(j[
"cat"] ==
"test_category");
403 CHECK(j[
"ph"] ==
"i");
404 CHECK(j[
"s"] ==
"t");
407 SECTION(
"profile logging") {
410 .name =
"log_event"_ss,
411 .ph = event_type::instant,
417 CHECK(profiler.
events.size() == 1);
418 CHECK(profiler.
events[0].name ==
"log_event"_scs);
419 CHECK(profiler.
events[0].ph == event_type::instant);
420 CHECK(profiler.
events[0].s == scope::thread);
423 SECTION(
"profile_scope automatic saving") {
424 path test_path =
"test_profile.json";
428 .name =
"scope_event"_ss,
429 .ph = event_type::instant,
433 profiler.p.
log(event);
436 CHECK(exists(test_path));
#define ein_inline
inline [[always_inline]]
#define ein_nodiscard
C++17 [[nodiscard]].
void make_args(nlohmann::json &) noexcept
void to_json(nlohmann::json &j, const profile_event< Duration, Clock > &t)
static constexpr char const * duration_suffix
std::optional< string > id
duration_event(Profile &profile, static_string name) noexcept
typename Profile::event event
profile_scope(profile_scope const &)=delete
profile_scope(std::filesystem::path filename) noexcept
std::filesystem::path filename
profile_scope & operator=(profile_scope &&)=delete
~profile_scope() noexcept
profile_scope(profile_scope &&)=delete
profile(profile const &)=delete
friend OStream & operator<<(OStream &o, profile &p)
void set_process_name(std::string name="") noexcept
void flow_start(static_string name, static_string category, static_string id) noexcept
void flow_end(std::string name, static_string cat, static_string id) noexcept
std::filesystem::path filename
void set_thread_name(std::string const &name) noexcept
profile & operator=(profile const &)=delete
void log(event const &e) noexcept
profile(profile &&)=delete
std::vector< event > events
void counter(static_string name, nlohmann::json args) noexcept
void save(std::filesystem::path filename) noexcept
void instant(static_string name, scope s) noexcept
typename Profile::duration duration
typename Profile::clock clock
scope_event(Profile &profile, static_string name) noexcept
typename Profile::mutex mutex