[sanitizer] Cleanup ReadFileToVector and ReadFileToBuffer

Summary:
Added unit-test.
Fixed behavior of max_len argument.
Call read syscall with all available buffer, not just a page.

Reviewers: eugenis

Subscribers: kubamracek, mgorny, llvm-commits

Differential Revision: https://reviews.llvm.org/D46618

llvm-svn: 334130
This commit is contained in:
Vitaly Buka 2018-06-06 20:53:43 +00:00
parent 69685524aa
commit 4ef9117b49
5 changed files with 109 additions and 45 deletions

View File

@ -639,18 +639,22 @@ enum ModuleArch {
// 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 ReadFileToVector(const char *file_name,
InternalMmapVectorNoCtor<char> *buff,
uptr max_len = 1 << 26, error_t *errno_p = nullptr);
// Opens the file 'file_name" and reads up to 'max_len' bytes.
// This function is less I/O efficient than ReadFileToVector as it may reread
// file multiple times to avoid mmap during read attempts. It's used to read
// procmap, so short reads with mmap in between can produce inconsistent result.
// 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<char> *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.

View File

@ -95,34 +95,40 @@ void ReportFile::SetReportPath(const char *path) {
bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
uptr *read_len, uptr max_len, error_t *errno_p) {
uptr PageSize = GetPageSizeCached();
uptr kMinFileLen = PageSize;
*buff = nullptr;
*buff_size = 0;
*read_len = 0;
if (!max_len)
return true;
uptr PageSize = GetPageSizeCached();
uptr kMinFileLen = Min(PageSize, max_len);
// The files we usually open are not seekable, so try different buffer sizes.
for (uptr size = kMinFileLen; size <= max_len; size *= 2) {
for (uptr size = kMinFileLen;; size = Min(size * 2, max_len)) {
UnmapOrDie(*buff, *buff_size);
*buff = (char*)MmapOrDie(size, __func__);
*buff_size = size;
fd_t fd = OpenFile(file_name, RdOnly, errno_p);
if (fd == kInvalidFd)
if (fd == kInvalidFd) {
UnmapOrDie(*buff, *buff_size);
return false;
}
*read_len = 0;
// Read up to one page at a time.
bool reached_eof = false;
while (*read_len + PageSize <= size) {
while (*read_len < size) {
uptr just_read;
if (!ReadFromFile(fd, *buff + *read_len, PageSize, &just_read, errno_p)) {
if (!ReadFromFile(fd, *buff + *read_len, size - *read_len, &just_read,
errno_p)) {
UnmapOrDie(*buff, *buff_size);
CloseFile(fd);
return false;
}
if (just_read == 0) {
*read_len += just_read;
if (just_read == 0 || *read_len == max_len) {
reached_eof = true;
break;
}
*read_len += just_read;
}
CloseFile(fd);
if (reached_eof) // We've read the whole file.
@ -131,40 +137,34 @@ bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
return true;
}
bool ReadFileToBuffer(const char *file_name,
bool ReadFileToVector(const char *file_name,
InternalMmapVectorNoCtor<char> *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)
if (!max_len)
return true;
uptr PageSize = GetPageSizeCached();
fd_t fd = OpenFile(file_name, RdOnly, errno_p);
if (fd == kInvalidFd)
return false;
uptr read_len = 0;
while (read_len < max_len) {
if (read_len >= buff->size())
buff->resize(Min(Max(PageSize, read_len * 2), max_len));
CHECK_LT(read_len, buff->size());
CHECK_LE(buff->size(), max_len);
uptr just_read;
if (!ReadFromFile(fd, buff->data() + read_len, buff->size() - read_len,
&just_read, errno_p)) {
CloseFile(fd);
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);
read_len += just_read;
if (!just_read)
break;
}
}
CloseFile(fd);
buff->resize(read_len);
return true;
}

View File

@ -975,7 +975,7 @@ bool ThreadLister::IsAlive(int tid) {
// 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())
if (!ReadFileToVector(path, &buffer_) || buffer_.empty())
return false;
buffer_.push_back(0);
static const char kPrefix[] = "\nPPid:";

View File

@ -54,7 +54,8 @@ set(SANITIZER_TEST_CFLAGS_COMMON
-fno-rtti
-O2
-Werror=sign-compare
-Wno-non-virtual-dtor)
-Wno-non-virtual-dtor
-Wno-gnu-zero-variadic-macro-arguments)
if(MSVC)
# Disable exceptions on Windows until they work reliably.

View File

@ -9,6 +9,7 @@
// Tests for sanitizer_libc.h.
//===----------------------------------------------------------------------===//
#include <algorithm>
#include <fstream>
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_file.h"
@ -82,8 +83,8 @@ static void temp_file_name(char *buf, size_t bufsize, const char *prefix) {
// on Android already.
tmpdir = GetEnv("EXTERNAL_STORAGE");
#endif
u32 uid = GetUid();
internal_snprintf(buf, bufsize, "%s/%s%d", tmpdir, prefix, uid);
internal_snprintf(buf, bufsize, "%s/%sXXXXXX", tmpdir, prefix);
ASSERT_TRUE(mktemp(buf));
#endif
}
@ -151,6 +152,64 @@ TEST(SanitizerCommon, FileOps) {
#endif
}
class SanitizerCommonFileTest : public ::testing::TestWithParam<uptr> {
void SetUp() override {
data_.resize(GetParam());
std::generate(data_.begin(), data_.end(), [] {
return rand() % 256; // NOLINT
});
temp_file_name(file_name_, sizeof(file_name_),
"sanitizer_common.ReadFile.tmp.");
std::ofstream f(file_name_, std::ios::out | std::ios::binary);
if (!data_.empty())
f.write(data_.data(), data_.size());
}
void TearDown() override { internal_unlink(file_name_); }
protected:
char file_name_[256];
std::vector<char> data_;
};
TEST_P(SanitizerCommonFileTest, ReadFileToBuffer) {
char *buff;
uptr size;
uptr len;
EXPECT_TRUE(ReadFileToBuffer(file_name_, &buff, &len, &size));
EXPECT_EQ(data_, std::vector<char>(buff, buff + size));
UnmapOrDie(buff, len);
}
TEST_P(SanitizerCommonFileTest, ReadFileToBufferHalf) {
char *buff;
uptr size;
uptr len;
data_.resize(data_.size() / 2);
EXPECT_TRUE(ReadFileToBuffer(file_name_, &buff, &len, &size, data_.size()));
EXPECT_EQ(data_, std::vector<char>(buff, buff + size));
UnmapOrDie(buff, len);
}
TEST_P(SanitizerCommonFileTest, ReadFileToVector) {
InternalMmapVector<char> buff;
EXPECT_TRUE(ReadFileToVector(file_name_, &buff));
EXPECT_EQ(data_, std::vector<char>(buff.begin(), buff.end()));
}
TEST_P(SanitizerCommonFileTest, ReadFileToVectorHalf) {
InternalMmapVector<char> buff;
data_.resize(data_.size() / 2);
EXPECT_TRUE(ReadFileToVector(file_name_, &buff, data_.size()));
EXPECT_EQ(data_, std::vector<char>(buff.begin(), buff.end()));
}
INSTANTIATE_TEST_CASE_P(FileSizes, SanitizerCommonFileTest,
::testing::Values(0, 1, 7, 13, 32, 4096, 4097, 1048575,
1048576, 1048577));
static const size_t kStrlcpyBufSize = 8;
void test_internal_strlcpy(char *dbuf, const char *sbuf) {
uptr retval = 0;