1311116Sdim//===-Caching.cpp - LLVM Link Time Optimizer Cache Handling ---------------===// 2311116Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6311116Sdim// 7311116Sdim//===----------------------------------------------------------------------===// 8311116Sdim// 9311116Sdim// This file implements the Caching for ThinLTO. 10311116Sdim// 11311116Sdim//===----------------------------------------------------------------------===// 12311116Sdim 13311116Sdim#include "llvm/LTO/Caching.h" 14311116Sdim#include "llvm/ADT/StringExtras.h" 15321369Sdim#include "llvm/Support/Errc.h" 16311116Sdim#include "llvm/Support/MemoryBuffer.h" 17311116Sdim#include "llvm/Support/Path.h" 18327952Sdim#include "llvm/Support/Process.h" 19311116Sdim#include "llvm/Support/raw_ostream.h" 20311116Sdim 21341825Sdim#if !defined(_MSC_VER) && !defined(__MINGW32__) 22341825Sdim#include <unistd.h> 23341825Sdim#else 24341825Sdim#include <io.h> 25341825Sdim#endif 26341825Sdim 27311116Sdimusing namespace llvm; 28311116Sdimusing namespace llvm::lto; 29311116Sdim 30321369SdimExpected<NativeObjectCache> lto::localCache(StringRef CacheDirectoryPath, 31321369Sdim AddBufferFn AddBuffer) { 32321369Sdim if (std::error_code EC = sys::fs::create_directories(CacheDirectoryPath)) 33321369Sdim return errorCodeToError(EC); 34311116Sdim 35311116Sdim return [=](unsigned Task, StringRef Key) -> AddStreamFn { 36321369Sdim // This choice of file name allows the cache to be pruned (see pruneCache() 37321369Sdim // in include/llvm/Support/CachePruning.h). 38321369Sdim SmallString<64> EntryPath; 39321369Sdim sys::path::append(EntryPath, CacheDirectoryPath, "llvmcache-" + Key); 40311116Sdim // First, see if we have a cache hit. 41341825Sdim SmallString<64> ResultPath; 42353358Sdim Expected<sys::fs::file_t> FDOrErr = sys::fs::openNativeFileForRead( 43353358Sdim Twine(EntryPath), sys::fs::OF_UpdateAtime, &ResultPath); 44353358Sdim std::error_code EC; 45353358Sdim if (FDOrErr) { 46341825Sdim ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = 47353358Sdim MemoryBuffer::getOpenFile(*FDOrErr, EntryPath, 48353358Sdim /*FileSize=*/-1, 49353358Sdim /*RequiresNullTerminator=*/false); 50353358Sdim sys::fs::closeFile(*FDOrErr); 51341825Sdim if (MBOrErr) { 52341825Sdim AddBuffer(Task, std::move(*MBOrErr)); 53341825Sdim return AddStreamFn(); 54341825Sdim } 55341825Sdim EC = MBOrErr.getError(); 56353358Sdim } else { 57353358Sdim EC = errorToErrorCode(FDOrErr.takeError()); 58311116Sdim } 59311116Sdim 60341825Sdim // On Windows we can fail to open a cache file with a permission denied 61341825Sdim // error. This generally means that another process has requested to delete 62341825Sdim // the file while it is still open, but it could also mean that another 63341825Sdim // process has opened the file without the sharing permissions we need. 64341825Sdim // Since the file is probably being deleted we handle it in the same way as 65341825Sdim // if the file did not exist at all. 66341825Sdim if (EC != errc::no_such_file_or_directory && EC != errc::permission_denied) 67321369Sdim report_fatal_error(Twine("Failed to open cache file ") + EntryPath + 68341825Sdim ": " + EC.message() + "\n"); 69321369Sdim 70311116Sdim // This native object stream is responsible for commiting the resulting 71321369Sdim // file to the cache and calling AddBuffer to add it to the link. 72311116Sdim struct CacheStream : NativeObjectStream { 73321369Sdim AddBufferFn AddBuffer; 74327952Sdim sys::fs::TempFile TempFile; 75311116Sdim std::string EntryPath; 76311116Sdim unsigned Task; 77311116Sdim 78321369Sdim CacheStream(std::unique_ptr<raw_pwrite_stream> OS, AddBufferFn AddBuffer, 79327952Sdim sys::fs::TempFile TempFile, std::string EntryPath, 80311116Sdim unsigned Task) 81321369Sdim : NativeObjectStream(std::move(OS)), AddBuffer(std::move(AddBuffer)), 82327952Sdim TempFile(std::move(TempFile)), EntryPath(std::move(EntryPath)), 83327952Sdim Task(Task) {} 84311116Sdim 85311116Sdim ~CacheStream() { 86327952Sdim // Make sure the stream is closed before committing it. 87311116Sdim OS.reset(); 88321369Sdim 89327952Sdim // Open the file first to avoid racing with a cache pruner. 90321369Sdim ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = 91353358Sdim MemoryBuffer::getOpenFile( 92353358Sdim sys::fs::convertFDToNativeFile(TempFile.FD), TempFile.TmpName, 93353358Sdim /*FileSize=*/-1, /*RequiresNullTerminator=*/false); 94321369Sdim if (!MBOrErr) 95327952Sdim report_fatal_error(Twine("Failed to open new cache file ") + 96327952Sdim TempFile.TmpName + ": " + 97327952Sdim MBOrErr.getError().message() + "\n"); 98327952Sdim 99327952Sdim // On POSIX systems, this will atomically replace the destination if 100327952Sdim // it already exists. We try to emulate this on Windows, but this may 101327952Sdim // fail with a permission denied error (for example, if the destination 102327952Sdim // is currently opened by another process that does not give us the 103327952Sdim // sharing permissions we need). Since the existing file should be 104327952Sdim // semantically equivalent to the one we are trying to write, we give 105327952Sdim // AddBuffer a copy of the bytes we wrote in that case. We do this 106327952Sdim // instead of just using the existing file, because the pruner might 107327952Sdim // delete the file before we get a chance to use it. 108327952Sdim Error E = TempFile.keep(EntryPath); 109327952Sdim E = handleErrors(std::move(E), [&](const ECError &E) -> Error { 110327952Sdim std::error_code EC = E.convertToErrorCode(); 111327952Sdim if (EC != errc::permission_denied) 112327952Sdim return errorCodeToError(EC); 113327952Sdim 114327952Sdim auto MBCopy = MemoryBuffer::getMemBufferCopy((*MBOrErr)->getBuffer(), 115327952Sdim EntryPath); 116327952Sdim MBOrErr = std::move(MBCopy); 117327952Sdim 118327952Sdim // FIXME: should we consume the discard error? 119327952Sdim consumeError(TempFile.discard()); 120327952Sdim 121327952Sdim return Error::success(); 122327952Sdim }); 123327952Sdim 124327952Sdim if (E) 125327952Sdim report_fatal_error(Twine("Failed to rename temporary file ") + 126327952Sdim TempFile.TmpName + " to " + EntryPath + ": " + 127327952Sdim toString(std::move(E)) + "\n"); 128327952Sdim 129341825Sdim AddBuffer(Task, std::move(*MBOrErr)); 130311116Sdim } 131311116Sdim }; 132311116Sdim 133311116Sdim return [=](size_t Task) -> std::unique_ptr<NativeObjectStream> { 134311116Sdim // Write to a temporary to avoid race condition 135327952Sdim SmallString<64> TempFilenameModel; 136321369Sdim sys::path::append(TempFilenameModel, CacheDirectoryPath, "Thin-%%%%%%.tmp.o"); 137327952Sdim Expected<sys::fs::TempFile> Temp = sys::fs::TempFile::create( 138327952Sdim TempFilenameModel, sys::fs::owner_read | sys::fs::owner_write); 139327952Sdim if (!Temp) { 140327952Sdim errs() << "Error: " << toString(Temp.takeError()) << "\n"; 141311116Sdim report_fatal_error("ThinLTO: Can't get a temporary file"); 142311116Sdim } 143311116Sdim 144311116Sdim // This CacheStream will move the temporary file into the cache when done. 145360784Sdim return std::make_unique<CacheStream>( 146360784Sdim std::make_unique<raw_fd_ostream>(Temp->FD, /* ShouldClose */ false), 147327952Sdim AddBuffer, std::move(*Temp), EntryPath.str(), Task); 148311116Sdim }; 149311116Sdim }; 150311116Sdim} 151