| // Copyright 2018 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 "garnet/lib/debugger_utils/jobs.h" |
| #include "garnet/lib/debugger_utils/sysinfo.h" |
| |
| #include "gtest/gtest.h" |
| |
| namespace debugger_utils { |
| namespace { |
| |
| zx_koid_t GetKoid(zx_handle_t task) { |
| zx_info_handle_basic_t info; |
| auto status = zx_object_get_info(task, ZX_INFO_HANDLE_BASIC, &info, |
| sizeof(info), nullptr, nullptr); |
| EXPECT_EQ(status, ZX_OK); |
| if (status != ZX_OK) { |
| return ZX_KOID_INVALID; |
| } |
| |
| return info.koid; |
| } |
| |
| zx_koid_t GetParentKoid(zx_handle_t task) { |
| zx_info_handle_basic_t info; |
| auto status = zx_object_get_info(task, ZX_INFO_HANDLE_BASIC, &info, |
| sizeof(info), nullptr, nullptr); |
| EXPECT_EQ(status, ZX_OK); |
| if (status != ZX_OK) { |
| return ZX_KOID_INVALID; |
| } |
| |
| return info.related_koid; |
| } |
| |
| TEST(JobsTest, ThisJobAndStop) { |
| auto job = GetDefaultJob(); |
| auto jid = GetKoid(job.get()); |
| |
| JobTreeJobCallback job_callback = [&](zx::job* job, zx_koid_t koid, |
| zx_koid_t parent_koid, |
| int depth) -> zx_status_t { |
| if (koid == jid) { |
| return ZX_ERR_STOP; |
| } |
| return ZX_OK; |
| }; |
| EXPECT_EQ(WalkJobTree(&job, &job_callback, nullptr, nullptr), ZX_ERR_STOP); |
| EXPECT_TRUE(job.is_valid()); |
| } |
| |
| TEST(JobsTest, ThisProcessAndStop) { |
| auto job = GetDefaultJob(); |
| auto pid = GetKoid(zx_process_self()); |
| |
| JobTreeProcessCallback process_callback = |
| [&](zx::process* process, zx_koid_t koid, zx_koid_t parent_koid, |
| int depth) -> zx_status_t { |
| if (koid == pid) { |
| return ZX_ERR_STOP; |
| } |
| return ZX_OK; |
| }; |
| EXPECT_EQ(WalkJobTree(&job, nullptr, &process_callback, nullptr), |
| ZX_ERR_STOP); |
| } |
| |
| TEST(JobsTest, ThisThreadAndStop) { |
| auto job = GetDefaultJob(); |
| auto tid = GetKoid(zx_thread_self()); |
| |
| JobTreeThreadCallback thread_callback = |
| [&](zx::thread* thread, zx_koid_t koid, zx_koid_t parent_koid, |
| int depth) -> zx_status_t { |
| if (koid == tid) { |
| return ZX_ERR_STOP; |
| } |
| return ZX_OK; |
| }; |
| EXPECT_EQ(WalkJobTree(&job, nullptr, nullptr, &thread_callback), ZX_ERR_STOP); |
| } |
| |
| static zx_status_t GetHandleInfo(zx_handle_t handle, |
| zx_info_handle_basic_t* info) { |
| return zx_object_get_info(handle, ZX_INFO_HANDLE_BASIC, info, sizeof(*info), |
| nullptr, nullptr); |
| } |
| |
| static void TestKoids(zx_handle_t task, zx_koid_t koid, zx_koid_t parent_koid) { |
| zx_info_handle_basic_t info; |
| ASSERT_EQ(GetHandleInfo(task, &info), ZX_OK); |
| EXPECT_EQ(koid, info.koid); |
| EXPECT_EQ(parent_koid, info.related_koid); |
| } |
| |
| static void TestJobProcessThread(zx::job* search_job) { |
| auto tid = GetKoid(zx_thread_self()); |
| auto pid = GetKoid(zx_process_self()); |
| auto jid = GetParentKoid(zx_process_self()); |
| |
| int job_depth = -1; |
| int process_depth = -1; |
| int thread_depth = -1; |
| |
| JobTreeJobCallback job_callback = [&](zx::job* task, zx_koid_t koid, |
| zx_koid_t parent_koid, |
| int depth) -> zx_status_t { |
| TestKoids(task->get(), koid, parent_koid); |
| EXPECT_GE(depth, 0); |
| if (koid == jid) { |
| job_depth = depth; |
| } |
| return ZX_OK; |
| }; |
| JobTreeProcessCallback process_callback = |
| [&](zx::process* task, zx_koid_t koid, zx_koid_t parent_koid, |
| int depth) -> zx_status_t { |
| TestKoids(task->get(), koid, parent_koid); |
| EXPECT_GT(depth, 0); |
| if (koid == pid) { |
| process_depth = depth; |
| } |
| return ZX_OK; |
| }; |
| JobTreeThreadCallback thread_callback = [&](zx::thread* task, zx_koid_t koid, |
| zx_koid_t parent_koid, |
| int depth) -> zx_status_t { |
| TestKoids(task->get(), koid, parent_koid); |
| EXPECT_GT(depth, 1); |
| if (koid == tid) { |
| thread_depth = depth; |
| } |
| return ZX_OK; |
| }; |
| |
| EXPECT_EQ(WalkJobTree(search_job, &job_callback, &process_callback, |
| &thread_callback), |
| ZX_OK); |
| EXPECT_EQ(job_depth + 1, process_depth); |
| EXPECT_EQ(process_depth + 1, thread_depth); |
| } |
| |
| TEST(JobsTest, JobProcessThread) { |
| auto job = GetDefaultJob(); |
| TestJobProcessThread(&job); |
| } |
| |
| TEST(JobsTest, RootJob) { |
| // Make sure we can find ourselves from the root job. |
| // This will likely evolve or be replaced, but it's useful to test |
| // current functionality. |
| auto job = GetRootJob(); |
| TestJobProcessThread(&job); |
| } |
| |
| TEST(JobsTest, FindProcess) { |
| auto job = GetDefaultJob(); |
| auto pid = GetKoid(zx_process_self()); |
| auto process = FindProcess(job.get(), pid); |
| zx_info_handle_basic_t info; |
| ASSERT_EQ(GetHandleInfo(process.get(), &info), ZX_OK); |
| EXPECT_EQ(info.koid, pid); |
| } |
| |
| TEST(JobsTest, TakeRootJobOwnership) { |
| auto top_job = GetDefaultJob(); |
| auto top_job_koid = GetKoid(top_job.get()); |
| zx::job my_job; |
| |
| JobTreeJobCallback job_callback = [&](zx::job* job, zx_koid_t koid, |
| zx_koid_t parent_koid, |
| int depth) -> zx_status_t { |
| my_job = std::move(*job); |
| return ZX_OK; |
| }; |
| EXPECT_EQ(WalkJobTree(&top_job, &job_callback, nullptr, nullptr), |
| ZX_ERR_STOP); |
| EXPECT_FALSE(top_job.is_valid()); |
| EXPECT_TRUE(my_job.is_valid()); |
| EXPECT_EQ(GetKoid(my_job.get()), top_job_koid); |
| } |
| |
| TEST(JobsTest, TakeChildJobOwnership) { |
| auto top_job = GetDefaultJob(); |
| zx::job child_job; |
| ASSERT_EQ(zx::job::create(top_job, 0, &child_job), ZX_OK); |
| auto child_job_koid = GetKoid(child_job.get()); |
| zx::job my_job; |
| |
| JobTreeJobCallback job_callback = [&](zx::job* job, zx_koid_t koid, |
| zx_koid_t parent_koid, |
| int depth) -> zx_status_t { |
| if (koid == child_job_koid) { |
| my_job = std::move(*job); |
| } |
| return ZX_OK; |
| }; |
| EXPECT_EQ(WalkJobTree(&top_job, &job_callback, nullptr, nullptr), |
| ZX_ERR_STOP); |
| EXPECT_TRUE(top_job.is_valid()); |
| EXPECT_TRUE(my_job.is_valid()); |
| EXPECT_EQ(GetKoid(my_job.get()), child_job_koid); |
| } |
| |
| } // namespace |
| } // namespace debugger_utils |