| // 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 |