blob: 7133c1f936289db1956e63c8d3bbefb18f0a7e11 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "src/trace_processor/importers/ftrace/binder_tracker.h"
#include "perfetto/base/compiler.h"
#include "perfetto/ext/base/string_utils.h"
#include "src/trace_processor/importers/common/flow_tracker.h"
#include "src/trace_processor/importers/common/process_tracker.h"
#include "src/trace_processor/importers/common/slice_tracker.h"
#include "src/trace_processor/importers/common/track_tracker.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/types/trace_processor_context.h"
namespace perfetto {
namespace trace_processor {
namespace {
constexpr int kOneWay = 0x01;
constexpr int kRootObject = 0x04;
constexpr int kStatusCode = 0x08;
constexpr int kAcceptFds = 0x10;
constexpr int kNoFlags = 0;
std::string BinderFlagsToHuman(uint32_t flag) {
std::string str;
if (flag & kOneWay) {
str += "this is a one-way call: async, no return; ";
}
if (flag & kRootObject) {
str += "contents are the components root object; ";
}
if (flag & kStatusCode) {
str += "contents are a 32-bit status code; ";
}
if (flag & kAcceptFds) {
str += "allow replies with file descriptors; ";
}
if (flag == kNoFlags) {
str += "No Flags Set";
}
return str;
}
} // namespace
BinderTracker::BinderTracker(TraceProcessorContext* context)
: context_(context),
binder_category_id_(context->storage->InternString("binder")),
lock_waiting_id_(context->storage->InternString("binder lock waiting")),
lock_held_id_(context->storage->InternString("binder lock held")),
transaction_slice_id_(
context->storage->InternString("binder transaction")),
transaction_async_id_(
context->storage->InternString("binder transaction async")),
reply_id_(context->storage->InternString("binder reply")),
async_rcv_id_(context->storage->InternString("binder async rcv")),
transaction_id_(context->storage->InternString("transaction id")),
dest_node_(context->storage->InternString("destination node")),
dest_process_(context->storage->InternString("destination process")),
dest_thread_(context->storage->InternString("destination thread")),
dest_name_(context->storage->InternString("destination name")),
is_reply_(context->storage->InternString("reply transaction?")),
flags_(context->storage->InternString("flags")),
code_(context->storage->InternString("code")),
calling_tid_(context->storage->InternString("calling tid")),
data_size_(context->storage->InternString("data size")),
offsets_size_(context->storage->InternString("offsets size")) {}
BinderTracker::~BinderTracker() = default;
void BinderTracker::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) {
UniqueTid src_utid = context_->process_tracker->GetOrCreateThread(tid);
TrackId track_id = context_->track_tracker->InternThreadTrack(src_utid);
auto args_inserter = [this, transaction_id, dest_node, dest_tgid, is_reply,
flags, code,
tid](ArgsTracker::BoundInserter* inserter) {
inserter->AddArg(transaction_id_, Variadic::Integer(transaction_id));
inserter->AddArg(dest_node_, Variadic::Integer(dest_node));
inserter->AddArg(dest_process_, Variadic::Integer(dest_tgid));
inserter->AddArg(is_reply_, Variadic::Boolean(is_reply));
std::string flag_str =
base::IntToHexString(flags) + " " + BinderFlagsToHuman(flags);
inserter->AddArg(flags_, Variadic::String(context_->storage->InternString(
base::StringView(flag_str))));
inserter->AddArg(code_, Variadic::String(code));
inserter->AddArg(calling_tid_, Variadic::UnsignedInteger(tid));
};
bool is_oneway = (flags & kOneWay) == kOneWay;
auto insert_slice = [&]() {
if (is_reply) {
UniqueTid utid = context_->process_tracker->GetOrCreateThread(
static_cast<uint32_t>(dest_tid));
auto dest_thread_name = context_->storage->thread_table().name()[utid];
auto dest_args_inserter = [this, dest_tid, &dest_thread_name](
ArgsTracker::BoundInserter* inserter) {
inserter->AddArg(dest_thread_, Variadic::Integer(dest_tid));
if (dest_thread_name.has_value()) {
inserter->AddArg(dest_name_, Variadic::String(*dest_thread_name));
}
};
context_->slice_tracker->AddArgs(track_id, binder_category_id_, reply_id_,
dest_args_inserter);
return context_->slice_tracker->End(ts, track_id, kNullStringId,
kNullStringId, args_inserter);
}
if (is_oneway) {
return context_->slice_tracker->Scoped(ts, track_id, binder_category_id_,
transaction_async_id_, 0,
args_inserter);
}
return context_->slice_tracker->Begin(ts, track_id, binder_category_id_,
transaction_slice_id_, args_inserter);
};
OutstandingTransaction transaction;
transaction.is_reply = is_reply;
transaction.is_oneway = is_oneway;
transaction.args_inserter = args_inserter;
transaction.send_track_id = track_id;
transaction.send_slice_id = insert_slice();
outstanding_transactions_.Insert(transaction_id, std::move(transaction));
}
void BinderTracker::TransactionReceived(int64_t ts,
uint32_t pid,
int32_t transaction_id) {
const OutstandingTransaction* opt_transaction =
outstanding_transactions_.Find(transaction_id);
if (!opt_transaction) {
// If we don't know what type of transaction it is, we don't know how to
// insert the slice.
// TODO(lalitm): maybe we should insert a dummy slice anyway - seems like
// a questionable idea to just ignore these completely.
return;
}
OutstandingTransaction transaction(std::move(*opt_transaction));
outstanding_transactions_.Erase(transaction_id);
UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
if (transaction.is_reply) {
// Simply end the slice started back when the first |expects_reply|
// transaction was sent.
context_->slice_tracker->End(ts, track_id);
return;
}
std::optional<SliceId> recv_slice_id;
if (transaction.is_oneway) {
recv_slice_id = context_->slice_tracker->Scoped(
ts, track_id, binder_category_id_, async_rcv_id_, 0,
std::move(transaction.args_inserter));
} else {
if (transaction.send_track_id) {
auto args_inserter = [this, utid,
pid](ArgsTracker::BoundInserter* inserter) {
inserter->AddArg(dest_thread_, Variadic::UnsignedInteger(pid));
auto dest_thread_name = context_->storage->thread_table().name()[utid];
if (dest_thread_name.has_value()) {
inserter->AddArg(dest_name_, Variadic::String(*dest_thread_name));
}
};
context_->slice_tracker->AddArgs(*transaction.send_track_id,
binder_category_id_,
transaction_slice_id_, args_inserter);
}
recv_slice_id = context_->slice_tracker->Begin(
ts, track_id, binder_category_id_, reply_id_);
}
// Create a flow between the sending slice and this slice.
if (transaction.send_slice_id && recv_slice_id) {
context_->flow_tracker->InsertFlow(*transaction.send_slice_id,
*recv_slice_id);
}
}
void BinderTracker::Lock(int64_t ts, uint32_t pid) {
attempt_lock_[pid] = ts;
UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
context_->slice_tracker->Begin(ts, track_id, binder_category_id_,
lock_waiting_id_);
}
void BinderTracker::Locked(int64_t ts, uint32_t pid) {
UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
if (!attempt_lock_.Find(pid))
return;
TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
context_->slice_tracker->End(ts, track_id);
context_->slice_tracker->Begin(ts, track_id, binder_category_id_,
lock_held_id_);
lock_acquired_[pid] = ts;
attempt_lock_.Erase(pid);
}
void BinderTracker::Unlock(int64_t ts, uint32_t pid) {
UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
if (!lock_acquired_.Find(pid))
return;
TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
context_->slice_tracker->End(ts, track_id, binder_category_id_,
lock_held_id_);
lock_acquired_.Erase(pid);
}
void BinderTracker::TransactionAllocBuf(int64_t ts,
uint32_t pid,
uint64_t data_size,
uint64_t offsets_size) {
UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
auto args_inserter = [this, &data_size,
offsets_size](ArgsTracker::BoundInserter* inserter) {
inserter->AddArg(data_size_, Variadic::UnsignedInteger(data_size));
inserter->AddArg(offsets_size_, Variadic::UnsignedInteger(offsets_size));
};
context_->slice_tracker->AddArgs(track_id, binder_category_id_,
transaction_slice_id_, args_inserter);
base::ignore_result(ts);
}
} // namespace trace_processor
} // namespace perfetto