| // Copyright 2019 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 "util/linux/proc_task_reader.h" |
| |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/strings/stringprintf.h" |
| #include "gtest/gtest.h" |
| #include "test/multiprocess_exec.h" |
| #include "third_party/lss/lss.h" |
| #include "util/synchronization/semaphore.h" |
| #include "util/thread/thread.h" |
| |
| namespace crashpad { |
| namespace test { |
| namespace { |
| |
| bool FindThreadID(pid_t tid, const std::vector<pid_t>& threads) { |
| for (const auto& thread : threads) { |
| if (thread == tid) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| class ScopedBlockingThread : public Thread { |
| public: |
| ScopedBlockingThread() : tid_sem_(0), join_sem_(0), tid_(-1) {} |
| |
| ~ScopedBlockingThread() { |
| join_sem_.Signal(); |
| Join(); |
| } |
| |
| pid_t ThreadID() { |
| tid_sem_.Wait(); |
| return tid_; |
| } |
| |
| private: |
| void ThreadMain() override { |
| tid_ = sys_gettid(); |
| tid_sem_.Signal(); |
| join_sem_.Wait(); |
| } |
| |
| Semaphore tid_sem_; |
| Semaphore join_sem_; |
| pid_t tid_; |
| }; |
| |
| TEST(ProcTaskReader, Self) { |
| std::vector<pid_t> tids; |
| ASSERT_TRUE(ReadThreadIDs(getpid(), &tids)); |
| EXPECT_TRUE(FindThreadID(getpid(), tids)); |
| EXPECT_TRUE(FindThreadID(sys_gettid(), tids)); |
| |
| ScopedBlockingThread thread1; |
| thread1.Start(); |
| |
| ScopedBlockingThread thread2; |
| thread2.Start(); |
| |
| pid_t thread1_tid = thread1.ThreadID(); |
| pid_t thread2_tid = thread2.ThreadID(); |
| |
| tids.clear(); |
| ASSERT_TRUE(ReadThreadIDs(getpid(), &tids)); |
| EXPECT_TRUE(FindThreadID(getpid(), tids)); |
| EXPECT_TRUE(FindThreadID(thread1_tid, tids)); |
| EXPECT_TRUE(FindThreadID(thread2_tid, tids)); |
| } |
| |
| TEST(ProcTaskReader, BadPID) { |
| std::vector<pid_t> tids; |
| EXPECT_FALSE(ReadThreadIDs(-1, &tids)); |
| |
| tids.clear(); |
| EXPECT_FALSE(ReadThreadIDs(0, &tids)); |
| } |
| |
| CRASHPAD_CHILD_TEST_MAIN(ProcTaskTestChild) { |
| FileHandle in = StdioFileHandle(StdioStream::kStandardInput); |
| FileHandle out = StdioFileHandle(StdioStream::kStandardOutput); |
| |
| pid_t tid = getpid(); |
| CheckedWriteFile(out, &tid, sizeof(tid)); |
| |
| tid = sys_gettid(); |
| CheckedWriteFile(out, &tid, sizeof(tid)); |
| |
| ScopedBlockingThread thread1; |
| thread1.Start(); |
| |
| ScopedBlockingThread thread2; |
| thread2.Start(); |
| |
| tid = thread1.ThreadID(); |
| CheckedWriteFile(out, &tid, sizeof(tid)); |
| |
| tid = thread2.ThreadID(); |
| CheckedWriteFile(out, &tid, sizeof(tid)); |
| |
| CheckedReadFileAtEOF(in); |
| return 0; |
| } |
| |
| class ProcTaskTest : public MultiprocessExec { |
| public: |
| ProcTaskTest() : MultiprocessExec() { |
| SetChildTestMainFunction("ProcTaskTestChild"); |
| } |
| |
| private: |
| bool ReadIDFromChild(std::vector<pid_t>* threads) { |
| pid_t tid; |
| if (!LoggingReadFileExactly(ReadPipeHandle(), &tid, sizeof(tid))) { |
| return false; |
| } |
| threads->push_back(tid); |
| return true; |
| } |
| |
| void MultiprocessParent() override { |
| std::vector<pid_t> ids_to_find; |
| for (size_t id_count = 0; id_count < 4; ++id_count) { |
| ASSERT_TRUE(ReadIDFromChild(&ids_to_find)); |
| } |
| |
| std::vector<pid_t> threads; |
| ASSERT_TRUE(ReadThreadIDs(ChildPID(), &threads)); |
| for (size_t index = 0; index < ids_to_find.size(); ++index) { |
| SCOPED_TRACE( |
| base::StringPrintf("index %zd, tid %d", index, ids_to_find[index])); |
| EXPECT_TRUE(FindThreadID(ids_to_find[index], threads)); |
| } |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(ProcTaskTest); |
| }; |
| |
| TEST(ProcTaskReader, ReadChild) { |
| ProcTaskTest test; |
| test.Run(); |
| } |
| |
| } // namespace |
| } // namespace test |
| } // namespace crashpad |