blob: 84acd0f19fa6d68b5677ade9f0fc880a046f51f4 [file] [log] [blame]
// Copyright 2024 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/async-loop/cpp/loop.h>
#include <lib/ld/fuchsia-debugdata.h>
#include <lib/ld/testing/mock-debugdata.h>
#include <zircon/status.h>
#include <zircon/types.h>
#include <string_view>
#include <gmock/gmock.h>
#include "load-tests.h"
namespace ld::testing {
namespace {
using namespace std::string_view_literals;
using ::testing::Ne;
TYPED_TEST(LdLoadTests, LlvmProfdata) {
if constexpr (!TestFixture::kRunsLdStartup) {
GTEST_SKIP() << "test only applies to startup dynamic linker";
}
ASSERT_NO_FATAL_FAILURE(this->Init());
ASSERT_NO_FATAL_FAILURE(this->Load("llvm-profdata"));
int64_t exit_code = this->Run();
this->ExpectLog("");
auto describe_exit_code = [exit_code]() -> std::string_view {
switch (exit_code) {
case ZX_TASK_RETCODE_EXCEPTION_KILL:
return "CRASHED"sv;
case 77:
return "SKIPPED"sv;
default:
return zx_status_get_string(static_cast<zx_status_t>(exit_code));
}
};
constexpr std::string_view kInProcess =
std::is_same_v<ld::testing::LdStartupInProcessTests, TestFixture> ? "in-process: "sv
: "test process: "sv;
if (exit_code == 77) {
GTEST_SKIP() << kInProcess << "test module not built with llvm-profdata instrumentation";
}
EXPECT_EQ(exit_code, 0) << kInProcess << describe_exit_code();
// On successful exit, the test has written a message back on the bootstrap
// channel.
std::array<char, 64> buffer;
zx::channel test_svc_server_end;
uint32_t actual_bytes, actual_handles;
zx_status_t status = this->bootstrap_sender().read(
0, buffer.data(), test_svc_server_end.reset_and_get_address(),
static_cast<uint32_t>(buffer.size()), 1, &actual_bytes, &actual_handles);
ASSERT_EQ(status, ZX_OK) << kInProcess << zx_status_get_string(status);
std::string_view bootstrap_reply_text(buffer.data(), actual_bytes);
EXPECT_EQ(bootstrap_reply_text, "llvm-profdata"sv);
// That delivered the /svc server end channel that the startup dynamic linker
// passed as the third argument to the user entry point.
ASSERT_EQ(actual_handles, 1u);
ASSERT_TRUE(test_svc_server_end.is_valid());
// Prime a mock fuchsia.debugdata service to expect the llvm-profdata dump.
auto mock = std::make_unique<::testing::StrictMock<ld::testing::MockDebugdata>>();
EXPECT_CALL(*mock, Publish(
// Expect the right sink name.
"llvm-profile"sv,
// There's nothing easy to assert about the dump
// other than that it's some VMO.
ld::testing::ObjKoidMatches(Ne(ZX_KOID_INVALID)),
// We could do a custom matcher to assert that the
// eventpair's peer is closed, but we only bother
// to assert that it's some eventpair.
ld::testing::ObjKoidMatches(Ne(ZX_KOID_INVALID))));
// Prime a mock /svc to supply that service.
ld::testing::MockSvcDirectory svc_dir;
ASSERT_NO_FATAL_FAILURE(svc_dir.Init());
ASSERT_NO_FATAL_FAILURE(svc_dir.AddEntry<fuchsia_debugdata::Publisher>(std::move(mock)));
// Get a client end to that /svc. This stands in for the /svc name table
// entry in the normal process launching for a real program.
fidl::ClientEnd<fuchsia_io::Directory> svc_client_end;
ASSERT_NO_FATAL_FAILURE(svc_dir.Serve(svc_client_end));
// Forward the startup dynamic linker's pipelined messages to /svc as the
// libc startup code would in a normal process.
auto result = ld::Debugdata::Forward(
// Consume the channel at the end of the full expression.
svc_client_end.TakeChannel().borrow(), std::move(test_svc_server_end));
EXPECT_TRUE(result.is_ok()) << result.status_string();
// Finally, drain the service message queues before expectations are checked.
status = svc_dir.loop().RunUntilIdle();
ASSERT_EQ(status, ZX_OK) << zx_status_get_string(status);
}
} // namespace
} // namespace ld::testing