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

Commit 81a56fa2 authored by Android Build Merger (Role)'s avatar Android Build Merger (Role) Committed by Android (Google) Code Review
Browse files

Merge changes from topic 'am-a3904e70ae584ef4bb777fa8f1c88fb1' into oc-dr1-dev-plus-aosp

* changes:
  Merge changes from topic 'binder_tests_aosp' am: 8f93ab17 am: 1bc06327 am: 9dada04d
  Add test for death notification workqueue. am: f7100e49 am: 88c9e4fe am: 07add199
  Fix percentile calculations am: 52d1540b am: d65c52d4 am: 73540c1c
  Refine the binder latency test am: 1302c7e3  -s ours am: cee537e6  -s ours am: 8c254e72  -s ours
parents 9041a486 b63d8e0e
Loading
Loading
Loading
Loading
+116 −0
Original line number Diff line number Diff line
@@ -32,6 +32,13 @@

using namespace android;

static ::testing::AssertionResult IsPageAligned(void *buf) {
    if (((unsigned long)buf & ((unsigned long)PAGE_SIZE - 1)) == 0)
        return ::testing::AssertionSuccess();
    else
        return ::testing::AssertionFailure() << buf << " is not page aligned";
}

static testing::Environment* binder_env;
static char *binderservername;
static char *binderserversuffix;
@@ -44,6 +51,7 @@ enum BinderLibTestTranscationCode {
    BINDER_LIB_TEST_REGISTER_SERVER,
    BINDER_LIB_TEST_ADD_SERVER,
    BINDER_LIB_TEST_CALL_BACK,
    BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF,
    BINDER_LIB_TEST_NOP_CALL_BACK,
    BINDER_LIB_TEST_GET_SELF_TRANSACTION,
    BINDER_LIB_TEST_GET_ID_TRANSACTION,
@@ -265,17 +273,23 @@ class BinderLibTestEvent
            pthread_mutex_unlock(&m_waitMutex);
            return ret;
        }
        pthread_t getTriggeringThread()
        {
            return m_triggeringThread;
        }
    protected:
        void triggerEvent(void) {
            pthread_mutex_lock(&m_waitMutex);
            pthread_cond_signal(&m_waitCond);
            m_eventTriggered = true;
            m_triggeringThread = pthread_self();
            pthread_mutex_unlock(&m_waitMutex);
        };
    private:
        pthread_mutex_t m_waitMutex;
        pthread_cond_t m_waitCond;
        bool m_eventTriggered;
        pthread_t m_triggeringThread;
};

class BinderLibTestCallBack : public BBinder, public BinderLibTestEvent
@@ -283,6 +297,7 @@ class BinderLibTestCallBack : public BBinder, public BinderLibTestEvent
    public:
        BinderLibTestCallBack()
            : m_result(NOT_ENOUGH_DATA)
            , m_prev_end(NULL)
        {
        }
        status_t getResult(void)
@@ -302,12 +317,35 @@ class BinderLibTestCallBack : public BBinder, public BinderLibTestEvent
                m_result = data.readInt32();
                triggerEvent();
                return NO_ERROR;
            case BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF: {
                sp<IBinder> server;
                int ret;
                const uint8_t *buf = data.data();
                size_t size = data.dataSize();
                if (m_prev_end) {
                    /* 64-bit kernel needs at most 8 bytes to align buffer end */
                    EXPECT_LE((size_t)(buf - m_prev_end), (size_t)8);
                } else {
                    EXPECT_TRUE(IsPageAligned((void *)buf));
                }

                m_prev_end = buf + size + data.objectsCount() * sizeof(binder_size_t);

                if (size > 0) {
                    server = static_cast<BinderLibTestEnv *>(binder_env)->getServer();
                    ret = server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION,
                                           data, reply);
                    EXPECT_EQ(NO_ERROR, ret);
                }
                return NO_ERROR;
            }
            default:
                return UNKNOWN_TRANSACTION;
            }
        }

        status_t m_result;
        const uint8_t *m_prev_end;
};

class TestDeathRecipient : public IBinder::DeathRecipient, public BinderLibTestEvent
@@ -606,6 +644,65 @@ TEST_F(BinderLibTest, DeathNotificationMultiple)
    }
}

