Skip to content
Merged
5 changes: 5 additions & 0 deletions lldb/source/Core/DataFileCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ bool DataFileCache::SetCachedData(llvm::StringRef key,
if (file_or_err) {
llvm::CachedFileStream *cfs = file_or_err->get();
cfs->OS->write((const char *)data.data(), data.size());
if (llvm::Error err = cfs->commit()) {
Log *log = GetLog(LLDBLog::Modules);
LLDB_LOG_ERROR(log, std::move(err),
"failed to commit to the cache for key: {0}");
}
return true;
} else {
Log *log = GetLog(LLDBLog::Modules);
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/Support/Caching.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class CachedFileStream {
CachedFileStream(std::unique_ptr<raw_pwrite_stream> OS,
std::string OSPath = "")
: OS(std::move(OS)), ObjectPathName(OSPath) {}
virtual Error commit() { return Error::success(); }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the expectation is that commit() should now always be called, should there be some error detection in the base class?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have moved the error detection to the base class.

std::unique_ptr<raw_pwrite_stream> OS;
std::string ObjectPathName;
virtual ~CachedFileStream() = default;
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/CGData/CodeGenData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,9 @@ void saveModuleForTwoRounds(const Module &TheModule, unsigned Task,

WriteBitcodeToFile(TheModule, *Stream->OS,
/*ShouldPreserveUseListOrder=*/true);

if (Error Err = Stream->commit())
report_fatal_error(std::move(Err));
}

std::unique_ptr<Module> loadModuleForTwoRounds(BitcodeModule &OrigModule,
Expand Down
9 changes: 9 additions & 0 deletions llvm/lib/Debuginfod/Debuginfod.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ class StreamedHTTPResponseHandler : public HTTPResponseHandler {
public:
StreamedHTTPResponseHandler(CreateStreamFn CreateStream, HTTPClient &Client)
: CreateStream(CreateStream), Client(Client) {}
Error commit();
virtual ~StreamedHTTPResponseHandler() = default;

Error handleBodyChunk(StringRef BodyChunk) override;
Expand All @@ -210,6 +211,12 @@ Error StreamedHTTPResponseHandler::handleBodyChunk(StringRef BodyChunk) {
return Error::success();
}

Error StreamedHTTPResponseHandler::commit() {
if (FileStream)
return FileStream->commit();
return Error::success();
}

// An over-accepting simplification of the HTTP RFC 7230 spec.
static bool isHeader(StringRef S) {
StringRef Name;
Expand Down Expand Up @@ -298,6 +305,8 @@ Expected<std::string> getCachedOrDownloadArtifact(
Error Err = Client.perform(Request, Handler);
if (Err)
return std::move(Err);
if (Err = Handler.commit())
return std::move(Err);

unsigned Code = Client.responseCode();
if (Code && Code != 200)
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/LTO/LTOBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,9 @@ static void codegen(const Config &Conf, TargetMachine *TM,

if (DwoOut)
DwoOut->keep();

if (Error Err = Stream->commit())
report_fatal_error(std::move(Err));
}

static void splitCodeGen(const Config &C, TargetMachine *TM,
Expand Down
34 changes: 23 additions & 11 deletions llvm/lib/Support/Caching.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ Expected<FileCache> llvm::localCache(const Twine &CacheNameRef,
sys::fs::TempFile TempFile;
std::string ModuleName;
unsigned Task;
bool Committed = false;

CacheStream(std::unique_ptr<raw_pwrite_stream> OS, AddBufferFn AddBuffer,
sys::fs::TempFile TempFile, std::string EntryPath,
Expand All @@ -88,9 +89,11 @@ Expected<FileCache> llvm::localCache(const Twine &CacheNameRef,
AddBuffer(std::move(AddBuffer)), TempFile(std::move(TempFile)),
ModuleName(ModuleName), Task(Task) {}

~CacheStream() {
// TODO: Manually commit rather than using non-trivial destructor,
// allowing to replace report_fatal_errors with a return Error.
Error commit() override {
if (Committed)
return createStringError(make_error_code(std::errc::invalid_argument),
Twine("CacheStream already committed."));
Committed = true;

// Make sure the stream is closed before committing it.
OS.reset();
Expand All @@ -100,10 +103,12 @@ Expected<FileCache> llvm::localCache(const Twine &CacheNameRef,
MemoryBuffer::getOpenFile(
sys::fs::convertFDToNativeFile(TempFile.FD), ObjectPathName,
/*FileSize=*/-1, /*RequiresNullTerminator=*/false);
if (!MBOrErr)
report_fatal_error(Twine("Failed to open new cache file ") +
TempFile.TmpName + ": " +
MBOrErr.getError().message() + "\n");
if (!MBOrErr) {
std::error_code EC = MBOrErr.getError();
return createStringError(EC, Twine("Failed to open new cache file ") +
TempFile.TmpName + ": " +
EC.message() + "\n");
}

// On POSIX systems, this will atomically replace the destination if
// it already exists. We try to emulate this on Windows, but this may
Expand All @@ -118,7 +123,10 @@ Expected<FileCache> llvm::localCache(const Twine &CacheNameRef,
E = handleErrors(std::move(E), [&](const ECError &E) -> Error {
std::error_code EC = E.convertToErrorCode();
if (EC != errc::permission_denied)
return errorCodeToError(EC);
return createStringError(
EC, Twine("Failed to rename temporary file ") +
TempFile.TmpName + " to " + ObjectPathName + ": " +
EC.message() + "\n");

auto MBCopy = MemoryBuffer::getMemBufferCopy((*MBOrErr)->getBuffer(),
ObjectPathName);
Expand All @@ -131,11 +139,15 @@ Expected<FileCache> llvm::localCache(const Twine &CacheNameRef,
});

if (E)
report_fatal_error(Twine("Failed to rename temporary file ") +
TempFile.TmpName + " to " + ObjectPathName + ": " +
toString(std::move(E)) + "\n");
return E;

AddBuffer(Task, ModuleName, std::move(*MBOrErr));
return Error::success();
}

~CacheStream() {
if (!Committed)
report_fatal_error("CacheStream was not committed.\n");
}
};

Expand Down
4 changes: 3 additions & 1 deletion llvm/tools/gold/gold-plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1119,7 +1119,9 @@ static std::vector<std::pair<SmallString<128>, bool>> runLTO() {

auto AddBuffer = [&](size_t Task, const Twine &moduleName,
std::unique_ptr<MemoryBuffer> MB) {
*AddStream(Task, moduleName)->OS << MB->getBuffer();
auto Stream = *AddStream(Task, ModuleName);
Stream->OS << MB->getBuffer();
check(Stream->commit(), "Failed to commit cache");
};

FileCache Cache;
Expand Down
4 changes: 3 additions & 1 deletion llvm/tools/llvm-lto2/llvm-lto2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,9 @@ static int run(int argc, char **argv) {

auto AddBuffer = [&](size_t Task, const Twine &ModuleName,
std::unique_ptr<MemoryBuffer> MB) {
*AddStream(Task, ModuleName)->OS << MB->getBuffer();
auto Stream = AddStream(Task, ModuleName);
*Stream->OS << MB->getBuffer();
check(Stream->commit(), "Failed to commit cache");
};

FileCache Cache;
Expand Down
1 change: 1 addition & 0 deletions llvm/unittests/Support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ add_llvm_unittest(SupportTests
BranchProbabilityTest.cpp
CachePruningTest.cpp
CrashRecoveryTest.cpp
Caching.cpp
Casting.cpp
CheckedArithmeticTest.cpp
Chrono.cpp
Expand Down
116 changes: 116 additions & 0 deletions llvm/unittests/Support/Caching.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//===- Caching.cpp --------------------------------------------------------===//
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you test the error when there is no commit?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes - I have added a test in my latest commit.

//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "llvm/Support/Caching.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"

using namespace llvm;

#define ASSERT_NO_ERROR(x) \
if (std::error_code ASSERT_NO_ERROR_ec = x) { \
SmallString<128> MessageStorage; \
raw_svector_ostream Message(MessageStorage); \
Message << #x ": did not return errc::success.\n" \
<< "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \
<< "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \
GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \
} else { \
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the empty else?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a standard defensive programming technique with multiline macros to cause a compile error when someone writes "ASSERT_NO_ERROR(...) else { ... }" by mistake.


char data[] = "some data";

TEST(Caching, Normal) {
SmallString<256> TempDir;
sys::path::system_temp_directory(true, TempDir);
SmallString<256> CacheDir;
sys::path::append(CacheDir, TempDir, "llvm_test_cache");

sys::fs::remove_directories(CacheDir.str());

std::unique_ptr<MemoryBuffer> CachedBuffer;
auto AddBuffer = [&CachedBuffer](unsigned Task, const Twine &ModuleName,
std::unique_ptr<MemoryBuffer> M) {
CachedBuffer = std::move(M);
};
auto CacheOrErr =
localCache("LLVMTestCache", "LLVMTest", CacheDir, AddBuffer);
ASSERT_TRUE(bool(CacheOrErr));

FileCache &Cache = *CacheOrErr;

{
auto AddStreamOrErr = Cache(1, "foo", "");
ASSERT_TRUE(bool(AddStreamOrErr));

AddStreamFn &AddStream = *AddStreamOrErr;
ASSERT_TRUE(AddStream);

auto FileOrErr = AddStream(1, "");
ASSERT_TRUE(bool(FileOrErr));

CachedFileStream *CFS = FileOrErr->get();
(*CFS->OS).write(data, sizeof(data));
ASSERT_THAT_ERROR(CFS->commit(), Succeeded());
}

{
auto AddStreamOrErr = Cache(1, "foo", "");
ASSERT_TRUE(bool(AddStreamOrErr));

AddStreamFn &AddStream = *AddStreamOrErr;
ASSERT_FALSE(AddStream);

ASSERT_TRUE(CachedBuffer->getBufferSize() == sizeof(data));
StringRef readData = CachedBuffer->getBuffer();
ASSERT_TRUE(memcmp(data, readData.data(), sizeof(data)) == 0);
}

ASSERT_NO_ERROR(sys::fs::remove_directories(CacheDir.str()));
}

TEST(Caching, WriteAfterCommit) {
SmallString<256> TempDir;
sys::path::system_temp_directory(true, TempDir);
SmallString<256> CacheDir;
sys::path::append(CacheDir, TempDir, "llvm_test_cache");

sys::fs::remove_directories(CacheDir.str());

std::unique_ptr<MemoryBuffer> CachedBuffer;
auto AddBuffer = [&CachedBuffer](unsigned Task, const Twine &ModuleName,
std::unique_ptr<MemoryBuffer> M) {
CachedBuffer = std::move(M);
};
auto CacheOrErr =
localCache("LLVMTestCache", "LLVMTest", CacheDir, AddBuffer);
ASSERT_TRUE(bool(CacheOrErr));

FileCache &Cache = *CacheOrErr;

auto AddStreamOrErr = Cache(1, "foo", "");
ASSERT_TRUE(bool(AddStreamOrErr));

AddStreamFn &AddStream = *AddStreamOrErr;
ASSERT_TRUE(AddStream);

auto FileOrErr = AddStream(1, "");
ASSERT_TRUE(bool(FileOrErr));

CachedFileStream *CFS = FileOrErr->get();
(*CFS->OS).write(data, sizeof(data));
ASSERT_THAT_ERROR(CFS->commit(), Succeeded());

EXPECT_DEATH({ (*CFS->OS).write(data, sizeof(data)); }, "")
<< "Write after commit did not cause abort";

ASSERT_NO_ERROR(sys::fs::remove_directories(CacheDir.str()));
}
Loading