| // Copyright 2015 The Crashpad Authors. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "snapshot/win/process_reader_win.h" |
| |
| #include <windows.h> |
| #include <string.h> |
| |
| #include "base/stl_util.h" |
| #include "gtest/gtest.h" |
| #include "test/win/win_multiprocess.h" |
| #include "util/misc/from_pointer_cast.h" |
| #include "util/synchronization/semaphore.h" |
| #include "util/thread/thread.h" |
| #include "util/win/context_wrappers.h" |
| #include "util/win/scoped_process_suspend.h" |
| |
| namespace crashpad { |
| namespace test { |
| namespace { |
| |
| TEST(ProcessReaderWin, SelfBasic) { |
| ProcessReaderWin process_reader; |
| ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(), |
| ProcessSuspensionState::kRunning)); |
| |
| #if !defined(ARCH_CPU_64_BITS) |
| EXPECT_FALSE(process_reader.Is64Bit()); |
| #else |
| EXPECT_TRUE(process_reader.Is64Bit()); |
| #endif |
| |
| EXPECT_EQ(process_reader.GetProcessInfo().ProcessID(), GetCurrentProcessId()); |
| |
| static constexpr char kTestMemory[] = "Some test memory"; |
| char buffer[base::size(kTestMemory)]; |
| ASSERT_TRUE(process_reader.Memory()->Read( |
| reinterpret_cast<uintptr_t>(kTestMemory), sizeof(kTestMemory), &buffer)); |
| EXPECT_STREQ(kTestMemory, buffer); |
| } |
| |
| constexpr char kTestMemory[] = "Read me from another process"; |
| |
| class ProcessReaderChild final : public WinMultiprocess { |
| public: |
| ProcessReaderChild() : WinMultiprocess() {} |
| ~ProcessReaderChild() {} |
| |
| private: |
| void WinMultiprocessParent() override { |
| ProcessReaderWin process_reader; |
| ASSERT_TRUE(process_reader.Initialize(ChildProcess(), |
| ProcessSuspensionState::kRunning)); |
| |
| #if !defined(ARCH_CPU_64_BITS) |
| EXPECT_FALSE(process_reader.Is64Bit()); |
| #else |
| EXPECT_TRUE(process_reader.Is64Bit()); |
| #endif |
| |
| WinVMAddress address; |
| CheckedReadFileExactly(ReadPipeHandle(), &address, sizeof(address)); |
| |
| char buffer[sizeof(kTestMemory)]; |
| ASSERT_TRUE( |
| process_reader.Memory()->Read(address, sizeof(kTestMemory), &buffer)); |
| EXPECT_EQ(strcmp(kTestMemory, buffer), 0); |
| } |
| |
| void WinMultiprocessChild() override { |
| WinVMAddress address = FromPointerCast<WinVMAddress>(kTestMemory); |
| CheckedWriteFile(WritePipeHandle(), &address, sizeof(address)); |
| |
| // Wait for the parent to signal that it's OK to exit by closing its end of |
| // the pipe. |
| CheckedReadFileAtEOF(ReadPipeHandle()); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(ProcessReaderChild); |
| }; |
| |
| TEST(ProcessReaderWin, ChildBasic) { |
| WinMultiprocess::Run<ProcessReaderChild>(); |
| } |
| |
| TEST(ProcessReaderWin, SelfOneThread) { |
| ProcessReaderWin process_reader; |
| ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(), |
| ProcessSuspensionState::kRunning)); |
| |
| const std::vector<ProcessReaderWin::Thread>& threads = |
| process_reader.Threads(); |
| |
| // If other tests ran in this process previously, threads may have been |
| // created and may still be running. This check must look for at least one |
| // thread, not exactly one thread. |
| ASSERT_GE(threads.size(), 1u); |
| |
| EXPECT_EQ(threads[0].id, GetCurrentThreadId()); |
| EXPECT_NE(ProgramCounterFromCONTEXT(&threads[0].context.native), nullptr); |
| EXPECT_EQ(threads[0].suspend_count, 0u); |
| } |
| |
| class ProcessReaderChildThreadSuspendCount final : public WinMultiprocess { |
| public: |
| ProcessReaderChildThreadSuspendCount() : WinMultiprocess() {} |
| ~ProcessReaderChildThreadSuspendCount() {} |
| |
| private: |
| enum : unsigned int { kCreatedThreads = 3 }; |
| |
| class SleepingThread : public Thread { |
| public: |
| SleepingThread() : done_(nullptr) {} |
| |
| void SetHandle(Semaphore* done) { |
| done_= done; |
| } |
| |
| void ThreadMain() override { |
| done_->Wait(); |
| } |
| |
| private: |
| Semaphore* done_; |
| }; |
| |
| void WinMultiprocessParent() override { |
| char c; |
| CheckedReadFileExactly(ReadPipeHandle(), &c, sizeof(c)); |
| ASSERT_EQ(c, ' '); |
| |
| { |
| ProcessReaderWin process_reader; |
| ASSERT_TRUE(process_reader.Initialize(ChildProcess(), |
| ProcessSuspensionState::kRunning)); |
| |
| const auto& threads = process_reader.Threads(); |
| ASSERT_GE(threads.size(), kCreatedThreads + 1); |
| for (const auto& thread : threads) |
| EXPECT_EQ(thread.suspend_count, 0u); |
| } |
| |
| { |
| ScopedProcessSuspend suspend(ChildProcess()); |
| |
| ProcessReaderWin process_reader; |
| ASSERT_TRUE(process_reader.Initialize( |
| ChildProcess(), ProcessSuspensionState::kSuspended)); |
| |
| // Confirm that thread counts are adjusted correctly for the process being |
| // suspended. |
| const auto& threads = process_reader.Threads(); |
| ASSERT_GE(threads.size(), kCreatedThreads + 1); |
| for (const auto& thread : threads) |
| EXPECT_EQ(thread.suspend_count, 0u); |
| } |
| } |
| |
| void WinMultiprocessChild() override { |
| // Create three dummy threads so we can confirm we read successfully read |
| // more than just the main thread. |
| SleepingThread threads[kCreatedThreads]; |
| Semaphore done(0); |
| for (auto& thread : threads) |
| thread.SetHandle(&done); |
| for (auto& thread : threads) |
| thread.Start(); |
| |
| char c = ' '; |
| CheckedWriteFile(WritePipeHandle(), &c, sizeof(c)); |
| |
| // Wait for the parent to signal that it's OK to exit by closing its end of |
| // the pipe. |
| CheckedReadFileAtEOF(ReadPipeHandle()); |
| |
| for (size_t i = 0; i < base::size(threads); ++i) |
| done.Signal(); |
| for (auto& thread : threads) |
| thread.Join(); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(ProcessReaderChildThreadSuspendCount); |
| }; |
| |
| TEST(ProcessReaderWin, ChildThreadSuspendCounts) { |
| WinMultiprocess::Run<ProcessReaderChildThreadSuspendCount>(); |
| } |
| |
| } // namespace |
| } // namespace test |
| } // namespace crashpad |