| Index: util/mac/process_reader_test.cc
|
| diff --git a/util/mac/process_reader_test.cc b/util/mac/process_reader_test.cc
|
| index 10a6352ab65d497f639235a0a3d2023f47a0e000..d09bc0f1370d31e42fceeec046f1ef10a8f0ca64 100644
|
| --- a/util/mac/process_reader_test.cc
|
| +++ b/util/mac/process_reader_test.cc
|
| @@ -15,22 +15,28 @@
|
| #include "util/mac/process_reader.h"
|
|
|
| #include <dispatch/dispatch.h>
|
| +#include <mach-o/dyld.h>
|
| +#include <mach-o/dyld_images.h>
|
| #include <mach/mach.h>
|
| #include <string.h>
|
| +#include <sys/stat.h>
|
|
|
| #include <map>
|
| #include <string>
|
| +#include <vector>
|
|
|
| #include "base/logging.h"
|
| #include "base/mac/scoped_mach_port.h"
|
| #include "base/posix/eintr_wrapper.h"
|
| +#include "base/strings/stringprintf.h"
|
| #include "build/build_config.h"
|
| #include "gtest/gtest.h"
|
| #include "util/file/fd_io.h"
|
| #include "util/stdlib/pointer_container.h"
|
| +#include "util/test/errors.h"
|
| +#include "util/test/mac/dyld.h"
|
| #include "util/test/mac/mach_errors.h"
|
| #include "util/test/mac/mach_multiprocess.h"
|
| -#include "util/test/errors.h"
|
|
|
| namespace {
|
|
|
| @@ -84,7 +90,7 @@ class ProcessReaderChild final : public MachMultiprocess {
|
| int read_fd = ReadPipeFD();
|
|
|
| mach_vm_address_t address;
|
| - int rv = ReadFD(read_fd, &address, sizeof(address));
|
| + ssize_t rv = ReadFD(read_fd, &address, sizeof(address));
|
| ASSERT_EQ(static_cast<ssize_t>(sizeof(address)), rv)
|
| << ErrnoMessage("read");
|
|
|
| @@ -105,7 +111,7 @@ class ProcessReaderChild final : public MachMultiprocess {
|
|
|
| mach_vm_address_t address =
|
| reinterpret_cast<mach_vm_address_t>(kTestMemory);
|
| - int rv = WriteFD(write_fd, &address, sizeof(address));
|
| + ssize_t rv = WriteFD(write_fd, &address, sizeof(address));
|
| ASSERT_EQ(static_cast<ssize_t>(sizeof(address)), rv)
|
| << ErrnoMessage("write");
|
|
|
| @@ -448,7 +454,7 @@ class ProcessReaderThreadedChild final : public MachMultiprocess {
|
| thread_index < thread_count_ + 1;
|
| ++thread_index) {
|
| uint64_t thread_id;
|
| - int rv = ReadFD(read_fd, &thread_id, sizeof(thread_id));
|
| + ssize_t rv = ReadFD(read_fd, &thread_id, sizeof(thread_id));
|
| ASSERT_EQ(static_cast<ssize_t>(sizeof(thread_id)), rv)
|
| << ErrnoMessage("read");
|
|
|
| @@ -481,7 +487,7 @@ class ProcessReaderThreadedChild final : public MachMultiprocess {
|
| // until the parent finished working with it.
|
| int write_fd = WritePipeFD();
|
| char c = '\0';
|
| - int rv = WriteFD(write_fd, &c, 1);
|
| + ssize_t rv = WriteFD(write_fd, &c, 1);
|
| ASSERT_EQ(1, rv) << ErrnoMessage("write");
|
| }
|
|
|
| @@ -498,7 +504,7 @@ class ProcessReaderThreadedChild final : public MachMultiprocess {
|
| // to inspect it. Write an entry for it.
|
| uint64_t thread_id = PthreadToThreadID(pthread_self());
|
|
|
| - int rv = WriteFD(write_fd, &thread_id, sizeof(thread_id));
|
| + ssize_t rv = WriteFD(write_fd, &thread_id, sizeof(thread_id));
|
| ASSERT_EQ(static_cast<ssize_t>(sizeof(thread_id)), rv)
|
| << ErrnoMessage("write");
|
|
|
| @@ -567,4 +573,197 @@ TEST(ProcessReader, ChildSeveralThreads) {
|
| process_reader_threaded_child.Run();
|
| }
|
|
|
| +TEST(ProcessReader, SelfModules) {
|
| + ProcessReader process_reader;
|
| + ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
|
| +
|
| + uint32_t dyld_image_count = _dyld_image_count();
|
| + const std::vector<ProcessReaderModule>& modules = process_reader.Modules();
|
| +
|
| + // There needs to be at least an entry for the main executable, for a dylib,
|
| + // and for dyld.
|
| + ASSERT_GE(modules.size(), 3u);
|
| +
|
| + // dyld_image_count doesn’t include an entry for dyld itself, but |modules|
|
| + // does.
|
| + ASSERT_EQ(dyld_image_count + 1, modules.size());
|
| +
|
| + for (uint32_t index = 0; index < dyld_image_count; ++index) {
|
| + SCOPED_TRACE(base::StringPrintf(
|
| + "index %u, name %s", index, modules[index].name.c_str()));
|
| +
|
| + const char* dyld_image_name = _dyld_get_image_name(index);
|
| + EXPECT_EQ(dyld_image_name, modules[index].name);
|
| + EXPECT_EQ(
|
| + reinterpret_cast<mach_vm_address_t>(_dyld_get_image_header(index)),
|
| + modules[index].address);
|
| +
|
| + if (index == 0) {
|
| + // dyld didn’t load the main executable, so it couldn’t record its
|
| + // timestamp, and it is reported as 0.
|
| + EXPECT_EQ(0, modules[index].timestamp);
|
| + } else {
|
| + // Hope that the module didn’t change on disk.
|
| + struct stat stat_buf;
|
| + int rv = stat(dyld_image_name, &stat_buf);
|
| + EXPECT_EQ(0, rv) << ErrnoMessage("stat");
|
| + if (rv == 0) {
|
| + EXPECT_EQ(stat_buf.st_mtime, modules[index].timestamp);
|
| + }
|
| + }
|
| + }
|
| +
|
| + size_t index = modules.size() - 1;
|
| + EXPECT_EQ("/usr/lib/dyld", modules[index].name);
|
| +
|
| + // dyld didn’t load itself either, so it couldn’t record its timestamp, and it
|
| + // is also reported as 0.
|
| + EXPECT_EQ(0, modules[index].timestamp);
|
| +
|
| + const struct dyld_all_image_infos* dyld_image_infos =
|
| + _dyld_get_all_image_infos();
|
| + if (dyld_image_infos->version >= 2) {
|
| + EXPECT_EQ(reinterpret_cast<mach_vm_address_t>(
|
| + dyld_image_infos->dyldImageLoadAddress), modules[index].address);
|
| + }
|
| +}
|
| +
|
| +class ProcessReaderModulesChild final : public MachMultiprocess {
|
| + public:
|
| + ProcessReaderModulesChild() : MachMultiprocess() {}
|
| +
|
| + ~ProcessReaderModulesChild() {}
|
| +
|
| + private:
|
| + void MachMultiprocessParent() override {
|
| + ProcessReader process_reader;
|
| + ASSERT_TRUE(process_reader.Initialize(ChildTask()));
|
| +
|
| + const std::vector<ProcessReaderModule>& modules = process_reader.Modules();
|
| +
|
| + // There needs to be at least an entry for the main executable, for a dylib,
|
| + // and for dyld.
|
| + ASSERT_GE(modules.size(), 3u);
|
| +
|
| + int read_fd = ReadPipeFD();
|
| +
|
| + uint32_t expect_modules;
|
| + ssize_t rv = ReadFD(read_fd, &expect_modules, sizeof(expect_modules));
|
| + ASSERT_EQ(static_cast<ssize_t>(sizeof(expect_modules)), rv)
|
| + << ErrnoMessage("read");
|
| +
|
| + ASSERT_EQ(expect_modules, modules.size());
|
| +
|
| + for (size_t index = 0; index < modules.size(); ++index) {
|
| + SCOPED_TRACE(base::StringPrintf(
|
| + "index %zu, name %s", index, modules[index].name.c_str()));
|
| +
|
| + uint32_t expect_name_length;
|
| + rv = ReadFD(
|
| + read_fd, &expect_name_length, sizeof(expect_name_length));
|
| + ASSERT_EQ(static_cast<ssize_t>(sizeof(expect_name_length)), rv)
|
| + << ErrnoMessage("read");
|
| +
|
| + // The NUL terminator is not read.
|
| + std::string expect_name(expect_name_length, '\0');
|
| + rv = ReadFD(read_fd, &expect_name[0], expect_name_length);
|
| + ASSERT_EQ(static_cast<ssize_t>(expect_name_length), rv)
|
| + << ErrnoMessage("read");
|
| +
|
| + EXPECT_EQ(expect_name, modules[index].name);
|
| +
|
| + mach_vm_address_t expect_address;
|
| + rv = ReadFD(read_fd, &expect_address, sizeof(expect_address));
|
| + ASSERT_EQ(static_cast<ssize_t>(sizeof(expect_address)), rv)
|
| + << ErrnoMessage("read");
|
| +
|
| + EXPECT_EQ(expect_address, modules[index].address);
|
| +
|
| + if (index == 0 || index == modules.size() - 1) {
|
| + // dyld didn’t load the main executable or itself, so it couldn’t record
|
| + // these timestamps, and they are reported as 0.
|
| + EXPECT_EQ(0, modules[index].timestamp);
|
| + } else {
|
| + // Hope that the module didn’t change on disk.
|
| + struct stat stat_buf;
|
| + int rv = stat(expect_name.c_str(), &stat_buf);
|
| + EXPECT_EQ(0, rv) << ErrnoMessage("stat");
|
| + if (rv == 0) {
|
| + EXPECT_EQ(stat_buf.st_mtime, modules[index].timestamp);
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Tell the child that it’s OK to exit. The child needed to be kept alive
|
| + // until the parent finished working with it.
|
| + int write_fd = WritePipeFD();
|
| + char c = '\0';
|
| + rv = WriteFD(write_fd, &c, 1);
|
| + ASSERT_EQ(1, rv) << ErrnoMessage("write");
|
| + }
|
| +
|
| + void MachMultiprocessChild() override {
|
| + int write_fd = WritePipeFD();
|
| +
|
| + uint32_t dyld_image_count = _dyld_image_count();
|
| + const struct dyld_all_image_infos* dyld_image_infos =
|
| + _dyld_get_all_image_infos();
|
| +
|
| + uint32_t write_image_count = dyld_image_count;
|
| + if (dyld_image_infos->version >= 2) {
|
| + // dyld_image_count doesn’t include an entry for dyld itself, but one will
|
| + // be written.
|
| + ++write_image_count;
|
| + }
|
| +
|
| + ssize_t rv = WriteFD(
|
| + write_fd, &write_image_count, sizeof(write_image_count));
|
| + ASSERT_EQ(static_cast<ssize_t>(sizeof(write_image_count)), rv)
|
| + << ErrnoMessage("write");
|
| +
|
| + for (size_t index = 0; index < write_image_count; ++index) {
|
| + const char* dyld_image_name;
|
| + mach_vm_address_t dyld_image_address;
|
| +
|
| + if (index < dyld_image_count) {
|
| + dyld_image_name = _dyld_get_image_name(index);
|
| + dyld_image_address =
|
| + reinterpret_cast<mach_vm_address_t>(_dyld_get_image_header(index));
|
| + } else {
|
| + dyld_image_name = "/usr/lib/dyld";
|
| + dyld_image_address = reinterpret_cast<mach_vm_address_t>(
|
| + dyld_image_infos->dyldImageLoadAddress);
|
| + }
|
| +
|
| + uint32_t dyld_image_name_length = strlen(dyld_image_name);
|
| + rv = WriteFD(
|
| + write_fd, &dyld_image_name_length, sizeof(dyld_image_name_length));
|
| + ASSERT_EQ(static_cast<ssize_t>(sizeof(dyld_image_name_length)), rv)
|
| + << ErrnoMessage("write");
|
| +
|
| + // The NUL terminator is not written.
|
| + rv = WriteFD(write_fd, dyld_image_name, dyld_image_name_length);
|
| + ASSERT_EQ(static_cast<ssize_t>(dyld_image_name_length), rv)
|
| + << ErrnoMessage("write");
|
| +
|
| + rv = WriteFD(write_fd, &dyld_image_address, sizeof(dyld_image_address));
|
| + ASSERT_EQ(static_cast<ssize_t>(sizeof(dyld_image_address)), rv)
|
| + << ErrnoMessage("write");
|
| + }
|
| +
|
| + // Wait for the parent to say that it’s OK to exit.
|
| + int read_fd = ReadPipeFD();
|
| + char c;
|
| + rv = ReadFD(read_fd, &c, 1);
|
| + ASSERT_EQ(1, rv) << ErrnoMessage("read");
|
| + }
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ProcessReaderModulesChild);
|
| +};
|
| +
|
| +TEST(ProcessReader, ChildModules) {
|
| + ProcessReaderModulesChild process_reader_modules_child;
|
| + process_reader_modules_child.Run();
|
| +}
|
| +
|
| } // namespace
|
|
|