Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c3d0c24f authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Snap for 13832108 from 82e7d4aa to 25Q4-release

Change-Id: I45a5ce03a803b3032e8582b99540796e7d58007d
parents c49fbfb2 82e7d4aa
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -234,6 +234,7 @@ filegroup {
    name: "libbinder_observer_sources",
    srcs: [
        "BinderObserver.cpp",
        "BinderObserverConfig.cpp",
        "BinderStatsPusher.cpp",
    ],
    visibility: [
+13 −2
Original line number Diff line number Diff line
@@ -27,6 +27,10 @@ constexpr int kSendIntervalSec = 5;

BinderObserver::CallInfo BinderObserver::onBeginTransaction(BBinder* binder, uint32_t code,
                                                            uid_t callingUid) {
    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.
@@ -34,21 +38,28 @@ BinderObserver::CallInfo BinderObserver::onBeginTransaction(BBinder* binder, uin
    String16 interfaceDescriptor =
            binder == nullptr ? kDeletedBinder : binder->getInterfaceDescriptor();

    BinderObserverConfig::TrackingInfo trackingInfo =
            mConfig->getTrackingInfo(interfaceDescriptor, code);

    return {
            .interfaceDescriptor = interfaceDescriptor,
            .code = code,
            .callingUid = callingUid,
            .startTimeNanos = uptimeNanos(),
            .trackingInfo = trackingInfo,
            .startTimeNanos = trackingInfo.isTracked() ? uptimeNanos() : 0,
    };
}

void BinderObserver::onEndTransaction(const std::shared_ptr<BinderStatsSpscQueue>& queue,
                                      const CallInfo& callInfo) {
    if (!mConfig->isEnabled() || !callInfo.trackingInfo.isTracked()) {
        return;
    }
    BinderCallData observerData = {
            .interfaceDescriptor = callInfo.interfaceDescriptor,
            .transactionCode = callInfo.code,
            .startTimeNanos = callInfo.startTimeNanos,
            .endTimeNanos = uptimeNanos(),
            .endTimeNanos = callInfo.trackingInfo.trackLatency ? uptimeNanos() : 0,
            .senderUid = callInfo.callingUid,
    };
    addStatMaybeFlush(queue, observerData);
+13 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@
#pragma once

#include <mutex>

#include "BinderObserverConfig.h"
#include "BinderStatsPusher.h"
#include "BinderStatsSpscQueue.h"
#include "BinderStatsUtils.h"
@@ -48,15 +50,24 @@ public:
        String16 interfaceDescriptor;
        uint32_t code;
        uid_t callingUid;
        BinderObserverConfig::TrackingInfo trackingInfo;
        int64_t startTimeNanos;
    };

    std::shared_ptr<BinderStatsSpscQueue> registerThread() {
        if (!mConfig->isEnabled()) {
            return nullptr;
        }
        std::shared_ptr<BinderStatsSpscQueue> queue = std::make_shared<BinderStatsSpscQueue>();
        mBinderStatsCollector.registerQueue(queue);
        return queue;
    }
    void deregisterThread(std::shared_ptr<BinderStatsSpscQueue>& queue) {
        if (!mConfig->isEnabled()) {
            LOG_ALWAYS_FATAL_IF(queue != nullptr,
                                "Non-null queue when BinderObserver is disabled.");
            return;
        }
        mBinderStatsCollector.deregisterQueue(queue);
    }
    CallInfo onBeginTransaction(BBinder* binder, uint32_t code, uid_t callingUid);
@@ -69,6 +80,8 @@ private:
                           const BinderCallData& stat);
    void flushStats(int64_t nowMillis);
    bool isFlushRequired(int64_t nowMillis);

    std::unique_ptr<BinderObserverConfig> mConfig = BinderObserverConfig::createConfig();
    // Time since last flush time. Used to trigger a flush if more than kSendTimeoutSec
    // has elapsed since last flush.
    std::atomic<int64_t> mLastFlushTimeSec;
+99 −0
Original line number Diff line number Diff line
/*
 * 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.BinderObserverConfig"

#include <cutils/android_filesystem_config.h> // for AID_SYSTEM
#include <stdlib.h>                           // for getprogname
#include <unistd.h>                           // for getuid()

#include "BinderObserverConfig.h"

namespace android {

// __BIONIC__ has getprogname() but __GLIBC__ and others have program_invocation_short_name
#if !defined(__BIONIC__)
const char* getprogname() {
    return program_invocation_short_name;
}
#endif

std::string BinderObserverConfig::Environment::readFileLine(const std::string& path) {
    std::string content;
    std::ifstream file(path);
    if (file.is_open()) {
        std::getline(file, content);
    }
    return content;
}

uid_t BinderObserverConfig::Environment::getUid() {
    return getuid();
}

std::string BinderObserverConfig::Environment::getProcessName() {
    return getprogname();
}

std::pair<size_t, size_t> BinderObserverConfig::getBootStableTokens(Environment& environment) {
    std::string bootToken = environment.readFileLine(kBootIdPath);

    // Boot id looks like this: "16e12b27-2a84-4355-84cd-948348d6c998"
    LOG_ALWAYS_FATAL_IF(bootToken.size() != kBootIdSize, "Bad boot_id: '%s'", bootToken.c_str());

    // Use the first half for process sharding and the second half for AIDL sharding.
    return std::make_pair(environment.hashString8(bootToken.substr(0, kBootIdSize / 2)),
                          environment.hashString8(bootToken.substr(kBootIdSize / 2)));
}

std::unique_ptr<BinderObserverConfig> BinderObserverConfig::createConfig(
        std::unique_ptr<BinderObserverConfig::Environment>&& environment) {
    // Determine if we are in the system_server process and get the appropriate sharding config.
    uid_t uid = environment->getUid();
    std::string processName = environment->getProcessName();

    ShardingConfig sharding = (uid == AID_SYSTEM && processName == "system_server")
            ? environment->getSystemServerSharding()
            : environment->getOtherProcessesSharding();

    if (sharding.processMod == 0) {
        // Sharding of 0 means disabled. No need to read further configuration.
        return std::unique_ptr<BinderObserverConfig>(
                new BinderObserverConfig(std::move(environment), false, sharding, 0));
    }

    // Note: we want sharding to be stable for each session. Otherwise, for short-lived
    // proecsses, we will track different AIDLs each time the process runs, which will
    // increase the cardinality of the metrics we track.

    // We also want process sharding and AIDL sharding to be independent, as otherwise
    // certain process+AIDL combinations may be reported more frequently than others.
    // That's why we use two independent tokens.
    auto [processOffset, aidlOffset] = getBootStableTokens(*environment);

    // We use simple modulo arithmetc to keep sharding easier to understand and test. Everything
    // is mod-ed before addition too to ensure we don't overflow.
    size_t modulo = sharding.processMod;

    size_t token = processOffset % modulo;
    token += uid % modulo;
    token += environment->hashString8(processName) % modulo;
    bool enabled = token % modulo == 0;

    return std::unique_ptr<BinderObserverConfig>(
            new BinderObserverConfig(std::move(environment), enabled, sharding, aidlOffset));
}

} // namespace android
+157 −0
Original line number Diff line number Diff line
/*
 * 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.
 */
#pragma once

#include <log/log.h>

#include <fstream>
#include <limits>
#include <string>

namespace android {

class BinderObserverConfigTest;

/**
 * Manages the configuration of the binder observer.
 *
 * Applies sharding to determine whether the observer should be enabled for the current process
 * and which AIDL methods should be tracked.
 *
 * The configuration does not change within the lifecycle of the process.
 */
class BinderObserverConfig {
public:
    struct TrackingInfo {
        bool trackSpam;
        bool trackLatency;

        bool operator==(const TrackingInfo&) const = default;
        bool isTracked() const { return trackSpam || trackLatency; }
    };

    static std::unique_ptr<BinderObserverConfig> createConfig() {
        return createConfig(std::make_unique<Environment>());
    }

    // Returns whether binder stats are enabled for the current process. Should not change
    // within the lifecycle of a process.
    bool isEnabled() { return mEnabled; }

    TrackingInfo getTrackingInfo(const std::u16string_view& interfaceDescriptor, uint32_t txnCode) {
        if (!mEnabled) {
            return {
                    .trackSpam = false,
                    .trackLatency = false,
            };
        }

        // If present callMod must be a multiple of spamMod, so use it as a modulo.
        size_t modulo = mSharding.callMod != 0 ? mSharding.callMod : mSharding.spamMod;
        // As above, mod before addition too to ensure we don't overflow.
        size_t token = mAidlOffset % modulo;
        token += txnCode % modulo;
        token += mEnvironment->hashString16(interfaceDescriptor) % modulo;
        return {
                .trackSpam = mSharding.spamMod > 0 && token % mSharding.spamMod == 0,
                .trackLatency = mSharding.callMod > 0 && token % mSharding.callMod == 0,
        };
    }

private:
    friend class BinderObserverConfigTest;

    struct ShardingConfig {
        size_t processMod;
        size_t spamMod;
        size_t callMod;

        bool operator==(const ShardingConfig&) const = default;
    };

    // Contains a 128-bit random number stable for the current session
    // Looks like this: "16e12b27-2a84-4355-84cd-948348d6c998"
    static constexpr const char* kBootIdPath = "/proc/sys/kernel/random/boot_id";

    // The expected length of /proc/sys/kernel/random/boot_id
    static constexpr size_t kBootIdSize = 36;

    // Interacting with the environment abstracted out for testability.
    class Environment {
    public:
        virtual ~Environment() = default;
        virtual std::string readFileLine(const std::string& path);
        virtual uid_t getUid();
        virtual std::string getProcessName();

        virtual ShardingConfig getSystemServerSharding() {
            // While the feature is in staging, set the sharding to full monitoring, so it can
            // be tested deterministically.
            // TODO(b/299356196): Read sharding config from system properties (and sanitize it!)
            return {
                    .processMod = 1,
                    .spamMod = 1,
                    .callMod = 1,
            };
        }

        virtual ShardingConfig getOtherProcessesSharding() {
            // While the feature is in staging, set the sharding to full monitoring, so it can
            // be tested deterministically.
            // TODO(b/299356196): Read sharding config from system properties (and sanitize it!)
            return {
                    .processMod = 1,
                    .spamMod = 1,
                    .callMod = 1,
            };
        }

        virtual size_t hashString8(const std::string& content) {
            return std::hash<std::string>{}(content);
        }

        virtual size_t hashString16(const std::u16string_view& content) {
            return std::hash<std::u16string_view>{}(content);
        }
    };

    BinderObserverConfig(std::unique_ptr<BinderObserverConfig::Environment>&& environment,
                         bool enabled, BinderObserverConfig::ShardingConfig sharding,
                         size_t aidlOffset)
          : mEnvironment(std::move(environment)),
            mEnabled(enabled),
            mSharding(sharding),
            mAidlOffset(aidlOffset) {
        if (mEnabled) {
            LOG_ALWAYS_FATAL_IF(mSharding.spamMod == 0, "Enabled but nothing to monitor.");

            // Call sharding must always be a multiple of spam sharding (or 0, which is also OK)
            LOG_ALWAYS_FATAL_IF(mSharding.callMod % mSharding.spamMod != 0,
                                "Call reporting must be a subset of spam reporting.");
        }
    }

    static std::pair<size_t, size_t> getBootStableTokens(Environment& environment);

    static std::unique_ptr<BinderObserverConfig> createConfig(
            std::unique_ptr<BinderObserverConfig::Environment>&& environment);

    const std::unique_ptr<Environment> mEnvironment;
    const bool mEnabled;
    const ShardingConfig mSharding;
    const size_t mAidlOffset;
};
} // namespace android
Loading