1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "src/trace_processor/importers/ftrace/binder_tracker.h"
18 #include "perfetto/base/compiler.h"
19 #include "perfetto/ext/base/string_utils.h"
20 #include "src/trace_processor/importers/common/flow_tracker.h"
21 #include "src/trace_processor/importers/common/process_tracker.h"
22 #include "src/trace_processor/importers/common/slice_tracker.h"
23 #include "src/trace_processor/importers/common/track_tracker.h"
24 #include "src/trace_processor/storage/trace_storage.h"
25 #include "src/trace_processor/types/trace_processor_context.h"
26
27 // Binder tracker: displays slices for binder transactions and other operations.
28 // =============================================================================
29 //
30 // Supported events
31 // ----------------
32 // # Transactions
33 //
34 // * binder/binder_transaction
35 // * binder/binder_transaction_reply
36 //
37 // With these two events the tracker can display slices for binder transactions
38 // in the sending and receiving threads. Rarely, when transactions fail in some
39 // way, it's possible that the tracker doesn't have enough information to
40 // properly terminate slices. See "Commands" below for a solution.
41 //
42 // # Buffer allocations
43 //
44 // * binder/binder_transaction_alloc_buf
45 //
46 // This annotates the transaction slices (from the above events) with info about
47 // allocations. The event alone doesn't make sense without the "Transactions"
48 // events.
49 //
50 // # Commands
51 //
52 // * binder/binder_command
53 // * binder/binder_return
54 //
55 // These two events are only useful in conjunction with the "Transactions"
56 // events. Their presence allow the tracker to terminate slices more reliably
57 // when a transaction fails.
58 //
59 // # Locking
60 //
61 // * binder/binder_lock
62 // * binder/binder_locked
63 // * binder/binder_unlock
64 //
65 // Obsolete: this was removed from kernel v4.14
66 //
67 // Implementation details
68 // ----------------------
69 //
70 // # Basic transaction tracking:
71 //
72 // For each transaction, two threads are involved.
73 //
74 // A oneway (aka asynchronous) transaction has these events:
75 //
76 // ```
77 // Thread Snd Thread Rcv
78 // | |
79 // binder_transaction(id, is_oneway) |
80 // |
81 // binder_transaction_received(id)
82 // ```
83 //
84 // The tracker will create one instant events one each thread.
85 //
86 // A regular (aka synchronous) transaction has these events:
87 //
88 // ```
89 // Thread Snd Thread Rcv
90 // | |
91 // binder_transaction(id) |
92 // | |
93 // | binder_transaction_received(id)
94 // | |
95 // | binder_transaction(other_id, is_reply)
96 // |
97 // binder_transaction_received(other_id, is_reply)
98 // ```
99 //
100 // The tracker will create a "binder transaction" slice on Thread 1 and a
101 // "binder reply" slice on Thread 2.
102 //
103 // synchronous transactions can be nested: inside a "binder reply", a thread can
104 // make a binder transaction to another thread (just regular synchronous
105 // function calls).
106 //
107 // If a regular transaction fails, the kernel will not emit some events, causing
108 // the tracker to leave some slices open forever, while the threads are actually
109 // not working on the transaction anymore.
110 //
111 // ```
112 // Thread Snd Thread Rcv
113 // | |
114 // binder_transaction(id) |
115 // | |
116 // ```
117 //
118 // or
119 //
120 // ```
121 // Thread Snd Thread Rcv
122 // | |
123 // binder_transaction(id) |
124 // | |
125 // | binder_transaction_received(id)
126 // | |
127 // | binder_transaction(other_id, is_reply)
128 // |
129 // ```
130 //
131 // In order to solve this problem (https://b.corp.google.com/issues/295124679),
132 // the tracker also understand commands and return commands. Binder commands are
133 // instructions that a userspace thread passes to the binder kernel driver (they
134 // all start with BC_), while binder return commands (they all start with BR_)
135 // are instructions that the binder kernel driver passes to the userspace
136 // thread.
137 //
138 // A synchronous transaction with commands and returns looks like this:
139 //
140 // ```
141 // Thread Snd Thread Rcv
142 // | |
143 // binder_command(BC_TRANSACTION) |
144 // | |
145 // binder_transaction(id) |
146 // | |
147 // | binder_transaction_received(id)
148 // | |
149 // | binder_return(BR_TRANSACTION)
150 // | |
151 // | binder_command(BC_REPLY)
152 // | |
153 // | binder_transaction(other_id, is_reply)
154 // | |
155 // | binder_return(BR_TRANSACTION_COMPLETE)
156 // | |
157 // binder_return(BR_TRANSACTION_COMPLETE) |
158 // | |
159 // binder_transaction_received(other_id, is_reply) |
160 // | |
161 // binder_return(BR_REPLY)
162 // ```
163 //
164 // For each thread, the tracker keeps a stack (since synchronous transactions
165 // can be nested). In case of failure, the tracker can observe special return
166 // commands (BR_DEAD_REPLY, BR_FROZEN_REPLY, ...): based on the state of the top
167 // of the stack it knows is it needs to terminate a slice.
168 //
169 // The tracking for commands and returns also tries to keep a correct stack, to
170 // avoid unbounded growth of the stack itself (even though it's internal only).
171 namespace perfetto {
172 namespace trace_processor {
173
174 namespace {
175 constexpr int kOneWay = 0x01;
176 constexpr int kRootObject = 0x04;
177 constexpr int kStatusCode = 0x08;
178 constexpr int kAcceptFds = 0x10;
179 constexpr int kNoFlags = 0;
180
BinderFlagsToHuman(uint32_t flag)181 std::string BinderFlagsToHuman(uint32_t flag) {
182 std::string str;
183 if (flag & kOneWay) {
184 str += "this is a one-way call: async, no return; ";
185 }
186 if (flag & kRootObject) {
187 str += "contents are the components root object; ";
188 }
189 if (flag & kStatusCode) {
190 str += "contents are a 32-bit status code; ";
191 }
192 if (flag & kAcceptFds) {
193 str += "allow replies with file descriptors; ";
194 }
195 if (flag == kNoFlags) {
196 str += "No Flags Set";
197 }
198 return str;
199 }
200
201 } // namespace
202
203 enum BinderTracker::TxnFrame::State : uint32_t {
204 kSndAfterBC_TRANSACTION,
205 kSndAfterTransaction,
206 kSndAfterBR_TRANSACTION_COMPLETE,
207 kSndAfterTransactionReceived,
208 kRcvAfterTransactionReceived,
209 kRcvAfterBR_TRANSACTION,
210 kRcvAfterBC_REPLY,
211 kRcvAfterTransaction,
212 };
213
BinderTracker(TraceProcessorContext * context)214 BinderTracker::BinderTracker(TraceProcessorContext* context)
215 : context_(context),
216 binder_category_id_(context->storage->InternString("binder")),
217 lock_waiting_id_(context->storage->InternString("binder lock waiting")),
218 lock_held_id_(context->storage->InternString("binder lock held")),
219 transaction_slice_id_(
220 context->storage->InternString("binder transaction")),
221 transaction_async_id_(
222 context->storage->InternString("binder transaction async")),
223 reply_id_(context->storage->InternString("binder reply")),
224 async_rcv_id_(context->storage->InternString("binder async rcv")),
225 transaction_id_(context->storage->InternString("transaction id")),
226 dest_node_(context->storage->InternString("destination node")),
227 dest_process_(context->storage->InternString("destination process")),
228 dest_thread_(context->storage->InternString("destination thread")),
229 dest_name_(context->storage->InternString("destination name")),
230 is_reply_(context->storage->InternString("reply transaction?")),
231 flags_(context->storage->InternString("flags")),
232 code_(context->storage->InternString("code")),
233 calling_tid_(context->storage->InternString("calling tid")),
234 data_size_(context->storage->InternString("data size")),
235 offsets_size_(context->storage->InternString("offsets size")) {}
236
237 BinderTracker::~BinderTracker() = default;
238
Transaction(int64_t ts,uint32_t tid,int32_t transaction_id,int32_t dest_node,uint32_t dest_tgid,uint32_t dest_tid,bool is_reply,uint32_t flags,StringId code)239 void BinderTracker::Transaction(int64_t ts,
240 uint32_t tid,
241 int32_t transaction_id,
242 int32_t dest_node,
243 uint32_t dest_tgid,
244 uint32_t dest_tid,
245 bool is_reply,
246 uint32_t flags,
247 StringId code) {
248 UniqueTid src_utid = context_->process_tracker->GetOrCreateThread(tid);
249 TrackId track_id = context_->track_tracker->InternThreadTrack(src_utid);
250
251 auto args_inserter = [this, transaction_id, dest_node, dest_tgid, is_reply,
252 flags, code,
253 tid](ArgsTracker::BoundInserter* inserter) {
254 inserter->AddArg(transaction_id_, Variadic::Integer(transaction_id));
255 inserter->AddArg(dest_node_, Variadic::Integer(dest_node));
256 inserter->AddArg(dest_process_, Variadic::Integer(dest_tgid));
257 inserter->AddArg(is_reply_, Variadic::Boolean(is_reply));
258 std::string flag_str =
259 base::IntToHexString(flags) + " " + BinderFlagsToHuman(flags);
260 inserter->AddArg(flags_, Variadic::String(context_->storage->InternString(
261 base::StringView(flag_str))));
262 inserter->AddArg(code_, Variadic::String(code));
263 inserter->AddArg(calling_tid_, Variadic::UnsignedInteger(tid));
264 };
265
266 bool is_oneway = (flags & kOneWay) == kOneWay;
267 auto insert_slice = [&]() {
268 if (is_reply) {
269 UniqueTid utid = context_->process_tracker->GetOrCreateThread(
270 static_cast<uint32_t>(dest_tid));
271 auto dest_thread_name = context_->storage->thread_table().name()[utid];
272 auto dest_args_inserter = [this, dest_tid, &dest_thread_name](
273 ArgsTracker::BoundInserter* inserter) {
274 inserter->AddArg(dest_thread_, Variadic::Integer(dest_tid));
275 if (dest_thread_name.has_value()) {
276 inserter->AddArg(dest_name_, Variadic::String(*dest_thread_name));
277 }
278 };
279 context_->slice_tracker->AddArgs(track_id, binder_category_id_, reply_id_,
280 dest_args_inserter);
281 return context_->slice_tracker->End(ts, track_id, kNullStringId,
282 kNullStringId, args_inserter);
283 }
284 if (is_oneway) {
285 return context_->slice_tracker->Scoped(ts, track_id, binder_category_id_,
286 transaction_async_id_, 0,
287 args_inserter);
288 }
289 return context_->slice_tracker->Begin(ts, track_id, binder_category_id_,
290 transaction_slice_id_, args_inserter);
291 };
292
293 OutstandingTransaction transaction;
294 transaction.is_reply = is_reply;
295 transaction.is_oneway = is_oneway;
296 transaction.args_inserter = args_inserter;
297 transaction.send_track_id = track_id;
298 transaction.send_slice_id = insert_slice();
299 outstanding_transactions_[transaction_id] = std::move(transaction);
300 auto* frame = GetTidTopFrame(tid);
301 if (frame) {
302 if (frame->state == TxnFrame::kSndAfterBC_TRANSACTION) {
303 frame->state = TxnFrame::kSndAfterTransaction;
304 frame->txn_info = {is_oneway, is_reply};
305 } else if (frame->state == TxnFrame::kRcvAfterBC_REPLY) {
306 frame->state = TxnFrame::kRcvAfterTransaction;
307 frame->txn_info = {is_oneway, is_reply};
308 } else if (frame->state == TxnFrame::kRcvAfterTransactionReceived) {
309 // Probably command tracking is disabled. Let's remove the frame.
310 PopTidFrame(tid);
311 }
312 }
313 }
314
TransactionReceived(int64_t ts,uint32_t pid,int32_t transaction_id)315 void BinderTracker::TransactionReceived(int64_t ts,
316 uint32_t pid,
317 int32_t transaction_id) {
318 auto it = outstanding_transactions_.find(transaction_id);
319 if (it == outstanding_transactions_.end()) {
320 // If we don't know what type of transaction it is, we don't know how to
321 // insert the slice.
322 // TODO(lalitm): maybe we should insert a dummy slice anyway - seems like
323 // a questionable idea to just ignore these completely.
324 return;
325 }
326 OutstandingTransaction transaction(std::move(it->second));
327 outstanding_transactions_.erase(it);
328
329 UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
330 TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
331
332 // If it's a oneway transaction, there's no stack to track on the receiving
333 // side.
334 if (!transaction.is_oneway) {
335 if (!transaction.is_reply) {
336 TxnFrame* frame = PushTidFrame(pid);
337 frame->state = TxnFrame::kRcvAfterTransactionReceived;
338 frame->txn_info.emplace();
339 frame->txn_info->is_oneway = transaction.is_oneway;
340 frame->txn_info->is_reply = transaction.is_reply;
341 } else {
342 TxnFrame* frame = GetTidTopFrame(pid);
343 if (frame && frame->state == TxnFrame::kSndAfterBR_TRANSACTION_COMPLETE) {
344 frame->state = TxnFrame::kSndAfterTransactionReceived;
345 }
346 }
347 }
348
349 if (transaction.is_reply) {
350 // Simply end the slice started back when the first |expects_reply|
351 // transaction was sent.
352 context_->slice_tracker->End(ts, track_id);
353 return;
354 }
355
356 std::optional<SliceId> recv_slice_id;
357 if (transaction.is_oneway) {
358 recv_slice_id = context_->slice_tracker->Scoped(
359 ts, track_id, binder_category_id_, async_rcv_id_, 0,
360 std::move(transaction.args_inserter));
361 } else {
362 if (transaction.send_track_id) {
363 auto args_inserter = [this, utid,
364 pid](ArgsTracker::BoundInserter* inserter) {
365 inserter->AddArg(dest_thread_, Variadic::UnsignedInteger(pid));
366 auto dest_thread_name = context_->storage->thread_table().name()[utid];
367 if (dest_thread_name.has_value()) {
368 inserter->AddArg(dest_name_, Variadic::String(*dest_thread_name));
369 }
370 };
371 context_->slice_tracker->AddArgs(*transaction.send_track_id,
372 binder_category_id_,
373 transaction_slice_id_, args_inserter);
374 }
375 recv_slice_id = context_->slice_tracker->Begin(
376 ts, track_id, binder_category_id_, reply_id_);
377 }
378
379 // Create a flow between the sending slice and this slice.
380 if (transaction.send_slice_id && recv_slice_id) {
381 context_->flow_tracker->InsertFlow(*transaction.send_slice_id,
382 *recv_slice_id);
383 }
384 }
385
CommandToKernel(int64_t,uint32_t tid,uint32_t cmd)386 void BinderTracker::CommandToKernel(int64_t /*ts*/,
387 uint32_t tid,
388 uint32_t cmd) {
389 switch (cmd) {
390 case kBC_TRANSACTION:
391 case kBC_TRANSACTION_SG: {
392 TxnFrame* frame = PushTidFrame(tid);
393 frame->state = TxnFrame::kSndAfterBC_TRANSACTION;
394 break;
395 }
396 case kBC_REPLY:
397 case kBC_REPLY_SG: {
398 TxnFrame* frame = GetTidTopFrame(tid);
399 if (frame && frame->state == TxnFrame::kRcvAfterBR_TRANSACTION) {
400 frame->state = TxnFrame::kRcvAfterBC_REPLY;
401 }
402 break;
403 }
404 default:
405 break;
406 }
407 }
408
ReturnFromKernel(int64_t ts,uint32_t tid,uint32_t cmd)409 void BinderTracker::ReturnFromKernel(int64_t ts, uint32_t tid, uint32_t cmd) {
410 switch (cmd) {
411 case kBR_DEAD_REPLY:
412 case kBR_FAILED_REPLY:
413 case kBR_FROZEN_REPLY:
414 case kBR_TRANSACTION_PENDING_FROZEN: {
415 TxnFrame* frame = GetTidTopFrame(tid);
416 if (frame) {
417 switch (frame->state) {
418 case TxnFrame::kSndAfterBC_TRANSACTION:
419 // The transaction has failed before we received the
420 // binder_transaction event, therefore no slice has been opened.
421 PopTidFrame(tid);
422 break;
423 case TxnFrame::kRcvAfterBC_REPLY:
424 case TxnFrame::kSndAfterTransaction:
425 case TxnFrame::kRcvAfterTransaction:
426 case TxnFrame::kSndAfterBR_TRANSACTION_COMPLETE:
427 if (frame->txn_info.has_value()) {
428 if (!frame->txn_info->is_oneway && !frame->txn_info->is_reply) {
429 UniqueTid utid =
430 context_->process_tracker->GetOrCreateThread(tid);
431 TrackId track_id =
432 context_->track_tracker->InternThreadTrack(utid);
433 context_->slice_tracker->End(ts, track_id);
434 }
435 }
436 PopTidFrame(tid);
437 break;
438 case TxnFrame::kSndAfterTransactionReceived:
439 case TxnFrame::kRcvAfterTransactionReceived:
440 case TxnFrame::kRcvAfterBR_TRANSACTION:
441 break;
442 }
443 }
444 break;
445 }
446
447 case kBR_TRANSACTION_COMPLETE:
448 case kBR_ONEWAY_SPAM_SUSPECT: {
449 TxnFrame* frame = GetTidTopFrame(tid);
450 if (frame) {
451 if (frame->state == TxnFrame::kRcvAfterTransaction) {
452 PopTidFrame(tid);
453 } else if (frame->state == TxnFrame::kSndAfterBC_TRANSACTION) {
454 // The transaction has failed before we received the
455 // binder_transaction event, therefore no slice has been opened.
456 // It's possible that the binder_transaction event was not enabled.
457 PopTidFrame(tid);
458 } else if (frame->state == TxnFrame::kSndAfterTransaction) {
459 if (frame->txn_info.has_value() && !frame->txn_info->is_oneway) {
460 frame->state = TxnFrame::kSndAfterBR_TRANSACTION_COMPLETE;
461 } else {
462 // For a oneway transaction, this is the last event. In any case, no
463 // slice has been opened.
464 PopTidFrame(tid);
465 }
466 }
467 }
468 break;
469 }
470
471 case kBR_REPLY: {
472 TxnFrame* frame = GetTidTopFrame(tid);
473 if (frame && frame->state == TxnFrame::kSndAfterTransactionReceived) {
474 // For a synchronous transaction, this is the last event.
475 PopTidFrame(tid);
476 }
477 break;
478 }
479
480 case kBR_TRANSACTION:
481 case kBR_TRANSACTION_SEC_CTX: {
482 TxnFrame* frame = GetTidTopFrame(tid);
483 if (frame) {
484 if (frame->state == TxnFrame::kRcvAfterTransactionReceived) {
485 frame->state = TxnFrame::kRcvAfterBR_TRANSACTION;
486 }
487 }
488 break;
489 }
490
491 default:
492 break;
493 }
494 }
495
Lock(int64_t ts,uint32_t pid)496 void BinderTracker::Lock(int64_t ts, uint32_t pid) {
497 attempt_lock_[pid] = ts;
498
499 UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
500 TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
501 context_->slice_tracker->Begin(ts, track_id, binder_category_id_,
502 lock_waiting_id_);
503 }
504
Locked(int64_t ts,uint32_t pid)505 void BinderTracker::Locked(int64_t ts, uint32_t pid) {
506 UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
507
508 if (!attempt_lock_.Find(pid))
509 return;
510
511 TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
512 context_->slice_tracker->End(ts, track_id);
513 context_->slice_tracker->Begin(ts, track_id, binder_category_id_,
514 lock_held_id_);
515
516 lock_acquired_[pid] = ts;
517 attempt_lock_.Erase(pid);
518 }
519
Unlock(int64_t ts,uint32_t pid)520 void BinderTracker::Unlock(int64_t ts, uint32_t pid) {
521 UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
522
523 if (!lock_acquired_.Find(pid))
524 return;
525
526 TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
527 context_->slice_tracker->End(ts, track_id, binder_category_id_,
528 lock_held_id_);
529 lock_acquired_.Erase(pid);
530 }
531
TransactionAllocBuf(int64_t ts,uint32_t pid,uint64_t data_size,uint64_t offsets_size)532 void BinderTracker::TransactionAllocBuf(int64_t ts,
533 uint32_t pid,
534 uint64_t data_size,
535 uint64_t offsets_size) {
536 UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
537 TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
538
539 auto args_inserter = [this, &data_size,
540 offsets_size](ArgsTracker::BoundInserter* inserter) {
541 inserter->AddArg(data_size_, Variadic::UnsignedInteger(data_size));
542 inserter->AddArg(offsets_size_, Variadic::UnsignedInteger(offsets_size));
543 };
544 context_->slice_tracker->AddArgs(track_id, binder_category_id_,
545 transaction_slice_id_, args_inserter);
546
547 base::ignore_result(ts);
548 }
549
GetTidTopFrame(uint32_t tid)550 BinderTracker::TxnFrame* BinderTracker::GetTidTopFrame(uint32_t tid) {
551 UniqueTid utid = context_->process_tracker->GetOrCreateThread(tid);
552 std::stack<BinderTracker::TxnFrame>* stack = utid_stacks_.Find(utid);
553 if (stack == nullptr || stack->empty()) {
554 return nullptr;
555 }
556 return &stack->top();
557 }
558
PushTidFrame(uint32_t tid)559 BinderTracker::TxnFrame* BinderTracker::PushTidFrame(uint32_t tid) {
560 UniqueTid utid = context_->process_tracker->GetOrCreateThread(tid);
561 auto& stack = utid_stacks_[utid];
562 stack.push({});
563 return &stack.top();
564 }
565
PopTidFrame(uint32_t tid)566 void BinderTracker::PopTidFrame(uint32_t tid) {
567 UniqueTid utid = context_->process_tracker->GetOrCreateThread(tid);
568 std::stack<BinderTracker::TxnFrame>* stack = utid_stacks_.Find(utid);
569 PERFETTO_CHECK(stack);
570 stack->pop();
571 if (stack->empty()) {
572 utid_stacks_.Erase(utid);
573 }
574 }
575
576 } // namespace trace_processor
577 } // namespace perfetto
578