Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(63)

Unified Diff: util/mac/process_reader_test.cc

Issue 546573002: Implement (and test) ProcessReader::Modules() (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Address review feedback Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « util/mac/process_reader.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « util/mac/process_reader.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698