blob: 8bd9480c07d6d78b6b553ddb5bb360676437a2ff [file] [log] [blame]
// 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;
}