Index: compiler-rt/lib/sanitizer_common/sanitizer_common.h =================================================================== --- compiler-rt/lib/sanitizer_common/sanitizer_common.h +++ compiler-rt/lib/sanitizer_common/sanitizer_common.h @@ -198,15 +198,6 @@ extern uptr stoptheworld_tracer_pid; extern uptr stoptheworld_tracer_ppid; -// Opens the file 'file_name" and reads up to 'max_len' bytes. -// The resulting buffer is mmaped and stored in '*buff'. -// The size of the mmaped region is stored in '*buff_size'. -// The total number of read bytes is stored in '*read_len'. -// Returns true if file was successfully opened and read. -bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, - uptr *read_len, uptr max_len = 1 << 26, - error_t *errno_p = nullptr); - bool IsAccessibleMemoryRange(uptr beg, uptr size); // Error report formatting. @@ -645,6 +636,21 @@ kModuleArchARM64 }; +// Opens the file 'file_name" and reads up to 'max_len' bytes. +// The resulting buffer is mmaped and stored in '*buff'. +// The size of the mmaped region is stored in '*buff_size'. +// The total number of read bytes is stored in '*read_len'. +// Returns true if file was successfully opened and read. +bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, + uptr *read_len, uptr max_len = 1 << 26, + error_t *errno_p = nullptr); +// Opens the file 'file_name" and reads up to 'max_len' bytes. +// The resulting buffer is mmaped and stored in '*buff'. +// Returns true if file was successfully opened and read. +bool ReadFileToBuffer(const char *file_name, + InternalMmapVectorNoCtor *buff, + uptr max_len = 1 << 26, error_t *errno_p = nullptr); + // When adding a new architecture, don't forget to also update // script/asan_symbolize.py and sanitizer_symbolizer_libcdep.cc. inline const char *ModuleArchToString(ModuleArch arch) { Index: compiler-rt/lib/sanitizer_common/sanitizer_file.cc =================================================================== --- compiler-rt/lib/sanitizer_common/sanitizer_file.cc +++ compiler-rt/lib/sanitizer_common/sanitizer_file.cc @@ -102,11 +102,12 @@ *read_len = 0; // The files we usually open are not seekable, so try different buffer sizes. for (uptr size = kMinFileLen; size <= max_len; size *= 2) { - fd_t fd = OpenFile(file_name, RdOnly, errno_p); - if (fd == kInvalidFd) return false; UnmapOrDie(*buff, *buff_size); *buff = (char*)MmapOrDie(size, __func__); *buff_size = size; + fd_t fd = OpenFile(file_name, RdOnly, errno_p); + if (fd == kInvalidFd) + return false; *read_len = 0; // Read up to one page at a time. bool reached_eof = false; @@ -114,6 +115,7 @@ uptr just_read; if (!ReadFromFile(fd, *buff + *read_len, PageSize, &just_read, errno_p)) { UnmapOrDie(*buff, *buff_size); + CloseFile(fd); return false; } if (just_read == 0) { @@ -129,6 +131,43 @@ return true; } +bool ReadFileToBuffer(const char *file_name, + InternalMmapVectorNoCtor *buff, uptr max_len, + error_t *errno_p) { + uptr PageSize = GetPageSizeCached(); + buff->clear(); + // The files we usually open are not seekable, so try different buffer sizes. + for (uptr size = Max(PageSize, buff->capacity()); size <= max_len; + size *= 2) { + buff->resize(size); + fd_t fd = OpenFile(file_name, RdOnly, errno_p); + if (fd == kInvalidFd) + return false; + uptr read_len = 0; + // Read up to one page at a time. + bool reached_eof = false; + while (read_len + PageSize <= buff->size()) { + uptr just_read; + if (!ReadFromFile(fd, buff->data() + read_len, PageSize, &just_read, + errno_p)) { + CloseFile(fd); + return false; + } + if (!just_read) { + reached_eof = true; + break; + } + read_len += just_read; + } + CloseFile(fd); + if (reached_eof) { // We've read the whole file. + buff->resize(read_len); + break; + } + } + return true; +} + static const char kPathSeparator = SANITIZER_WINDOWS ? ';' : ':'; char *FindPathToBinary(const char *name) { 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,58 @@ } } -bool ThreadLister::ListThreads(InternalMmapVector *threads) { - if (internal_iserror(descriptor_)) - return false; +ThreadLister::Result ThreadLister::ListThreads( + InternalMmapVector *threads) { + if (internal_iserror(descriptor_)) return Error; internal_lseek(descriptor_, 0, SEEK_SET); threads->clear(); - while (uptr read = internal_getdents(descriptor_, - (struct linux_dirent *)buffer_.data(), - buffer_.size())) { + 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 (!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; } 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. + // Inode 1 is for bad blocks and also can be a reason for early return. + // See proc_task_readdir implementation in Linux. + if (entry->d_ino == 1) + 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 (!ReadFileToBuffer(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,22 @@ 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; - } - for (int tid : threads) - if (SuspendThread(tid)) + switch (thread_lister.ListThreads(&threads)) { + case ThreadLister::Error: + ResumeAllThreads(); + return false; + case ThreadLister::Incomplete: added_threads = true; - if (first_iteration && !added_threads) { - // Detach threads and fail. - ResumeAllThreads(); - return false; + break; + case ThreadLister::Ok: + break; } - first_iteration = false; + for (int tid : threads) + if (SuspendThread(tid)) added_threads = true; } while (added_threads); return true; }