| // 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 |