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

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

Issue 491963002: Add most of ProcessReader and its test (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Tolerate extra threads; test each thread’s suspend count Created 6 years, 4 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
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 <dispatch/dispatch.h>
18 #include <mach/mach.h>
19 #include <string.h>
20
21 #include <map>
22 #include <string>
23
24 #include "base/logging.h"
25 #include "base/mac/scoped_mach_port.h"
26 #include "base/posix/eintr_wrapper.h"
27 #include "build/build_config.h"
28 #include "gtest/gtest.h"
29 #include "util/file/fd_io.h"
30 #include "util/stdlib/pointer_container.h"
31 #include "util/test/mac/mach_errors.h"
32 #include "util/test/mac/mach_multiprocess.h"
33 #include "util/test/errors.h"
34
35 namespace {
36
37 using namespace crashpad;
38 using namespace crashpad::test;
39
40 TEST(ProcessReader, SelfBasic) {
41 ProcessReader process_reader;
42 ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
43
44 #if !defined(ARCH_CPU_64_BITS)
45 EXPECT_FALSE(process_reader.Is64Bit());
46 #else
47 EXPECT_TRUE(process_reader.Is64Bit());
48 #endif
49
50 EXPECT_EQ(getpid(), process_reader.ProcessID());
51 EXPECT_EQ(getppid(), process_reader.ParentProcessID());
52
53 const char kTestMemory[] = "Some test memory";
54 char buffer[arraysize(kTestMemory)];
55 ASSERT_TRUE(process_reader.Memory()->Read(
56 reinterpret_cast<mach_vm_address_t>(kTestMemory),
57 sizeof(kTestMemory),
58 &buffer));
59 EXPECT_STREQ(kTestMemory, buffer);
60 }
61
62 const char kTestMemory[] = "Read me from another process";
63
64 class ProcessReaderChild final : public MachMultiprocess {
65 public:
66 ProcessReaderChild() : MachMultiprocess() {}
67
68 ~ProcessReaderChild() {}
69
70 protected:
71 void Parent() override {
72 ProcessReader process_reader;
73 ASSERT_TRUE(process_reader.Initialize(ChildTask()));
74
75 #if !defined(ARCH_CPU_64_BITS)
76 EXPECT_FALSE(process_reader.Is64Bit());
77 #else
78 EXPECT_TRUE(process_reader.Is64Bit());
79 #endif
80
81 EXPECT_EQ(getpid(), process_reader.ParentProcessID());
82 EXPECT_EQ(ChildPID(), process_reader.ProcessID());
83
84 int read_fd = ReadPipeFD();
85
86 mach_vm_address_t address;
87 int rv = ReadFD(read_fd, &address, sizeof(address));
88 ASSERT_EQ(static_cast<ssize_t>(sizeof(address)), rv)
89 << ErrnoMessage("read");
90
91 std::string read_string;
92 ASSERT_TRUE(process_reader.Memory()->ReadCString(address, &read_string));
93 EXPECT_EQ(kTestMemory, read_string);
94
95 // Tell the child that it’s OK to exit. The child needed to be kept alive
96 // until the parent finished working with it.
97 int write_fd = WritePipeFD();
98 char c = '\0';
99 rv = WriteFD(write_fd, &c, 1);
100 ASSERT_EQ(1, rv) << ErrnoMessage("write");
101 }
102
103 void Child() override {
104 int write_fd = WritePipeFD();
105
106 mach_vm_address_t address =
107 reinterpret_cast<mach_vm_address_t>(kTestMemory);
108 int rv = WriteFD(write_fd, &address, sizeof(address));
109 ASSERT_EQ(static_cast<ssize_t>(sizeof(address)), rv)
110 << ErrnoMessage("write");
111
112 // Wait for the parent to say that it’s OK to exit.
113 int read_fd = ReadPipeFD();
114 char c;
115 rv = ReadFD(read_fd, &c, 1);
116 ASSERT_EQ(1, rv) << ErrnoMessage("read");
117 }
118
119 private:
120 DISALLOW_COPY_AND_ASSIGN(ProcessReaderChild);
121 };
122
123 TEST(ProcessReader, ChildBasic) {
124 ProcessReaderChild process_reader_child;
125 process_reader_child.Run();
126 }
127
128 // Returns a thread ID given a pthread_t. This wraps pthread_threadid_np() but
129 // that function has a cumbersome interface because it returns a success value.
130 // This function CHECKs success and returns the thread ID directly.
131 uint64_t PthreadToThreadID(pthread_t pthread) {
132 uint64_t thread_id;
133 int rv = pthread_threadid_np(pthread, &thread_id);
134 CHECK_EQ(rv, 0);
135 return thread_id;
136 }
137
138 TEST(ProcessReader, SelfOneThread) {
139 ProcessReader process_reader;
140 ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
141
142 const std::vector<ProcessReaderThread>& threads = process_reader.Threads();
143
144 // If other tests ran in this process previously, threads may have been
145 // created and may still be running. This check must look for at least one
146 // thread, not exactly one thread.
147 ASSERT_GE(threads.size(), 1u);
148
149 EXPECT_EQ(PthreadToThreadID(pthread_self()), threads[0].id);
150
151 base::mac::ScopedMachSendRight thread_self(mach_thread_self());
152 EXPECT_EQ(thread_self, threads[0].port);
153
154 EXPECT_EQ(0, threads[0].suspend_count);
155 }
156
157 class TestThreadPool {
158 public:
159 struct ThreadExpectation {
160 mach_vm_address_t stack_address;
161 int suspend_count;
162 };
163
164 TestThreadPool() : thread_infos_() {
165 }
166
167 // Resumes suspended threads, signals each thread’s exit semaphore asking it
168 // to exit, and joins each thread, blocking until they have all exited.
169 ~TestThreadPool() {
170 for (ThreadInfo* thread_info : thread_infos_) {
171 mach_port_t thread_port = pthread_mach_thread_np(thread_info->pthread);
172 while (thread_info->suspend_count > 0) {
173 kern_return_t kr = thread_resume(thread_port);
174 EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "thread_resume");
175 --thread_info->suspend_count;
176 }
177 }
178
179 for (const ThreadInfo* thread_info : thread_infos_) {
180 dispatch_semaphore_signal(thread_info->exit_semaphore);
181 }
182
183 for (const ThreadInfo* thread_info : thread_infos_) {
184 int rv = pthread_join(thread_info->pthread, NULL);
185 CHECK_EQ(0, rv);
186 }
187 }
188
189 // Starts |thread_count| threads and waits on each thread’s ready semaphore,
190 // so that when this function returns, all threads have been started and have
191 // all run to the point that they’ve signalled that they are ready.
192 void StartThreads(size_t thread_count) {
193 ASSERT_TRUE(thread_infos_.empty());
194
195 for (size_t thread_index = 0; thread_index < thread_count; ++thread_index) {
196 ThreadInfo* thread_info = new ThreadInfo();
197 thread_infos_.push_back(thread_info);
198
199 int rv = pthread_create(&thread_info->pthread,
200 NULL,
201 ThreadMain,
202 thread_info);
203 ASSERT_EQ(0, rv);
204 }
205
206 for (const ThreadInfo* thread_info : thread_infos_) {
207 long rv = dispatch_semaphore_wait(thread_info->ready_semaphore,
208 DISPATCH_TIME_FOREVER);
209 ASSERT_EQ(0, rv);
210 }
211
212 // If present, suspend the thread at indices 1 through 3 the same number of
213 // times as their index. This tests reporting of suspend counts.
214 for (size_t thread_index = 1;
215 thread_index < thread_infos_.size() && thread_index < 4;
216 ++thread_index) {
217 mach_port_t thread_port =
218 pthread_mach_thread_np(thread_infos_[thread_index]->pthread);
219 for (size_t suspend_count = 0;
220 suspend_count < thread_index;
221 ++suspend_count) {
222 kern_return_t kr = thread_suspend(thread_port);
223 EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "thread_suspend");
224 if (kr == KERN_SUCCESS) {
225 ++thread_infos_[thread_index]->suspend_count;
226 }
227 }
228 }
229 }
230
231 uint64_t GetThreadInfo(size_t thread_index,
232 ThreadExpectation* expectation) {
233 CHECK_LT(thread_index, thread_infos_.size());
234
235 const ThreadInfo* thread_info = thread_infos_[thread_index];
236 expectation->stack_address = thread_info->stack_address;
237 expectation->suspend_count = thread_info->suspend_count;
238
239 return PthreadToThreadID(thread_info->pthread);
240 }
241
242 private:
243 struct ThreadInfo {
244 ThreadInfo()
245 : pthread(NULL),
246 stack_address(0),
247 ready_semaphore(dispatch_semaphore_create(0)),
248 exit_semaphore(dispatch_semaphore_create(0)),
249 suspend_count(0) {
250 }
251
252 ~ThreadInfo() {
253 dispatch_release(exit_semaphore);
254 dispatch_release(ready_semaphore);
255 }
256
257 // The thread’s ID, set at the time the thread is created.
258 pthread_t pthread;
259
260 // An address somewhere within the thread’s stack. The thread sets this in
261 // its ThreadMain().
262 mach_vm_address_t stack_address;
263
264 // The worker thread signals ready_semaphore to indicate that it’s done
265 // setting up its ThreadInfo structure. The main thread waits on this
266 // semaphore before using any data that the worker thread is responsible for
267 // setting.
268 dispatch_semaphore_t ready_semaphore;
269
270 // The worker thread waits on exit_semaphore to determine when it’s safe to
271 // exit. The main thread signals exit_semaphore when it no longer needs the
272 // worker thread.
273 dispatch_semaphore_t exit_semaphore;
274
275 // The thread’s suspend count.
276 int suspend_count;
277 };
278
279 static void* ThreadMain(void* argument) {
280 ThreadInfo* thread_info = static_cast<ThreadInfo*>(argument);
281
282 thread_info->stack_address =
283 reinterpret_cast<mach_vm_address_t>(&thread_info);
284
285 dispatch_semaphore_signal(thread_info->ready_semaphore);
286 dispatch_semaphore_wait(thread_info->exit_semaphore, DISPATCH_TIME_FOREVER);
287
288 // Check this here after everything’s known to be synchronized, otherwise
289 // there’s a race between the parent thread storing this thread’s pthread_t
290 // in thread_info_pthread and this thread starting and attempting to access
291 // it.
292 CHECK_EQ(pthread_self(), thread_info->pthread);
293
294 return NULL;
295 }
296
297 // This is a PointerVector because the address of a ThreadInfo object is
298 // passed to each thread’s ThreadMain(), so they cannot move around in memory.
299 PointerVector<ThreadInfo> thread_infos_;
300
301 DISALLOW_COPY_AND_ASSIGN(TestThreadPool);
302 };
303
304 typedef std::map<uint64_t, TestThreadPool::ThreadExpectation> ThreadMap;
305
306 // Verifies that all of the threads in |threads|, obtained from ProcessReader,
307 // agree with the expectation in |thread_map|. If |tolerate_extra_threads| is
308 // true, |threads| is allowed to contain threads that are not listed in
309 // |thread_map|. This is useful when testing situations where code outside of
310 // the test’s control (such as system libraries) may start threads, or may have
311 // started threads prior to a test’s execution.
312 void ExpectSeveralThreads(ThreadMap* thread_map,
313 const std::vector<ProcessReaderThread>& threads,
314 const bool tolerate_extra_threads) {
315 if (tolerate_extra_threads) {
316 ASSERT_GE(threads.size(), thread_map->size());
317 } else {
318 ASSERT_EQ(thread_map->size(), threads.size());
319 }
320
321 for (size_t thread_index = 0; thread_index < threads.size(); ++thread_index) {
322 const ProcessReaderThread& thread = threads[thread_index];
323 mach_vm_address_t thread_stack_region_end =
324 thread.stack_region_address + thread.stack_region_size;
325
326 const auto& iterator = thread_map->find(thread.id);
327 if (!tolerate_extra_threads) {
328 // Make sure that the thread is in the expectation map.
329 ASSERT_NE(thread_map->end(), iterator);
330 }
331
332 if (iterator != thread_map->end()) {
333 EXPECT_GE(iterator->second.stack_address, thread.stack_region_address);
334 EXPECT_LT(iterator->second.stack_address, thread_stack_region_end);
335
336 EXPECT_EQ(iterator->second.suspend_count, thread.suspend_count);
337
338 // Remove the thread from the expectation map since it’s already been
339 // found. This makes it easy to check for duplicate thread IDs, and makes
340 // it easy to check that all expected threads were found.
341 thread_map->erase(iterator);
342 }
343
344 // Make sure that this thread’s ID, stack region, and port don’t conflict
345 // with any other thread’s. Each thread should have a unique value for its
346 // ID and port, and each should have its own stack that doesn’t touch any
347 // other thread’s stack.
348 for (size_t other_thread_index = 0;
349 other_thread_index < threads.size();
350 ++other_thread_index) {
351 if (thread_index == other_thread_index) {
352 continue;
353 }
354
355 const ProcessReaderThread& other_thread = threads[other_thread_index];
356
357 EXPECT_NE(thread.id, other_thread.id);
358 EXPECT_NE(thread.port, other_thread.port);
359
360 mach_vm_address_t other_thread_stack_region_end =
361 other_thread.stack_region_address + other_thread.stack_region_size;
362 EXPECT_FALSE(
363 thread.stack_region_address >= other_thread.stack_region_address &&
364 thread.stack_region_address < other_thread_stack_region_end);
365 EXPECT_FALSE(
366 thread_stack_region_end > other_thread.stack_region_address &
367 thread_stack_region_end <= other_thread_stack_region_end);
368 }
369 }
370
371 // Make sure that each expected thread was found.
372 EXPECT_TRUE(thread_map->empty());
373 }
374
375 TEST(ProcessReader, SelfSeveralThreads) {
376 // Set up the ProcessReader here, before any other threads are running. This
377 // tests that the threads it returns are lazily initialized as a snapshot of
378 // the threads at the time of the first call to Threads(), and not at the
379 // time the ProcessReader was created or initialized.
380 ProcessReader process_reader;
381 ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
382
383 TestThreadPool thread_pool;
384 const size_t kChildThreads = 16;
385 thread_pool.StartThreads(kChildThreads);
386 if (Test::HasFatalFailure()) {
387 return;
388 }
389
390 // Build a map of all expected threads, keyed by each thread’s ID. The values
391 // are addresses that should lie somewhere within each thread’s stack.
392 ThreadMap thread_map;
393 const uint64_t self_thread_id = PthreadToThreadID(pthread_self());
394 TestThreadPool::ThreadExpectation expectation;
395 expectation.stack_address = reinterpret_cast<mach_vm_address_t>(&thread_map);
396 expectation.suspend_count = 0;
397 thread_map[self_thread_id] = expectation;
398 for (size_t thread_index = 0; thread_index < kChildThreads; ++thread_index) {
399 uint64_t thread_id = thread_pool.GetThreadInfo(thread_index, &expectation);
400
401 // There can’t be any duplicate thread IDs.
402 EXPECT_EQ(0u, thread_map.count(thread_id));
403
404 thread_map[thread_id] = expectation;
405 }
406
407 const std::vector<ProcessReaderThread>& threads = process_reader.Threads();
408
409 // Other tests that have run previously may have resulted in the creation of
410 // threads that still exist, so pass true for |tolerate_extra_threads|.
411 ExpectSeveralThreads(&thread_map, threads, true);
412
413 // When testing in-process, verify that when this thread shows up in the
414 // vector, it has the expected thread port, and that this thread port only
415 // shows up once.
416 base::mac::ScopedMachSendRight thread_self(mach_thread_self());
417 bool found_thread_self = false;
418 for (const ProcessReaderThread& thread : threads) {
419 if (thread.port == thread_self) {
420 EXPECT_FALSE(found_thread_self);
421 found_thread_self = true;
422 EXPECT_EQ(self_thread_id, thread.id);
423 }
424 }
425 EXPECT_TRUE(found_thread_self);
426 }
427
428 class ProcessReaderThreadedChild final : public MachMultiprocess {
429 public:
430 explicit ProcessReaderThreadedChild(size_t thread_count)
431 : MachMultiprocess(),
432 thread_count_(thread_count) {
433 }
434
435 ~ProcessReaderThreadedChild() {}
436
437 protected:
438 void Parent() override {
439 ProcessReader process_reader;
440 ASSERT_TRUE(process_reader.Initialize(ChildTask()));
441
442 int read_fd = ReadPipeFD();
443
444 // Build a map of all expected threads, keyed by each thread’s ID, and with
445 // addresses that should lie somewhere within each thread’s stack as values.
446 // These IDs and addresses all come from the child process via the pipe.
447 ThreadMap thread_map;
448 for (size_t thread_index = 0;
449 thread_index < thread_count_ + 1;
450 ++thread_index) {
451 uint64_t thread_id;
452 int rv = ReadFD(read_fd, &thread_id, sizeof(thread_id));
453 ASSERT_EQ(static_cast<ssize_t>(sizeof(thread_id)), rv)
454 << ErrnoMessage("read");
455
456 TestThreadPool::ThreadExpectation expectation;
457 rv = ReadFD(read_fd,
458 &expectation.stack_address,
459 sizeof(expectation.stack_address));
460 ASSERT_EQ(static_cast<ssize_t>(sizeof(expectation.stack_address)), rv)
461 << ErrnoMessage("read");
462
463 rv = ReadFD(read_fd,
464 &expectation.suspend_count,
465 sizeof(expectation.suspend_count));
466 ASSERT_EQ(static_cast<ssize_t>(sizeof(expectation.suspend_count)), rv)
467 << ErrnoMessage("read");
468
469 // There can’t be any duplicate thread IDs.
470 EXPECT_EQ(0u, thread_map.count(thread_id));
471
472 thread_map[thread_id] = expectation;
473 }
474
475 const std::vector<ProcessReaderThread>& threads = process_reader.Threads();
476
477 // The child shouldn’t have any threads other than its main thread and the
478 // ones it created in its pool, so pass false for |tolerate_extra_threads|.
479 ExpectSeveralThreads(&thread_map, threads, false);
480
481 // Tell the child that it’s OK to exit. The child needed to be kept alive
482 // until the parent finished working with it.
483 int write_fd = WritePipeFD();
484 char c = '\0';
485 int rv = WriteFD(write_fd, &c, 1);
486 ASSERT_EQ(1, rv) << ErrnoMessage("write");
487 }
488
489 void Child() override {
490 TestThreadPool thread_pool;
491 thread_pool.StartThreads(thread_count_);
492 if (testing::Test::HasFatalFailure()) {
493 return;
494 }
495
496 int write_fd = WritePipeFD();
497
498 // This thread isn’t part of the thread pool, but the parent will be able
499 // to inspect it. Write an entry for it.
500 uint64_t thread_id = PthreadToThreadID(pthread_self());
501
502 int rv = WriteFD(write_fd, &thread_id, sizeof(thread_id));
503 ASSERT_EQ(static_cast<ssize_t>(sizeof(thread_id)), rv)
504 << ErrnoMessage("write");
505
506 TestThreadPool::ThreadExpectation expectation;
507 expectation.stack_address = reinterpret_cast<mach_vm_address_t>(&thread_id);
508 expectation.suspend_count = 0;
509
510 rv = WriteFD(write_fd,
511 &expectation.stack_address,
512 sizeof(expectation.stack_address));
513 ASSERT_EQ(static_cast<ssize_t>(sizeof(expectation.stack_address)), rv)
514 << ErrnoMessage("write");
515
516 rv = WriteFD(write_fd,
517 &expectation.suspend_count,
518 sizeof(expectation.suspend_count));
519 ASSERT_EQ(static_cast<ssize_t>(sizeof(expectation.suspend_count)), rv)
520 << ErrnoMessage("write");
521
522 // Write an entry for everything in the thread pool.
523 for (size_t thread_index = 0;
524 thread_index < thread_count_;
525 ++thread_index) {
526 uint64_t thread_id =
527 thread_pool.GetThreadInfo(thread_index, &expectation);
528
529 rv = WriteFD(write_fd, &thread_id, sizeof(thread_id));
530 ASSERT_EQ(static_cast<ssize_t>(sizeof(thread_id)), rv)
531 << ErrnoMessage("write");
532
533 rv = WriteFD(write_fd,
534 &expectation.stack_address,
535 sizeof(expectation.stack_address));
536 ASSERT_EQ(static_cast<ssize_t>(sizeof(expectation.stack_address)), rv)
537 << ErrnoMessage("write");
538
539 rv = WriteFD(write_fd,
540 &expectation.suspend_count,
541 sizeof(expectation.suspend_count));
542 ASSERT_EQ(static_cast<ssize_t>(sizeof(expectation.suspend_count)), rv)
543 << ErrnoMessage("write");
544 }
545
546 // Wait for the parent to say that it’s OK to exit.
547 int read_fd = ReadPipeFD();
548 char c;
549 rv = ReadFD(read_fd, &c, 1);
550 ASSERT_EQ(1, rv) << ErrnoMessage("read");
551 }
552
553 private:
554 size_t thread_count_;
555
556 DISALLOW_COPY_AND_ASSIGN(ProcessReaderThreadedChild);
557 };
558
559 TEST(ProcessReader, ChildOneThread) {
560 // The main thread plus zero child threads equals one thread.
561 const size_t kChildThreads = 0;
562 ProcessReaderThreadedChild process_reader_threaded_child(kChildThreads);
563 process_reader_threaded_child.Run();
564 }
565
566 TEST(ProcessReader, ChildSeveralThreads) {
567 const size_t kChildThreads = 64;
568 ProcessReaderThreadedChild process_reader_threaded_child(kChildThreads);
569 process_reader_threaded_child.Run();
570 }
571
572 } // namespace
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698