LCOV - code coverage report
Current view: top level - lib/Support - FileOutputBuffer.cpp (source / functions) Hit Total Coverage
Test: llvm-toolchain.info Lines: 42 51 82.4 %
Date: 2018-06-17 00:07:59 Functions: 14 16 87.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //===- FileOutputBuffer.cpp - File Output Buffer ----------------*- C++ -*-===//
       2             : //
       3             : //                     The LLVM Compiler Infrastructure
       4             : //
       5             : // This file is distributed under the University of Illinois Open Source
       6             : // License. See LICENSE.TXT for details.
       7             : //
       8             : //===----------------------------------------------------------------------===//
       9             : //
      10             : // Utility for creating a in-memory buffer that will be written to a file.
      11             : //
      12             : //===----------------------------------------------------------------------===//
      13             : 
      14             : #include "llvm/Support/FileOutputBuffer.h"
      15             : #include "llvm/ADT/STLExtras.h"
      16             : #include "llvm/ADT/SmallString.h"
      17             : #include "llvm/Support/Errc.h"
      18             : #include "llvm/Support/Memory.h"
      19             : #include "llvm/Support/Path.h"
      20             : #include <system_error>
      21             : 
      22             : #if !defined(_MSC_VER) && !defined(__MINGW32__)
      23             : #include <unistd.h>
      24             : #else
      25             : #include <io.h>
      26             : #endif
      27             : 
      28             : using namespace llvm;
      29             : using namespace llvm::sys;
      30             : 
      31             : namespace {
      32             : // A FileOutputBuffer which creates a temporary file in the same directory
      33             : // as the final output file. The final output file is atomically replaced
      34             : // with the temporary file on commit().
      35             : class OnDiskBuffer : public FileOutputBuffer {
      36             : public:
      37        5066 :   OnDiskBuffer(StringRef Path, fs::TempFile Temp,
      38             :                std::unique_ptr<fs::mapped_file_region> Buf)
      39       15198 :       : FileOutputBuffer(Path), Buffer(std::move(Buf)), Temp(std::move(Temp)) {}
      40             : 
      41       18898 :   uint8_t *getBufferStart() const override { return (uint8_t *)Buffer->data(); }
      42             : 
      43          92 :   uint8_t *getBufferEnd() const override {
      44         184 :     return (uint8_t *)Buffer->data() + Buffer->size();
      45             :   }
      46             : 
      47         770 :   size_t getBufferSize() const override { return Buffer->size(); }
      48             : 
      49        2827 :   Error commit() override {
      50             :     // Unmap buffer, letting OS flush dirty pages to file on disk.
      51             :     Buffer.reset();
      52             : 
      53             :     // Atomically replace the existing file with the new one.
      54        5654 :     return Temp.keep(FinalPath);
      55             :   }
      56             : 
      57       20260 :   ~OnDiskBuffer() override {
      58             :     // Close the mapping before deleting the temp file, so that the removal
      59             :     // succeeds.
      60        5065 :     Buffer.reset();
      61       10130 :     consumeError(Temp.discard());
      62       10130 :   }
      63             : 
      64             : private:
      65             :   std::unique_ptr<fs::mapped_file_region> Buffer;
      66             :   fs::TempFile Temp;
      67             : };
      68             : 
      69             : // A FileOutputBuffer which keeps data in memory and writes to the final
      70             : // output file on commit(). This is used only when we cannot use OnDiskBuffer.
      71          39 : class InMemoryBuffer : public FileOutputBuffer {
      72             : public:
      73             :   InMemoryBuffer(StringRef Path, MemoryBlock Buf, unsigned Mode)
      74          39 :       : FileOutputBuffer(Path), Buffer(Buf), Mode(Mode) {}
      75             : 
      76          34 :   uint8_t *getBufferStart() const override { return (uint8_t *)Buffer.base(); }
      77             : 
      78           0 :   uint8_t *getBufferEnd() const override {
      79           0 :     return (uint8_t *)Buffer.base() + Buffer.size();
      80             :   }
      81             : 
      82           0 :   size_t getBufferSize() const override { return Buffer.size(); }
      83             : 
      84           5 :   Error commit() override {
      85             :     using namespace sys::fs;
      86             :     int FD;
      87             :     std::error_code EC;
      88           5 :     if (auto EC =
      89          10 :             openFileForWrite(FinalPath, FD, CD_CreateAlways, OF_None, Mode))
      90           0 :       return errorCodeToError(EC);
      91          10 :     raw_fd_ostream OS(FD, /*shouldClose=*/true, /*unbuffered=*/true);
      92           5 :     OS << StringRef((const char *)Buffer.base(), Buffer.size());
      93             :     return Error::success();
      94             :   }
      95             : 
      96             : private:
      97             :   OwningMemoryBlock Buffer;
      98             :   unsigned Mode;
      99             : };
     100             : } // namespace
     101             : 
     102             : static Expected<std::unique_ptr<InMemoryBuffer>>
     103          13 : createInMemoryBuffer(StringRef Path, size_t Size, unsigned Mode) {
     104             :   std::error_code EC;
     105             :   MemoryBlock MB = Memory::allocateMappedMemory(
     106          13 :       Size, nullptr, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC);
     107          13 :   if (EC)
     108           0 :     return errorCodeToError(EC);
     109          26 :   return llvm::make_unique<InMemoryBuffer>(Path, MB, Mode);
     110             : }
     111             : 
     112             : static Expected<std::unique_ptr<OnDiskBuffer>>
     113        5076 : createOnDiskBuffer(StringRef Path, size_t Size, unsigned Mode) {
     114             :   Expected<fs::TempFile> FileOrErr =
     115       10152 :       fs::TempFile::create(Path + ".tmp%%%%%%%", Mode);
     116        5076 :   if (!FileOrErr)
     117             :     return FileOrErr.takeError();
     118       10132 :   fs::TempFile File = std::move(*FileOrErr);
     119             : 
     120             : #ifndef _WIN32
     121             :   // On Windows, CreateFileMapping (the mmap function on Windows)
     122             :   // automatically extends the underlying file. We don't need to
     123             :   // extend the file beforehand. _chsize (ftruncate on Windows) is
     124             :   // pretty slow just like it writes specified amount of bytes,
     125             :   // so we should avoid calling that function.
     126        5066 :   if (auto EC = fs::resize_file(File.FD, Size)) {
     127           0 :     consumeError(File.discard());
     128           0 :     return errorCodeToError(EC);
     129             :   }
     130             : #endif
     131             : 
     132             :   // Mmap it.
     133             :   std::error_code EC;
     134             :   auto MappedFile = llvm::make_unique<fs::mapped_file_region>(
     135       10132 :       File.FD, fs::mapped_file_region::readwrite, Size, 0, EC);
     136        5066 :   if (EC) {
     137           0 :     consumeError(File.discard());
     138           0 :     return errorCodeToError(EC);
     139             :   }
     140       10132 :   return llvm::make_unique<OnDiskBuffer>(Path, std::move(File),
     141             :                                          std::move(MappedFile));
     142             : }
     143             : 
     144             : // Create an instance of FileOutputBuffer.
     145             : Expected<std::unique_ptr<FileOutputBuffer>>
     146        5091 : FileOutputBuffer::create(StringRef Path, size_t Size, unsigned Flags) {
     147             :   unsigned Mode = fs::all_read | fs::all_write;
     148        5091 :   if (Flags & F_executable)
     149             :     Mode |= fs::all_exe;
     150             : 
     151        5091 :   fs::file_status Stat;
     152        5091 :   fs::status(Path, Stat);
     153             : 
     154             :   // Usually, we want to create OnDiskBuffer to create a temporary file in
     155             :   // the same directory as the destination file and atomically replaces it
     156             :   // by rename(2).
     157             :   //
     158             :   // However, if the destination file is a special file, we don't want to
     159             :   // use rename (e.g. we don't want to replace /dev/null with a regular
     160             :   // file.) If that's the case, we create an in-memory buffer, open the
     161             :   // destination file and write to it on commit().
     162        5091 :   switch (Stat.type()) {
     163             :   case fs::file_type::directory_file:
     164           4 :     return errorCodeToError(errc::is_a_directory);
     165        5076 :   case fs::file_type::regular_file:
     166             :   case fs::file_type::file_not_found:
     167             :   case fs::file_type::status_error:
     168       10152 :     return createOnDiskBuffer(Path, Size, Mode);
     169          13 :   default:
     170          26 :     return createInMemoryBuffer(Path, Size, Mode);
     171             :   }
     172             : }

Generated by: LCOV version 1.13