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

Side by Side 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: Fix 32-bit x86 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 unified diff | Download patch
« util/mac/process_reader.cc ('K') | « util/mac/process_reader.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Crashpad Authors. All rights reserved. 1 // Copyright 2014 The Crashpad Authors. All rights reserved.
2 // 2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License. 4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at 5 // You may obtain a copy of the License at
6 // 6 //
7 // http://www.apache.org/licenses/LICENSE-2.0 7 // http://www.apache.org/licenses/LICENSE-2.0
8 // 8 //
9 // Unless required by applicable law or agreed to in writing, software 9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, 10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and 12 // See the License for the specific language governing permissions and
13 // limitations under the License. 13 // limitations under the License.
14 14
15 #include "util/mac/process_reader.h" 15 #include "util/mac/process_reader.h"
16 16
17 #include <dispatch/dispatch.h> 17 #include <dispatch/dispatch.h>
18 #include <mach-o/dyld.h>
19 #include <mach-o/dyld_images.h>
18 #include <mach/mach.h> 20 #include <mach/mach.h>
19 #include <string.h> 21 #include <string.h>
22 #include <sys/stat.h>
20 23
21 #include <map> 24 #include <map>
22 #include <string> 25 #include <string>
26 #include <vector>
23 27
24 #include "base/logging.h" 28 #include "base/logging.h"
25 #include "base/mac/scoped_mach_port.h" 29 #include "base/mac/scoped_mach_port.h"
26 #include "base/posix/eintr_wrapper.h" 30 #include "base/posix/eintr_wrapper.h"
31 #include "base/strings/stringprintf.h"
27 #include "build/build_config.h" 32 #include "build/build_config.h"
28 #include "gtest/gtest.h" 33 #include "gtest/gtest.h"
29 #include "util/file/fd_io.h" 34 #include "util/file/fd_io.h"
30 #include "util/stdlib/pointer_container.h" 35 #include "util/stdlib/pointer_container.h"
36 #include "util/test/errors.h"
37 #include "util/test/mac/dyld.h"
31 #include "util/test/mac/mach_errors.h" 38 #include "util/test/mac/mach_errors.h"
32 #include "util/test/mac/mach_multiprocess.h" 39 #include "util/test/mac/mach_multiprocess.h"
33 #include "util/test/errors.h"
34 40
35 namespace { 41 namespace {
36 42
37 using namespace crashpad; 43 using namespace crashpad;
38 using namespace crashpad::test; 44 using namespace crashpad::test;
39 45
40 TEST(ProcessReader, SelfBasic) { 46 TEST(ProcessReader, SelfBasic) {
41 ProcessReader process_reader; 47 ProcessReader process_reader;
42 ASSERT_TRUE(process_reader.Initialize(mach_task_self())); 48 ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
43 49
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
77 #else 83 #else
78 EXPECT_TRUE(process_reader.Is64Bit()); 84 EXPECT_TRUE(process_reader.Is64Bit());
79 #endif 85 #endif
80 86
81 EXPECT_EQ(getpid(), process_reader.ParentProcessID()); 87 EXPECT_EQ(getpid(), process_reader.ParentProcessID());
82 EXPECT_EQ(ChildPID(), process_reader.ProcessID()); 88 EXPECT_EQ(ChildPID(), process_reader.ProcessID());
83 89
84 int read_fd = ReadPipeFD(); 90 int read_fd = ReadPipeFD();
85 91
86 mach_vm_address_t address; 92 mach_vm_address_t address;
87 int rv = ReadFD(read_fd, &address, sizeof(address)); 93 ssize_t rv = ReadFD(read_fd, &address, sizeof(address));
88 ASSERT_EQ(static_cast<ssize_t>(sizeof(address)), rv) 94 ASSERT_EQ(static_cast<ssize_t>(sizeof(address)), rv)
89 << ErrnoMessage("read"); 95 << ErrnoMessage("read");
90 96
91 std::string read_string; 97 std::string read_string;
92 ASSERT_TRUE(process_reader.Memory()->ReadCString(address, &read_string)); 98 ASSERT_TRUE(process_reader.Memory()->ReadCString(address, &read_string));
93 EXPECT_EQ(kTestMemory, read_string); 99 EXPECT_EQ(kTestMemory, read_string);
94 100
95 // Tell the child that it’s OK to exit. The child needed to be kept alive 101 // Tell the child that it’s OK to exit. The child needed to be kept alive
96 // until the parent finished working with it. 102 // until the parent finished working with it.
97 int write_fd = WritePipeFD(); 103 int write_fd = WritePipeFD();
98 char c = '\0'; 104 char c = '\0';
99 rv = WriteFD(write_fd, &c, 1); 105 rv = WriteFD(write_fd, &c, 1);
100 ASSERT_EQ(1, rv) << ErrnoMessage("write"); 106 ASSERT_EQ(1, rv) << ErrnoMessage("write");
101 } 107 }
102 108
103 void MachMultiprocessChild() override { 109 void MachMultiprocessChild() override {
104 int write_fd = WritePipeFD(); 110 int write_fd = WritePipeFD();
105 111
106 mach_vm_address_t address = 112 mach_vm_address_t address =
107 reinterpret_cast<mach_vm_address_t>(kTestMemory); 113 reinterpret_cast<mach_vm_address_t>(kTestMemory);
108 int rv = WriteFD(write_fd, &address, sizeof(address)); 114 ssize_t rv = WriteFD(write_fd, &address, sizeof(address));
109 ASSERT_EQ(static_cast<ssize_t>(sizeof(address)), rv) 115 ASSERT_EQ(static_cast<ssize_t>(sizeof(address)), rv)
110 << ErrnoMessage("write"); 116 << ErrnoMessage("write");
111 117
112 // Wait for the parent to say that it’s OK to exit. 118 // Wait for the parent to say that it’s OK to exit.
113 int read_fd = ReadPipeFD(); 119 int read_fd = ReadPipeFD();
114 char c; 120 char c;
115 rv = ReadFD(read_fd, &c, 1); 121 rv = ReadFD(read_fd, &c, 1);
116 ASSERT_EQ(1, rv) << ErrnoMessage("read"); 122 ASSERT_EQ(1, rv) << ErrnoMessage("read");
117 } 123 }
118 124
(...skipping 322 matching lines...) Expand 10 before | Expand all | Expand 10 after
441 int read_fd = ReadPipeFD(); 447 int read_fd = ReadPipeFD();
442 448
443 // Build a map of all expected threads, keyed by each thread’s ID, and with 449 // Build a map of all expected threads, keyed by each thread’s ID, and with
444 // addresses that should lie somewhere within each thread’s stack as values. 450 // addresses that should lie somewhere within each thread’s stack as values.
445 // These IDs and addresses all come from the child process via the pipe. 451 // These IDs and addresses all come from the child process via the pipe.
446 ThreadMap thread_map; 452 ThreadMap thread_map;
447 for (size_t thread_index = 0; 453 for (size_t thread_index = 0;
448 thread_index < thread_count_ + 1; 454 thread_index < thread_count_ + 1;
449 ++thread_index) { 455 ++thread_index) {
450 uint64_t thread_id; 456 uint64_t thread_id;
451 int rv = ReadFD(read_fd, &thread_id, sizeof(thread_id)); 457 ssize_t rv = ReadFD(read_fd, &thread_id, sizeof(thread_id));
452 ASSERT_EQ(static_cast<ssize_t>(sizeof(thread_id)), rv) 458 ASSERT_EQ(static_cast<ssize_t>(sizeof(thread_id)), rv)
453 << ErrnoMessage("read"); 459 << ErrnoMessage("read");
454 460
455 TestThreadPool::ThreadExpectation expectation; 461 TestThreadPool::ThreadExpectation expectation;
456 rv = ReadFD(read_fd, 462 rv = ReadFD(read_fd,
457 &expectation.stack_address, 463 &expectation.stack_address,
458 sizeof(expectation.stack_address)); 464 sizeof(expectation.stack_address));
459 ASSERT_EQ(static_cast<ssize_t>(sizeof(expectation.stack_address)), rv) 465 ASSERT_EQ(static_cast<ssize_t>(sizeof(expectation.stack_address)), rv)
460 << ErrnoMessage("read"); 466 << ErrnoMessage("read");
461 467
(...skipping 12 matching lines...) Expand all
474 const std::vector<ProcessReaderThread>& threads = process_reader.Threads(); 480 const std::vector<ProcessReaderThread>& threads = process_reader.Threads();
475 481
476 // The child shouldn’t have any threads other than its main thread and the 482 // The child shouldn’t have any threads other than its main thread and the
477 // ones it created in its pool, so pass false for |tolerate_extra_threads|. 483 // ones it created in its pool, so pass false for |tolerate_extra_threads|.
478 ExpectSeveralThreads(&thread_map, threads, false); 484 ExpectSeveralThreads(&thread_map, threads, false);
479 485
480 // Tell the child that it’s OK to exit. The child needed to be kept alive 486 // Tell the child that it’s OK to exit. The child needed to be kept alive
481 // until the parent finished working with it. 487 // until the parent finished working with it.
482 int write_fd = WritePipeFD(); 488 int write_fd = WritePipeFD();
483 char c = '\0'; 489 char c = '\0';
484 int rv = WriteFD(write_fd, &c, 1); 490 ssize_t rv = WriteFD(write_fd, &c, 1);
485 ASSERT_EQ(1, rv) << ErrnoMessage("write"); 491 ASSERT_EQ(1, rv) << ErrnoMessage("write");
486 } 492 }
487 493
488 void MachMultiprocessChild() override { 494 void MachMultiprocessChild() override {
489 TestThreadPool thread_pool; 495 TestThreadPool thread_pool;
490 thread_pool.StartThreads(thread_count_); 496 thread_pool.StartThreads(thread_count_);
491 if (testing::Test::HasFatalFailure()) { 497 if (testing::Test::HasFatalFailure()) {
492 return; 498 return;
493 } 499 }
494 500
495 int write_fd = WritePipeFD(); 501 int write_fd = WritePipeFD();
496 502
497 // This thread isn’t part of the thread pool, but the parent will be able 503 // This thread isn’t part of the thread pool, but the parent will be able
498 // to inspect it. Write an entry for it. 504 // to inspect it. Write an entry for it.
499 uint64_t thread_id = PthreadToThreadID(pthread_self()); 505 uint64_t thread_id = PthreadToThreadID(pthread_self());
500 506
501 int rv = WriteFD(write_fd, &thread_id, sizeof(thread_id)); 507 ssize_t rv = WriteFD(write_fd, &thread_id, sizeof(thread_id));
502 ASSERT_EQ(static_cast<ssize_t>(sizeof(thread_id)), rv) 508 ASSERT_EQ(static_cast<ssize_t>(sizeof(thread_id)), rv)
503 << ErrnoMessage("write"); 509 << ErrnoMessage("write");
504 510
505 TestThreadPool::ThreadExpectation expectation; 511 TestThreadPool::ThreadExpectation expectation;
506 expectation.stack_address = reinterpret_cast<mach_vm_address_t>(&thread_id); 512 expectation.stack_address = reinterpret_cast<mach_vm_address_t>(&thread_id);
507 expectation.suspend_count = 0; 513 expectation.suspend_count = 0;
508 514
509 rv = WriteFD(write_fd, 515 rv = WriteFD(write_fd,
510 &expectation.stack_address, 516 &expectation.stack_address,
511 sizeof(expectation.stack_address)); 517 sizeof(expectation.stack_address));
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
560 ProcessReaderThreadedChild process_reader_threaded_child(kChildThreads); 566 ProcessReaderThreadedChild process_reader_threaded_child(kChildThreads);
561 process_reader_threaded_child.Run(); 567 process_reader_threaded_child.Run();
562 } 568 }
563 569
564 TEST(ProcessReader, ChildSeveralThreads) { 570 TEST(ProcessReader, ChildSeveralThreads) {
565 const size_t kChildThreads = 64; 571 const size_t kChildThreads = 64;
566 ProcessReaderThreadedChild process_reader_threaded_child(kChildThreads); 572 ProcessReaderThreadedChild process_reader_threaded_child(kChildThreads);
567 process_reader_threaded_child.Run(); 573 process_reader_threaded_child.Run();
568 } 574 }
569 575
576 TEST(ProcessReader, SelfModules) {
577 ProcessReader process_reader;
578 ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
579
580 uint32_t dyld_image_count = _dyld_image_count();
581 const std::vector<ProcessReaderModule>& modules = process_reader.Modules();
582
583 // There needs to be at least an entry for the main executable, for a dylib,
584 // and for dyld.
585 ASSERT_GE(modules.size(), 3u);
586
587 // dyld_image_count doesn’t include an entry for dyld itself, but |modules|
588 // does.
589 ASSERT_EQ(dyld_image_count + 1, modules.size());
590
591 for (uint32_t index = 0; index < dyld_image_count; ++index) {
592 SCOPED_TRACE(base::StringPrintf(
593 "index %u, name %s", index, modules[index].name.c_str()));
594
595 const char* dyld_image_name = _dyld_get_image_name(index);
596 EXPECT_EQ(dyld_image_name, modules[index].name);
597 EXPECT_EQ(
598 reinterpret_cast<mach_vm_address_t>(_dyld_get_image_header(index)),
599 modules[index].address);
600
601 if (index == 0) {
602 // dyld didn’t load the main executable, so it couldn’t record its
603 // timestamp, and it is reported as 0.
604 EXPECT_EQ(0, modules[index].timestamp);
605 } else {
606 // Hope that the module didn’t change on disk.
607 struct stat stat_buf;
608 int rv = stat(dyld_image_name, &stat_buf);
609 EXPECT_EQ(0, rv) << ErrnoMessage("stat");
610 if (rv == 0) {
611 EXPECT_EQ(stat_buf.st_mtime, modules[index].timestamp);
612 }
613 }
614 }
615
616 size_t index = modules.size() - 1;
617 EXPECT_EQ("/usr/lib/dyld", modules[index].name);
618
619 // dyld didn’t load itself either, so it couldn’t record its timestamp, and it
620 // is also reported as 0.
621 EXPECT_EQ(0, modules[index].timestamp);
622
623 const struct dyld_all_image_infos* dyld_image_infos =
624 _dyld_get_all_image_infos();
625 if (dyld_image_infos->version >= 2) {
626 EXPECT_EQ(reinterpret_cast<mach_vm_address_t>(
627 dyld_image_infos->dyldImageLoadAddress), modules[index].address);
628 }
629 }
630
631 class ProcessReaderModulesChild final : public MachMultiprocess {
632 public:
633 ProcessReaderModulesChild() : MachMultiprocess() {}
634
635 ~ProcessReaderModulesChild() {}
636
637 private:
638 void MachMultiprocessParent() override {
639 ProcessReader process_reader;
640 ASSERT_TRUE(process_reader.Initialize(ChildTask()));
641
642 const std::vector<ProcessReaderModule>& modules = process_reader.Modules();
643
644 // There needs to be at least an entry for the main executable, for a dylib,
645 // and for dyld.
646 ASSERT_GE(modules.size(), 3u);
647
648 int read_fd = ReadPipeFD();
649
650 uint32_t expect_modules;
651 ssize_t rv = ReadFD(read_fd, &expect_modules, sizeof(expect_modules));
652 ASSERT_EQ(static_cast<ssize_t>(sizeof(expect_modules)), rv)
653 << ErrnoMessage("read");
654
655 ASSERT_EQ(expect_modules, modules.size());
656
657 for (size_t index = 0; index < modules.size(); ++index) {
658 SCOPED_TRACE(base::StringPrintf(
659 "index %zu, name %s", index, modules[index].name.c_str()));
660
661 uint32_t expect_name_length;
662 rv = ReadFD(
663 read_fd, &expect_name_length, sizeof(expect_name_length));
664 ASSERT_EQ(static_cast<ssize_t>(sizeof(expect_name_length)), rv)
665 << ErrnoMessage("read");
666
667 // The NUL terminator is not read.
668 std::string expect_name(expect_name_length, '\0');
669 rv = ReadFD(read_fd, &expect_name[0], expect_name_length);
670 ASSERT_EQ(static_cast<ssize_t>(expect_name_length), rv)
671 << ErrnoMessage("read");
672
673 EXPECT_EQ(expect_name, modules[index].name);
674
675 mach_vm_address_t expect_address;
676 rv = ReadFD(read_fd, &expect_address, sizeof(expect_address));
677 ASSERT_EQ(static_cast<ssize_t>(sizeof(expect_address)), rv)
678 << ErrnoMessage("read");
679
680 EXPECT_EQ(expect_address, modules[index].address);
681
682 if (index == 0 || index == modules.size() - 1) {
683 // dyld didn’t load the main executable or itself, so it couldn’t record
684 // these timestamps, and they are reported as 0.
685 EXPECT_EQ(0, modules[index].timestamp);
686 } else {
687 // Hope that the module didn’t change on disk.
688 struct stat stat_buf;
689 int rv = stat(expect_name.c_str(), &stat_buf);
690 EXPECT_EQ(0, rv) << ErrnoMessage("stat");
691 if (rv == 0) {
692 EXPECT_EQ(stat_buf.st_mtime, modules[index].timestamp);
693 }
694 }
695 }
696
697 // Tell the child that it’s OK to exit. The child needed to be kept alive
698 // until the parent finished working with it.
699 int write_fd = WritePipeFD();
700 char c = '\0';
701 rv = WriteFD(write_fd, &c, 1);
702 ASSERT_EQ(1, rv) << ErrnoMessage("write");
703 }
704
705 void MachMultiprocessChild() override {
706 int write_fd = WritePipeFD();
707
708 uint32_t dyld_image_count = _dyld_image_count();
709 const struct dyld_all_image_infos* dyld_image_infos =
710 _dyld_get_all_image_infos();
711
712 uint32_t write_image_count = dyld_image_count;
713 if (dyld_image_infos->version >= 2) {
714 // dyld_image_count doesn’t include an entry for dyld itself, but one will
715 // be written.
716 ++write_image_count;
717 }
718
719 ssize_t rv = WriteFD(
720 write_fd, &write_image_count, sizeof(write_image_count));
721 ASSERT_EQ(static_cast<ssize_t>(sizeof(write_image_count)), rv)
722 << ErrnoMessage("write");
723
724 for (size_t index = 0; index < write_image_count; ++index) {
725 const char* dyld_image_name;
726 mach_vm_address_t dyld_image_address;
727
728 if (index < dyld_image_count) {
729 dyld_image_name = _dyld_get_image_name(index);
730 dyld_image_address =
731 reinterpret_cast<mach_vm_address_t>(_dyld_get_image_header(index));
732 } else {
733 dyld_image_name = "/usr/lib/dyld";
734 dyld_image_address = reinterpret_cast<mach_vm_address_t>(
735 dyld_image_infos->dyldImageLoadAddress);
736 }
737
738 uint32_t dyld_image_name_length = strlen(dyld_image_name);
739 rv = WriteFD(
740 write_fd, &dyld_image_name_length, sizeof(dyld_image_name_length));
741 ASSERT_EQ(static_cast<ssize_t>(sizeof(dyld_image_name_length)), rv)
742 << ErrnoMessage("write");
743
744 // The NUL terminator is not written.
745 rv = WriteFD(write_fd, dyld_image_name, dyld_image_name_length);
746 ASSERT_EQ(static_cast<ssize_t>(dyld_image_name_length), rv)
747 << ErrnoMessage("write");
748
749 rv = WriteFD(write_fd, &dyld_image_address, sizeof(dyld_image_address));
750 ASSERT_EQ(static_cast<ssize_t>(sizeof(dyld_image_address)), rv)
751 << ErrnoMessage("write");
752 }
753
754 // Wait for the parent to say that it’s OK to exit.
755 int read_fd = ReadPipeFD();
756 char c;
757 rv = ReadFD(read_fd, &c, 1);
758 ASSERT_EQ(1, rv) << ErrnoMessage("read");
759 }
760
761 DISALLOW_COPY_AND_ASSIGN(ProcessReaderModulesChild);
762 };
763
764 TEST(ProcessReader, ChildModules) {
765 ProcessReaderModulesChild process_reader_modules_child;
766 process_reader_modules_child.Run();
767 }
768
570 } // namespace 769 } // namespace
OLDNEW
« util/mac/process_reader.cc ('K') | « util/mac/process_reader.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698