| // 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 <zircon/assert.h> |
| #include <zircon/process.h> |
| #include <zircon/processargs.h> |
| #include <zircon/syscalls.h> |
| |
| #include <perftest/perftest.h> |
| |
| #include "assert.h" |
| #include "lib/zx/time.h" |
| |
| namespace { |
| |
| constexpr const char* kPath = "/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; |
| zx_handle_t server_handle = chan2.release(); |
| fdio_spawn_action_t actions[1] = { |
| {.action = FDIO_SPAWN_ACTION_ADD_HANDLE, .h = {.id = PA_USER0, .handle = server_handle}}}; |
| ASSERT_OK(fdio_spawn_etc(job.get(), FDIO_SPAWN_CLONE_ALL, kPath, argv, nullptr /* environ */, |
| 1 /* action count */, actions, process.reset_and_get_address(), |
| nullptr /* err message 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 |