Index: include/lldb/Host/File.h =================================================================== --- include/lldb/Host/File.h +++ include/lldb/Host/File.h @@ -265,51 +265,6 @@ off_t SeekFromStart(off_t offset, Error *error_ptr = nullptr); //------------------------------------------------------------------ - /// Seek to an offset relative to the current file position. - /// - /// NOTE: This function is NOT thread safe, other threads that - /// access this object might also change the current file position. - /// For thread safe reads and writes see the following functions: - /// @see File::Read (void *, size_t, off_t &) - /// @see File::Write (const void *, size_t, off_t &) - /// - /// @param[in] offset - /// The offset to seek to within the file relative to the - /// current file position. - /// - /// @param[in] error_ptr - /// A pointer to a lldb_private::Error object that will be - /// filled in if non-nullptr. - /// - /// @return - /// The resulting seek offset, or -1 on error. - //------------------------------------------------------------------ - off_t SeekFromCurrent(off_t offset, Error *error_ptr = nullptr); - - //------------------------------------------------------------------ - /// Seek to an offset relative to the end of the file. - /// - /// NOTE: This function is NOT thread safe, other threads that - /// access this object might also change the current file position. - /// For thread safe reads and writes see the following functions: - /// @see File::Read (void *, size_t, off_t &) - /// @see File::Write (const void *, size_t, off_t &) - /// - /// @param[in,out] offset - /// The offset to seek to within the file relative to the - /// end of the file which gets filled in with the resulting - /// absolute file offset. - /// - /// @param[in] error_ptr - /// A pointer to a lldb_private::Error object that will be - /// filled in if non-nullptr. - /// - /// @return - /// The resulting seek offset, or -1 on error. - //------------------------------------------------------------------ - off_t SeekFromEnd(off_t offset, Error *error_ptr = nullptr); - - //------------------------------------------------------------------ /// Read bytes from a file from the specified file offset. /// /// NOTE: This function is thread safe in that clients manager their @@ -403,15 +358,6 @@ Error Flush(); //------------------------------------------------------------------ - /// Sync to disk. - /// - /// @return - /// An error object that indicates success or the reason for - /// failure. - //------------------------------------------------------------------ - Error Sync(); - - //------------------------------------------------------------------ /// Get the permissions for a this file. /// /// @return @@ -472,6 +418,9 @@ void CalculateInteractiveAndTerminal(); + Error DoRead(void *dst, size_t &num_bytes, off_t *offset); + + Error DoWrite(const void *src, size_t &num_bytes, off_t *offset); //------------------------------------------------------------------ // Member variables //------------------------------------------------------------------ Index: source/Host/common/File.cpp =================================================================== --- source/Host/common/File.cpp +++ source/Host/common/File.cpp @@ -367,58 +367,6 @@ return result; } -off_t File::SeekFromCurrent(off_t offset, Error *error_ptr) { - off_t result = -1; - if (DescriptorIsValid()) { - result = ::lseek(m_descriptor, offset, SEEK_CUR); - - if (error_ptr) { - if (result == -1) - error_ptr->SetErrorToErrno(); - else - error_ptr->Clear(); - } - } else if (StreamIsValid()) { - result = ::fseek(m_stream, offset, SEEK_CUR); - - if (error_ptr) { - if (result == -1) - error_ptr->SetErrorToErrno(); - else - error_ptr->Clear(); - } - } else if (error_ptr) { - error_ptr->SetErrorString("invalid file handle"); - } - return result; -} - -off_t File::SeekFromEnd(off_t offset, Error *error_ptr) { - off_t result = -1; - if (DescriptorIsValid()) { - result = ::lseek(m_descriptor, offset, SEEK_END); - - if (error_ptr) { - if (result == -1) - error_ptr->SetErrorToErrno(); - else - error_ptr->Clear(); - } - } else if (StreamIsValid()) { - result = ::fseek(m_stream, offset, SEEK_END); - - if (error_ptr) { - if (result == -1) - error_ptr->SetErrorToErrno(); - else - error_ptr->Clear(); - } - } else if (error_ptr) { - error_ptr->SetErrorString("invalid file handle"); - } - return result; -} - Error File::Flush() { Error error; if (StreamIsValid()) { @@ -435,218 +383,104 @@ return error; } -Error File::Sync() { - Error error; - if (DescriptorIsValid()) { -#ifdef _WIN32 - int err = FlushFileBuffers((HANDLE)_get_osfhandle(m_descriptor)); - if (err == 0) - error.SetErrorToGenericError(); -#else - int err = 0; - do { - err = ::fsync(m_descriptor); - } while (err == -1 && errno == EINTR); - - if (err == -1) - error.SetErrorToErrno(); -#endif - } else { - error.SetErrorString("invalid file handle"); - } - return error; -} - #if defined(__APPLE__) // Darwin kernels only can read/write <= INT_MAX bytes -#define MAX_READ_SIZE INT_MAX -#define MAX_WRITE_SIZE INT_MAX +constexpr size_t MAX_READ_SIZE = INT_MAX; +constexpr size_t MAX_WRITE_SIZE = INT_MAX; +#elif defined(LLVM_ON_WIN32) +constexpr size_t MAX_READ_SIZE = ULONG_MAX; +constexpr size_t MAX_WRITE_SIZE = ULONG_MAX; +#else +constexpr size_t MAX_READ_SIZE = SIZE_MAX; +constexpr size_t MAX_WRITE_SIZE = SIZE_MAX; #endif Error File::Read(void *buf, size_t &num_bytes) { - Error error; - -#if defined(MAX_READ_SIZE) - if (num_bytes > MAX_READ_SIZE) { - uint8_t *p = (uint8_t *)buf; - size_t bytes_left = num_bytes; - // Init the num_bytes read to zero - num_bytes = 0; - - while (bytes_left > 0) { - size_t curr_num_bytes; - if (bytes_left > MAX_READ_SIZE) - curr_num_bytes = MAX_READ_SIZE; - else - curr_num_bytes = bytes_left; - - error = Read(p + num_bytes, curr_num_bytes); - - // Update how many bytes were read - num_bytes += curr_num_bytes; - if (bytes_left < curr_num_bytes) - bytes_left = 0; - else - bytes_left -= curr_num_bytes; - - if (error.Fail()) - break; - } - return error; - } -#endif - - ssize_t bytes_read = -1; - if (DescriptorIsValid()) { - do { - bytes_read = ::read(m_descriptor, buf, num_bytes); - } while (bytes_read < 0 && errno == EINTR); - - if (bytes_read == -1) { - error.SetErrorToErrno(); - num_bytes = 0; - } else - num_bytes = bytes_read; - } else if (StreamIsValid()) { - bytes_read = ::fread(buf, 1, num_bytes, m_stream); - - if (bytes_read == 0) { - if (::feof(m_stream)) - error.SetErrorString("feof"); - else if (::ferror(m_stream)) - error.SetErrorString("ferror"); - num_bytes = 0; - } else - num_bytes = bytes_read; - } else { - num_bytes = 0; - error.SetErrorString("invalid file handle"); - } - return error; + return DoRead(buf, num_bytes, nullptr); } Error File::Write(const void *buf, size_t &num_bytes) { - Error error; - -#if defined(MAX_WRITE_SIZE) - if (num_bytes > MAX_WRITE_SIZE) { - const uint8_t *p = (const uint8_t *)buf; - size_t bytes_left = num_bytes; - // Init the num_bytes written to zero - num_bytes = 0; - - while (bytes_left > 0) { - size_t curr_num_bytes; - if (bytes_left > MAX_WRITE_SIZE) - curr_num_bytes = MAX_WRITE_SIZE; - else - curr_num_bytes = bytes_left; - - error = Write(p + num_bytes, curr_num_bytes); - - // Update how many bytes were read - num_bytes += curr_num_bytes; - if (bytes_left < curr_num_bytes) - bytes_left = 0; - else - bytes_left -= curr_num_bytes; - - if (error.Fail()) - break; - } - return error; - } -#endif - - ssize_t bytes_written = -1; - if (DescriptorIsValid()) { - do { - bytes_written = ::write(m_descriptor, buf, num_bytes); - } while (bytes_written < 0 && errno == EINTR); - - if (bytes_written == -1) { - error.SetErrorToErrno(); - num_bytes = 0; - } else - num_bytes = bytes_written; - } else if (StreamIsValid()) { - bytes_written = ::fwrite(buf, 1, num_bytes, m_stream); - - if (bytes_written == 0) { - if (::feof(m_stream)) - error.SetErrorString("feof"); - else if (::ferror(m_stream)) - error.SetErrorString("ferror"); - num_bytes = 0; - } else - num_bytes = bytes_written; + return DoWrite(buf, num_bytes, nullptr); +} - } else { - num_bytes = 0; - error.SetErrorString("invalid file handle"); - } +Error File::Read(void *buf, size_t &num_bytes, off_t &offset) { + return DoRead(buf, num_bytes, &offset); +} - return error; +Error File::Write(const void *buf, size_t &num_bytes, off_t &offset) { + return DoWrite(buf, num_bytes, &offset); } -Error File::Read(void *buf, size_t &num_bytes, off_t &offset) { +Error File::DoRead(void *buf, size_t &num_bytes, off_t *offset) { Error error; - -#if defined(MAX_READ_SIZE) - if (num_bytes > MAX_READ_SIZE) { - uint8_t *p = (uint8_t *)buf; - size_t bytes_left = num_bytes; - // Init the num_bytes read to zero + size_t bytes_left = num_bytes; + int fd = GetDescriptor(); + if (fd == kInvalidDescriptor) { num_bytes = 0; - - while (bytes_left > 0) { - size_t curr_num_bytes; - if (bytes_left > MAX_READ_SIZE) - curr_num_bytes = MAX_READ_SIZE; - else - curr_num_bytes = bytes_left; - - error = Read(p + num_bytes, curr_num_bytes, offset); - - // Update how many bytes were read - num_bytes += curr_num_bytes; - if (bytes_left < curr_num_bytes) - bytes_left = 0; - else - bytes_left -= curr_num_bytes; - - if (error.Fail()) - break; - } - return error; + return Error("invalid file handle"); } -#endif + uint8_t *dest = static_cast(buf); #ifndef _WIN32 - int fd = GetDescriptor(); - if (fd != kInvalidDescriptor) { - ssize_t bytes_read = -1; + ssize_t bytes_read = -1; + size_t bytes_to_read = std::min(MAX_READ_SIZE, bytes_left); + while (bytes_left != 0 && bytes_read != 0) { do { - bytes_read = ::pread(fd, buf, num_bytes, offset); + if (offset) + bytes_read = ::pread(fd, dest, bytes_to_read, *offset); + else + bytes_read = ::read(fd, dest, bytes_to_read); } while (bytes_read < 0 && errno == EINTR); if (bytes_read < 0) { num_bytes = 0; error.SetErrorToErrno(); - } else { - offset += bytes_read; - num_bytes = bytes_read; + return error; } - } else { - num_bytes = 0; - error.SetErrorString("invalid file handle"); + + dest += bytes_read; + if (offset) + *offset += bytes_read; + bytes_left -= bytes_read; + if (size_t(bytes_read) != MAX_READ_SIZE) + break; } + num_bytes -= bytes_left; #else - long cur = ::lseek(m_descriptor, 0, SEEK_CUR); - SeekFromStart(offset); - error = Read(buf, num_bytes); - if (!error.Fail()) - SeekFromStart(cur); + HANDLE H = reinterpret_cast(_get_osfhandle(fd)); + if (!H || H == INVALID_HANDLE_VALUE) + return Error("invalid file handle"); + + while (bytes_left != 0) { + OVERLAPPED OV = {}; + if (offset) { + LARGE_INTEGER large_off; + large_off.QuadPart = *offset; + + OV.Offset = large_off.LowPart; + OV.OffsetHigh = large_off.HighPart; + } + + DWORD req_size = static_cast(std::min(MAX_READ_SIZE, bytes_left)); + DWORD actual_size = 0; + BOOL result = + ::ReadFile(H, dest, req_size, &actual_size, offset ? &OV : nullptr); + if (actual_size == 0) { + // EOF was reached. + break; + } + if (!result) { + error.SetError(GetLastError(), eErrorTypeWin32); + return error; + } + dest += actual_size; + bytes_left -= actual_size; + if(offset) + *offset += actual_size; + if (actual_size != MAX_READ_SIZE) + break; + } + num_bytes -= bytes_left; #endif return error; } @@ -695,68 +529,76 @@ return error; } -Error File::Write(const void *buf, size_t &num_bytes, off_t &offset) { +Error File::DoWrite(const void *buf, size_t &num_bytes, off_t *offset) { Error error; - -#if defined(MAX_WRITE_SIZE) - if (num_bytes > MAX_WRITE_SIZE) { - const uint8_t *p = (const uint8_t *)buf; - size_t bytes_left = num_bytes; - // Init the num_bytes written to zero + size_t bytes_left = num_bytes; + int fd = GetDescriptor(); + if (fd == kInvalidDescriptor) { num_bytes = 0; - - while (bytes_left > 0) { - size_t curr_num_bytes; - if (bytes_left > MAX_WRITE_SIZE) - curr_num_bytes = MAX_WRITE_SIZE; - else - curr_num_bytes = bytes_left; - - error = Write(p + num_bytes, curr_num_bytes, offset); - - // Update how many bytes were read - num_bytes += curr_num_bytes; - if (bytes_left < curr_num_bytes) - bytes_left = 0; - else - bytes_left -= curr_num_bytes; - - if (error.Fail()) - break; - } - return error; + return Error("invalid file handle"); } -#endif - int fd = GetDescriptor(); - if (fd != kInvalidDescriptor) { + const uint8_t *src = static_cast(buf); #ifndef _WIN32 - ssize_t bytes_written = -1; + ssize_t bytes_written = -1; + size_t bytes_to_write = std::min(MAX_WRITE_SIZE, bytes_left); + while (bytes_left != 0) { do { - bytes_written = ::pwrite(m_descriptor, buf, num_bytes, offset); + if (offset) + bytes_written = ::pwrite(fd, src, bytes_to_write, *offset); + else + bytes_written = ::write(fd, src, bytes_to_write); } while (bytes_written < 0 && errno == EINTR); if (bytes_written < 0) { num_bytes = 0; error.SetErrorToErrno(); - } else { - offset += bytes_written; - num_bytes = bytes_written; + return error; } -#else - long cur = ::lseek(m_descriptor, 0, SEEK_CUR); - error = Write(buf, num_bytes); - long after = ::lseek(m_descriptor, 0, SEEK_CUR); - if (!error.Fail()) - SeekFromStart(cur); - - offset = after; -#endif - } else { - num_bytes = 0; - error.SetErrorString("invalid file handle"); + src += bytes_written; + if (offset) + *offset += bytes_written; + bytes_left -= bytes_written; + if (size_t(bytes_written) != MAX_WRITE_SIZE) + break; } +#else + HANDLE H = reinterpret_cast(_get_osfhandle(fd)); + if (!H || H == INVALID_HANDLE_VALUE) + return Error("invalid file handle"); + + while (bytes_left != 0) { + DWORD req_size = static_cast(std::min(MAX_WRITE_SIZE, bytes_left)); + DWORD actual_size = 0; + BOOL result; + if (!offset) + result = ::WriteFile(H, src, req_size, &actual_size, nullptr); + else { + OVERLAPPED OV = {}; + if (m_options & File::eOpenOptionAppend) + OV.Offset = OV.OffsetHigh = 0xFFFFFFFF; + else { + LARGE_INTEGER large_off; + large_off.QuadPart = *offset; + + OV.Offset = large_off.LowPart; + OV.OffsetHigh = large_off.HighPart; + } + result = ::WriteFile(H, src, req_size, &actual_size, &OV); + } + if (!result) { + error.SetError(GetLastError(), eErrorTypeWin32); + return error; + } + src += actual_size; + bytes_left -= actual_size; + if (offset) + *offset += actual_size; + if (actual_size != MAX_WRITE_SIZE) + break; + } +#endif return error; } Index: unittests/Host/CMakeLists.txt =================================================================== --- unittests/Host/CMakeLists.txt +++ unittests/Host/CMakeLists.txt @@ -1,7 +1,13 @@ add_lldb_unittest(HostTests FileSpecTest.cpp FileSystemTest.cpp + FileTest.cpp SocketAddressTest.cpp SocketTest.cpp SymbolsTest.cpp ) + +set(test_inputs + Read.test) + +add_unittest_inputs(HostTests "${test_inputs}") Index: unittests/Host/FileTest.cpp =================================================================== --- /dev/null +++ unittests/Host/FileTest.cpp @@ -0,0 +1,167 @@ +//===-- FileTest.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "lldb/Core/Error.h" +#include "lldb/Host/File.h" +#include "lldb/Host/Pipe.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" + +extern const char *TestMainArgv0; + +using namespace lldb_private; + +class FileTest : public testing::Test { +public: + void SetUp() override { + llvm::StringRef exe_folder = llvm::sys::path::parent_path(TestMainArgv0); + llvm::SmallString<128> output_folder = exe_folder; + llvm::sys::path::append(output_folder, "Output"); + ASSERT_FALSE(llvm::sys::fs::create_directory(output_folder, true)); + + output_file = output_folder; + llvm::StringRef test_name = + testing::UnitTest::GetInstance()->current_test_info()->name(); + llvm::sys::path::append(output_file, test_name + ".test"); + + input_file = exe_folder; + llvm::sys::path::append(input_file, "Inputs", "Read.test"); + } + +protected: + llvm::SmallString<128> input_file; + llvm::SmallString<128> output_file; +}; + +TEST_F(FileTest, Write) { + File f(output_file.c_str(), + File::eOpenOptionWrite | File::eOpenOptionRead | + File::eOpenOptionTruncate | File::eOpenOptionCanCreate); + ASSERT_TRUE(f.IsValid()); + + char buf[100] = "Write"; + size_t len = strlen(buf); + ASSERT_TRUE(f.Write(buf, len).Success()); + EXPECT_EQ(strlen(buf), len); + ASSERT_TRUE(f.Write(buf, len).Success()); + EXPECT_EQ(strlen(buf), len); + + len = sizeof buf; + off_t offset = 0; + ASSERT_TRUE(f.Read(buf, len, offset).Success()); + EXPECT_EQ(10, offset); + EXPECT_EQ(10u, len); + EXPECT_STREQ("WriteWrite", buf); +} + +TEST_F(FileTest, WriteOffset) { + File f(output_file.c_str(), + File::eOpenOptionWrite | File::eOpenOptionRead | + File::eOpenOptionTruncate | File::eOpenOptionCanCreate); + ASSERT_TRUE(f.IsValid()); + + char buf[100] = "Write"; + size_t len = strlen(buf); + off_t offset = 0; + ASSERT_TRUE(f.Write(buf, len, offset).Success()); + EXPECT_EQ(off_t(strlen(buf)), offset); + EXPECT_EQ(strlen(buf), len); + offset = 1; + ASSERT_TRUE(f.Write(buf, len, offset).Success()); + EXPECT_EQ(off_t(strlen(buf)+1), offset); + EXPECT_EQ(strlen(buf), len); + + len = sizeof buf; + offset = 0; + ASSERT_TRUE(f.Read(buf, len, offset).Success()); + EXPECT_EQ(6, offset); + EXPECT_EQ(6u, len); + EXPECT_STREQ("WWrite", buf); +} + +TEST_F(FileTest, WriteAppend) { + File f(output_file.c_str(), + File::eOpenOptionWrite | File::eOpenOptionRead | + File::eOpenOptionTruncate | File::eOpenOptionAppend | + File::eOpenOptionCanCreate); + ASSERT_TRUE(f.IsValid()); + + char buf[100] = "Write"; + size_t len = strlen(buf); + off_t offset = 0; + ASSERT_TRUE(f.Write(buf, len, offset).Success()); + EXPECT_EQ(off_t(strlen(buf)), offset); + EXPECT_EQ(strlen(buf), len); + offset = 1; + ASSERT_TRUE(f.Write(buf, len, offset).Success()); + EXPECT_EQ(off_t(strlen(buf)+1), offset); + EXPECT_EQ(strlen(buf), len); + + len = sizeof buf; + offset = 0; + ASSERT_TRUE(f.Read(buf, len, offset).Success()); + EXPECT_EQ(10, offset); + EXPECT_EQ(10u, len); + EXPECT_STREQ("WriteWrite", buf); +} + +TEST_F(FileTest, Read) { + File f(input_file.c_str(), File::eOpenOptionRead); + ASSERT_TRUE(f.IsValid()); + + char buf[100] = ""; + size_t len = 2; + ASSERT_TRUE(f.Read(buf, len).Success()); + EXPECT_EQ(2u, len); + EXPECT_STREQ("Re", buf); + len = sizeof buf; + ASSERT_TRUE(f.Read(buf, len).Success()); + EXPECT_EQ(8u, len); + EXPECT_STREQ("ad\nRead\n", buf); +} + +TEST_F(FileTest, ReadOffset) { + File f(input_file.c_str(), File::eOpenOptionRead); + ASSERT_TRUE(f.IsValid()); + + char buf[100] = ""; + size_t len = 2; + off_t offset = 0; + ASSERT_TRUE(f.Read(buf, len, offset).Success()); + EXPECT_EQ(2u, len); + EXPECT_EQ(2, offset); + EXPECT_STREQ("Re", buf); + len = sizeof buf; + offset = 5; + ASSERT_TRUE(f.Read(buf, len, offset).Success()); + EXPECT_EQ(5u, len); + EXPECT_EQ(10, offset); + EXPECT_STREQ("Read\n", buf); +} + +#if LLVM_ON_UNIX +#define REQUIRES_UNIX(Test) Test +#else +#define REQUIRES_UNIX(Test) DISABLED_##Test +#endif + +TEST_F(FileTest, ReadPipe) { + Pipe pipe; + ASSERT_TRUE(pipe.CreateNew(false).Success()); + File f(pipe.GetReadFileDescriptor(), false); + size_t bytes_written; + ASSERT_TRUE(pipe.Write("X", 1, bytes_written).Success()); + char buf[10] = ""; + size_t len = sizeof buf; + ASSERT_TRUE(f.Read(buf, len).Success()); + EXPECT_EQ(1u, len); +} Index: unittests/Host/Inputs/Read.test =================================================================== --- /dev/null +++ unittests/Host/Inputs/Read.test @@ -0,0 +1,2 @@ +Read +Read