Path: blob/main/contrib/llvm-project/lld/Common/Filesystem.cpp
34879 views
//===- Filesystem.cpp -----------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file contains a few utility functions to handle files.9//10//===----------------------------------------------------------------------===//1112#include "lld/Common/Filesystem.h"13#include "lld/Common/ErrorHandler.h"14#include "llvm/Config/llvm-config.h"15#include "llvm/Support/FileOutputBuffer.h"16#include "llvm/Support/FileSystem.h"17#include "llvm/Support/Parallel.h"18#include "llvm/Support/Path.h"19#include "llvm/Support/TimeProfiler.h"20#if LLVM_ON_UNIX21#include <unistd.h>22#endif23#include <thread>2425using namespace llvm;26using namespace lld;2728// Removes a given file asynchronously. This is a performance hack,29// so remove this when operating systems are improved.30//31// On Linux (and probably on other Unix-like systems), unlink(2) is a32// noticeably slow system call. As of 2016, unlink takes 25033// milliseconds to remove a 1 GB file on ext4 filesystem on my machine.34//35// To create a new result file, we first remove existing file. So, if36// you repeatedly link a 1 GB program in a regular compile-link-debug37// cycle, every cycle wastes 250 milliseconds only to remove a file.38// Since LLD can link a 1 GB binary in about 5 seconds, that waste39// actually counts.40//41// This function spawns a background thread to remove the file.42// The calling thread returns almost immediately.43void lld::unlinkAsync(StringRef path) {44if (!sys::fs::exists(path) || !sys::fs::is_regular_file(path))45return;4647// Removing a file is async on windows.48#if defined(_WIN32)49// On Windows co-operative programs can be expected to open LLD's50// output in FILE_SHARE_DELETE mode. This allows us to delete the51// file (by moving it to a temporary filename and then deleting52// it) so that we can link another output file that overwrites53// the existing file, even if the current file is in use.54//55// This is done on a best effort basis - we do not error if the56// operation fails. The consequence is merely that the user57// experiences an inconvenient work-flow.58//59// The code here allows LLD to work on all versions of Windows.60// However, at Windows 10 1903 it seems that the behavior of61// Windows has changed, so that we could simply delete the output62// file. This code should be simplified once support for older63// versions of Windows is dropped.64//65// Warning: It seems that the WINVER and _WIN32_WINNT preprocessor66// defines affect the behavior of the Windows versions of the calls67// we are using here. If this code stops working this is worth68// bearing in mind.69SmallString<128> tmpName;70if (!sys::fs::createUniqueFile(path + "%%%%%%%%.tmp", tmpName)) {71if (!sys::fs::rename(path, tmpName))72path = tmpName;73else74sys::fs::remove(tmpName);75}76sys::fs::remove(path);77#else78if (parallel::strategy.ThreadsRequested == 1)79return;8081// We cannot just remove path from a different thread because we are now going82// to create path as a new file.83// Instead we open the file and unlink it on this thread. The unlink is fast84// since the open fd guarantees that it is not removing the last reference.85int fd;86std::error_code ec = sys::fs::openFileForRead(path, fd);87sys::fs::remove(path);8889if (ec)90return;9192// close and therefore remove TempPath in background.93std::mutex m;94std::condition_variable cv;95bool started = false;96std::thread([&, fd] {97{98std::lock_guard<std::mutex> l(m);99started = true;100cv.notify_all();101}102::close(fd);103}).detach();104105// GLIBC 2.26 and earlier have race condition that crashes an entire process106// if the main thread calls exit(2) while other thread is starting up.107std::unique_lock<std::mutex> l(m);108cv.wait(l, [&] { return started; });109#endif110}111112// Simulate file creation to see if Path is writable.113//114// Determining whether a file is writable or not is amazingly hard,115// and after all the only reliable way of doing that is to actually116// create a file. But we don't want to do that in this function117// because LLD shouldn't update any file if it will end in a failure.118// We also don't want to reimplement heuristics to determine if a119// file is writable. So we'll let FileOutputBuffer do the work.120//121// FileOutputBuffer doesn't touch a destination file until commit()122// is called. We use that class without calling commit() to predict123// if the given file is writable.124std::error_code lld::tryCreateFile(StringRef path) {125llvm::TimeTraceScope timeScope("Try create output file");126if (path.empty())127return std::error_code();128if (path == "-")129return std::error_code();130return errorToErrorCode(FileOutputBuffer::create(path, 1).takeError());131}132133// Creates an empty file to and returns a raw_fd_ostream to write to it.134std::unique_ptr<raw_fd_ostream> lld::openFile(StringRef file) {135std::error_code ec;136auto ret =137std::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::OF_None);138if (ec) {139error("cannot open " + file + ": " + ec.message());140return nullptr;141}142return ret;143}144145// The merged bitcode after LTO is large. Try opening a file stream that146// supports reading, seeking and writing. Such a file allows BitcodeWriter to147// flush buffered data to reduce memory consumption. If this fails, open a file148// stream that supports only write.149std::unique_ptr<raw_fd_ostream> lld::openLTOOutputFile(StringRef file) {150std::error_code ec;151std::unique_ptr<raw_fd_ostream> fs =152std::make_unique<raw_fd_stream>(file, ec);153if (!ec)154return fs;155return openFile(file);156}157158159