blob: c192341c6bc8ba145d91c4f1c80c8bfec6b596d1 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/fdio/spawn.h>
#include <lib/zx/handle.h>
#include <lib/zx/job.h>
#include <lib/zx/process.h>
#include <lib/zx/time.h>
#include <zircon/assert.h>
#include <zircon/process.h>
#include <zircon/processargs.h>
#include <zircon/syscalls.h>
#include <perftest/perftest.h>
#include "assert.h"
namespace {
constexpr const char* kPath = "/pkg/bin/context_switch_overhead_helper";
void ChannelWait(const zx::channel& channel) {
// If we only pass ZX_CHANNEL_READABLE here, we will end up waiting
// forever if the process holding the other channel endpoint dies, so we
// should pass ZX_CHANNEL_PEER_CLOSED too.
ASSERT_OK(channel.wait_one(ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED, zx::time::infinite(),
nullptr));
}
// Measure the time taken for NUM_CPUS pairs of threads, running in parallel, to each start up and
// then execute |number_of_switches| round trips (via eventpair wakeup), when running in a separate
// process. Each pair of threads is pinned to a different CPU on the system.
//
// The flow is as follows:
// Host = this process
// Helper = The helper process runnning the tests.
//
// 1. Helper sends a small message over the channel, to signal it is ready to run a test.
// 2. Host sends a message containing the number of context switches to do.
// 3. Helper sends a small message to signal the setup is ready.
// 4. Helper runs the test, sending another small message to signal the test is done.
// 5. Helper waits for another message and returns to step 2.
//
// The test is intended to reach peak context switches on all cores, and it is meant to be sensitive
// to changes that modify shared data on cache-lines between cores.
bool ContextSwitchTest(perftest::RepeatState* state, size_t number_of_switches) {
zx::channel chan1, chan2;
ASSERT_OK(zx::channel::create(0, &chan1, &chan2));
const char* const argv[] = {kPath, nullptr};
zx::job job;
ASSERT_OK(zx::job::create(*zx::job::default_job(), 0, &job));
zx::handle process;
fdio_spawn_action_t actions[] = {
{
.action = FDIO_SPAWN_ACTION_ADD_HANDLE,
.h =
{
.id = PA_USER0,
.handle = chan2.release(),
},
},
};
char err_msg_out[FDIO_SPAWN_ERR_MSG_MAX_LENGTH] = {};
if (zx_status_t status =
fdio_spawn_etc(job.get(), FDIO_SPAWN_CLONE_ALL, kPath, argv, nullptr /* environ */,
std::size(actions), actions, process.reset_and_get_address(), err_msg_out);
status != ZX_OK) {
ZX_PANIC("fdio_spawn_etc(): %s; %s", zx_status_get_string(status), err_msg_out);
}
char out[1024];
uint32_t len;
ChannelWait(chan1);
ASSERT_OK(chan1.read(0, &out, nullptr, sizeof(out), 0, &len, nullptr));
state->DeclareStep("setup");
state->DeclareStep("execute");
while (state->KeepRunning()) {
ASSERT_OK(chan1.write(0, &number_of_switches, sizeof(number_of_switches), nullptr, 0));
ChannelWait(chan1);
ASSERT_OK(chan1.read(0, &out, nullptr, sizeof(out), 0, &len, nullptr));
state->NextStep();
ChannelWait(chan1);
ASSERT_OK(chan1.read(0, &out, nullptr, sizeof(out), 0, &len, nullptr));
}
ASSERT_OK(job.kill());
ASSERT_OK(process.wait_one(ZX_TASK_TERMINATED, zx::time::infinite(), nullptr));
return true;
}
void RegisterTests() { perftest::RegisterTest("ContextSwitch/1000", ContextSwitchTest, 1000); }
PERFTEST_CTOR(RegisterTests)
} // namespace