reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
//===- DependencyScanningFilesystem.h - clang-scan-deps fs ===---*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_FILESYSTEM_H
#define LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_FILESYSTEM_H

#include "clang/Basic/LLVM.h"
#include "clang/Lex/PreprocessorExcludedConditionalDirectiveSkipMapping.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/VirtualFileSystem.h"
#include <mutex>

namespace clang {
namespace tooling {
namespace dependencies {

/// An in-memory representation of a file system entity that is of interest to
/// the dependency scanning filesystem.
///
/// It represents one of the following:
/// - an opened source file with minimized contents and a stat value.
/// - an opened source file with original contents and a stat value.
/// - a directory entry with its stat value.
/// - an error value to represent a file system error.
/// - a placeholder with an invalid stat indicating a not yet initialized entry.
class CachedFileSystemEntry {
public:
  /// Default constructor creates an entry with an invalid stat.
  CachedFileSystemEntry() : MaybeStat(llvm::vfs::Status()) {}

  CachedFileSystemEntry(std::error_code Error) : MaybeStat(std::move(Error)) {}

  /// Create an entry that represents an opened source file with minimized or
  /// original contents.
  ///
  /// The filesystem opens the file even for `stat` calls open to avoid the
  /// issues with stat + open of minimized files that might lead to a
  /// mismatching size of the file. If file is not minimized, the full file is
  /// read and copied into memory to ensure that it's not memory mapped to avoid
  /// running out of file descriptors.
  static CachedFileSystemEntry createFileEntry(StringRef Filename,
                                               llvm::vfs::FileSystem &FS,
                                               bool Minimize = true);

  /// Create an entry that represents a directory on the filesystem.
  static CachedFileSystemEntry createDirectoryEntry(llvm::vfs::Status &&Stat);

  /// \returns True if the entry is valid.
  bool isValid() const { return !MaybeStat || MaybeStat->isStatusKnown(); }

  /// \returns True if the current entry points to a directory.
  bool isDirectory() const { return MaybeStat && MaybeStat->isDirectory(); }

  /// \returns The error or the file's contents.
  llvm::ErrorOr<StringRef> getContents() const {
    if (!MaybeStat)
      return MaybeStat.getError();
    assert(!MaybeStat->isDirectory() && "not a file");
    assert(isValid() && "not initialized");
    return StringRef(Contents);
  }

  /// \returns The error or the status of the entry.
  llvm::ErrorOr<llvm::vfs::Status> getStatus() const {
    assert(isValid() && "not initialized");
    return MaybeStat;
  }

  /// \returns the name of the file.
  StringRef getName() const {
    assert(isValid() && "not initialized");
    return MaybeStat->getName();
  }

  /// Return the mapping between location -> distance that is used to speed up
  /// the block skipping in the preprocessor.
  const PreprocessorSkippedRangeMapping &getPPSkippedRangeMapping() const {
    return PPSkippedRangeMapping;
  }

  CachedFileSystemEntry(CachedFileSystemEntry &&) = default;
  CachedFileSystemEntry &operator=(CachedFileSystemEntry &&) = default;

  CachedFileSystemEntry(const CachedFileSystemEntry &) = delete;
  CachedFileSystemEntry &operator=(const CachedFileSystemEntry &) = delete;

private:
  llvm::ErrorOr<llvm::vfs::Status> MaybeStat;
  // Store the contents in a small string to allow a
  // move from the small string for the minimized contents.
  // Note: small size of 1 allows us to store an empty string with an implicit
  // null terminator without any allocations.
  llvm::SmallString<1> Contents;
  PreprocessorSkippedRangeMapping PPSkippedRangeMapping;
};

/// This class is a shared cache, that caches the 'stat' and 'open' calls to the
/// underlying real file system.
///
/// It is sharded based on the hash of the key to reduce the lock contention for
/// the worker threads.
class DependencyScanningFilesystemSharedCache {
public:
  struct SharedFileSystemEntry {
    std::mutex ValueLock;
    CachedFileSystemEntry Value;
  };

  DependencyScanningFilesystemSharedCache();

  /// Returns a cache entry for the corresponding key.
  ///
  /// A new cache entry is created if the key is not in the cache. This is a
  /// thread safe call.
  SharedFileSystemEntry &get(StringRef Key);

private:
  struct CacheShard {
    std::mutex CacheLock;
    llvm::StringMap<SharedFileSystemEntry, llvm::BumpPtrAllocator> Cache;
  };
  std::unique_ptr<CacheShard[]> CacheShards;
  unsigned NumShards;
};

/// A virtual file system optimized for the dependency discovery.
///
/// It is primarily designed to work with source files whose contents was was
/// preprocessed to remove any tokens that are unlikely to affect the dependency
/// computation.
///
/// This is not a thread safe VFS. A single instance is meant to be used only in
/// one thread. Multiple instances are allowed to service multiple threads
/// running in parallel.
class DependencyScanningWorkerFilesystem : public llvm::vfs::ProxyFileSystem {
public:
  DependencyScanningWorkerFilesystem(
      DependencyScanningFilesystemSharedCache &SharedCache,
      IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
      ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings)
      : ProxyFileSystem(std::move(FS)), SharedCache(SharedCache),
        PPSkipMappings(PPSkipMappings) {}

  llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override;
  llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
  openFileForRead(const Twine &Path) override;

  /// The set of files that should not be minimized.
  llvm::StringSet<> IgnoredFiles;

private:
  void setCachedEntry(StringRef Filename, const CachedFileSystemEntry *Entry) {
    bool IsInserted = Cache.try_emplace(Filename, Entry).second;
    (void)IsInserted;
    assert(IsInserted && "local cache is updated more than once");
  }

  const CachedFileSystemEntry *getCachedEntry(StringRef Filename) {
    auto It = Cache.find(Filename);
    return It == Cache.end() ? nullptr : It->getValue();
  }

  llvm::ErrorOr<const CachedFileSystemEntry *>
  getOrCreateFileSystemEntry(const StringRef Filename);

  DependencyScanningFilesystemSharedCache &SharedCache;
  /// The local cache is used by the worker thread to cache file system queries
  /// locally instead of querying the global cache every time.
  llvm::StringMap<const CachedFileSystemEntry *, llvm::BumpPtrAllocator> Cache;
  /// The optional mapping structure which records information about the
  /// excluded conditional directive skip mappings that are used by the
  /// currently active preprocessor.
  ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings;
};

} // end namespace dependencies
} // end namespace tooling
} // end namespace clang

#endif // LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_FILESYSTEM_H