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

Commit bb7a0208 authored by Prabir Pradhan's avatar Prabir Pradhan
Browse files

InputTracer: Separate the threading logic into a wrapper backend

InputTracer writes to the tracing backend interface. However, since
perfetto expects events to be written from a single thread, InputTracer
would write the events from a separate thread.

To simplify the logic inside InputTracer, remove the threading logic.
Instead, we introduce the ThreadingBackend class, which wraps around an
abitrary backend and forwards events to the inner backend from a new
thread.

Another benefit of this is that we no longer have to use the threaded
backend for tests, which makes the tracing part of InputDispatcher_test
deterministic.

Bug: 210460522
Test: atest inputflinger_tests
Change-Id: If7bbc912c05bc975ec9585f0a0ebce68683925bb
parent c8693d4c
Loading
Loading
Loading
Loading
+11 −3
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@
#include "InputDispatcher.h"
#include "trace/InputTracer.h"
#include "trace/InputTracingPerfettoBackend.h"
#include "trace/ThreadedBackend.h"

#define INDENT "  "
#define INDENT2 "    "
@@ -86,6 +87,15 @@ bool isInputTracingEnabled() {
    return input_flags::enable_input_event_tracing() && isUserdebugOrEng;
}

// Create the input tracing backend that writes to perfetto from a single thread.
std::unique_ptr<trace::InputTracingBackendInterface> createInputTracingBackendIfEnabled() {
    if (!isInputTracingEnabled()) {
        return nullptr;
    }
    return std::make_unique<trace::impl::ThreadedBackend<trace::impl::PerfettoBackend>>(
            trace::impl::PerfettoBackend());
}

template <class Entry>
void ensureEventTraced(const Entry& entry) {
    if (!entry.traceTracker) {
@@ -832,9 +842,7 @@ std::pair<bool /*cancelPointers*/, bool /*cancelNonPointers*/> expandCancellatio
// --- InputDispatcher ---

InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy)
      : InputDispatcher(policy,
                        isInputTracingEnabled() ? std::make_unique<trace::impl::PerfettoBackend>()
                                                : nullptr) {}
      : InputDispatcher(policy, createInputTracingBackendIfEnabled()) {}

InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy,
                                 std::unique_ptr<trace::InputTracingBackendInterface> traceBackend)
+57 −118
Original line number Diff line number Diff line
@@ -19,12 +19,17 @@
#include "InputTracer.h"

#include <android-base/logging.h>
#include <utils/AndroidThreads.h>

