| // Copyright 2022 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 <fuchsia/gpu/agis/cpp/fidl.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/default.h> |
| #include <lib/sys/cpp/component_context.h> |
| #include <lib/syslog/cpp/log_settings.h> |
| #include <lib/syslog/cpp/macros.h> |
| #include <zircon/assert.h> |
| |
| #include <thread> |
| |
| /*** |
| * vtc-test |
| * |
| * Manual validation for Vulkan Traceable Component behavior when parented to actual |
| * /core/agis:vulkan-trace. Vtcs, in production, can only access the ComponentRegistry |
| * protocol from Agis. |
| * |
| * fx ffx component run --recreate /core/agis/vulkan-trace:vtc-test |
| * fuchsia-pkg://fuchsia.com/vtc-test#meta/vtc-test.cm |
| * |
| ***/ |
| |
| namespace { |
| std::atomic<int> outstanding = 0; |
| |
| zx_koid_t ProcessKoid() { |
| const zx::unowned<zx::process> process = zx::process::self(); |
| zx_info_handle_basic_t info; |
| const zx_status_t status = |
| zx_object_get_info(process->get(), ZX_INFO_HANDLE_BASIC, &info, sizeof(info), |
| nullptr /* actual */, nullptr /* avail */); |
| ZX_ASSERT(status == ZX_OK); |
| return info.koid; |
| } |
| |
| std::string ProcessName() { |
| const zx::unowned<zx::process> process = zx::process::self(); |
| char process_name[ZX_MAX_NAME_LEN]; |
| process->get_property(ZX_PROP_NAME, process_name, sizeof(process_name)); |
| return process_name; |
| } |
| |
| uint64_t TimeMS() { |
| return std::chrono::duration_cast<std::chrono::milliseconds>( |
| std::chrono::system_clock::now().time_since_epoch()) |
| .count(); |
| } |
| |
| } // namespace |
| |
| class VtcTest { |
| public: |
| // Register, retrieve vulkan socket, listen for commands and log them to the console. |
| void Communicate(uint32_t wait_secs = 35); |
| |
| void SetUp() { |
| num_vtcs_ = 0; |
| loop_ = std::make_unique<async::Loop>(&kAsyncLoopConfigAttachToCurrentThread); |
| |
| std::unique_ptr<sys::ComponentContext> context = sys::ComponentContext::Create(); |
| |
| const zx_status_t status = |
| context->svc()->Connect(component_registry_.NewRequest(loop_->dispatcher())); |
| ZX_ASSERT(status == ZX_OK); |
| component_registry_.set_error_handler([this](zx_status_t status) { |
| FX_SLOG(ERROR, "|component_registry_| error handler", KV("status", status)); |
| loop_->Quit(); |
| assert(false); |
| }); |
| |
| process_koid_ = ProcessKoid(); |
| process_name_ = ProcessName(); |
| client_id_ = TimeMS(); |
| } |
| |
| void TearDown() { |
| if (loop_) { |
| LoopWait(); |
| loop_->Quit(); |
| } |
| } |
| |
| // LoopWait() guarantees that the callback supplied to any interface in the agis protocol |
| // library has been called and completed. The callbacks compute / verify return results |
| // from the protocol methods. |
| void LoopWait() const { |
| while (outstanding) { |
| loop_->RunUntilIdle(); |
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); |
| } |
| } |
| |
| void Register(uint64_t id, zx_koid_t process_koid, std::string process_name) { |
| outstanding++; |
| component_registry_->Register( |
| id, process_koid, std::move(process_name), |
| [&](fuchsia::gpu::agis::ComponentRegistry_Register_Result result) { |
| ZX_ASSERT(!result.err()); |
| outstanding--; |
| }); |
| LoopWait(); |
| } |
| |
| void Unregister(uint64_t client_id) const { |
| outstanding++; |
| component_registry_->Unregister( |
| client_id, [&](fuchsia::gpu::agis::ComponentRegistry_Unregister_Result result) { |
| ZX_ASSERT(!result.err()); |
| outstanding--; |
| }); |
| } |
| |
| // Ivars. |
| std::unique_ptr<async::Loop> loop_; |
| fuchsia::gpu::agis::ComponentRegistryPtr component_registry_; |
| size_t num_vtcs_; |
| zx_koid_t process_koid_; |
| std::string process_name_; |
| uint64_t client_id_; |
| }; |
| |
| void VtcTest::Communicate(uint32_t wait_secs) { |
| printf("VtcTest::Communicate()\n"); |
| Register(client_id_, process_koid_, process_name_); |
| |
| zx::socket vulkan_socket; |
| component_registry_->GetVulkanSocket( |
| client_id_, [&](fuchsia::gpu::agis::ComponentRegistry_GetVulkanSocket_Result result) { |
| fuchsia::gpu::agis::ComponentRegistry_GetVulkanSocket_Response response( |
| std::move(result.response())); |
| ZX_ASSERT(!result.err()); |
| vulkan_socket = response.ResultValue_(); |
| ZX_ASSERT(vulkan_socket.is_valid()); |
| printf("VtcTest::Communicate: vulkan socket established %d\n", vulkan_socket.is_valid()); |
| }); |
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); |
| loop_->RunUntilIdle(); |
| |
| // |vulkan_socket| will resolve when `ffx agis listen <global_id>` is called. |
| ZX_ASSERT(!vulkan_socket.is_valid()); |
| |
| printf("(1) VtcTest::Communicate: Retrieve global_id from `ffx agis vtcs`\n"); |
| printf( |
| "(2) VtcTest::Communicate: Awaiting `ffx agis listen <global_id>` invocation to resolve " |
| "vulkan socket for %d secs...\n", |
| wait_secs); |
| fflush(stdout); |
| |
| // Allow time to run `ffx agis listen`. |
| std::this_thread::sleep_for(std::chrono::seconds(wait_secs)); |
| loop_->RunUntilIdle(); |
| |
| // |vulkan_socket| should be valid after `ffx agis listen`. |
| ZX_ASSERT(vulkan_socket.is_valid()); |
| |
| // External step write something to the unix socket. |
| printf( |
| "(3) External step: now write something to the pipe `/tmp/agis<global_id>`, sleeping %d " |
| "seconds ...\n", |
| wait_secs); |
| fflush(stdout); |
| std::this_thread::sleep_for(std::chrono::seconds(wait_secs)); |
| |
| // Read something from the unix socket. |
| char buf[128]; |
| size_t actual = 0; |
| const zx_status_t status = vulkan_socket.read(0u, buf, sizeof(buf), &actual); |
| ZX_ASSERT(status == ZX_OK); |
| printf("Read %zu bytes from |vulkan_socket|.\n", actual); |
| fflush(stdout); |
| |
| // Remove registration. |
| Unregister(client_id_); |
| } |
| |
| int main(int argc, char **argv) { |
| VtcTest test; |
| fuchsia_logging::SetTags({"vtc-test"}); |
| |
| test.SetUp(); |
| |
| if (argc == 2) { |
| const uint32_t wait_secs = atoi(argv[1]); |
| test.Communicate(wait_secs); |
| } else { |
| test.Communicate(); |
| } |
| |
| test.TearDown(); |
| |
| return 0; |
| } |