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

Side by Side Diff: util/mac/process_reader_test.cc

Issue 666483002: Create snapshot/mac and move some files from snapshot and util to there (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad/+/master
Patch Set: Move process_reader, process_types, and mach_o_image*_reader from util/mac to snapshot/mac Created 6 years, 2 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
« no previous file with comments | « util/mac/process_reader.cc ('k') | util/mac/process_types.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2014 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (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
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "util/mac/process_reader.h"
16
17 #include <mach-o/dyld.h>
18 #include <mach-o/dyld_images.h>
19 #include <mach/mach.h>
20 #include <string.h>
21 #include <sys/stat.h>
22
23 #include <map>
24 #include <string>
25 #include <vector>
26
27 #include "base/logging.h"
28 #include "base/mac/scoped_mach_port.h"
29 #include "base/posix/eintr_wrapper.h"
30 #include "base/strings/stringprintf.h"
31 #include "build/build_config.h"
32 #include "gtest/gtest.h"
33 #include "util/file/fd_io.h"
34 #include "util/mac/mach_o_image_reader.h"
35 #include "util/mach/mach_extensions.h"
36 #include "util/stdlib/pointer_container.h"
37 #include "util/synchronization/semaphore.h"
38 #include "util/test/errors.h"
39 #include "util/test/mac/dyld.h"
40 #include "util/test/mac/mach_errors.h"
41 #include "util/test/mac/mach_multiprocess.h"
42
43 namespace crashpad {
44 namespace test {
45 namespace {
46
47 TEST(ProcessReader, SelfBasic) {
48 ProcessReader process_reader;
49 ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
50
51 #if !defined(ARCH_CPU_64_BITS)
52 EXPECT_FALSE(process_reader.Is64Bit());
53 #else
54 EXPECT_TRUE(process_reader.Is64Bit());
55 #endif
56
57 EXPECT_EQ(getpid(), process_reader.ProcessID());
58 EXPECT_EQ(getppid(), process_reader.ParentProcessID());
59
60 const char kTestMemory[] = "Some test memory";
61 char buffer[arraysize(kTestMemory)];
62 ASSERT_TRUE(process_reader.Memory()->Read(
63 reinterpret_cast<mach_vm_address_t>(kTestMemory),
64 sizeof(kTestMemory),
65 &buffer));
66 EXPECT_STREQ(kTestMemory, buffer);
67 }
68
69 const char kTestMemory[] = "Read me from another process";
70
71 class ProcessReaderChild final : public MachMultiprocess {
72 public:
73 ProcessReaderChild() : MachMultiprocess() {}
74
75 ~ProcessReaderChild() {}
76
77 private:
78 void MachMultiprocessParent() override {
79 ProcessReader process_reader;
80 ASSERT_TRUE(process_reader.Initialize(ChildTask()));
81
82 #if !defined(ARCH_CPU_64_BITS)
83 EXPECT_FALSE(process_reader.Is64Bit());
84 #else
85 EXPECT_TRUE(process_reader.Is64Bit());
86 #endif
87
88 EXPECT_EQ(getpid(), process_reader.ParentProcessID());
89 EXPECT_EQ(ChildPID(), process_reader.ProcessID());
90
91 int read_fd = ReadPipeFD();
92
93 mach_vm_address_t address;
94 CheckedReadFD(read_fd, &address, sizeof(address));
95
96 std::string read_string;
97 ASSERT_TRUE(process_reader.Memory()->ReadCString(address, &read_string));
98 EXPECT_EQ(kTestMemory, read_string);
99 }
100
101 void MachMultiprocessChild() override {
102 int write_fd = WritePipeFD();
103
104 mach_vm_address_t address =
105 reinterpret_cast<mach_vm_address_t>(kTestMemory);
106 CheckedWriteFD(write_fd, &address, sizeof(address));
107
108 // Wait for the parent to signal that it’s OK to exit by closing its end of
109 // the pipe.
110 CheckedReadFDAtEOF(ReadPipeFD());
111 }
112
113 DISALLOW_COPY_AND_ASSIGN(ProcessReaderChild);
114 };
115
116 TEST(ProcessReader, ChildBasic) {
117 ProcessReaderChild process_reader_child;
118 process_reader_child.Run();
119 }
120
121 // Returns a thread ID given a pthread_t. This wraps pthread_threadid_np() but
122 // that function has a cumbersome interface because it returns a success value.
123 // This function CHECKs success and returns the thread ID directly.
124 uint64_t PthreadToThreadID(pthread_t pthread) {
125 uint64_t thread_id;
126 int rv = pthread_threadid_np(pthread, &thread_id);
127 CHECK_EQ(rv, 0);
128 return thread_id;
129 }
130
131 TEST(ProcessReader, SelfOneThread) {
132 ProcessReader process_reader;
133 ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
134
135 const std::vector<ProcessReader::Thread>& threads = process_reader.Threads();
136
137 // If other tests ran in this process previously, threads may have been
138 // created and may still be running. This check must look for at least one
139 // thread, not exactly one thread.
140 ASSERT_GE(threads.size(), 1u);
141
142 EXPECT_EQ(PthreadToThreadID(pthread_self()), threads[0].id);
143
144 thread_t thread_self = MachThreadSelf();
145 EXPECT_EQ(thread_self, threads[0].port);
146
147 EXPECT_EQ(0, threads[0].suspend_count);
148 }
149
150 class TestThreadPool {
151 public:
152 struct ThreadExpectation {
153 mach_vm_address_t stack_address;
154 int suspend_count;
155 };
156
157 TestThreadPool() : thread_infos_() {
158 }
159
160 // Resumes suspended threads, signals each thread’s exit semaphore asking it
161 // to exit, and joins each thread, blocking until they have all exited.
162 ~TestThreadPool() {
163 for (ThreadInfo* thread_info : thread_infos_) {
164 thread_t thread_port = pthread_mach_thread_np(thread_info->pthread);
165 while (thread_info->suspend_count > 0) {
166 kern_return_t kr = thread_resume(thread_port);
167 EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "thread_resume");
168 --thread_info->suspend_count;
169 }
170 }
171
172 for (ThreadInfo* thread_info : thread_infos_) {
173 thread_info->exit_semaphore.Signal();
174 }
175
176 for (const ThreadInfo* thread_info : thread_infos_) {
177 int rv = pthread_join(thread_info->pthread, nullptr);
178 CHECK_EQ(0, rv);
179 }
180 }
181
182 // Starts |thread_count| threads and waits on each thread’s ready semaphore,
183 // so that when this function returns, all threads have been started and have
184 // all run to the point that they’ve signalled that they are ready.
185 void StartThreads(size_t thread_count) {
186 ASSERT_TRUE(thread_infos_.empty());
187
188 for (size_t thread_index = 0; thread_index < thread_count; ++thread_index) {
189 ThreadInfo* thread_info = new ThreadInfo();
190 thread_infos_.push_back(thread_info);
191
192 int rv = pthread_create(&thread_info->pthread,
193 nullptr,
194 ThreadMain,
195 thread_info);
196 ASSERT_EQ(0, rv);
197 }
198
199 for (ThreadInfo* thread_info : thread_infos_) {
200 thread_info->ready_semaphore.Wait();
201 }
202
203 // If present, suspend the thread at indices 1 through 3 the same number of
204 // times as their index. This tests reporting of suspend counts.
205 for (size_t thread_index = 1;
206 thread_index < thread_infos_.size() && thread_index < 4;
207 ++thread_index) {
208 thread_t thread_port =
209 pthread_mach_thread_np(thread_infos_[thread_index]->pthread);
210 for (size_t suspend_count = 0;
211 suspend_count < thread_index;
212 ++suspend_count) {
213 kern_return_t kr = thread_suspend(thread_port);
214 EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "thread_suspend");
215 if (kr == KERN_SUCCESS) {
216 ++thread_infos_[thread_index]->suspend_count;
217 }
218 }
219 }
220 }
221
222 uint64_t GetThreadInfo(size_t thread_index,
223 ThreadExpectation* expectation) {
224 CHECK_LT(thread_index, thread_infos_.size());
225
226 const ThreadInfo* thread_info = thread_infos_[thread_index];
227 expectation->stack_address = thread_info->stack_address;
228 expectation->suspend_count = thread_info->suspend_count;
229
230 return PthreadToThreadID(thread_info->pthread);
231 }
232
233 private:
234 struct ThreadInfo {
235 ThreadInfo()
236 : pthread(nullptr),
237 stack_address(0),
238 ready_semaphore(0),
239 exit_semaphore(0),
240 suspend_count(0) {
241 }
242
243 ~ThreadInfo() {}
244
245 // The thread’s ID, set at the time the thread is created.
246 pthread_t pthread;
247
248 // An address somewhere within the thread’s stack. The thread sets this in
249 // its ThreadMain().
250 mach_vm_address_t stack_address;
251
252 // The worker thread signals ready_semaphore to indicate that it’s done
253 // setting up its ThreadInfo structure. The main thread waits on this
254 // semaphore before using any data that the worker thread is responsible for
255 // setting.
256 Semaphore ready_semaphore;
257
258 // The worker thread waits on exit_semaphore to determine when it’s safe to
259 // exit. The main thread signals exit_semaphore when it no longer needs the
260 // worker thread.
261 Semaphore exit_semaphore;
262
263 // The thread’s suspend count.
264 int suspend_count;
265 };
266
267 static void* ThreadMain(void* argument) {
268 ThreadInfo* thread_info = static_cast<ThreadInfo*>(argument);
269
270 thread_info->stack_address =
271 reinterpret_cast<mach_vm_address_t>(&thread_info);
272
273 thread_info->ready_semaphore.Signal();
274 thread_info->exit_semaphore.Wait();
275
276 // Check this here after everything’s known to be synchronized, otherwise
277 // there’s a race between the parent thread storing this thread’s pthread_t
278 // in thread_info_pthread and this thread starting and attempting to access
279 // it.
280 CHECK_EQ(pthread_self(), thread_info->pthread);
281
282 return nullptr;
283 }
284
285 // This is a PointerVector because the address of a ThreadInfo object is
286 // passed to each thread’s ThreadMain(), so they cannot move around in memory.
287 PointerVector<ThreadInfo> thread_infos_;
288
289 DISALLOW_COPY_AND_ASSIGN(TestThreadPool);
290 };
291
292 typedef std::map<uint64_t, TestThreadPool::ThreadExpectation> ThreadMap;
293
294 // Verifies that all of the threads in |threads|, obtained from ProcessReader,
295 // agree with the expectation in |thread_map|. If |tolerate_extra_threads| is
296 // true, |threads| is allowed to contain threads that are not listed in
297 // |thread_map|. This is useful when testing situations where code outside of
298 // the test’s control (such as system libraries) may start threads, or may have
299 // started threads prior to a test’s execution.
300 void ExpectSeveralThreads(ThreadMap* thread_map,
301 const std::vector<ProcessReader::Thread>& threads,
302 const bool tolerate_extra_threads) {
303 if (tolerate_extra_threads) {
304 ASSERT_GE(threads.size(), thread_map->size());
305 } else {
306 ASSERT_EQ(thread_map->size(), threads.size());
307 }
308
309 for (size_t thread_index = 0; thread_index < threads.size(); ++thread_index) {
310 const ProcessReader::Thread& thread = threads[thread_index];
311 mach_vm_address_t thread_stack_region_end =
312 thread.stack_region_address + thread.stack_region_size;
313
314 const auto& iterator = thread_map->find(thread.id);
315 if (!tolerate_extra_threads) {
316 // Make sure that the thread is in the expectation map.
317 ASSERT_NE(thread_map->end(), iterator);
318 }
319
320 if (iterator != thread_map->end()) {
321 EXPECT_GE(iterator->second.stack_address, thread.stack_region_address);
322 EXPECT_LT(iterator->second.stack_address, thread_stack_region_end);
323
324 EXPECT_EQ(iterator->second.suspend_count, thread.suspend_count);
325
326 // Remove the thread from the expectation map since it’s already been
327 // found. This makes it easy to check for duplicate thread IDs, and makes
328 // it easy to check that all expected threads were found.
329 thread_map->erase(iterator);
330 }
331
332 // Make sure that this thread’s ID, stack region, and port don’t conflict
333 // with any other thread’s. Each thread should have a unique value for its
334 // ID and port, and each should have its own stack that doesn’t touch any
335 // other thread’s stack.
336 for (size_t other_thread_index = 0;
337 other_thread_index < threads.size();
338 ++other_thread_index) {
339 if (thread_index == other_thread_index) {
340 continue;
341 }
342
343 const ProcessReader::Thread& other_thread = threads[other_thread_index];
344
345 EXPECT_NE(thread.id, other_thread.id);
346 EXPECT_NE(thread.port, other_thread.port);
347
348 mach_vm_address_t other_thread_stack_region_end =
349 other_thread.stack_region_address + other_thread.stack_region_size;
350 EXPECT_FALSE(
351 thread.stack_region_address >= other_thread.stack_region_address &&
352 thread.stack_region_address < other_thread_stack_region_end);
353 EXPECT_FALSE(
354 thread_stack_region_end > other_thread.stack_region_address &&
355 thread_stack_region_end <= other_thread_stack_region_end);
356 }
357 }
358
359 // Make sure that each expected thread was found.
360 EXPECT_TRUE(thread_map->empty());
361 }
362
363 TEST(ProcessReader, SelfSeveralThreads) {
364 // Set up the ProcessReader here, before any other threads are running. This
365 // tests that the threads it returns are lazily initialized as a snapshot of
366 // the threads at the time of the first call to Threads(), and not at the
367 // time the ProcessReader was created or initialized.
368 ProcessReader process_reader;
369 ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
370
371 TestThreadPool thread_pool;
372 const size_t kChildThreads = 16;
373 ASSERT_NO_FATAL_FAILURE(thread_pool.StartThreads(kChildThreads));
374
375 // Build a map of all expected threads, keyed by each thread’s ID. The values
376 // are addresses that should lie somewhere within each thread’s stack.
377 ThreadMap thread_map;
378 const uint64_t self_thread_id = PthreadToThreadID(pthread_self());
379 TestThreadPool::ThreadExpectation expectation;
380 expectation.stack_address = reinterpret_cast<mach_vm_address_t>(&thread_map);
381 expectation.suspend_count = 0;
382 thread_map[self_thread_id] = expectation;
383 for (size_t thread_index = 0; thread_index < kChildThreads; ++thread_index) {
384 uint64_t thread_id = thread_pool.GetThreadInfo(thread_index, &expectation);
385
386 // There can’t be any duplicate thread IDs.
387 EXPECT_EQ(0u, thread_map.count(thread_id));
388
389 thread_map[thread_id] = expectation;
390 }
391
392 const std::vector<ProcessReader::Thread>& threads = process_reader.Threads();
393
394 // Other tests that have run previously may have resulted in the creation of
395 // threads that still exist, so pass true for |tolerate_extra_threads|.
396 ExpectSeveralThreads(&thread_map, threads, true);
397
398 // When testing in-process, verify that when this thread shows up in the
399 // vector, it has the expected thread port, and that this thread port only
400 // shows up once.
401 thread_t thread_self = MachThreadSelf();
402 bool found_thread_self = false;
403 for (const ProcessReader::Thread& thread : threads) {
404 if (thread.port == thread_self) {
405 EXPECT_FALSE(found_thread_self);
406 found_thread_self = true;
407 EXPECT_EQ(self_thread_id, thread.id);
408 }
409 }
410 EXPECT_TRUE(found_thread_self);
411 }
412
413 class ProcessReaderThreadedChild final : public MachMultiprocess {
414 public:
415 explicit ProcessReaderThreadedChild(size_t thread_count)
416 : MachMultiprocess(),
417 thread_count_(thread_count) {
418 }
419
420 ~ProcessReaderThreadedChild() {}
421
422 private:
423 void MachMultiprocessParent() override {
424 ProcessReader process_reader;
425 ASSERT_TRUE(process_reader.Initialize(ChildTask()));
426
427 int read_fd = ReadPipeFD();
428
429 // Build a map of all expected threads, keyed by each thread’s ID, and with
430 // addresses that should lie somewhere within each thread’s stack as values.
431 // These IDs and addresses all come from the child process via the pipe.
432 ThreadMap thread_map;
433 for (size_t thread_index = 0;
434 thread_index < thread_count_ + 1;
435 ++thread_index) {
436 uint64_t thread_id;
437 CheckedReadFD(read_fd, &thread_id, sizeof(thread_id));
438
439 TestThreadPool::ThreadExpectation expectation;
440 CheckedReadFD(read_fd,
441 &expectation.stack_address,
442 sizeof(expectation.stack_address));
443 CheckedReadFD(read_fd,
444 &expectation.suspend_count,
445 sizeof(expectation.suspend_count));
446
447 // There can’t be any duplicate thread IDs.
448 EXPECT_EQ(0u, thread_map.count(thread_id));
449
450 thread_map[thread_id] = expectation;
451 }
452
453 const std::vector<ProcessReader::Thread>& threads = process_reader.Threads() ;
454
455 // The child shouldn’t have any threads other than its main thread and the
456 // ones it created in its pool, so pass false for |tolerate_extra_threads|.
457 ExpectSeveralThreads(&thread_map, threads, false);
458 }
459
460 void MachMultiprocessChild() override {
461 TestThreadPool thread_pool;
462 ASSERT_NO_FATAL_FAILURE(thread_pool.StartThreads(thread_count_));
463
464 int write_fd = WritePipeFD();
465
466 // This thread isn’t part of the thread pool, but the parent will be able
467 // to inspect it. Write an entry for it.
468 uint64_t thread_id = PthreadToThreadID(pthread_self());
469
470 CheckedWriteFD(write_fd, &thread_id, sizeof(thread_id));
471
472 TestThreadPool::ThreadExpectation expectation;
473 expectation.stack_address = reinterpret_cast<mach_vm_address_t>(&thread_id);
474 expectation.suspend_count = 0;
475
476 CheckedWriteFD(write_fd,
477 &expectation.stack_address,
478 sizeof(expectation.stack_address));
479 CheckedWriteFD(write_fd,
480 &expectation.suspend_count,
481 sizeof(expectation.suspend_count));
482
483 // Write an entry for everything in the thread pool.
484 for (size_t thread_index = 0;
485 thread_index < thread_count_;
486 ++thread_index) {
487 uint64_t thread_id =
488 thread_pool.GetThreadInfo(thread_index, &expectation);
489
490 CheckedWriteFD(write_fd, &thread_id, sizeof(thread_id));
491 CheckedWriteFD(write_fd,
492 &expectation.stack_address,
493 sizeof(expectation.stack_address));
494 CheckedWriteFD(write_fd,
495 &expectation.suspend_count,
496 sizeof(expectation.suspend_count));
497 }
498
499 // Wait for the parent to signal that it’s OK to exit by closing its end of
500 // the pipe.
501 CheckedReadFDAtEOF(ReadPipeFD());
502 }
503
504 size_t thread_count_;
505
506 DISALLOW_COPY_AND_ASSIGN(ProcessReaderThreadedChild);
507 };
508
509 TEST(ProcessReader, ChildOneThread) {
510 // The main thread plus zero child threads equals one thread.
511 const size_t kChildThreads = 0;
512 ProcessReaderThreadedChild process_reader_threaded_child(kChildThreads);
513 process_reader_threaded_child.Run();
514 }
515
516 TEST(ProcessReader, ChildSeveralThreads) {
517 const size_t kChildThreads = 64;
518 ProcessReaderThreadedChild process_reader_threaded_child(kChildThreads);
519 process_reader_threaded_child.Run();
520 }
521
522 TEST(ProcessReader, SelfModules) {
523 ProcessReader process_reader;
524 ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
525
526 uint32_t dyld_image_count = _dyld_image_count();
527 const std::vector<ProcessReader::Module>& modules = process_reader.Modules();
528
529 // There needs to be at least an entry for the main executable, for a dylib,
530 // and for dyld.
531 ASSERT_GE(modules.size(), 3u);
532
533 // dyld_image_count doesn’t include an entry for dyld itself, but |modules|
534 // does.
535 ASSERT_EQ(dyld_image_count + 1, modules.size());
536
537 for (uint32_t index = 0; index < dyld_image_count; ++index) {
538 SCOPED_TRACE(base::StringPrintf(
539 "index %u, name %s", index, modules[index].name.c_str()));
540
541 const char* dyld_image_name = _dyld_get_image_name(index);
542 EXPECT_EQ(dyld_image_name, modules[index].name);
543 EXPECT_EQ(
544 reinterpret_cast<mach_vm_address_t>(_dyld_get_image_header(index)),
545 modules[index].reader->Address());
546
547 if (index == 0) {
548 // dyld didn’t load the main executable, so it couldn’t record its
549 // timestamp, and it is reported as 0.
550 EXPECT_EQ(0, modules[index].timestamp);
551 } else {
552 // Hope that the module didn’t change on disk.
553 struct stat stat_buf;
554 int rv = stat(dyld_image_name, &stat_buf);
555 EXPECT_EQ(0, rv) << ErrnoMessage("stat");
556 if (rv == 0) {
557 EXPECT_EQ(stat_buf.st_mtime, modules[index].timestamp);
558 }
559 }
560 }
561
562 size_t index = modules.size() - 1;
563 EXPECT_EQ("/usr/lib/dyld", modules[index].name);
564
565 // dyld didn’t load itself either, so it couldn’t record its timestamp, and it
566 // is also reported as 0.
567 EXPECT_EQ(0, modules[index].timestamp);
568
569 const struct dyld_all_image_infos* dyld_image_infos =
570 _dyld_get_all_image_infos();
571 if (dyld_image_infos->version >= 2) {
572 EXPECT_EQ(
573 reinterpret_cast<mach_vm_address_t>(
574 dyld_image_infos->dyldImageLoadAddress),
575 modules[index].reader->Address());
576 }
577 }
578
579 class ProcessReaderModulesChild final : public MachMultiprocess {
580 public:
581 ProcessReaderModulesChild() : MachMultiprocess() {}
582
583 ~ProcessReaderModulesChild() {}
584
585 private:
586 void MachMultiprocessParent() override {
587 ProcessReader process_reader;
588 ASSERT_TRUE(process_reader.Initialize(ChildTask()));
589
590 const std::vector<ProcessReader::Module>& modules =
591 process_reader.Modules();
592
593 // There needs to be at least an entry for the main executable, for a dylib,
594 // and for dyld.
595 ASSERT_GE(modules.size(), 3u);
596
597 int read_fd = ReadPipeFD();
598
599 uint32_t expect_modules;
600 CheckedReadFD(read_fd, &expect_modules, sizeof(expect_modules));
601
602 ASSERT_EQ(expect_modules, modules.size());
603
604 for (size_t index = 0; index < modules.size(); ++index) {
605 SCOPED_TRACE(base::StringPrintf(
606 "index %zu, name %s", index, modules[index].name.c_str()));
607
608 uint32_t expect_name_length;
609 CheckedReadFD(
610 read_fd, &expect_name_length, sizeof(expect_name_length));
611
612 // The NUL terminator is not read.
613 std::string expect_name(expect_name_length, '\0');
614 CheckedReadFD(read_fd, &expect_name[0], expect_name_length);
615 EXPECT_EQ(expect_name, modules[index].name);
616
617 mach_vm_address_t expect_address;
618 CheckedReadFD(read_fd, &expect_address, sizeof(expect_address));
619 EXPECT_EQ(expect_address, modules[index].reader->Address());
620
621 if (index == 0 || index == modules.size() - 1) {
622 // dyld didn’t load the main executable or itself, so it couldn’t record
623 // these timestamps, and they are reported as 0.
624 EXPECT_EQ(0, modules[index].timestamp);
625 } else {
626 // Hope that the module didn’t change on disk.
627 struct stat stat_buf;
628 int rv = stat(expect_name.c_str(), &stat_buf);
629 EXPECT_EQ(0, rv) << ErrnoMessage("stat");
630 if (rv == 0) {
631 EXPECT_EQ(stat_buf.st_mtime, modules[index].timestamp);
632 }
633 }
634 }
635 }
636
637 void MachMultiprocessChild() override {
638 int write_fd = WritePipeFD();
639
640 uint32_t dyld_image_count = _dyld_image_count();
641 const struct dyld_all_image_infos* dyld_image_infos =
642 _dyld_get_all_image_infos();
643
644 uint32_t write_image_count = dyld_image_count;
645 if (dyld_image_infos->version >= 2) {
646 // dyld_image_count doesn’t include an entry for dyld itself, but one will
647 // be written.
648 ++write_image_count;
649 }
650
651 CheckedWriteFD(write_fd, &write_image_count, sizeof(write_image_count));
652
653 for (size_t index = 0; index < write_image_count; ++index) {
654 const char* dyld_image_name;
655 mach_vm_address_t dyld_image_address;
656
657 if (index < dyld_image_count) {
658 dyld_image_name = _dyld_get_image_name(index);
659 dyld_image_address =
660 reinterpret_cast<mach_vm_address_t>(_dyld_get_image_header(index));
661 } else {
662 dyld_image_name = "/usr/lib/dyld";
663 dyld_image_address = reinterpret_cast<mach_vm_address_t>(
664 dyld_image_infos->dyldImageLoadAddress);
665 }
666
667 uint32_t dyld_image_name_length = strlen(dyld_image_name);
668 CheckedWriteFD(
669 write_fd, &dyld_image_name_length, sizeof(dyld_image_name_length));
670
671 // The NUL terminator is not written.
672 CheckedWriteFD(write_fd, dyld_image_name, dyld_image_name_length);
673
674 CheckedWriteFD(write_fd, &dyld_image_address, sizeof(dyld_image_address));
675 }
676
677 // Wait for the parent to signal that it’s OK to exit by closing its end of
678 // the pipe.
679 CheckedReadFDAtEOF(ReadPipeFD());
680 }
681
682 DISALLOW_COPY_AND_ASSIGN(ProcessReaderModulesChild);
683 };
684
685 TEST(ProcessReader, ChildModules) {
686 ProcessReaderModulesChild process_reader_modules_child;
687 process_reader_modules_child.Run();
688 }
689
690 } // namespace
691 } // namespace test
692 } // namespace crashpad
OLDNEW
« no previous file with comments | « util/mac/process_reader.cc ('k') | util/mac/process_types.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698