1 // Copyright (C) 2020 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "perfetto/perfetto_consumer.h"
16
17 #include "common/trace.h"
18
19 #include <android-base/logging.h>
20 #include <android-base/properties.h>
21 #include <utils/Looper.h>
22 #include <utils/Printer.h>
23
24 #include <limits>
25 #include <map>
26 #include <memory>
27 #include <mutex>
28 #include <sstream>
29 #include <vector>
30
31 #include <inttypes.h>
32 #include <time.h>
33
34 namespace iorap::perfetto {
35
36 using State = PerfettoConsumer::State;
37 using Handle = PerfettoConsumer::Handle;
38 static constexpr Handle kInvalidHandle = PerfettoConsumer::kInvalidHandle;
39 using OnStateChangedCb = PerfettoConsumer::OnStateChangedCb;
40 using TraceBuffer = PerfettoConsumer::TraceBuffer;
41
42 enum class StateKind {
43 kUncreated,
44 kCreated,
45 kStartedTracing,
46 kReadTracing,
47 kTimedOutDestroyed, // same as kDestroyed but timed out.
48 kDestroyed, // calling kDestroyed before timing out.
49 };
50
operator <<(std::ostream & os,StateKind kind)51 std::ostream& operator<<(std::ostream& os, StateKind kind) {
52 switch (kind) {
53 case StateKind::kUncreated:
54 os << "kUncreated";
55 break;
56 case StateKind::kCreated:
57 os << "kCreated";
58 break;
59 case StateKind::kStartedTracing:
60 os << "kStartedTracing";
61 break;
62 case StateKind::kReadTracing:
63 os << "kReadTracing";
64 break;
65 case StateKind::kTimedOutDestroyed:
66 os << "kTimedOutDestroyed";
67 break;
68 case StateKind::kDestroyed:
69 os << "kDestroyed";
70 break;
71 default:
72 os << "(invalid)";
73 break;
74 }
75 return os;
76 }
77
ToString(StateKind kind)78 std::string ToString(StateKind kind) {
79 std::stringstream ss;
80 ss << kind;
81 return ss.str();
82 }
83
84 static constexpr uint64_t kSecToNano = 1000000000LL;
85
GetTimeNanoseconds()86 static uint64_t GetTimeNanoseconds() {
87 struct timespec now;
88 clock_gettime(CLOCK_REALTIME, &now);
89
90 uint64_t now_ns = (now.tv_sec * kSecToNano + now.tv_nsec);
91 return now_ns;
92 }
93
94 // Describe the state of our handle in detail for debugging/logging.
95 struct HandleDescription {
96 Handle handle_;
97 StateKind kind_{StateKind::kUncreated}; // Our state. required for correctness.
98 OnStateChangedCb callback_{nullptr}; // Required for Destroy callbacks.
99 void* callback_arg_{nullptr};
100
101 // For dumping to logs:
102 State state_{State::kSessionNotFound}; // perfetto state
103 std::optional<uint64_t> started_tracing_ns_; // when StartedTracing last called.
104 std::optional<uint64_t> read_trace_ns_; // When ReadTrace last called.
105 std::uint64_t last_transition_ns_{0};
106 std::optional<uint64_t> trace_cookie_; // atrace beginning at StartTracing.
107 bool trace_ended_{false}; // atrace ending at ReadTrace or Destroy.
108
HandleDescriptioniorap::perfetto::HandleDescription109 HandleDescription(Handle handle): handle_(handle) {}
110 };
111
112 // pimpl idiom to hide the implementation details from header
113 //
114 // Track and verify that our perfetto usage is sane.
115 struct PerfettoConsumerImpl::Impl {
Impliorap::perfetto::PerfettoConsumerImpl::Impl116 Impl() : raw_{new PerfettoConsumerRawImpl{}},
117 message_handler_{new TraceMessageHandler{this}} {
__anon2c5028df0102() 118 std::thread watchdog_thread{ [this]() {
119 ::android::sp<::android::Looper> looper;
120 {
121 std::lock_guard<std::mutex> guard{looper_mutex_};
122 looper = ::android::Looper::prepare(/*opts*/0);
123 looper_ = looper;
124 }
125
126 static constexpr int kTimeoutMillis = std::numeric_limits<int>::max();
127
128 while (true) {
129 // Execute any pending callbacks, otherwise just block forever.
130 int result = looper->pollAll(kTimeoutMillis);
131
132 if (result == ::android::Looper::POLL_ERROR) {
133 LOG(ERROR) << "PerfettoConsumerImpl::Looper got a POLL_ERROR";
134 } else {
135 LOG(DEBUG) << "PerfettoConsumerImpl::Looper result was " << result;
136 }
137 }
138 }};
139
140 // Let thread run freely on its own.
141 watchdog_thread.detach();
142
143 // Block until looper_ is prepared.
144 while (true) {
145 std::lock_guard<std::mutex> guard{looper_mutex_};
146 if (looper_ != nullptr) {
147 break;
148 }
149 }
150 }
151
152 private:
153 std::unique_ptr<PerfettoConsumerRawImpl> raw_;
154 std::map<Handle, HandleDescription> states_;
155
156 // We need this to be a counter to avoid memory leaks.
157 Handle last_created_{0};
158 Handle last_destroyed_{0};
159 uint64_t trace_cookie_{0};
160
161 std::mutex mutex_; // Guard above values.
162
163 ::android::sp<::android::Looper> looper_;
164 std::mutex looper_mutex_; // Guard looper_.
165
166 struct TraceMessageHandler : public ::android::MessageHandler {
TraceMessageHandleriorap::perfetto::PerfettoConsumerImpl::Impl::TraceMessageHandler167 TraceMessageHandler(Impl* impl) : impl_{impl} {
168 CHECK(impl != nullptr);
169 }
170
171 Impl* impl_;
172
handleMessageiorap::perfetto::PerfettoConsumerImpl::Impl::TraceMessageHandler173 virtual void handleMessage(const ::android::Message& message) override {
174 impl_->OnTraceMessage(static_cast<Handle>(message.what));
175 }
176 };
177
178 ::android::sp<TraceMessageHandler> message_handler_;
179
180 public:
Createiorap::perfetto::PerfettoConsumerImpl::Impl181 Handle Create(const void* config_proto,
182 size_t config_len,
183 OnStateChangedCb callback,
184 void* callback_arg) {
185 LOG(VERBOSE) << "PerfettoConsumer::Create("
186 << "config_len=" << config_len << ")";
187 Handle handle = raw_->Create(config_proto, config_len, callback, callback_arg);
188
189 std::lock_guard<std::mutex> guard{mutex_};
190
191 // Assume every Handle starts at 0 and then increments by 1 every Create.
192 ++last_created_;
193 CHECK_EQ(last_created_, handle) << "perfetto handle had unexpected behavior.";
194 // Without this^ increment-by-1 behavior our detection of untracked state values is broken.
195 // If we have to, we can go with Untracked=Uncreated|Destroyed but it's better to distinguish
196 // the two if possible.
197
198 HandleDescription handle_desc{handle};
199 handle_desc.handle_ = handle;
200 handle_desc.callback_ = callback;
201 handle_desc.callback_arg_ = callback_arg;
202 UpdateHandleDescription(/*inout*/&handle_desc, StateKind::kCreated);
203
204 // assume we never wrap around due to using int64
205 bool inserted = states_.insert({handle, handle_desc}).second;
206 CHECK(inserted) << "perfetto handle was re-used: " << handle;
207
208 return handle;
209 }
210
StartTracingiorap::perfetto::PerfettoConsumerImpl::Impl211 void StartTracing(Handle handle) {
212 LOG(DEBUG) << "PerfettoConsumer::StartTracing(handle=" << handle << ")";
213
214 uint64_t trace_cookie;
215 {
216 std::lock_guard<std::mutex> guard{mutex_};
217
218 auto it = states_.find(handle);
219 if (it == states_.end()) {
220 LOG(ERROR) << "Cannot StartTracing(" << handle << "), untracked handle";
221 return;
222 }
223 HandleDescription& handle_desc = it->second;
224
225 raw_->StartTracing(handle);
226 UpdateHandleDescription(/*inout*/&handle_desc, StateKind::kStartedTracing);
227 }
228
229 // Use a looper here to add a timeout and immediately destroy the trace buffer.
230 CHECK_LE(static_cast<int64_t>(handle), static_cast<int64_t>(std::numeric_limits<int>::max()));
231 int message_code = static_cast<int>(handle);
232 ::android::Message message{message_code};
233
234 std::lock_guard<std::mutex> looper_guard{looper_mutex_};
235 looper_->sendMessageDelayed(static_cast<nsecs_t>(GetPropertyTraceTimeoutNs()),
236 message_handler_,
237 message);
238 }
239
ReadTraceiorap::perfetto::PerfettoConsumerImpl::Impl240 TraceBuffer ReadTrace(Handle handle) {
241 LOG(DEBUG) << "PerfettoConsumer::ReadTrace(handle=" << handle << ")";
242
243 std::lock_guard<std::mutex> guard{mutex_};
244
245 auto it = states_.find(handle);
246 if (it == states_.end()) {
247 LOG(ERROR) << "Cannot ReadTrace(" << handle << "), untracked handle";
248 return TraceBuffer{};
249 }
250
251 HandleDescription& handle_desc = it->second;
252
253 TraceBuffer trace_buffer = raw_->ReadTrace(handle);
254 UpdateHandleDescription(/*inout*/&handle_desc, StateKind::kReadTracing);
255
256 return trace_buffer;
257 }
258
Destroyiorap::perfetto::PerfettoConsumerImpl::Impl259 void Destroy(Handle handle) {
260 HandleDescription handle_desc{handle};
261 TryDestroy(handle, /*do_destroy*/true, /*out*/&handle_desc);;
262 }
263
TryDestroyiorap::perfetto::PerfettoConsumerImpl::Impl264 bool TryDestroy(Handle handle, bool do_destroy, /*out*/HandleDescription* handle_desc_out) {
265 CHECK(handle_desc_out != nullptr);
266
267 LOG(VERBOSE) << "PerfettoConsumer::Destroy(handle=" << handle << ")";
268
269 std::lock_guard<std::mutex> guard{mutex_};
270
271 auto it = states_.find(handle);
272 if (it == states_.end()) {
273 // Leniency for calling Destroy multiple times. It's not a mistake.
274 LOG(ERROR) << "Cannot Destroy(" << handle << "), untracked handle";
275 return false;
276 }
277
278 HandleDescription& handle_desc = it->second;
279
280 if (do_destroy) {
281 raw_->Destroy(handle);
282 }
283 UpdateHandleDescription(/*inout*/&handle_desc, StateKind::kDestroyed);
284
285 *handle_desc_out = handle_desc;
286
287 // No longer track this handle to avoid memory leaks.
288 last_destroyed_ = handle;
289 states_.erase(it);
290
291 return true;
292 }
293
PollStateiorap::perfetto::PerfettoConsumerImpl::Impl294 State PollState(Handle handle) {
295 // Just pass-through the call, we never use it directly anyway.
296 return raw_->PollState(handle);
297 }
298
299 // Either fetch or infer the current handle state from a handle.
300 // Meant for debugging/logging only.
GetOrInferHandleDescriptioniorap::perfetto::PerfettoConsumerImpl::Impl301 HandleDescription GetOrInferHandleDescription(Handle handle) {
302 std::lock_guard<std::mutex> guard{mutex_};
303
304 auto it = states_.find(handle);
305 if (it == states_.end()) {
306 HandleDescription state{handle};
307 // If it's untracked it hasn't been created yet, or it was already destroyed.
308 if (IsDestroyed(handle)) {
309 UpdateHandleDescription(/*inout*/&state, StateKind::kDestroyed);
310 } else {
311 if (!IsUncreated(handle)) {
312 LOG(WARNING) << "bad state detection";
313 }
314 UpdateHandleDescription(/*inout*/&state, StateKind::kUncreated);
315 }
316 return state;
317 }
318 return it->second;
319 }
320
OnTraceMessageiorap::perfetto::PerfettoConsumerImpl::Impl321 void OnTraceMessage(Handle handle) {
322 LOG(VERBOSE) << "OnTraceMessage(" << static_cast<int64_t>(handle) << ")";
323 HandleDescription handle_desc{handle};
324 {
325 std::lock_guard<std::mutex> guard{mutex_};
326
327 auto it = states_.find(handle);
328 if (it == states_.end()) {
329 // Handle values are never re-used, so we can simply ignore the message here
330 // instead of having to remove it from the message queue.
331 LOG(VERBOSE) << "OnTraceMessage(" << static_cast<int64_t>(handle)
332 << ") no longer tracked handle";
333 return;
334 }
335 handle_desc = it->second;
336 }
337
338 // First check. Has this trace been active for too long?
339 uint64_t now_ns = GetTimeNanoseconds();
340 if (handle_desc.kind_ == StateKind::kStartedTracing) {
341 // Ignore other kinds of traces because they don't exhaust perfetto resources.
342 CHECK(handle_desc.started_tracing_ns_.has_value()) << static_cast<int64_t>(handle);
343
344 uint64_t started_tracing_ns = *handle_desc.started_tracing_ns_;
345
346 if ((now_ns - started_tracing_ns) > GetPropertyTraceTimeoutNs()) {
347 LOG(WARNING) << "Perfetto Handle timed out after " << (now_ns - started_tracing_ns) << "ns"
348 << ", forcibly destroying";
349
350 // Let the callback handler call Destroy.
351 handle_desc.callback_(handle, State::kTraceFailed, handle_desc.callback_arg_);
352 }
353 }
354
355 // Second check. Are there too many traces now? Cull the old traces.
356 std::vector<HandleDescription> handle_list;
357 do {
358 std::lock_guard<std::mutex> guard{mutex_};
359
360 size_t max_trace_count = GetPropertyMaxTraceCount();
361 if (states_.size() > max_trace_count) {
362 size_t overflow_count = states_.size() - max_trace_count;
363 LOG(WARNING) << "Too many perfetto handles, overflowed by " << overflow_count
364 << ", pruning down to " << max_trace_count;
365 } else {
366 break;
367 }
368
369 size_t prune_count = states_.size() - max_trace_count;
370 auto it = states_.begin();
371 for (size_t i = 0; i < prune_count; ++i) {
372 // Simply prune by handle 1,2,3,4...
373 // We could do better with a timestamp if we wanted to.
374 ++it;
375 handle_list.push_back(it->second);
376 }
377 } while (false);
378
379 for (HandleDescription& handle_desc : handle_list) {
380 LOG(DEBUG) << "Perfetto handle pruned: " << static_cast<int64_t>(handle);
381
382 // Let the callback handler call Destroy.
383 handle_desc.callback_(handle, State::kTraceFailed, handle_desc.callback_arg_);
384 }
385 }
386
387 private:
GetPropertyTraceTimeoutNsiorap::perfetto::PerfettoConsumerImpl::Impl388 static uint64_t GetPropertyTraceTimeoutNs() {
389 static uint64_t value = // property is timeout in seconds
390 ::android::base::GetUintProperty<uint64_t>("iorapd.perfetto.timeout", /*default*/10);
391 return value * kSecToNano;
392 }
393
GetPropertyMaxTraceCountiorap::perfetto::PerfettoConsumerImpl::Impl394 static size_t GetPropertyMaxTraceCount() {
395 static size_t value =
396 ::android::base::GetUintProperty<size_t>("iorapd.perfetto.max_traces", /*default*/5);
397 return value;
398 }
399
UpdateHandleDescriptioniorap::perfetto::PerfettoConsumerImpl::Impl400 void UpdateHandleDescription(/*inout*/HandleDescription* handle_desc, StateKind kind) {
401 CHECK(handle_desc != nullptr);
402 handle_desc->kind_ = kind;
403 handle_desc->state_ = raw_->PollState(handle_desc->handle_);
404
405 handle_desc->last_transition_ns_ = GetTimeNanoseconds();
406 if (kind == StateKind::kStartedTracing) {
407 if (!handle_desc->started_tracing_ns_) {
408 handle_desc->started_tracing_ns_ = handle_desc->last_transition_ns_;
409
410 handle_desc->trace_cookie_ = ++trace_cookie_;
411
412 atrace_async_begin(ATRACE_TAG_ACTIVITY_MANAGER,
413 "Perfetto Scoped Trace",
414 *handle_desc->trace_cookie_);
415 atrace_int(ATRACE_TAG_ACTIVITY_MANAGER,
416 "Perfetto::Trace Handle",
417 static_cast<int32_t>(handle_desc->handle_));
418 }
419 }
420
421 if (kind == StateKind::kReadTracing) {
422 if (!handle_desc->read_trace_ns_) {
423 handle_desc->read_trace_ns_ = handle_desc->last_transition_ns_;
424
425 if (handle_desc->trace_cookie_.has_value() && !handle_desc->trace_ended_) {
426 atrace_async_end(ATRACE_TAG_ACTIVITY_MANAGER,
427 "Perfetto Scoped Trace",
428 handle_desc->trace_cookie_.value());
429
430 handle_desc->trace_ended_ = true;
431 }
432 }
433 }
434
435 // If Destroy is called prior to ReadTrace, mark the atrace as finished.
436 if (kind == StateKind::kDestroyed && handle_desc->trace_cookie_ && !handle_desc->trace_ended_) {
437 atrace_async_end(ATRACE_TAG_ACTIVITY_MANAGER,
438 "Perfetto Scoped Trace",
439 *handle_desc->trace_cookie_);
440 handle_desc->trace_ended_ = true;
441 }
442 }
443
444 // The following state detection is for debugging only.
445 // We figure out if something is destroyed, uncreated, or live.
446
447 // Does not distinguish between kTimedOutDestroyed and kDestroyed.
IsDestroyediorap::perfetto::PerfettoConsumerImpl::Impl448 bool IsDestroyed(Handle handle) const {
449 auto it = states_.find(handle);
450 if (it != states_.end()) {
451 // Tracked values are not destroyed yet.
452 return false;
453 }
454
455 if (handle == kInvalidHandle) {
456 return false;
457 }
458
459 // The following assumes handles are incrementally generated:
460 if (it == states_.end()) {
461 // value is in range of [0, last_destroyed] => destroyed.
462 return handle <= last_destroyed_;
463 }
464
465 auto min_it = states_.begin();
466 if (handle < min_it->first) {
467 // value smaller than anything tracked: it was destroyed and we stopped tracking it.
468 return true;
469 }
470
471 auto max_it = states_.rbegin();
472 if (handle > max_it->first) {
473 // value too big: it's uncreated;
474 return false;
475 }
476
477 // else it was a value that was previously tracked within [min,max] but no longer
478 return true;
479 }
480
IsUncreatediorap::perfetto::PerfettoConsumerImpl::Impl481 bool IsUncreated(Handle handle) const {
482 auto it = states_.find(handle);
483 if (it != states_.end()) {
484 // Tracked values are not uncreated.
485 return false;
486 }
487
488 if (handle == kInvalidHandle) {
489 // Strangely enough, an invalid handle can never be created.
490 return true;
491 }
492
493 // The following assumes handles are incrementally generated:
494 if (it == states_.end()) {
495 // value is in range of (last_destroyed, inf) => uncreated.
496 return handle > last_destroyed_;
497 }
498
499 auto min_it = states_.begin();
500 if (handle < min_it->first) {
501 // value smaller than anything tracked: it was destroyed and we stopped tracking it.
502 return false;
503 }
504
505 auto max_it = states_.rbegin();
506 if (handle > max_it->first) {
507 // value too big: it's uncreated;
508 return true;
509 }
510
511 // else it was a value that was previously tracked within [min,max] but no longer
512 return false;
513 }
514
515 public:
Dumpiorap::perfetto::PerfettoConsumerImpl::Impl516 void Dump(::android::Printer& printer) {
517 // Locking can fail if we dump during a deadlock, so just do a best-effort lock here.
518 bool is_it_locked = mutex_.try_lock();
519
520 printer.printFormatLine("Perfetto consumer state:");
521 if (!is_it_locked) {
522 printer.printLine(""""" (possible deadlock)");
523 }
524 printer.printFormatLine(" Last destroyed handle: %" PRId64, last_destroyed_);
525 printer.printFormatLine(" Last created handle: %" PRId64, last_created_);
526 printer.printFormatLine("");
527 printer.printFormatLine(" In-flight handles:");
528
529 for (auto it = states_.begin(); it != states_.end(); ++it) {
530 HandleDescription& handle_desc = it->second;
531 uint64_t started_tracing =
532 handle_desc.started_tracing_ns_ ? *handle_desc.started_tracing_ns_ : 0;
533 printer.printFormatLine(" Handle %" PRId64, handle_desc.handle_);
534 printer.printFormatLine(" Kind: %s", ToString(handle_desc.kind_).c_str());
535 printer.printFormatLine(" Perfetto State: %d", static_cast<int>(handle_desc.state_));
536 printer.printFormatLine(" Started tracing at: %" PRIu64, started_tracing);
537 printer.printFormatLine(" Last transition at: %" PRIu64,
538 handle_desc.last_transition_ns_);
539 }
540 if (states_.empty()) {
541 printer.printFormatLine(" (None)");
542 }
543
544 printer.printFormatLine("");
545
546 if (is_it_locked) { // u.b. if calling unlock on an unlocked mutex.
547 mutex_.unlock();
548 }
549 }
550
GetImplSingletoniorap::perfetto::PerfettoConsumerImpl::Impl551 static PerfettoConsumerImpl::Impl* GetImplSingleton() {
552 static PerfettoConsumerImpl::Impl impl;
553 return &impl;
554 }
555 };
556
557 // Use a singleton because fruit instantiates a new PerfettoConsumer object for every
558 // new rx chain in RxProducerFactory. However, we want to track all perfetto transitions globally
559 // through 1 impl object.
560 //
561 // TODO: Avoiding a singleton would mean a more significant refactoring to remove the fruit/perfetto
562 // usage.
563
564
565 //
566 // Forward all calls to PerfettoConsumerImpl::Impl
567 //
568
~PerfettoConsumerImpl()569 PerfettoConsumerImpl::~PerfettoConsumerImpl() {
570 // delete impl_; // TODO: no singleton
571 }
572
Initialize()573 void PerfettoConsumerImpl::Initialize() {
574 // impl_ = new PerfettoConsumerImpl::Impl(); // TODO: no singleton
575 impl_ = PerfettoConsumerImpl::Impl::GetImplSingleton();
576 }
577
Dump(::android::Printer & printer)578 void PerfettoConsumerImpl::Dump(::android::Printer& printer) {
579 PerfettoConsumerImpl::Impl::GetImplSingleton()->Dump(/*borrow*/printer);
580 }
581
Create(const void * config_proto,size_t config_len,PerfettoConsumer::OnStateChangedCb callback,void * callback_arg)582 PerfettoConsumer::Handle PerfettoConsumerImpl::Create(const void* config_proto,
583 size_t config_len,
584 PerfettoConsumer::OnStateChangedCb callback,
585 void* callback_arg) {
586 return impl_->Create(config_proto,
587 config_len,
588 callback,
589 callback_arg);
590 }
591
StartTracing(PerfettoConsumer::Handle handle)592 void PerfettoConsumerImpl::StartTracing(PerfettoConsumer::Handle handle) {
593 impl_->StartTracing(handle);
594 }
595
ReadTrace(PerfettoConsumer::Handle handle)596 PerfettoConsumer::TraceBuffer PerfettoConsumerImpl::ReadTrace(PerfettoConsumer::Handle handle) {
597 return impl_->ReadTrace(handle);
598 }
599
Destroy(PerfettoConsumer::Handle handle)600 void PerfettoConsumerImpl::Destroy(PerfettoConsumer::Handle handle) {
601 impl_->Destroy(handle);
602 }
603
PollState(PerfettoConsumer::Handle handle)604 PerfettoConsumer::State PerfettoConsumerImpl::PollState(PerfettoConsumer::Handle handle) {
605 return impl_->PollState(handle);
606 }
607
608 } // namespace iorap::perfetto
609