blob: 911f6d3d2f6a0e488fc056e1fdb6c4f61738dc20 [file] [log] [blame]
// 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