1*03ce13f7SAndroid Build Coastguard Worker // Copyright 2019 The Marl Authors.
2*03ce13f7SAndroid Build Coastguard Worker //
3*03ce13f7SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*03ce13f7SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*03ce13f7SAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*03ce13f7SAndroid Build Coastguard Worker //
7*03ce13f7SAndroid Build Coastguard Worker // https://www.apache.org/licenses/LICENSE-2.0
8*03ce13f7SAndroid Build Coastguard Worker //
9*03ce13f7SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*03ce13f7SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*03ce13f7SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*03ce13f7SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*03ce13f7SAndroid Build Coastguard Worker // limitations under the License.
14*03ce13f7SAndroid Build Coastguard Worker
15*03ce13f7SAndroid Build Coastguard Worker // The Trace API produces a trace event file that can be consumed with Chrome's
16*03ce13f7SAndroid Build Coastguard Worker // about:tracing viewer.
17*03ce13f7SAndroid Build Coastguard Worker // Documentation can be found at:
18*03ce13f7SAndroid Build Coastguard Worker // https://www.chromium.org/developers/how-tos/trace-event-profiling-tool
19*03ce13f7SAndroid Build Coastguard Worker // https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit
20*03ce13f7SAndroid Build Coastguard Worker
21*03ce13f7SAndroid Build Coastguard Worker #include "marl/trace.h"
22*03ce13f7SAndroid Build Coastguard Worker
23*03ce13f7SAndroid Build Coastguard Worker #include "marl/defer.h"
24*03ce13f7SAndroid Build Coastguard Worker #include "marl/scheduler.h"
25*03ce13f7SAndroid Build Coastguard Worker #include "marl/thread.h"
26*03ce13f7SAndroid Build Coastguard Worker
27*03ce13f7SAndroid Build Coastguard Worker #if MARL_TRACE_ENABLED
28*03ce13f7SAndroid Build Coastguard Worker
29*03ce13f7SAndroid Build Coastguard Worker #include <atomic>
30*03ce13f7SAndroid Build Coastguard Worker #include <fstream>
31*03ce13f7SAndroid Build Coastguard Worker
32*03ce13f7SAndroid Build Coastguard Worker namespace {
33*03ce13f7SAndroid Build Coastguard Worker
34*03ce13f7SAndroid Build Coastguard Worker // Chrome traces can choke or error on very large trace files.
35*03ce13f7SAndroid Build Coastguard Worker // Limit the number of events created to this number.
36*03ce13f7SAndroid Build Coastguard Worker static constexpr int MaxEvents = 100000;
37*03ce13f7SAndroid Build Coastguard Worker
threadFiberID(uint32_t threadID,uint32_t fiberID)38*03ce13f7SAndroid Build Coastguard Worker uint64_t threadFiberID(uint32_t threadID, uint32_t fiberID) {
39*03ce13f7SAndroid Build Coastguard Worker return static_cast<uint64_t>(threadID) * 31 + static_cast<uint64_t>(fiberID);
40*03ce13f7SAndroid Build Coastguard Worker }
41*03ce13f7SAndroid Build Coastguard Worker
42*03ce13f7SAndroid Build Coastguard Worker } // anonymous namespace
43*03ce13f7SAndroid Build Coastguard Worker
44*03ce13f7SAndroid Build Coastguard Worker namespace marl {
45*03ce13f7SAndroid Build Coastguard Worker
get()46*03ce13f7SAndroid Build Coastguard Worker Trace* Trace::get() {
47*03ce13f7SAndroid Build Coastguard Worker static Trace trace;
48*03ce13f7SAndroid Build Coastguard Worker return &trace;
49*03ce13f7SAndroid Build Coastguard Worker }
50*03ce13f7SAndroid Build Coastguard Worker
Trace()51*03ce13f7SAndroid Build Coastguard Worker Trace::Trace() {
52*03ce13f7SAndroid Build Coastguard Worker nameThread("main");
53*03ce13f7SAndroid Build Coastguard Worker thread = std::thread([&] {
54*03ce13f7SAndroid Build Coastguard Worker Thread::setName("Trace worker");
55*03ce13f7SAndroid Build Coastguard Worker
56*03ce13f7SAndroid Build Coastguard Worker auto out = std::fstream("chrome.trace", std::ios_base::out);
57*03ce13f7SAndroid Build Coastguard Worker
58*03ce13f7SAndroid Build Coastguard Worker out << "[" << std::endl;
59*03ce13f7SAndroid Build Coastguard Worker defer(out << std::endl << "]" << std::endl);
60*03ce13f7SAndroid Build Coastguard Worker
61*03ce13f7SAndroid Build Coastguard Worker auto first = true;
62*03ce13f7SAndroid Build Coastguard Worker for (int i = 0; i < MaxEvents; i++) {
63*03ce13f7SAndroid Build Coastguard Worker auto event = take();
64*03ce13f7SAndroid Build Coastguard Worker if (event->type() == Event::Type::Shutdown) {
65*03ce13f7SAndroid Build Coastguard Worker return;
66*03ce13f7SAndroid Build Coastguard Worker }
67*03ce13f7SAndroid Build Coastguard Worker if (!first) {
68*03ce13f7SAndroid Build Coastguard Worker out << "," << std::endl;
69*03ce13f7SAndroid Build Coastguard Worker };
70*03ce13f7SAndroid Build Coastguard Worker first = false;
71*03ce13f7SAndroid Build Coastguard Worker out << "{" << std::endl;
72*03ce13f7SAndroid Build Coastguard Worker event->write(out);
73*03ce13f7SAndroid Build Coastguard Worker out << "}";
74*03ce13f7SAndroid Build Coastguard Worker }
75*03ce13f7SAndroid Build Coastguard Worker
76*03ce13f7SAndroid Build Coastguard Worker stopped = true;
77*03ce13f7SAndroid Build Coastguard Worker
78*03ce13f7SAndroid Build Coastguard Worker while (take()->type() != Event::Type::Shutdown) {
79*03ce13f7SAndroid Build Coastguard Worker }
80*03ce13f7SAndroid Build Coastguard Worker });
81*03ce13f7SAndroid Build Coastguard Worker }
82*03ce13f7SAndroid Build Coastguard Worker
~Trace()83*03ce13f7SAndroid Build Coastguard Worker Trace::~Trace() {
84*03ce13f7SAndroid Build Coastguard Worker put(new Shutdown());
85*03ce13f7SAndroid Build Coastguard Worker thread.join();
86*03ce13f7SAndroid Build Coastguard Worker }
87*03ce13f7SAndroid Build Coastguard Worker
nameThread(const char * fmt,...)88*03ce13f7SAndroid Build Coastguard Worker void Trace::nameThread(const char* fmt, ...) {
89*03ce13f7SAndroid Build Coastguard Worker if (stopped) {
90*03ce13f7SAndroid Build Coastguard Worker return;
91*03ce13f7SAndroid Build Coastguard Worker }
92*03ce13f7SAndroid Build Coastguard Worker auto event = new NameThreadEvent();
93*03ce13f7SAndroid Build Coastguard Worker
94*03ce13f7SAndroid Build Coastguard Worker va_list vararg;
95*03ce13f7SAndroid Build Coastguard Worker va_start(vararg, fmt);
96*03ce13f7SAndroid Build Coastguard Worker vsnprintf(event->name, Trace::MaxEventNameLength, fmt, vararg);
97*03ce13f7SAndroid Build Coastguard Worker va_end(vararg);
98*03ce13f7SAndroid Build Coastguard Worker
99*03ce13f7SAndroid Build Coastguard Worker put(event);
100*03ce13f7SAndroid Build Coastguard Worker }
101*03ce13f7SAndroid Build Coastguard Worker
beginEvent(const char * fmt,...)102*03ce13f7SAndroid Build Coastguard Worker void Trace::beginEvent(const char* fmt, ...) {
103*03ce13f7SAndroid Build Coastguard Worker if (stopped) {
104*03ce13f7SAndroid Build Coastguard Worker return;
105*03ce13f7SAndroid Build Coastguard Worker }
106*03ce13f7SAndroid Build Coastguard Worker auto event = new BeginEvent();
107*03ce13f7SAndroid Build Coastguard Worker
108*03ce13f7SAndroid Build Coastguard Worker va_list vararg;
109*03ce13f7SAndroid Build Coastguard Worker va_start(vararg, fmt);
110*03ce13f7SAndroid Build Coastguard Worker vsnprintf(event->name, Trace::MaxEventNameLength, fmt, vararg);
111*03ce13f7SAndroid Build Coastguard Worker va_end(vararg);
112*03ce13f7SAndroid Build Coastguard Worker
113*03ce13f7SAndroid Build Coastguard Worker event->timestamp = timestamp();
114*03ce13f7SAndroid Build Coastguard Worker put(event);
115*03ce13f7SAndroid Build Coastguard Worker }
116*03ce13f7SAndroid Build Coastguard Worker
endEvent()117*03ce13f7SAndroid Build Coastguard Worker void Trace::endEvent() {
118*03ce13f7SAndroid Build Coastguard Worker if (stopped) {
119*03ce13f7SAndroid Build Coastguard Worker return;
120*03ce13f7SAndroid Build Coastguard Worker }
121*03ce13f7SAndroid Build Coastguard Worker auto event = new EndEvent();
122*03ce13f7SAndroid Build Coastguard Worker event->timestamp = timestamp();
123*03ce13f7SAndroid Build Coastguard Worker put(event);
124*03ce13f7SAndroid Build Coastguard Worker }
125*03ce13f7SAndroid Build Coastguard Worker
beginAsyncEvent(uint32_t id,const char * fmt,...)126*03ce13f7SAndroid Build Coastguard Worker void Trace::beginAsyncEvent(uint32_t id, const char* fmt, ...) {
127*03ce13f7SAndroid Build Coastguard Worker if (stopped) {
128*03ce13f7SAndroid Build Coastguard Worker return;
129*03ce13f7SAndroid Build Coastguard Worker }
130*03ce13f7SAndroid Build Coastguard Worker auto event = new AsyncStartEvent();
131*03ce13f7SAndroid Build Coastguard Worker
132*03ce13f7SAndroid Build Coastguard Worker va_list vararg;
133*03ce13f7SAndroid Build Coastguard Worker va_start(vararg, fmt);
134*03ce13f7SAndroid Build Coastguard Worker vsnprintf(event->name, Trace::MaxEventNameLength, fmt, vararg);
135*03ce13f7SAndroid Build Coastguard Worker va_end(vararg);
136*03ce13f7SAndroid Build Coastguard Worker
137*03ce13f7SAndroid Build Coastguard Worker event->timestamp = timestamp();
138*03ce13f7SAndroid Build Coastguard Worker event->id = id;
139*03ce13f7SAndroid Build Coastguard Worker put(event);
140*03ce13f7SAndroid Build Coastguard Worker }
141*03ce13f7SAndroid Build Coastguard Worker
endAsyncEvent(uint32_t id,const char * fmt,...)142*03ce13f7SAndroid Build Coastguard Worker void Trace::endAsyncEvent(uint32_t id, const char* fmt, ...) {
143*03ce13f7SAndroid Build Coastguard Worker if (stopped) {
144*03ce13f7SAndroid Build Coastguard Worker return;
145*03ce13f7SAndroid Build Coastguard Worker }
146*03ce13f7SAndroid Build Coastguard Worker auto event = new AsyncEndEvent();
147*03ce13f7SAndroid Build Coastguard Worker
148*03ce13f7SAndroid Build Coastguard Worker va_list vararg;
149*03ce13f7SAndroid Build Coastguard Worker va_start(vararg, fmt);
150*03ce13f7SAndroid Build Coastguard Worker vsnprintf(event->name, Trace::MaxEventNameLength, fmt, vararg);
151*03ce13f7SAndroid Build Coastguard Worker va_end(vararg);
152*03ce13f7SAndroid Build Coastguard Worker
153*03ce13f7SAndroid Build Coastguard Worker event->timestamp = timestamp();
154*03ce13f7SAndroid Build Coastguard Worker event->id = id;
155*03ce13f7SAndroid Build Coastguard Worker put(event);
156*03ce13f7SAndroid Build Coastguard Worker }
157*03ce13f7SAndroid Build Coastguard Worker
timestamp()158*03ce13f7SAndroid Build Coastguard Worker uint64_t Trace::timestamp() {
159*03ce13f7SAndroid Build Coastguard Worker auto now = std::chrono::high_resolution_clock::now();
160*03ce13f7SAndroid Build Coastguard Worker auto diff =
161*03ce13f7SAndroid Build Coastguard Worker std::chrono::duration_cast<std::chrono::microseconds>(now - createdAt);
162*03ce13f7SAndroid Build Coastguard Worker return static_cast<uint64_t>(diff.count());
163*03ce13f7SAndroid Build Coastguard Worker }
164*03ce13f7SAndroid Build Coastguard Worker
put(Event * event)165*03ce13f7SAndroid Build Coastguard Worker void Trace::put(Event* event) {
166*03ce13f7SAndroid Build Coastguard Worker auto idx = eventQueueWriteIdx++ % eventQueues.size();
167*03ce13f7SAndroid Build Coastguard Worker auto& queue = eventQueues[idx];
168*03ce13f7SAndroid Build Coastguard Worker std::unique_lock<std::mutex> lock(queue.mutex);
169*03ce13f7SAndroid Build Coastguard Worker auto notify = queue.data.size() == 0;
170*03ce13f7SAndroid Build Coastguard Worker queue.data.push(std::unique_ptr<Event>(event));
171*03ce13f7SAndroid Build Coastguard Worker lock.unlock();
172*03ce13f7SAndroid Build Coastguard Worker if (notify) {
173*03ce13f7SAndroid Build Coastguard Worker queue.condition.notify_one();
174*03ce13f7SAndroid Build Coastguard Worker }
175*03ce13f7SAndroid Build Coastguard Worker }
176*03ce13f7SAndroid Build Coastguard Worker
take()177*03ce13f7SAndroid Build Coastguard Worker std::unique_ptr<Trace::Event> Trace::take() {
178*03ce13f7SAndroid Build Coastguard Worker auto idx = eventQueueReadIdx++ % eventQueues.size();
179*03ce13f7SAndroid Build Coastguard Worker auto& queue = eventQueues[idx];
180*03ce13f7SAndroid Build Coastguard Worker std::unique_lock<std::mutex> lock(queue.mutex);
181*03ce13f7SAndroid Build Coastguard Worker queue.condition.wait(lock, [&queue] { return queue.data.size() > 0; });
182*03ce13f7SAndroid Build Coastguard Worker auto event = std::move(queue.data.front());
183*03ce13f7SAndroid Build Coastguard Worker queue.data.pop();
184*03ce13f7SAndroid Build Coastguard Worker return event;
185*03ce13f7SAndroid Build Coastguard Worker }
186*03ce13f7SAndroid Build Coastguard Worker
187*03ce13f7SAndroid Build Coastguard Worker #define QUOTE(x) "\"" << x << "\""
188*03ce13f7SAndroid Build Coastguard Worker #define INDENT " "
189*03ce13f7SAndroid Build Coastguard Worker
Event()190*03ce13f7SAndroid Build Coastguard Worker Trace::Event::Event()
191*03ce13f7SAndroid Build Coastguard Worker : threadID(std::hash<std::thread::id>()(std::this_thread::get_id())) {
192*03ce13f7SAndroid Build Coastguard Worker if (auto fiber = Scheduler::Fiber::current()) {
193*03ce13f7SAndroid Build Coastguard Worker fiberID = fiber->id;
194*03ce13f7SAndroid Build Coastguard Worker }
195*03ce13f7SAndroid Build Coastguard Worker }
196*03ce13f7SAndroid Build Coastguard Worker
write(std::ostream & out) const197*03ce13f7SAndroid Build Coastguard Worker void Trace::Event::write(std::ostream& out) const {
198*03ce13f7SAndroid Build Coastguard Worker out << INDENT << QUOTE("name") << ": " << QUOTE(name) << "," << std::endl;
199*03ce13f7SAndroid Build Coastguard Worker if (categories != nullptr) {
200*03ce13f7SAndroid Build Coastguard Worker out << INDENT << QUOTE("cat") << ": "
201*03ce13f7SAndroid Build Coastguard Worker << "\"";
202*03ce13f7SAndroid Build Coastguard Worker auto first = true;
203*03ce13f7SAndroid Build Coastguard Worker for (auto category = *categories; category != nullptr; category++) {
204*03ce13f7SAndroid Build Coastguard Worker if (!first) {
205*03ce13f7SAndroid Build Coastguard Worker out << ",";
206*03ce13f7SAndroid Build Coastguard Worker }
207*03ce13f7SAndroid Build Coastguard Worker out << category;
208*03ce13f7SAndroid Build Coastguard Worker }
209*03ce13f7SAndroid Build Coastguard Worker out << "\"," << std::endl;
210*03ce13f7SAndroid Build Coastguard Worker }
211*03ce13f7SAndroid Build Coastguard Worker if (fiberID != 0) {
212*03ce13f7SAndroid Build Coastguard Worker out << INDENT << QUOTE("args") << ": "
213*03ce13f7SAndroid Build Coastguard Worker << "{" << std::endl
214*03ce13f7SAndroid Build Coastguard Worker << INDENT << INDENT << QUOTE("fiber") << ": " << fiberID << std::endl
215*03ce13f7SAndroid Build Coastguard Worker << INDENT << "}," << std::endl;
216*03ce13f7SAndroid Build Coastguard Worker }
217*03ce13f7SAndroid Build Coastguard Worker if (threadID != 0) {
218*03ce13f7SAndroid Build Coastguard Worker out << INDENT << QUOTE("tid") << ": " << threadFiberID(threadID, fiberID)
219*03ce13f7SAndroid Build Coastguard Worker << "," << std::endl;
220*03ce13f7SAndroid Build Coastguard Worker }
221*03ce13f7SAndroid Build Coastguard Worker out << INDENT << QUOTE("ph") << ": " << QUOTE(static_cast<char>(type()))
222*03ce13f7SAndroid Build Coastguard Worker << "," << std::endl
223*03ce13f7SAndroid Build Coastguard Worker << INDENT << QUOTE("pid") << ": " << processID << "," << std::endl
224*03ce13f7SAndroid Build Coastguard Worker << INDENT << QUOTE("ts") << ": " << timestamp << std::endl;
225*03ce13f7SAndroid Build Coastguard Worker }
226*03ce13f7SAndroid Build Coastguard Worker
write(std::ostream & out) const227*03ce13f7SAndroid Build Coastguard Worker void Trace::NameThreadEvent::write(std::ostream& out) const {
228*03ce13f7SAndroid Build Coastguard Worker out << INDENT << QUOTE("name") << ": " << QUOTE("thread_name") << ","
229*03ce13f7SAndroid Build Coastguard Worker << std::endl
230*03ce13f7SAndroid Build Coastguard Worker << INDENT << QUOTE("ph") << ": " << QUOTE("M") << "," << std::endl
231*03ce13f7SAndroid Build Coastguard Worker << INDENT << QUOTE("pid") << ": " << processID << "," << std::endl
232*03ce13f7SAndroid Build Coastguard Worker << INDENT << QUOTE("tid") << ": " << threadFiberID(threadID, fiberID)
233*03ce13f7SAndroid Build Coastguard Worker << "," << std::endl
234*03ce13f7SAndroid Build Coastguard Worker << INDENT << QUOTE("args") << ": {" << QUOTE("name") << ": "
235*03ce13f7SAndroid Build Coastguard Worker << QUOTE(name) << "}" << std::endl;
236*03ce13f7SAndroid Build Coastguard Worker }
237*03ce13f7SAndroid Build Coastguard Worker
write(std::ostream & out) const238*03ce13f7SAndroid Build Coastguard Worker void Trace::AsyncEvent::write(std::ostream& out) const {
239*03ce13f7SAndroid Build Coastguard Worker out << INDENT << QUOTE("id") << ": " << QUOTE(id) << "," << std::endl
240*03ce13f7SAndroid Build Coastguard Worker << INDENT << QUOTE("cat") << ": " << QUOTE("async") << "," << std::endl;
241*03ce13f7SAndroid Build Coastguard Worker Event::write(out);
242*03ce13f7SAndroid Build Coastguard Worker }
243*03ce13f7SAndroid Build Coastguard Worker
244*03ce13f7SAndroid Build Coastguard Worker } // namespace marl
245*03ce13f7SAndroid Build Coastguard Worker
246*03ce13f7SAndroid Build Coastguard Worker #endif // MARL_TRACE_ENABLED