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

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

Snap for 9000567 from 20e23735 to tm-qpr1-release

Change-Id: Ie255834980cb71e5a3ac76145ee9b8ea136ac86a
parents 605ebd55 20e23735
Loading
Loading
Loading
Loading
+9 −6
Original line number Diff line number Diff line
@@ -22,7 +22,8 @@ namespace android::mediautils {

std::shared_ptr<std::vector<std::string>>
getStatisticsClassesForModule(std::string_view moduleName) {
    static const std::map<std::string, std::shared_ptr<std::vector<std::string>>> m {
    static const std::map<std::string, std::shared_ptr<std::vector<std::string>>,
            std::less<> /* transparent comparator */> m {
        {
            METHOD_STATISTICS_MODULE_NAME_AUDIO_HIDL,
            std::shared_ptr<std::vector<std::string>>(
@@ -34,13 +35,14 @@ getStatisticsClassesForModule(std::string_view moduleName) {
              })
        },
    };
    auto it = m.find({moduleName.begin(), moduleName.end()});
    auto it = m.find(moduleName);
    if (it == m.end()) return {};
    return it->second;
}

static void addClassesToMap(const std::shared_ptr<std::vector<std::string>> &classNames,
        std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>> &map) {
        std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>,
                std::less<> /* transparent comparator */> &map) {
    if (classNames) {
        for (const auto& className : *classNames) {
            map.emplace(className, std::make_shared<MethodStatistics<std::string>>());
@@ -51,17 +53,18 @@ static void addClassesToMap(const std::shared_ptr<std::vector<std::string>> &cla
// singleton statistics for DeviceHalHidl StreamOutHalHidl StreamInHalHidl
std::shared_ptr<MethodStatistics<std::string>>
getStatisticsForClass(std::string_view className) {
    static const std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>> m =
    static const std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>,
            std::less<> /* transparent comparator */> m =
        // copy elided initialization of map m.
        [](){
            std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>> m;
            std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>, std::less<>> m;
            addClassesToMap(
                    getStatisticsClassesForModule(METHOD_STATISTICS_MODULE_NAME_AUDIO_HIDL),
                    m);
            return m;
        }();

    auto it = m.find({className.begin(), className.end()});
    auto it = m.find(className);
    if (it == m.end()) return {};
    return it->second;
}
+8 −7
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <android-base/logging.h>
#include <audio_utils/clock.h>
#include <mediautils/EventLog.h>
#include <mediautils/FixedString.h>
#include <mediautils/MethodStatistics.h>
#include <mediautils/TimeCheck.h>
#include <utils/Log.h>
@@ -138,11 +139,11 @@ std::string TimeCheck::toString() {
    return getTimeCheckThread().toString();
}

TimeCheck::TimeCheck(std::string tag, OnTimerFunc&& onTimer, uint32_t timeoutMs,
TimeCheck::TimeCheck(std::string_view tag, OnTimerFunc&& onTimer, uint32_t timeoutMs,
        bool crashOnTimeout)
    : mTimeCheckHandler(new TimeCheckHandler{
            std::move(tag), std::move(onTimer), crashOnTimeout,
            std::chrono::system_clock::now(), gettid()})
    : mTimeCheckHandler{ std::make_shared<TimeCheckHandler>(
            tag, std::move(onTimer), crashOnTimeout,
            std::chrono::system_clock::now(), gettid()) }
    , mTimerHandle(timeoutMs == 0
              ? getTimeCheckThread().trackTask(mTimeCheckHandler->tag)
              : getTimeCheckThread().scheduleTask(
@@ -231,14 +232,14 @@ mediautils::TimeCheck makeTimeCheckStatsForClassMethod(
            mediautils::getStatisticsForClass(className);
    if (!statistics) return {}; // empty TimeCheck.
    return mediautils::TimeCheck(
            std::string(className).append("::").append(methodName),
            [ clazz = std::string(className), method = std::string(methodName),
            FixedString62(className).append("::").append(methodName),
            [ safeMethodName = FixedString30(methodName),
              stats = std::move(statistics) ]
            (bool timeout, float elapsedMs) {
                    if (timeout) {
                        ; // ignored, there is no timeout value.
                    } else {
                        stats->event(method, elapsedMs);
                        stats->event(safeMethodName.asStringView(), elapsedMs);
                    }
            }, 0 /* timeoutMs */);
}
+6 −8
Original line number Diff line number Diff line
@@ -31,17 +31,15 @@ extern std::string formatTime(std::chrono::system_clock::time_point t);
extern std::string_view timeSuffix(std::string_view time1, std::string_view time2);

TimerThread::Handle TimerThread::scheduleTask(
        std::string tag, std::function<void()>&& func, std::chrono::milliseconds timeout) {
        std::string_view tag, std::function<void()>&& func, std::chrono::milliseconds timeout) {
    const auto now = std::chrono::system_clock::now();
    std::shared_ptr<const Request> request{
            new Request{ now, now + timeout, gettid(), std::move(tag) }};
    auto request = std::make_shared<const Request>(now, now + timeout, gettid(), tag);
    return mMonitorThread.add(std::move(request), std::move(func), timeout);
}

TimerThread::Handle TimerThread::trackTask(std::string tag) {
TimerThread::Handle TimerThread::trackTask(std::string_view tag) {
    const auto now = std::chrono::system_clock::now();
    std::shared_ptr<const Request> request{
            new Request{ now, now, gettid(), std::move(tag) }};
    auto request = std::make_shared<const Request>(now, now, gettid(), tag);
    return mNoTimeoutMap.add(std::move(request));
}

@@ -106,10 +104,10 @@ std::string TimerThread::toString(size_t retiredCount) const {
//
/* static */
bool TimerThread::isRequestFromHal(const std::shared_ptr<const Request>& request) {
    const size_t hidlPos = request->tag.find("Hidl");
    const size_t hidlPos = request->tag.asStringView().find("Hidl");
    if (hidlPos == std::string::npos) return false;
    // should be a separator afterwards Hidl which indicates the string was in the class.
    const size_t separatorPos = request->tag.find("::", hidlPos);
    const size_t separatorPos = request->tag.asStringView().find("::", hidlPos);
    return separatorPos != std::string::npos;
}

+282 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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 <algorithm>
#include <string>
#include <string_view>

namespace android::mediautils {

/*
 * FixedString is a stack allocatable string buffer that supports
 * simple appending of other strings and string_views.
 *
 * It is designed for no-malloc operation when std::string
 * small buffer optimization is insufficient.
 *
 * To keep code small, use asStringView() for operations on this.
 *
 * Notes:
 * 1) Appending beyond the internal buffer size results in truncation.
 *
 * Alternatives:
 * 1) If you want a sharable copy-on-write string implementation,
 *    consider using the legacy android::String8().
 * 2) Using std::string with a fixed stack allocator may suit your needs,
 *    but exception avoidance is tricky.
 * 3) Using C++20 ranges https://en.cppreference.com/w/cpp/ranges if you don't
 *    need backing store.  Be careful about allocation with ranges.
 *
 * Good small sizes are multiples of 16 minus 2, e.g. 14, 30, 46, 62.
 *
 * Implementation Notes:
 * 1) No iterators or [] for FixedString - please convert to std::string_view.
 * 2) For small N (e.g. less than 62), consider a change to always zero fill and
 *    potentially prevent always zero terminating (if one only does append).
 *
 * Possible arguments to create/append:
 * 1) A FixedString.
 * 2) A single char.
 * 3) A char * pointer.
 * 4) A std::string.
 * 5) A std::string_view (or something convertible to it).
 *
 * Example:
 *
 * FixedString s1(std::string("a"));    // ctor
 * s1 << "bc" << 'd' << '\n';           // streaming append
 * s1 += "hello";                       // += append
 * ASSERT_EQ(s1, "abcd\nhello");
 */
template <uint32_t N>
struct FixedString
{
    // Find the best size type.
    using strsize_t = std::conditional_t<(N > 255), uint32_t, uint8_t>;

    // constructors
    FixedString() { // override default
        buffer_[0] = '\0';
    }

    FixedString(const FixedString& other) { // override default.
        copyFrom<N>(other);
    }

    // The following constructor is not explicit to allow
    // FixedString<8> s = "abcd";
    template <typename ...Types>
    FixedString(Types&&... args) {
        append(std::forward<Types>(args)...);
    }

    // copy assign (copyFrom checks for equality and returns *this).
    FixedString& operator=(const FixedString& other) { // override default.
        return copyFrom<N>(other);
    }

    template <typename ...Types>
    FixedString& operator=(Types&&... args) {
        size_ = 0;
        return append(std::forward<Types>(args)...);
    }

    // operator equals
    bool operator==(const char *s) const {
        return strncmp(c_str(), s, capacity() + 1) == 0;
    }

    bool operator==(std::string_view s) const {
        return size() == s.size() && memcmp(data(), s.data(), size()) == 0;
    }

    // operator not-equals
    template <typename T>
    bool operator!=(const T& other) const {
        return !operator==(other);
    }

    // operator +=
    template <typename ...Types>
    FixedString& operator+=(Types&&... args) {
        return append(std::forward<Types>(args)...);
    }

    // conversion to std::string_view.
    operator std::string_view() const {
        return asStringView();
    }

    // basic observers
    size_t buffer_offset() const { return offsetof(std::decay_t<decltype(*this)>, buffer_); }
    static constexpr uint32_t capacity() { return N; }
    uint32_t size() const { return size_; }
    uint32_t remaining() const { return size_ >= N ? 0 : N - size_; }
    bool empty() const { return size_ == 0; }
    bool full() const { return size_ == N; }  // when full, possible truncation risk.
    char * data() { return buffer_; }
    const char * data() const { return buffer_; }
    const char * c_str() const { return buffer_; }

    inline std::string_view asStringView() const {
        return { buffer_, static_cast<size_t>(size_) };
    }
    inline std::string asString() const {
        return { buffer_, static_cast<size_t>(size_) };
    }

    void clear() { size_ = 0; buffer_[0] = 0; }

    // Implementation of append - using templates
    // to guarantee precedence in the presence of ambiguity.
    //
    // Consider C++20 template overloading through constraints and concepts.
    template <typename T>
    FixedString& append(const T& t) {
        using decayT = std::decay_t<T>;
        if constexpr (is_specialization_v<decayT, FixedString>) {
            // A FixedString<U>
            if (size_ == 0) {
                // optimization to erase everything.
                return copyFrom(t);
            } else {
                return appendStringView({t.data(), t.size()});
            }
        } else if constexpr(std::is_same_v<decayT, char>) {
            if (size_ < N) {
                buffer_[size_++] = t;
                buffer_[size_] = '\0';
            }
            return *this;
        } else if constexpr(std::is_same_v<decayT, char *>) {
            // Some char* ptr.
            return appendString(t);
        } else if constexpr (std::is_convertible_v<decayT, std::string_view>) {
            // std::string_view, std::string, or some other convertible type.
            return appendStringView(t);
        } else /* constexpr */ {
            static_assert(dependent_false_v<T>, "non-matching append type");
        }
    }

    FixedString& appendStringView(std::string_view s) {
        uint32_t total = std::min(static_cast<size_t>(N - size_), s.size());
        memcpy(buffer_ + size_, s.data(), total);
        size_ += total;
        buffer_[size_] = '\0';
        return *this;
    }

    FixedString& appendString(const char *s) {
        // strncpy zero pads to the end,
        // strlcpy returns total expected length,
        // we don't have strncpy_s in Bionic,
        // so we write our own here.
        while (size_ < N && *s != '\0') {
            buffer_[size_++] = *s++;
        }
        buffer_[size_] = '\0';
        return *this;
    }

    // Copy initialize the struct.
    // Note: We are POD but customize the copying for acceleration
    // of moving small strings embedded in a large buffers.
    template <uint32_t U>
    FixedString& copyFrom(const FixedString<U>& other) {
        if ((void*)this != (void*)&other) { // not a self-assignment
            if (other.size() == 0) {
                size_ = 0;
                buffer_[0] = '\0';
                return *this;
            }
            constexpr size_t kSizeToCopyWhole = 64;
            if constexpr (N == U &&
                    sizeof(*this) == sizeof(other) &&
                    sizeof(*this) <= kSizeToCopyWhole) {
                // As we have the same str size type, we can just
                // memcpy with fixed size, which can be easily optimized.
                memcpy(static_cast<void*>(this), static_cast<const void*>(&other), sizeof(*this));
                return *this;
            }
            if constexpr (std::is_same_v<strsize_t, typename FixedString<U>::strsize_t>) {
                constexpr size_t kAlign = 8;  // align to a multiple of 8.
                static_assert((kAlign & (kAlign - 1)) == 0); // power of 2.
                // we check any alignment issues.
                if (buffer_offset() == other.buffer_offset() && other.size() <= capacity()) {
                    // improve on standard POD copying by reducing size.
                    const size_t mincpy = buffer_offset() + other.size() + 1 /* nul */;
                    const size_t maxcpy = std::min(sizeof(*this), sizeof(other));
                    const size_t cpysize = std::min(mincpy + kAlign - 1 & ~(kAlign - 1), maxcpy);
                    memcpy(static_cast<void*>(this), static_cast<const void*>(&other), cpysize);
                    return *this;
                }
            }
            size_ = std::min(other.size(), capacity());
            memcpy(buffer_, other.data(), size_);
            buffer_[size_] = '\0';  // zero terminate.
        }
        return *this;
    }

private:
    //  Template helper methods

    template <typename Test, template <uint32_t> class Ref>
    struct is_specialization : std::false_type {};

    template <template <uint32_t> class Ref, uint32_t UU>
    struct is_specialization<Ref<UU>, Ref>: std::true_type {};

    template <typename Test, template <uint32_t> class Ref>
    static inline constexpr bool is_specialization_v = is_specialization<Test, Ref>::value;

    // For static assert(false) we need a template version to avoid early failure.
    template <typename T>
    static inline constexpr bool dependent_false_v = false;

    // POD variables
    strsize_t size_ = 0;
    char buffer_[N + 1 /* allow zero termination */];
};

// Stream operator syntactic sugar.
// Example:
// s << 'b' << "c" << "d" << '\n';
template <uint32_t N, typename ...Types>
FixedString<N>& operator<<(FixedString<N>& fs, Types&&... args) {
    return fs.append(std::forward<Types>(args)...);
}

// We do not use a default size for fixed string as changing
// the default size would lead to different behavior - we want the
// size to be explicitly known.

// FixedString62 of 62 chars fits in one typical cache line.
using FixedString62 = FixedString<62>;

// Slightly smaller
using FixedString30 = FixedString<30>;

// Since we have added copy and assignment optimizations,
// we are no longer trivially assignable and copyable.
// But we check standard layout here to prevent inclusion of unacceptable members or virtuals.
static_assert(std::is_standard_layout_v<FixedString62>);
static_assert(std::is_standard_layout_v<FixedString30>);

}  // namespace android::mediautils
+16 −7
Original line number Diff line number Diff line
@@ -55,15 +55,23 @@ public:
    /**
     * Adds a method event, typically execution time in ms.
     */
    void event(Code code, FloatType executeMs) {
    template <typename C>
    void event(C&& code, FloatType executeMs) {
        std::lock_guard lg(mLock);
        mStatisticsMap[code].add(executeMs);
        auto it = mStatisticsMap.lower_bound(code);
        if (it != mStatisticsMap.end() && it->first == code) {
            it->second.add(executeMs);
        } else {
            // StatsType ctor takes an optional array of data for initialization.
            FloatType dataArray[1] = { executeMs };
            mStatisticsMap.emplace_hint(it, std::forward<C>(code), dataArray);
        }
    }

    /**
     * Returns the name for the method code.
     */
    std::string getMethodForCode(Code code) const {
    std::string getMethodForCode(const Code& code) const {
        auto it = mMethodMap.find(code);
        return it == mMethodMap.end() ? std::to_string((int)code) : it->second;
    }
@@ -71,7 +79,7 @@ public:
    /**
     * Returns the number of times the method was invoked by event().
     */
    size_t getMethodCount(Code code) const {
    size_t getMethodCount(const Code& code) const {
        std::lock_guard lg(mLock);
        auto it = mStatisticsMap.find(code);
        return it == mStatisticsMap.end() ? 0 : it->second.getN();
@@ -80,7 +88,7 @@ public:
    /**
     * Returns the statistics object for the method.
     */
    StatsType getStatistics(Code code) const {
    StatsType getStatistics(const Code& code) const {
        std::lock_guard lg(mLock);
        auto it = mStatisticsMap.find(code);
        return it == mStatisticsMap.end() ? StatsType{} : it->second;
@@ -107,9 +115,10 @@ public:
    }

private:
    const std::map<Code, std::string> mMethodMap;
    // Note: we use a transparent comparator std::less<> for heterogeneous key lookup.
    const std::map<Code, std::string, std::less<>> mMethodMap;
    mutable std::mutex mLock;
    std::map<Code, StatsType> mStatisticsMap GUARDED_BY(mLock);
    std::map<Code, StatsType, std::less<>> mStatisticsMap GUARDED_BY(mLock);
};

// Managed Statistics support.
Loading