• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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