namespace android::inputdispatcher::trace::impl {

namespace {

// Helper to std::visit with lambdas.
template <typename... V>
struct Visitor : V... {
    using V::operator()...;
};

TracedEvent createTracedEvent(const MotionEntry& e) {
    return TracedMotionEvent{e.id,
                             e.eventTime,
@@ -59,19 +64,9 @@ TracedEvent createTracedEvent(const KeyEntry& e) {
// --- InputTracer ---

InputTracer::InputTracer(std::unique_ptr<InputTracingBackendInterface> backend)
      : mTracerThread(&InputTracer::threadLoop, this), mBackend(std::move(backend)) {}

InputTracer::~InputTracer() {
    {
        std::scoped_lock lock(mLock);
        mThreadExit = true;
    }
    mThreadWakeCondition.notify_all();
    mTracerThread.join();
}
      : mBackend(std::move(backend)) {}

std::unique_ptr<EventTrackerInterface> InputTracer::traceInboundEvent(const EventEntry& entry) {
    std::scoped_lock lock(mLock);
    TracedEvent traced;

    if (entry.type == EventEntry::Type::MOTION) {
@@ -89,7 +84,6 @@ std::unique_ptr<EventTrackerInterface> InputTracer::traceInboundEvent(const Even

void InputTracer::dispatchToTargetHint(const EventTrackerInterface& cookie,
                                       const InputTarget& target) {
    std::scoped_lock lock(mLock);
    auto& cookieState = getState(cookie);
    if (!cookieState) {
        LOG(FATAL) << "dispatchToTargetHint() should not be called after eventProcessingComplete()";
@@ -98,24 +92,20 @@ void InputTracer::dispatchToTargetHint(const EventTrackerInterface& cookie,
}

void InputTracer::eventProcessingComplete(const EventTrackerInterface& cookie) {
    {
        std::scoped_lock lock(mLock);
    auto& cookieState = getState(cookie);
    if (!cookieState) {
        LOG(FATAL) << "Traced event was already logged. "
                      "eventProcessingComplete() was likely called more than once.";
    }
        mTraceQueue.emplace_back(std::move(*cookieState));
        cookieState.reset();
    } // release lock

    mThreadWakeCondition.notify_all();
    std::visit(Visitor{[&](const TracedMotionEvent& e) { mBackend->traceMotionEvent(e); },
                       [&](const TracedKeyEvent& e) { mBackend->traceKeyEvent(e); }},
               cookieState->event);
    cookieState.reset();
}

void InputTracer::traceEventDispatch(const DispatchEntry& dispatchEntry,
                                     const EventTrackerInterface* cookie) {
    {
        std::scoped_lock lock(mLock);
    const EventEntry& entry = *dispatchEntry.eventEntry;

    TracedEvent traced;
@@ -131,82 +121,32 @@ void InputTracer::traceEventDispatch(const DispatchEntry& dispatchEntry,

    if (!cookie) {
        // This event was not tracked as an inbound event, so trace it now.
            mTraceQueue.emplace_back(traced);
        std::visit(Visitor{[&](const TracedMotionEvent& e) { mBackend->traceMotionEvent(e); },
                           [&](const TracedKeyEvent& e) { mBackend->traceKeyEvent(e); }},
                   traced);
    }

    // The vsyncId only has meaning if the event is targeting a window.
    const int32_t windowId = dispatchEntry.windowId.value_or(0);
    const int32_t vsyncId = dispatchEntry.windowId.has_value() ? dispatchEntry.vsyncId : 0;

        mDispatchTraceQueue.emplace_back(std::move(traced), dispatchEntry.deliveryTime,
                                         dispatchEntry.resolvedFlags, dispatchEntry.targetUid,
                                         vsyncId, windowId, dispatchEntry.transform,
                                         dispatchEntry.rawTransform);
    } // release lock

    mThreadWakeCondition.notify_all();
    mBackend->traceWindowDispatch({std::move(traced), dispatchEntry.deliveryTime,
                                   dispatchEntry.resolvedFlags, dispatchEntry.targetUid, vsyncId,
                                   windowId, dispatchEntry.transform, dispatchEntry.rawTransform,
                                   /*hmac=*/{}});
}

std::optional<InputTracer::EventState>& InputTracer::getState(const EventTrackerInterface& cookie) {
    return static_cast<const EventTrackerImpl&>(cookie).mLockedState;
}

void InputTracer::threadLoop() {
    androidSetThreadName("InputTracer");

    std::vector<const EventState> eventsToTrace;
    std::vector<const WindowDispatchArgs> dispatchEventsToTrace;

    while (true) {
        { // acquire lock
            std::unique_lock lock(mLock);
            base::ScopedLockAssertion assumeLocked(mLock);

            // Wait until we need to process more events or exit.
            mThreadWakeCondition.wait(lock, [&]() REQUIRES(mLock) {
                return mThreadExit || !mTraceQueue.empty() || !mDispatchTraceQueue.empty();
            });
            if (mThreadExit) {
                return;
            }

            mTraceQueue.swap(eventsToTrace);
            mDispatchTraceQueue.swap(dispatchEventsToTrace);
        } // release lock

        // Trace the events into the backend without holding the lock to reduce the amount of
        // work performed in the critical section.
        writeEventsToBackend(eventsToTrace, dispatchEventsToTrace);
        eventsToTrace.clear();
        dispatchEventsToTrace.clear();
    }
}

void InputTracer::writeEventsToBackend(
        const std::vector<const EventState>& events,
        const std::vector<const WindowDispatchArgs>& dispatchEvents) {
    for (const auto& event : events) {
        if (auto* motion = std::get_if<TracedMotionEvent>(&event.event); motion != nullptr) {
            mBackend->traceMotionEvent(*motion);
        } else {
            mBackend->traceKeyEvent(std::get<TracedKeyEvent>(event.event));
        }
    }

    for (const auto& dispatchArgs : dispatchEvents) {
        mBackend->traceWindowDispatch(dispatchArgs);
    }
    return static_cast<const EventTrackerImpl&>(cookie).mState;
}

// --- InputTracer::EventTrackerImpl ---

InputTracer::EventTrackerImpl::EventTrackerImpl(InputTracer& tracer, TracedEvent&& event)
      : mTracer(tracer), mLockedState(event) {}
      : mTracer(tracer), mState(event) {}

InputTracer::EventTrackerImpl::~EventTrackerImpl() {
    {
        std::scoped_lock lock(mTracer.mLock);
        if (!mLockedState) {
    if (!mState) {
        // This event has already been written to the trace as expected.
        return;
    }
@@ -214,11 +154,10 @@ InputTracer::EventTrackerImpl::~EventTrackerImpl() {
    // Write it to the trace now.
    // TODO(b/210460522): Determine why/where the event is being destroyed before
    //   eventProcessingComplete() is called.
        mTracer.mTraceQueue.emplace_back(std::move(*mLockedState));
        mLockedState.reset();
    } // release lock

    mTracer.mThreadWakeCondition.notify_all();
    std::visit(Visitor{[&](const TracedMotionEvent& e) { mTracer.mBackend->traceMotionEvent(e); },
                       [&](const TracedKeyEvent& e) { mTracer.mBackend->traceKeyEvent(e); }},
               mState->event);
    mState.reset();
}

} // namespace android::inputdispatcher::trace::impl
+9 −29
Original line number Diff line number Diff line
@@ -18,14 +18,7 @@

#include "InputTracerInterface.h"

#include <android-base/thread_annotations.h>
#include <gui/WindowInfo.h>

#include <memory>
#include <mutex>
#include <thread>
#include <unordered_set>
#include <vector>

#include "../Entry.h"
#include "InputTracingBackendInterface.h"
@@ -35,17 +28,16 @@ namespace android::inputdispatcher::trace::impl {
/**
 * The tracer implementation for InputDispatcher.
 *
 * InputTracer is thread-safe, so it can be called from any thread. Upon construction, InputTracer
 * will start its own thread that it uses for write events into the tracing backend. That is the
 * one and only thread that will interact with the tracing backend, since the Perfetto backend
 * uses thread-local storage.
 * InputTracer's responsibility is to keep track of events as they are processed by InputDispatcher,
 * and to write the events to the tracing backend when enough information is collected. InputTracer
 * is not thread-safe.
 *
 * See the documentation in InputTracerInterface for the API surface.
 */
class InputTracer : public InputTracerInterface {
public:
    explicit InputTracer(std::unique_ptr<InputTracingBackendInterface>);
    ~InputTracer() override;
    ~InputTracer() = default;
    InputTracer(const InputTracer&) = delete;
    InputTracer& operator=(const InputTracer&) = delete;

@@ -55,10 +47,6 @@ public:
    void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface*) override;

private:
    std::mutex mLock;
    std::thread mTracerThread;
    bool mThreadExit GUARDED_BY(mLock){false};
    std::condition_variable mThreadWakeCondition;
    std::unique_ptr<InputTracingBackendInterface> mBackend;

    // The state of a tracked event.
@@ -67,14 +55,12 @@ private:
        // TODO(b/210460522): Add additional args for tracking event sensitivity and
        //  dispatch target UIDs.
    };
    std::vector<const EventState> mTraceQueue GUARDED_BY(mLock);
    using WindowDispatchArgs = InputTracingBackendInterface::WindowDispatchArgs;
    std::vector<const WindowDispatchArgs> mDispatchTraceQueue GUARDED_BY(mLock);

    // Provides thread-safe access to the state from an event tracker cookie.
    std::optional<EventState>& getState(const EventTrackerInterface&) REQUIRES(mLock);
    // Get the event state associated with a tracking cookie.
    std::optional<EventState>& getState(const EventTrackerInterface&);

    // Implementation of the event tracker cookie.
    // Implementation of the event tracker cookie. The cookie holds the event state directly for
    // convenience to avoid the overhead of tracking the state separately in InputTracer.
    class EventTrackerImpl : public EventTrackerInterface {
    public:
        explicit EventTrackerImpl(InputTracer&, TracedEvent&& entry);
@@ -84,16 +70,10 @@ private:
        InputTracer& mTracer;
        // This event tracker cookie will only hold the state as long as it has not been written
        // to the trace. The state is released when the event is written to the trace.
        mutable std::optional<EventState> mLockedState;
        mutable std::optional<EventState> mState;

        // Only allow InputTracer access to the locked state through getTrackerState() to ensure
        // that the InputTracer lock is held when this is accessed.
        friend std::optional<EventState>& InputTracer::getState(const EventTrackerInterface&);
    };

    void threadLoop();
    void writeEventsToBackend(const std::vector<const EventState>& events,
                              const std::vector<const WindowDispatchArgs>& dispatchEvents);
};

} // namespace android::inputdispatcher::trace::impl
+3 −3
Original line number Diff line number Diff line
@@ -82,10 +82,10 @@ public:
    virtual ~InputTracingBackendInterface() = default;

    /** Trace a KeyEvent. */
    virtual void traceKeyEvent(const TracedKeyEvent&) const = 0;
    virtual void traceKeyEvent(const TracedKeyEvent&) = 0;

    /** Trace a MotionEvent. */
    virtual void traceMotionEvent(const TracedMotionEvent&) const = 0;
    virtual void traceMotionEvent(const TracedMotionEvent&) = 0;

    /** Trace an event being sent to a window. */
    struct WindowDispatchArgs {
@@ -99,7 +99,7 @@ public:
        ui::Transform rawTransform;
        std::array<uint8_t, 32> hmac;
    };
    virtual void traceWindowDispatch(const WindowDispatchArgs&) const = 0;
    virtual void traceWindowDispatch(const WindowDispatchArgs&) = 0;
};

} // namespace android::inputdispatcher::trace
+3 −3
Original line number Diff line number Diff line
@@ -63,7 +63,7 @@ PerfettoBackend::PerfettoBackend() {
    });
}

void PerfettoBackend::traceMotionEvent(const TracedMotionEvent& event) const {
void PerfettoBackend::traceMotionEvent(const TracedMotionEvent& event) {
    InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
        auto tracePacket = ctx.NewTracePacket();
        auto* inputEvent = tracePacket->set_android_input_event();
@@ -72,7 +72,7 @@ void PerfettoBackend::traceMotionEvent(const TracedMotionEvent& event) const {
    });
}

void PerfettoBackend::traceKeyEvent(const TracedKeyEvent& event) const {
void PerfettoBackend::traceKeyEvent(const TracedKeyEvent& event) {
    InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
        auto tracePacket = ctx.NewTracePacket();
        auto* inputEvent = tracePacket->set_android_input_event();
@@ -81,7 +81,7 @@ void PerfettoBackend::traceKeyEvent(const TracedKeyEvent& event) const {
    });
}

void PerfettoBackend::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs) const {
void PerfettoBackend::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs) {
    InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
        auto tracePacket = ctx.NewTracePacket();
        auto* inputEventProto = tracePacket->set_android_input_event();
Loading