| // Copyright 2019 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 "debugdata.h" |
| |
| #include <fuchsia/debugdata/llcpp/fidl.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/default.h> |
| #include <lib/async/cpp/wait.h> |
| #include <lib/fdio/io.h> |
| #include <lib/fdio/spawn.h> |
| #include <lib/fidl-async/cpp/bind.h> |
| #include <lib/fidl/cpp/message_buffer.h> |
| #include <lib/zx/channel.h> |
| #include <lib/zx/job.h> |
| #include <lib/zx/process.h> |
| #include <lib/zx/vmar.h> |
| #include <lib/zx/vmo.h> |
| #include <zircon/sanitizer.h> |
| #include <zircon/status.h> |
| |
| #include <string> |
| #include <unordered_map> |
| #include <vector> |
| |
| #include <fbl/auto_call.h> |
| #include <fbl/string.h> |
| #include <fbl/unique_fd.h> |
| #include <fbl/vector.h> |
| #include <fs/pseudo_dir.h> |
| #include <fs/service.h> |
| #include <fs/synchronous_vfs.h> |
| #include <fs/vfs_types.h> |
| #include <zxtest/zxtest.h> |
| |
| namespace { |
| |
| const char kTestHelper[] = "/bin/debugdata-test-helper"; |
| |
| struct DebugData : public ::llcpp::fuchsia::debugdata::DebugData::Interface { |
| std::unordered_map<std::string, zx::vmo> data; |
| std::unordered_map<std::string, zx::vmo> configs; |
| |
| void Publish(fidl::StringView data_sink, zx::vmo vmo, PublishCompleter::Sync&) { |
| std::string name(data_sink.data(), data_sink.size()); |
| data.emplace(name, std::move(vmo)); |
| } |
| |
| void LoadConfig(fidl::StringView config_name, LoadConfigCompleter::Sync& completer) { |
| std::string name(config_name.data(), config_name.size()); |
| if (auto it = configs.find(name); it != configs.end()) { |
| completer.Reply(std::move(it->second)); |
| } else { |
| completer.Close(ZX_ERR_NOT_FOUND); |
| } |
| } |
| |
| void Serve(async_dispatcher_t* dispatcher, std::unique_ptr<fs::SynchronousVfs>* vfs, |
| zx::channel* client) { |
| auto dir = fbl::MakeRefCounted<fs::PseudoDir>(); |
| auto node = fbl::MakeRefCounted<fs::Service>([dispatcher, this](zx::channel channel) { |
| return fidl::BindSingleInFlightOnly(dispatcher, std::move(channel), this); |
| }); |
| dir->AddEntry(::llcpp::fuchsia::debugdata::DebugData::Name, node); |
| |
| zx::channel server; |
| ASSERT_OK(zx::channel::create(0, client, &server)); |
| |
| *vfs = std::make_unique<fs::SynchronousVfs>(dispatcher); |
| ASSERT_OK((*vfs)->ServeDirectory(std::move(dir), std::move(server), fs::Rights::ReadWrite())); |
| } |
| }; |
| |
| void RunHelper(const char* mode, const size_t action_count, const fdio_spawn_action_t* fdio_actions, |
| int expected_return_code) { |
| zx::job test_job; |
| ASSERT_OK(zx::job::create(*zx::job::default_job(), 0, &test_job)); |
| auto auto_call_kill_job = fbl::MakeAutoCall([&test_job]() { test_job.kill(); }); |
| |
| std::string test_helper = std::string(getenv("TEST_ROOT_DIR")) + kTestHelper; |
| const char* args[] = {test_helper.c_str(), mode, nullptr}; |
| |
| zx::process process; |
| char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH]; |
| ASSERT_OK(fdio_spawn_etc(test_job.get(), FDIO_SPAWN_CLONE_ALL & ~FDIO_SPAWN_CLONE_NAMESPACE, |
| args[0], args, nullptr, action_count, fdio_actions, |
| process.reset_and_get_address(), err_msg)); |
| |
| ASSERT_OK(process.wait_one(ZX_PROCESS_TERMINATED, zx::time::infinite(), nullptr)); |
| |
| zx_info_process_t proc_info; |
| ASSERT_OK(process.get_info(ZX_INFO_PROCESS, &proc_info, sizeof(proc_info), nullptr, nullptr)); |
| ASSERT_EQ(expected_return_code, proc_info.return_code); |
| } |
| |
| void RunHelperWithSvc(const char* mode, zx::channel svc_handle, int expected_return_code) { |
| fdio_spawn_action_t fdio_actions[] = { |
| fdio_spawn_action_t{ |
| .action = FDIO_SPAWN_ACTION_ADD_NS_ENTRY, |
| .ns = |
| { |
| .prefix = "/svc", |
| .handle = svc_handle.release(), |
| }, |
| }, |
| }; |
| RunHelper(mode, 1, fdio_actions, expected_return_code); |
| } |
| |
| void RunHelperWithoutSvc(const char* mode, int expected_return_code) { |
| RunHelper(mode, 0, nullptr, expected_return_code); |
| } |
| |
| TEST(DebugDataTests, PublishData) { |
| async::Loop loop{&kAsyncLoopConfigNoAttachToCurrentThread}; |
| std::unique_ptr<fs::SynchronousVfs> vfs; |
| zx::channel client; |
| DebugData svc; |
| ASSERT_NO_FATAL_FAILURES(svc.Serve(loop.dispatcher(), &vfs, &client)); |
| |
| ASSERT_NO_FATAL_FAILURES(RunHelperWithSvc("publish_data", std::move(client), 0)); |
| |
| ASSERT_OK(loop.RunUntilIdle()); |
| |
| loop.Shutdown(); |
| vfs.reset(); |
| |
| auto it = svc.data.find(kTestName); |
| ASSERT_TRUE(it != svc.data.end()); |
| |
| char content[sizeof(kTestData)]; |
| ASSERT_OK(it->second.read(content, 0, sizeof(content))); |
| ASSERT_EQ(memcmp(content, kTestData, sizeof(kTestData)), 0); |
| } |
| |
| TEST(DebugDataTests, PublishDataWithoutSvc) { |
| ASSERT_NO_FATAL_FAILURES(RunHelperWithoutSvc("publish_data", 0)); |
| } |
| |
| TEST(DebugDataTests, LoadConfig) { |
| async::Loop loop{&kAsyncLoopConfigNoAttachToCurrentThread}; |
| std::unique_ptr<fs::SynchronousVfs> vfs; |
| zx::channel client; |
| DebugData svc; |
| ASSERT_NO_FATAL_FAILURES(svc.Serve(loop.dispatcher(), &vfs, &client)); |
| ASSERT_OK(loop.StartThread("debugdata")); |
| |
| zx::vmo vmo; |
| ASSERT_OK(zx::vmo::create(ZX_PAGE_SIZE, 0, &vmo)); |
| ASSERT_OK(vmo.write(kTestData, 0, sizeof(kTestData))); |
| |
| svc.configs.emplace(kTestName, std::move(vmo)); |
| |
| ASSERT_NO_FATAL_FAILURES(RunHelperWithSvc("load_config", std::move(client), 0)); |
| |
| loop.Shutdown(); |
| vfs.reset(); |
| } |
| |
| TEST(DebugDataTests, LoadConfigNotFound) { |
| async::Loop loop{&kAsyncLoopConfigNoAttachToCurrentThread}; |
| std::unique_ptr<fs::SynchronousVfs> vfs; |
| zx::channel client; |
| DebugData svc; |
| ASSERT_NO_FATAL_FAILURES(svc.Serve(loop.dispatcher(), &vfs, &client)); |
| ASSERT_OK(loop.StartThread("debugdata")); |
| |
| ASSERT_NO_FATAL_FAILURES(RunHelperWithSvc("load_config", std::move(client), ZX_ERR_PEER_CLOSED)); |
| |
| loop.Shutdown(); |
| vfs.reset(); |
| } |
| |
| TEST(DebugDataTests, LoadConfigWithoutSvc) { |
| ASSERT_NO_FATAL_FAILURES(RunHelperWithoutSvc("load_config", ZX_ERR_BAD_HANDLE)); |
| } |
| |
| } // anonymous namespace |