blob: c7bcb8b1cc0458a27fc2f4ac3c29a4ee9d1637c0 [file]
/*
* Copyright (C) 2025 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.
*/
#define LOG_TAG "libbinder.BinderObserver"
#include "BinderObserver.h"
#include <mutex>
#include <binder/IServiceManager.h>
#include <binder/internal/JavaBBinderBase.h>
#include <utils/SystemClock.h>
#include "../BuildFlags.h"
#include "BinderStatsUtils.h"
namespace android {
#if !defined(LIBBINDER_BINDER_OBSERVER_V2)
constexpr int kSendIntervalSec = 5;
#endif
constexpr int64_t kNanoSecondsPerSec = 1000'000'000LL;
BinderObserver::BinderObserver(std::unique_ptr<BinderObserverConfig> config)
: mConfig(std::move(config)) {}
BinderObserver::CallInfo BinderObserver::onBeginTransaction(BBinder* binder, uint32_t code,
uid_t callingUid) {
LOG_ALWAYS_FATAL_IF(binder == nullptr, "Null binder sent to binder observer.");
if (!mConfig->isEnabled()) {
return {};
}
// Sharding based on the pointer would be faster but would also increase the cardinality of
// the different AIDLs that we report. Ideally, we want something stable within the
// current boot session, so we use the interface descriptor.
const String16& interfaceDescriptor = binder->getInterfaceDescriptor();
BinderObserverConfig::TrackingInfo trackingInfo =
mConfig->getTrackingInfo(interfaceDescriptor, code);
// For V1, the aggregation strategy requires a start time for every tracked transaction. For
// V2, we only need the start time if we are tracking latency for a transaction.
bool trackStartTime =
kBinderObserverV2Enabled ? trackingInfo.trackLatency : trackingInfo.isTracked();
String16 aidlMethodName;
if (trackingInfo.isTracked()) {
if (binder->checkSubclass(android::internal::JavaBBinderBase::getExtSubclassID())) {
static_cast<internal::JavaBBinderBase*>(binder)
->getFunctionName(code, [&aidlMethodName](const char* name) {
if (name) {
aidlMethodName = String16(name);
}
});
} else {
aidlMethodName = String16(binder->getFunctionName(code).c_str());
}
}
return {
.startTimeNanos = trackStartTime ? uptimeNanos() : 0,
.cpuUsageStartTimeNanos = trackingInfo.trackCpu ? mConfig->getCpuTimeNanos() : 0,
.interfaceDescriptor = interfaceDescriptor,
// TODO(b/299356196): Reduce std::string and String16 allocations.
.aidlMethodName = aidlMethodName,
.code = code,
.callingUid = callingUid,
.trackingInfo = trackingInfo,
};
}
void BinderObserver::onEndTransaction(std::shared_ptr<BinderStatsSpscQueue>& queue,
const CallInfo& callInfo) {
if (!mConfig->isEnabled() || !callInfo.trackingInfo.isTracked()) {
return;
}
if (queue == nullptr) {
queue = std::make_shared<BinderStatsSpscQueue>();
mBinderStatsCollector.registerQueue(queue);
}
// For V2, the aggregation strategy requires an end time for every transaction. For V1, we
// only record an end time if we are tracking latency.
bool shouldRecordEndTime = kBinderObserverV2Enabled || callInfo.trackingInfo.trackLatency;
int64_t endTimeNanos = shouldRecordEndTime ? uptimeNanos() : 0;
BinderCallData observerData = {
.startTimeNanos = callInfo.startTimeNanos,
.endTimeNanos = endTimeNanos,
.cpuTimeNanos = callInfo.trackingInfo.trackCpu && callInfo.cpuUsageStartTimeNanos != 0
? mConfig->getCpuTimeNanos() - callInfo.cpuUsageStartTimeNanos
: 0,
.interfaceDescriptor = callInfo.interfaceDescriptor,
.aidlMethodName = callInfo.aidlMethodName,
.transactionCode = callInfo.code,
.senderUid = callInfo.callingUid,
};
addStatMaybeFlush(queue, observerData);
}
void BinderObserver::deregisterThread(std::shared_ptr<BinderStatsSpscQueue>& queue) {
if (queue != nullptr) {
mBinderStatsCollector.deregisterQueue(queue);
queue = nullptr;
}
}
bool BinderObserver::isFlushRequired(int64_t nowSec) {
int64_t previousFlushTimeSec = mLastFlushTimeSec.load();
#if defined(LIBBINDER_BINDER_OBSERVER_V2)
return previousFlushTimeSec < nowSec;
#else
return nowSec - previousFlushTimeSec >= kSendIntervalSec;
#endif
}
void BinderObserver::addStatMaybeFlush(const std::shared_ptr<BinderStatsSpscQueue>& queue,
const BinderCallData& stat) {
// If write fails, then buffer is full.
int64_t nowSec = stat.endTimeNanos / kNanoSecondsPerSec;
if (!queue->push(stat)) {
flushStats(nowSec);
// If write fails again, we drop the stat.
// TODO(b/299356196): Track dropped stats separately.
queue->push(stat);
return;
}
if (isFlushRequired(nowSec)) {
flushStats(nowSec);
}
}
void BinderObserver::flushStats(int64_t nowSec) {
std::unique_lock<std::mutex> lock(mFlushLock, std::defer_lock);
// skip flushing if flushing is already in progress
if (!lock.try_lock()) {
return;
}
// flush
mLastFlushTimeSec = nowSec;
mBinderStatsCollector.consumeData(mPusher.getAddCallDataToBufferLockedFunction());
mPusher.pushLocked(nowSec);
}
} // namespace android