TEST_F(BinderLibTest, DeathNotificationThread)
{
    status_t ret;
    sp<BinderLibTestCallBack> callback;
    sp<IBinder> target = addServer();
    ASSERT_TRUE(target != NULL);
    sp<IBinder> client = addServer();
    ASSERT_TRUE(client != NULL);

    sp<TestDeathRecipient> testDeathRecipient = new TestDeathRecipient();

    ret = target->linkToDeath(testDeathRecipient);
    EXPECT_EQ(NO_ERROR, ret);

    {
        Parcel data, reply;
        ret = target->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY);
        EXPECT_EQ(0, ret);
    }

    /* Make sure it's dead */
    testDeathRecipient->waitEvent(5);

    /* Now, pass the ref to another process and ask that process to
     * call linkToDeath() on it, and wait for a response. This tests
     * two things:
     * 1) You still get death notifications when calling linkToDeath()
     *    on a ref that is already dead when it was passed to you.
     * 2) That death notifications are not directly pushed to the thread
     *    registering them, but to the threadpool (proc workqueue) instead.
     *
     * 2) is tested because the thread handling BINDER_LIB_TEST_DEATH_TRANSACTION
     * is blocked on a condition variable waiting for the death notification to be
     * called; therefore, that thread is not available for handling proc work.
     * So, if the death notification was pushed to the thread workqueue, the callback
     * would never be called, and the test would timeout and fail.
     *
     * Note that we can't do this part of the test from this thread itself, because
     * the binder driver would only push death notifications to the thread if
     * it is a looper thread, which this thread is not.
     *
     * See b/23525545 for details.
     */
    {
        Parcel data, reply;

        callback = new BinderLibTestCallBack();
        data.writeStrongBinder(target);
        data.writeStrongBinder(callback);
        ret = client->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply, TF_ONE_WAY);
        EXPECT_EQ(NO_ERROR, ret);
    }

    ret = callback->waitEvent(5);
    EXPECT_EQ(NO_ERROR, ret);
    ret = callback->getResult();
    EXPECT_EQ(NO_ERROR, ret);
}

TEST_F(BinderLibTest, PassFile) {
    int ret;
    int pipefd[2];
@@ -728,6 +825,25 @@ TEST_F(BinderLibTest, FreedBinder) {
    }
}

TEST_F(BinderLibTest, CheckNoHeaderMappedInUser) {
    status_t ret;
    Parcel data, reply;
    sp<BinderLibTestCallBack> callBack = new BinderLibTestCallBack();
    for (int i = 0; i < 2; i++) {
        BinderLibTestBundle datai;
        datai.appendFrom(&data, 0, data.dataSize());

        data.freeData();
        data.writeInt32(1);
        data.writeStrongBinder(callBack);
        data.writeInt32(BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF);

        datai.appendTo(&data);
    }
    ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply);
    EXPECT_EQ(NO_ERROR, ret);
}

class BinderLibTestService : public BBinder
{
    public:
+109 −51
Original line number Diff line number Diff line
@@ -101,18 +101,21 @@ public:
};

static const uint32_t num_buckets = 128;
static const uint64_t max_time_bucket = 50ull * 1000000;
static const uint64_t time_per_bucket = max_time_bucket / num_buckets;
static constexpr float time_per_bucket_ms = time_per_bucket / 1.0E6;
static uint64_t max_time_bucket = 50ull * 1000000;
static uint64_t time_per_bucket = max_time_bucket / num_buckets;

struct ProcResults {
    uint64_t m_best = max_time_bucket;
    uint64_t m_worst = 0;
    uint32_t m_buckets[num_buckets] = {0};
    uint64_t m_transactions = 0;
    uint64_t m_long_transactions = 0;
    uint64_t m_total_time = 0;
    uint64_t m_best = max_time_bucket;

