blob: cfdc5b1a9838d925dc6de67cfef6e60c56a575e0 [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/eventpair.h>
#include <lib/zx/handle.h>
#include <lib/zx/job.h>
#include <lib/zx/process.h>
#include <lib/zx/thread.h>
#include <zircon/assert.h>
#include <zircon/process.h>
#include <zircon/processargs.h>
#include <zircon/syscalls.h>
#include <string>
#include <thread>
#include <vector>
#include <perftest/perftest.h>
#include "assert.h"
#include "lib/zx/time.h"
namespace {
constexpr const char* kPath = "/bin/get_info_helper";
// Measure the time taken by various zx_object_get_info() calls on collections of processes and
// threads. Specifically:
// - zx_object_get_info()/ZX_INFO_TASK_RUNTIME on a process
// - zx_object_get_info()/ZX_INFO_TASK_RUNTIME on a job
// - zx_object_get_info()/ZX_INFO_JOB_PROCESSES + zx_object_get_child() for fetching handles for
// all the processes in the job
// - zx_object_get_info()/ZX_INFO_PROCESS_THREADS + zx_object_get_info() for fetching handles for
// all threads in the processes.
// - zx_object_get_info()/ZX_INFO_TASK_RUNTIME on all threads.
bool GetRuntimeInfoTest(perftest::RepeatState* state, size_t processes, size_t threads) {
ZX_ASSERT(processes > 0);
std::string run_str = std::to_string(threads);
const char* const argv[] = {kPath, run_str.c_str(), nullptr};
zx::job job;
ASSERT_OK(zx::job::create(*zx::job::default_job(), 0, &job));
std::vector<zx::handle> process_list;
for (size_t i = 0; i < processes; i++) {
zx::channel chan1, chan2;
ASSERT_OK(zx::channel::create(0, &chan1, &chan2));
zx_handle_t server_handle = chan2.release();
zx::handle process;
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;
ASSERT_OK(chan1.wait_one(ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED, zx::time::infinite(),
nullptr));
ASSERT_OK(chan1.read(0, &out, nullptr, sizeof(out), 0, &len, nullptr));
process_list.emplace_back(std::move(process));
}
state->DeclareStep("process");
state->DeclareStep("job");
state->DeclareStep("enumerate_job");
state->DeclareStep("enumerate_processes");
state->DeclareStep("threads");
zx_info_task_runtime_t info;
while (state->KeepRunning()) {
ASSERT_OK(
process_list[0].get_info(ZX_INFO_TASK_RUNTIME, &info, sizeof(info), nullptr, nullptr));
state->NextStep();
ASSERT_OK(job.get_info(ZX_INFO_TASK_RUNTIME, &info, sizeof(info), nullptr, nullptr));
state->NextStep();
zx_koid_t process_koids[128];
zx::process process_handles[128];
zx::thread thread_handles[128];
size_t thread_count = 0;
size_t process_count = 0;
ASSERT_OK(job.get_info(ZX_INFO_JOB_PROCESSES, process_koids, sizeof(process_koids),
&process_count, nullptr));
for (size_t i = 0; i < process_count; i++) {
ASSERT_OK(job.get_child(process_koids[i], ZX_RIGHT_SAME_RIGHTS, &process_handles[i]));
}
state->NextStep();
for (size_t i = 0; i < process_count; i++) {
size_t count;
zx_koid_t thread_koids[128];
ASSERT_OK(process_handles[i].get_info(ZX_INFO_PROCESS_THREADS, thread_koids,
sizeof(thread_koids), &count, nullptr));
for (size_t j = 0; j < count; j++) {
ASSERT_OK(process_handles[i].get_child(thread_koids[j], ZX_RIGHT_SAME_RIGHTS,
&thread_handles[thread_count++]));
}
}
state->NextStep();
for (size_t i = 0; i < thread_count; i++) {
ASSERT_OK(
thread_handles[i].get_info(ZX_INFO_TASK_RUNTIME, &info, sizeof(info), nullptr, nullptr));
}
}
ASSERT_OK(job.kill());
ASSERT_OK(job.wait_one(ZX_TASK_TERMINATED, zx::time::infinite(), nullptr));
return true;
}
// Measures the time to call zx_object_get_info()/ZX_INFO_TASK_RUNTIME on the current thread.
bool GetRuntimeInfoThread(perftest::RepeatState* state) {
auto self = zx::thread::self();
zx_info_task_runtime_t info;
while (state->KeepRunning()) {
ASSERT_OK(self->get_info(ZX_INFO_TASK_RUNTIME, &info, sizeof(info), nullptr, nullptr));
}
return true;
}
// Measures the time to call zx_object_get_info()/ZX_INFO_TASK_RUNTIME on the current process while
// two threads rapidly context switch.
bool GetRuntimeInfoThreadsConcurrent(perftest::RepeatState* state) {
zx::eventpair e1, e2;
ASSERT_OK(zx::eventpair::create(0, &e1, &e2));
pthread_barrier_t start_barrier;
pthread_barrier_init(&start_barrier, nullptr, 3);
std::atomic<bool> done = false;
auto thread_action = [&](zx::eventpair* event, bool first) {
pthread_barrier_wait(&start_barrier);
if (first) {
ASSERT_OK(event->signal_peer(0, ZX_USER_SIGNAL_0));
}
do {
ASSERT_OK(event->wait_one(ZX_USER_SIGNAL_0, zx::time::infinite(), nullptr));
ASSERT_OK(event->signal(ZX_USER_SIGNAL_0, 0));
ASSERT_OK(event->signal_peer(0, ZX_USER_SIGNAL_0));
} while (!done);
};
std::thread t1(thread_action, &e1, true);
std::thread t2(thread_action, &e2, false);
pthread_barrier_wait(&start_barrier);
auto self = zx::process::self();
zx_info_task_runtime_t info;
while (state->KeepRunning()) {
ASSERT_OK(self->get_info(ZX_INFO_TASK_RUNTIME, &info, sizeof(info), nullptr, nullptr));
}
done = true;
t1.join();
t2.join();
return true;
}
void RegisterTests() {
perftest::RegisterTest("GetInfo/Runtime/P=1/T=1", GetRuntimeInfoTest, 1, 1);
perftest::RegisterTest("GetInfo/Runtime/P=1/T=10", GetRuntimeInfoTest, 1, 10);
perftest::RegisterTest("GetInfo/Runtime/P=10/T=1", GetRuntimeInfoTest, 10, 1);
perftest::RegisterTest("GetInfo/Runtime/P=10/T=10", GetRuntimeInfoTest, 10, 10);
perftest::RegisterTest("GetInfo/Runtime/ThreadOnly", GetRuntimeInfoThread);
perftest::RegisterTest("GetInfo/Runtime/ConcurrentThreads", GetRuntimeInfoThreadsConcurrent);
}
PERFTEST_CTOR(RegisterTests)
} // namespace