| OLD | NEW |
| (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 | |
| OLD | NEW |