Index: compiler-rt/lib/sanitizer_common/sanitizer_linux.h =================================================================== --- compiler-rt/lib/sanitizer_common/sanitizer_linux.h +++ compiler-rt/lib/sanitizer_common/sanitizer_linux.h @@ -76,9 +76,16 @@ public: explicit ThreadLister(int pid); ~ThreadLister(); - bool ListThreads(InternalMmapVector *threads); + enum Result { + Error, + Incomplete, + Ok, + }; + Result ListThreads(InternalMmapVector *threads); private: + bool IsAlive(int tid); + int pid_; int descriptor_ = -1; InternalMmapVector buffer_; Index: compiler-rt/lib/sanitizer_common/sanitizer_linux.cc =================================================================== --- compiler-rt/lib/sanitizer_common/sanitizer_linux.cc +++ compiler-rt/lib/sanitizer_common/sanitizer_linux.cc @@ -906,7 +906,6 @@ // ThreadLister implementation. ThreadLister::ThreadLister(int pid) : pid_(pid), buffer_(4096) { - buffer_.resize(buffer_.capacity()); char task_directory_path[80]; internal_snprintf(task_directory_path, sizeof(task_directory_path), "/proc/%d/task/", pid); @@ -916,30 +915,77 @@ } } -bool ThreadLister::ListThreads(InternalMmapVector *threads) { +ThreadLister::Result ThreadLister::ListThreads( + InternalMmapVector *threads) { if (internal_iserror(descriptor_)) - return false; + return Error; internal_lseek(descriptor_, 0, SEEK_SET); threads->clear(); - while (uptr read = internal_getdents(descriptor_, - (struct linux_dirent *)buffer_.data(), - buffer_.size())) { + uptr read_total = 0; + for (int i = 0;; ++i) { + // Resize to max capacity if it was downsized by IsAlive. + buffer_.resize(buffer_.capacity()); + uptr read = internal_getdents( + descriptor_, (struct linux_dirent *)buffer_.data(), buffer_.size()); + if (!read) { + if (read_total > buffer_.size() - 1024) { + // If proc_task_readdir returned because buffer was not enough and it + // stopped on thread which is termination it may not recover position. + // In this case we lose the rest and may be unable to recognize this as + // a short read. + buffer_.resize(buffer_.size() * 2); + return Incomplete; + } + if (i > 1) { + // With probably should not get here with check about. + // We can have short read because of small buffer (handled above), + // and because kernel tried to emit dying process (handled as Inode 1). + return Incomplete; + } + if (!threads->empty()) { + // Linux always stop enumeration after returning thread !pid_alive + // thread. See next_tid implementation in Linux. + if (!IsAlive(threads->back())) + return Incomplete; + } + return Ok; + } if (internal_iserror(read)) { Report("Can't read directory entries from /proc/%d/task.\n", pid_); - return false; + return Error; } + read_total += read; for (uptr begin = (uptr)buffer_.data(), end = begin + read; begin < end;) { struct linux_dirent *entry = (struct linux_dirent *)begin; begin += entry->d_reclen; - if (entry->d_ino && *entry->d_name >= '0' && *entry->d_name <= '9') { - // Found a valid tid. - threads->push_back(internal_atoll(entry->d_name)); + if (entry->d_ino == 1) { + // Inode 1 is for bad blocks and also can be a reason for early return. + // Should be emitted if kernel started tried to output dying thread. + // See proc_task_readdir implementation in Linux. + return Incomplete; } + if (entry->d_ino && *entry->d_name >= '0' && *entry->d_name <= '9') + threads->push_back(internal_atoll(entry->d_name)); } } - return true; +} + +bool ThreadLister::IsAlive(int tid) { + // /proc/%d/task/%d/status uses same call to detect alive threads as + // proc_task_readdir. See task_state implementation in Linux. + char path[80]; + internal_snprintf(path, sizeof(path), "/proc/%d/task/%d/status", pid_, tid); + if (!ReadFileToVector(path, &buffer_) || buffer_.empty()) + return false; + buffer_.push_back(0); + static const char kPrefix[] = "\nPPid:"; + const char *field = internal_strstr(buffer_.data(), kPrefix); + if (!field) + return false; + field += internal_strlen(kPrefix); + return (int)internal_atoll(field) != 0; } ThreadLister::~ThreadLister() { Index: compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc =================================================================== --- compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc +++ compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc @@ -210,25 +210,23 @@ bool ThreadSuspender::SuspendAllThreads() { ThreadLister thread_lister(pid_); bool added_threads; - bool first_iteration = true; InternalMmapVector threads; threads.reserve(128); do { - // Run through the directory entries once. added_threads = false; - if (!thread_lister.ListThreads(&threads)) { - ResumeAllThreads(); - return false; + switch (thread_lister.ListThreads(&threads)) { + case ThreadLister::Error: + ResumeAllThreads(); + return false; + case ThreadLister::Incomplete: + added_threads = true; + break; + case ThreadLister::Ok: + break; } for (int tid : threads) if (SuspendThread(tid)) added_threads = true; - if (first_iteration && !added_threads) { - // Detach threads and fail. - ResumeAllThreads(); - return false; - } - first_iteration = false; } while (added_threads); return true; }