1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <err.h> 18 #include <inttypes.h> 19 #include <pthread.h> 20 #include <stdio.h> 21 #include <string.h> 22 #include <sys/mman.h> 23 #include <sys/user.h> 24 #include <unistd.h> 25 26 #include <gtest/gtest.h> 27 28 #include <chrono> 29 #include <thread> 30 #include <vector> 31 32 #include <android-base/macros.h> 33 34 #include "utils.h" 35 36 using namespace std::chrono_literals; 37 38 static size_t GetMappingSize() { 39 std::vector<map_record> maps; 40 if (!Maps::parse_maps(&maps)) { 41 err(1, "failed to parse maps"); 42 } 43 44 size_t result = 0; 45 for (const map_record& map : maps) { 46 result += map.addr_end - map.addr_start; 47 } 48 49 return result; 50 } 51 52 static void WaitUntilAllExited(pid_t* pids, size_t pid_count) { 53 // Wait until all children have exited. 54 bool alive = true; 55 while (alive) { 56 alive = false; 57 for (size_t i = 0; i < pid_count; ++i) { 58 if (pids[i] != 0) { 59 if (kill(pids[i], 0) == 0) { 60 alive = true; 61 } else { 62 EXPECT_EQ(errno, ESRCH); 63 pids[i] = 0; // Skip in next loop. 64 } 65 } 66 } 67 } 68 } 69 70 class LeakChecker { 71 public: 72 LeakChecker() { 73 Reset(); 74 } 75 76 ~LeakChecker() { 77 Check(); 78 } 79 80 void Reset() { 81 previous_size_ = GetMappingSize(); 82 } 83 84 void DumpTo(std::ostream& os) const { 85 os << previous_size_; 86 } 87 88 private: 89 size_t previous_size_; 90 91 void Check() { 92 auto current_size = GetMappingSize(); 93 if (current_size > previous_size_) { 94 FAIL() << "increase in process map size: " << previous_size_ << " -> " << current_size; 95 } 96 } 97 }; 98 99 std::ostream& operator<<(std::ostream& os, const LeakChecker& lc) { 100 lc.DumpTo(os); 101 return os; 102 } 103 104 // http://b/36045112 105 TEST(pthread_leak, join) { 106 LeakChecker lc; 107 for (int i = 0; i < 100; ++i) { 108 pthread_t thread; 109 ASSERT_EQ(0, pthread_create(&thread, nullptr, [](void*) -> void* { return nullptr; }, nullptr)); 110 ASSERT_EQ(0, pthread_join(thread, nullptr)); 111 } 112 } 113 114 // http://b/36045112 115 TEST(pthread_leak, detach) { 116 LeakChecker lc; 117 118 for (size_t pass = 0; pass < 2; ++pass) { 119 pthread_barrier_t barrier; 120 constexpr int thread_count = 100; 121 ASSERT_EQ(pthread_barrier_init(&barrier, nullptr, thread_count + 1), 0); 122 123 // Start child threads. 124 struct thread_data { pthread_barrier_t* barrier; pid_t* tid; }; 125 pid_t tids[thread_count]; 126 for (int i = 0; i < thread_count; ++i) { 127 thread_data* td = new thread_data{&barrier, &tids[i]}; 128 const auto thread_function = +[](void* ptr) -> void* { 129 thread_data* data = static_cast<thread_data*>(ptr); 130 *data->tid = gettid(); 131 pthread_barrier_wait(data->barrier); 132 // Doing this delete allocates new VMAs for jemalloc bookkeeping, 133 // but the two-pass nature of this test means we can check that 134 // it's a pool rather than an unbounded leak. 135 delete data; 136 return nullptr; 137 }; 138 pthread_t thread; 139 ASSERT_EQ(0, pthread_create(&thread, nullptr, thread_function, td)); 140 ASSERT_EQ(0, pthread_detach(thread)); 141 } 142 143 pthread_barrier_wait(&barrier); 144 ASSERT_EQ(pthread_barrier_destroy(&barrier), 0); 145 146 WaitUntilAllExited(tids, arraysize(tids)); 147 148 // houdini keeps a thread pool, so we ignore the first pass while the 149 // pool fills, but then on the second pass require that the "pool" isn't 150 // actually an unbounded leak. https://issuetracker.google.com/37920774. 151 if (pass == 0) lc.Reset(); 152 } 153 } 154