1 | #include "taichi/system/timeline.h" |
2 | |
3 | namespace taichi { |
4 | |
5 | std::string TimelineEvent::to_json() { |
6 | std::string json{"{" }; |
7 | json += fmt::format("\"cat\":\"taichi\"," ); |
8 | json += fmt::format("\"pid\":0," ); |
9 | json += fmt::format("\"tid\":\"{}\"," , tid); |
10 | json += fmt::format("\"ph\":\"{}\"," , begin ? "B" : "E" ); |
11 | json += fmt::format("\"name\":\"{}\"," , name); |
12 | json += fmt::format("\"ts\":\"{}\"" , uint64(time * 1000000)); |
13 | json += "}" ; |
14 | return json; |
15 | } |
16 | |
17 | Timeline::Timeline() : tid_("unnamed" ) { |
18 | Timelines::get_instance().insert_timeline(this); |
19 | } |
20 | |
21 | Timeline &Timeline::get_this_thread_instance() { |
22 | thread_local Timeline instance; |
23 | return instance; |
24 | } |
25 | |
26 | Timeline::~Timeline() { |
27 | Timelines::get_instance().insert_events(fetch_events()); |
28 | Timelines::get_instance().remove_timeline(this); |
29 | } |
30 | |
31 | void Timeline::clear() { |
32 | std::lock_guard<std::mutex> _(mut_); |
33 | events_.clear(); |
34 | } |
35 | void Timeline::insert_event(const TimelineEvent &e) { |
36 | if (!Timelines::get_instance().get_enabled()) |
37 | return; |
38 | std::lock_guard<std::mutex> _(mut_); |
39 | events_.push_back(e); |
40 | } |
41 | |
42 | std::vector<TimelineEvent> Timeline::fetch_events() { |
43 | std::lock_guard<std::mutex> _(mut_); |
44 | std::vector<TimelineEvent> fetched; |
45 | std::swap(fetched, events_); |
46 | return fetched; |
47 | } |
48 | |
49 | Timeline::Guard::Guard(const std::string &name) : name_(name) { |
50 | auto &timeline = Timeline::get_this_thread_instance(); |
51 | timeline.insert_event({name, true, Time::get_time(), timeline.tid_}); |
52 | } |
53 | |
54 | Timeline::Guard::~Guard() { |
55 | auto &timeline = Timeline::get_this_thread_instance(); |
56 | timeline.insert_event({name_, false, Time::get_time(), timeline.tid_}); |
57 | } |
58 | |
59 | void Timelines::insert_events(const std::vector<TimelineEvent> &events) { |
60 | std::lock_guard<std::mutex> _(mut_); |
61 | insert_events_without_locking(events); |
62 | } |
63 | |
64 | void Timelines::insert_events_without_locking( |
65 | const std::vector<TimelineEvent> &events) { |
66 | events_.insert(events_.end(), events.begin(), events.end()); |
67 | } |
68 | |
69 | Timelines &taichi::Timelines::get_instance() { |
70 | static auto instance = new Timelines(); |
71 | return *instance; |
72 | } |
73 | |
74 | void Timelines::clear() { |
75 | std::lock_guard<std::mutex> _(mut_); |
76 | events_.clear(); |
77 | for (auto timeline : timelines_) { |
78 | timeline->clear(); |
79 | } |
80 | } |
81 | |
82 | void Timelines::save(const std::string &filename) { |
83 | std::lock_guard<std::mutex> _(mut_); |
84 | std::sort(timelines_.begin(), timelines_.end(), [](Timeline *a, Timeline *b) { |
85 | return a->get_name() < b->get_name(); |
86 | }); |
87 | for (auto timeline : timelines_) { |
88 | insert_events_without_locking(timeline->fetch_events()); |
89 | } |
90 | if (!ends_with(filename, ".json" )) { |
91 | TI_WARN("Timeline filename {} should end with '.json'." , filename); |
92 | } |
93 | std::ofstream fout(filename); |
94 | fout << "[" ; |
95 | bool first = true; |
96 | for (auto &e : events_) { |
97 | if (first) { |
98 | first = false; |
99 | } else { |
100 | fout << "," ; |
101 | } |
102 | fout << e.to_json() << std::endl; |
103 | } |
104 | fout << "]" ; |
105 | } |
106 | |
107 | void Timelines::insert_timeline(Timeline *timeline) { |
108 | std::lock_guard<std::mutex> _(mut_); |
109 | timelines_.push_back(timeline); |
110 | } |
111 | |
112 | void Timelines::remove_timeline(Timeline *timeline) { |
113 | std::lock_guard<std::mutex> _(mut_); |
114 | trash(std::remove(timelines_.begin(), timelines_.end(), timeline)); |
115 | } |
116 | |
117 | bool Timelines::get_enabled() { |
118 | return enabled_; |
119 | } |
120 | |
121 | void Timelines::set_enabled(bool enabled) { |
122 | enabled_ = enabled; |
123 | } |
124 | |
125 | } // namespace taichi |
126 | |