LCOV - code coverage report
Current view: top level - lib/Support - FileOutputBuffer.cpp (source / functions) Hit Total Coverage
Test: llvm-toolchain.info Lines: 50 68 73.5 %
Date: 2018-10-20 13:21:21 Functions: 11 14 78.6 %
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             :   OnDiskBuffer(StringRef Path, fs::TempFile Temp,
      38             :                std::unique_ptr<fs::mapped_file_region> Buf)
      39        5327 :       : FileOutputBuffer(Path), Buffer(std::move(Buf)), Temp(std::move(Temp)) {}
      40             : 
      41      674774 :   uint8_t *getBufferStart() const override { return (uint8_t *)Buffer->data(); }
      42             : 
      43         201 :   uint8_t *getBufferEnd() const override {
      44         201 :     return (uint8_t *)Buffer->data() + Buffer->size();
      45             :   }
      46             : 
      47         956 :   size_t getBufferSize() const override { return Buffer->size(); }
      48             : 
      49        3097 :   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        6194 :     return Temp.keep(FinalPath);
      55             :   }
      56             : 
      57       10652 :   ~OnDiskBuffer() override {
      58             :     // Close the mapping before deleting the temp file, so that the removal
      59             :     // succeeds.
      60        5326 :     Buffer.reset();
      61       10652 :     consumeError(Temp.discard());
      62       10652 :   }
      63        5326 : 
      64             :   void discard() override {
      65             :     // Delete the temp file if it still was open, but keeping the mapping
      66             :     // active.
      67             :     consumeError(Temp.discard());
      68        5326 :   }
      69        5326 : 
      70             : private:
      71             :   std::unique_ptr<fs::mapped_file_region> Buffer;
      72        5326 :   fs::TempFile Temp;
      73       10652 : };
      74        5326 : 
      75             : // A FileOutputBuffer which keeps data in memory and writes to the final
      76           0 : // output file on commit(). This is used only when we cannot use OnDiskBuffer.
      77             : class InMemoryBuffer : public FileOutputBuffer {
      78             : public:
      79           0 :   InMemoryBuffer(StringRef Path, MemoryBlock Buf, unsigned Mode)
      80           0 :       : FileOutputBuffer(Path), Buffer(Buf), Mode(Mode) {}
      81             : 
      82             :   uint8_t *getBufferStart() const override { return (uint8_t *)Buffer.base(); }
      83             : 
      84             :   uint8_t *getBufferEnd() const override {
      85             :     return (uint8_t *)Buffer.base() + Buffer.size();
      86             :   }
      87             : 
      88             :   size_t getBufferSize() const override { return Buffer.size(); }
      89           0 : 
      90             :   Error commit() override {
      91             :     using namespace sys::fs;
      92           0 :     int FD;
      93             :     std::error_code EC;
      94         776 :     if (auto EC =
      95             :             openFileForWrite(FinalPath, FD, CD_CreateAlways, OF_None, Mode))
      96           0 :       return errorCodeToError(EC);
      97           0 :     raw_fd_ostream OS(FD, /*shouldClose=*/true, /*unbuffered=*/true);
      98             :     OS << StringRef((const char *)Buffer.base(), Buffer.size());
      99             :     return Error::success();
     100           0 :   }
     101             : 
     102         108 : private:
     103             :   OwningMemoryBlock Buffer;
     104             :   unsigned Mode;
     105             : };
     106         108 : } // namespace
     107         108 : 
     108           0 : static Expected<std::unique_ptr<InMemoryBuffer>>
     109         216 : createInMemoryBuffer(StringRef Path, size_t Size, unsigned Mode) {
     110         108 :   std::error_code EC;
     111             :   MemoryBlock MB = Memory::allocateMappedMemory(
     112             :       Size, nullptr, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC);
     113             :   if (EC)
     114             :     return errorCodeToError(EC);
     115             :   return llvm::make_unique<InMemoryBuffer>(Path, MB, Mode);
     116             : }
     117             : 
     118             : static Expected<std::unique_ptr<OnDiskBuffer>>
     119             : createOnDiskBuffer(StringRef Path, size_t Size, bool InitExisting,
     120             :                    unsigned Mode) {
     121         345 :   Expected<fs::TempFile> FileOrErr =
     122             :       fs::TempFile::create(Path + ".tmp%%%%%%%", Mode);
     123             :   if (!FileOrErr)
     124         345 :     return FileOrErr.takeError();
     125         345 :   fs::TempFile File = std::move(*FileOrErr);
     126           0 : 
     127         345 :   if (InitExisting) {
     128             :     if (auto EC = sys::fs::copy_file(Path, File.FD))
     129             :       return errorCodeToError(EC);
     130             :   } else {
     131        5337 : #ifndef _WIN32
     132             :     // On Windows, CreateFileMapping (the mmap function on Windows)
     133             :     // automatically extends the underlying file. We don't need to
     134       10674 :     // extend the file beforehand. _chsize (ftruncate on Windows) is
     135        5337 :     // pretty slow just like it writes specified amount of bytes,
     136             :     // so we should avoid calling that function.
     137       10654 :     if (auto EC = fs::resize_file(File.FD, Size)) {
     138             :       consumeError(File.discard());
     139        5327 :       return errorCodeToError(EC);
     140           2 :     }
     141           0 : #endif
     142             :   }
     143             : 
     144             :   // Mmap it.
     145             :   std::error_code EC;
     146             :   auto MappedFile = llvm::make_unique<fs::mapped_file_region>(
     147             :       File.FD, fs::mapped_file_region::readwrite, Size, 0, EC);
     148             :   if (EC) {
     149        5326 :     consumeError(File.discard());
     150           0 :     return errorCodeToError(EC);
     151           0 :   }
     152             :   return llvm::make_unique<OnDiskBuffer>(Path, std::move(File),
     153             :                                          std::move(MappedFile));
     154             : }
     155             : 
     156             : // Create an instance of FileOutputBuffer.
     157             : Expected<std::unique_ptr<FileOutputBuffer>>
     158             : FileOutputBuffer::create(StringRef Path, size_t Size, unsigned Flags) {
     159       10654 :   unsigned Mode = fs::all_read | fs::all_write;
     160        5327 :   if (Flags & F_executable)
     161           0 :     Mode |= fs::all_exe;
     162           0 : 
     163             :   fs::file_status Stat;
     164        5327 :   fs::status(Path, Stat);
     165             : 
     166             :   if ((Flags & F_modify) && Size == size_t(-1)) {
     167             :     if (Stat.type() == fs::file_type::regular_file)
     168             :       Size = Stat.getSize();
     169             :     else if (Stat.type() == fs::file_type::file_not_found)
     170        5684 :       return errorCodeToError(errc::no_such_file_or_directory);
     171             :     else
     172        5684 :       return errorCodeToError(errc::invalid_argument);
     173             :   }
     174             : 
     175        5684 :   // Usually, we want to create OnDiskBuffer to create a temporary file in
     176        5684 :   // the same directory as the destination file and atomically replaces it
     177             :   // by rename(2).
     178        5684 :   //
     179           1 :   // However, if the destination file is a special file, we don't want to
     180           1 :   // use rename (e.g. we don't want to replace /dev/null with a regular
     181           0 :   // file.) If that's the case, we create an in-memory buffer, open the
     182           0 :   // destination file and write to it on commit().
     183             :   switch (Stat.type()) {
     184           0 :   case fs::file_type::directory_file:
     185             :     return errorCodeToError(errc::is_a_directory);
     186             :   case fs::file_type::regular_file:
     187             :   case fs::file_type::file_not_found:
     188             :   case fs::file_type::status_error:
     189             :     return createOnDiskBuffer(Path, Size, !!(Flags & F_modify), Mode);
     190             :   default:
     191             :     return createInMemoryBuffer(Path, Size, Mode);
     192             :   }
     193             : }

Generated by: LCOV version 1.13