    void add_time(uint64_t time) {
        if (time > max_time_bucket) {
            m_long_transactions++;
        }
        m_buckets[min(time, max_time_bucket-1) / time_per_bucket] += 1;
        m_best = min(time, m_best);
        m_worst = max(time, m_worst);
@@ -127,16 +130,24 @@ struct ProcResults {
        ret.m_worst = max(a.m_worst, b.m_worst);
        ret.m_best = min(a.m_best, b.m_best);
        ret.m_transactions = a.m_transactions + b.m_transactions;
        ret.m_long_transactions = a.m_long_transactions + b.m_long_transactions;
        ret.m_total_time = a.m_total_time + b.m_total_time;
        return ret;
    }
    void dump() {
        if (m_long_transactions > 0) {
            cout << (double)m_long_transactions / m_transactions << "% of transactions took longer "
                "than estimated max latency. Consider setting -m to be higher than "
                 << m_worst / 1000 << " microseconds" << endl;
        }

        double best = (double)m_best / 1.0E6;
        double worst = (double)m_worst / 1.0E6;
        double average = (double)m_total_time / m_transactions / 1.0E6;
        cout << "average:" << average << "ms worst:" << worst << "ms best:" << best << "ms" << endl;

        uint64_t cur_total = 0;
        float time_per_bucket_ms = time_per_bucket / 1.0E6;
        for (int i = 0; i < num_buckets; i++) {
            float cur_time = time_per_bucket_ms * i + 0.5f * time_per_bucket_ms;
            if ((cur_total < 0.5f * m_transactions) && (cur_total + m_buckets[i] >= 0.5f * m_transactions)) {
@@ -154,7 +165,6 @@ struct ProcResults {
            cur_total += m_buckets[i];
        }
        cout << endl;

    }
};

@@ -166,8 +176,7 @@ String16 generateServiceName(int num)
    return serviceName;
}

void worker_fx(
    int num,
void worker_fx(int num,
               int worker_count,
               int iterations,
               int payload_size,
@@ -264,47 +273,19 @@ void signal_all(vector<Pipe>& v)
    }
}

int main(int argc, char *argv[])
void run_main(int iterations,
              int workers,
              int payload_size,
              int cs_pair,
              bool training_round=false)
{
    int workers = 2;
    int iterations = 10000;
    int payload_size = 0;
    bool cs_pair = false;
    (void)argc;
    (void)argv;
    vector<Pipe> pipes;

    // Parse arguments.
    for (int i = 1; i < argc; i++) {
        if (string(argv[i]) == "-w") {
            workers = atoi(argv[i+1]);
            i++;
            continue;
        }
        if (string(argv[i]) == "-i") {
            iterations = atoi(argv[i+1]);
            i++;
            continue;
        }
        if (string(argv[i]) == "-s") {
            payload_size = atoi(argv[i+1]);
	    i++;
	}
        if (string(argv[i]) == "-p") {
		// client/server pairs instead of spreading
		// requests to all workers. If true, half
		// the workers become clients and half servers
		cs_pair = true;
	}
    }

    // Create all the workers and wait for them to spawn.
    for (int i = 0; i < workers; i++) {
        pipes.push_back(make_worker(i, iterations, workers, payload_size, cs_pair));
    }
    wait_all(pipes);


    // Run the workers and wait for completion.
    chrono::time_point<chrono::high_resolution_clock> start, end;
    cout << "waiting for workers to complete" << endl;
@@ -326,7 +307,6 @@ int main(int argc, char *argv[])
        pipes[i].recv(tmp_results);
        tot_results = ProcResults::combine(tot_results, tmp_results);
    }
    tot_results.dump();

    // Kill all the workers.
    cout << "killing workers" << endl;
@@ -338,5 +318,83 @@ int main(int argc, char *argv[])
            cout << "nonzero child status" << status << endl;
        }
    }
    if (training_round) {
        // sets max_time_bucket to 2 * m_worst from the training round.
        // Also needs to adjust time_per_bucket accordingly.
        max_time_bucket = 2 * tot_results.m_worst;
        time_per_bucket = max_time_bucket / num_buckets;
        cout << "Max latency during training: " << tot_results.m_worst / 1.0E6 << "ms" << endl;
    } else {
            tot_results.dump();
    }
}

int main(int argc, char *argv[])
{
    int workers = 2;
    int iterations = 10000;
    int payload_size = 0;
    bool cs_pair = false;
    bool training_round = false;
    (void)argc;
    (void)argv;

    // Parse arguments.
    for (int i = 1; i < argc; i++) {
        if (string(argv[i]) == "--help") {
            cout << "Usage: binderThroughputTest [OPTIONS]" << endl;
            cout << "\t-i N    : Specify number of iterations." << endl;
            cout << "\t-m N    : Specify expected max latency in microseconds." << endl;
            cout << "\t-p      : Split workers into client/server pairs." << endl;
            cout << "\t-s N    : Specify payload size." << endl;
            cout << "\t-t N    : Run training round." << endl;
            cout << "\t-w N    : Specify total number of workers." << endl;
            return 0;
        }
        if (string(argv[i]) == "-w") {
            workers = atoi(argv[i+1]);
            i++;
            continue;
        }
        if (string(argv[i]) == "-i") {
            iterations = atoi(argv[i+1]);
            i++;
            continue;
        }
        if (string(argv[i]) == "-s") {
            payload_size = atoi(argv[i+1]);
            i++;
        }
        if (string(argv[i]) == "-p") {
            // client/server pairs instead of spreading
            // requests to all workers. If true, half
            // the workers become clients and half servers
            cs_pair = true;
        }
        if (string(argv[i]) == "-t") {
            // Run one training round before actually collecting data
            // to get an approximation of max latency.
            training_round = true;
        }
        if (string(argv[i]) == "-m") {
            // Caller specified the max latency in microseconds.
            // No need to run training round in this case.
            if (atoi(argv[i+1]) > 0) {
                max_time_bucket = strtoull(argv[i+1], (char **)NULL, 10) * 1000;
                i++;
            } else {
                cout << "Max latency -m must be positive." << endl;
                exit(EXIT_FAILURE);
            }
        }
    }

    if (training_round) {
        cout << "Start training round" << endl;
        run_main(iterations, workers, payload_size, cs_pair, training_round=true);
        cout << "Completed training round" << endl << endl;
    }

    run_main(iterations, workers, payload_size, cs_pair);
    return 0;
}