Coverage Report

Created: 2022-01-18 06:27

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h
Line
Count
Source (jump to first uncovered line)
1
//===- DependencyScanningFilesystem.h - clang-scan-deps fs ===---*- C++ -*-===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#ifndef LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNINGFILESYSTEM_H
10
#define LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNINGFILESYSTEM_H
11
12
#include "clang/Basic/LLVM.h"
13
#include "clang/Lex/PreprocessorExcludedConditionalDirectiveSkipMapping.h"
14
#include "llvm/ADT/StringMap.h"
15
#include "llvm/ADT/StringSet.h"
16
#include "llvm/Support/Allocator.h"
17
#include "llvm/Support/ErrorOr.h"
18
#include "llvm/Support/VirtualFileSystem.h"
19
#include <mutex>
20
21
namespace clang {
22
namespace tooling {
23
namespace dependencies {
24
25
/// An in-memory representation of a file system entity that is of interest to
26
/// the dependency scanning filesystem.
27
///
28
/// It represents one of the following:
29
/// - opened file with original contents and a stat value,
30
/// - opened file with original contents, minimized contents and a stat value,
31
/// - directory entry with its stat value,
32
/// - filesystem error,
33
/// - uninitialized entry with unknown status.
34
class CachedFileSystemEntry {
35
public:
36
  /// Creates an uninitialized entry.
37
  CachedFileSystemEntry()
38
4.26k
      : MaybeStat(llvm::vfs::Status()), MinimizedContentsAccess(nullptr) {}
39
40
  /// Initialize the cached file system entry.
41
  void init(llvm::ErrorOr<llvm::vfs::Status> &&MaybeStatus, StringRef Filename,
42
            llvm::vfs::FileSystem &FS);
43
44
  /// Initialize the entry as file with minimized or original contents.
45
  ///
46
  /// The filesystem opens the file even for `stat` calls open to avoid the
47
  /// issues with stat + open of minimized files that might lead to a
48
  /// mismatching size of the file.
49
  llvm::ErrorOr<llvm::vfs::Status> initFile(StringRef Filename,
50
                                            llvm::vfs::FileSystem &FS);
51
52
  /// Minimize contents of the file.
53
  void minimizeFile();
54
55
  /// \returns True if the entry is initialized.
56
14.5k
  bool isInitialized() const {
57
14.5k
    return !MaybeStat || 
MaybeStat->isStatusKnown()14.4k
;
58
14.5k
  }
59
60
  /// \returns True if the current entry points to a directory.
61
700
  bool isDirectory() const { return MaybeStat && 
MaybeStat->isDirectory()618
; }
62
63
  /// \returns The error or the file's original contents.
64
787
  llvm::ErrorOr<StringRef> getOriginalContents() const {
65
787
    if (!MaybeStat)
66
0
      return MaybeStat.getError();
67
787
    assert(!MaybeStat->isDirectory() && "not a file");
68
0
    assert(isInitialized() && "not initialized");
69
0
    assert(OriginalContents && "not read");
70
0
    return OriginalContents->getBuffer();
71
787
  }
72
73
  /// \returns The error or the file's minimized contents.
74
3.35k
  llvm::ErrorOr<StringRef> getMinimizedContents() const {
75
3.35k
    if (!MaybeStat)
76
82
      return MaybeStat.getError();
77
3.27k
    assert(!MaybeStat->isDirectory() && "not a file");
78
0
    assert(isInitialized() && "not initialized");
79
0
    llvm::MemoryBuffer *Buffer = MinimizedContentsAccess.load();
80
3.27k
    assert(Buffer && "not minimized");
81
0
    return Buffer->getBuffer();
82
3.35k
  }
83
84
  /// \returns True if this entry represents a file that can be read.
85
4.37k
  bool isReadable() const { return MaybeStat && 
!MaybeStat->isDirectory()4.24k
; }
86
87
  /// \returns True if this cached entry needs to be updated.
88
4.37k
  bool needsUpdate(bool ShouldBeMinimized) const {
89
4.37k
    return isReadable() && 
needsMinimization(ShouldBeMinimized)3.44k
;
90
4.37k
  }
91
92
  /// \returns True if the contents of this entry need to be minimized.
93
3.44k
  bool needsMinimization(bool ShouldBeMinimized) const {
94
3.44k
    return ShouldBeMinimized && 
!MinimizedContentsAccess.load()2.98k
;
95
3.44k
  }
96
97
  /// \returns The error or the status of the entry.
98
4.29k
  llvm::ErrorOr<llvm::vfs::Status> getStatus() const {
99
4.29k
    assert(isInitialized() && "not initialized");
100
0
    return MaybeStat;
101
4.29k
  }
102
103
  /// \returns the name of the file.
104
614
  StringRef getName() const {
105
614
    assert(isInitialized() && "not initialized");
106
0
    return MaybeStat->getName();
107
614
  }
108
109
  /// Return the mapping between location -> distance that is used to speed up
110
  /// the block skipping in the preprocessor.
111
296
  const PreprocessorSkippedRangeMapping &getPPSkippedRangeMapping() const {
112
296
    return PPSkippedRangeMapping;
113
296
  }
114
115
private:
116
  llvm::ErrorOr<llvm::vfs::Status> MaybeStat;
117
  std::unique_ptr<llvm::MemoryBuffer> OriginalContents;
118
119
  /// Owning storage for the minimized file contents.
120
  std::unique_ptr<llvm::MemoryBuffer> MinimizedContentsStorage;
121
  /// Atomic view of the minimized file contents.
122
  /// This prevents data races when multiple threads call `needsMinimization`.
123
  std::atomic<llvm::MemoryBuffer *> MinimizedContentsAccess;
124
125
  PreprocessorSkippedRangeMapping PPSkippedRangeMapping;
126
};
127
128
/// This class is a shared cache, that caches the 'stat' and 'open' calls to the
129
/// underlying real file system. It distinguishes between minimized and original
130
/// files.
131
///
132
/// It is sharded based on the hash of the key to reduce the lock contention for
133
/// the worker threads.
134
class DependencyScanningFilesystemSharedCache {
135
public:
136
  struct SharedFileSystemEntry {
137
    std::mutex ValueLock;
138
    CachedFileSystemEntry Value;
139
  };
140
141
  DependencyScanningFilesystemSharedCache();
142
143
  /// Returns a cache entry for the corresponding key.
144
  ///
145
  /// A new cache entry is created if the key is not in the cache. This is a
146
  /// thread safe call.
147
  SharedFileSystemEntry &get(StringRef Key);
148
149
private:
150
  struct CacheShard {
151
    std::mutex CacheLock;
152
    llvm::StringMap<SharedFileSystemEntry, llvm::BumpPtrAllocator> Cache;
153
  };
154
  std::unique_ptr<CacheShard[]> CacheShards;
155
  unsigned NumShards;
156
};
157
158
/// This class is a local cache, that caches the 'stat' and 'open' calls to the
159
/// underlying real file system. It distinguishes between minimized and original
160
/// files.
161
class DependencyScanningFilesystemLocalCache {
162
  llvm::StringMap<const CachedFileSystemEntry *, llvm::BumpPtrAllocator> Cache;
163
164
public:
165
5.54k
  const CachedFileSystemEntry *getCachedEntry(StringRef Filename) {
166
5.54k
    return Cache[Filename];
167
5.54k
  }
168
};
169
170
/// Reference to a CachedFileSystemEntry.
171
/// If the underlying entry is an opened file, this wrapper returns the correct
172
/// contents (original or minimized) and ensures consistency with file size
173
/// reported by status.
174
class EntryRef {
175
  /// For entry that is an opened file, this bit signifies whether its contents
176
  /// are minimized.
177
  bool Minimized;
178
179
  /// The underlying cached entry.
180
  const CachedFileSystemEntry &Entry;
181
182
public:
183
  EntryRef(bool Minimized, const CachedFileSystemEntry &Entry)
184
4.37k
      : Minimized(Minimized), Entry(Entry) {}
185
186
4.29k
  llvm::ErrorOr<llvm::vfs::Status> getStatus() const {
187
4.29k
    auto MaybeStat = Entry.getStatus();
188
4.29k
    if (!MaybeStat || 
MaybeStat->isDirectory()4.24k
)
189
846
      return MaybeStat;
190
3.44k
    return llvm::vfs::Status::copyWithNewSize(*MaybeStat,
191
3.44k
                                              getContents()->size());
192
4.29k
  }
193
194
700
  bool isDirectory() const { return Entry.isDirectory(); }
195
196
613
  StringRef getName() const { return Entry.getName(); }
197
198
4.14k
  llvm::ErrorOr<StringRef> getContents() const {
199
4.14k
    return Minimized ? 
Entry.getMinimizedContents()3.35k
200
4.14k
                     : 
Entry.getOriginalContents()787
;
201
4.14k
  }
202
203
616
  const PreprocessorSkippedRangeMapping *getPPSkippedRangeMapping() const {
204
616
    return Minimized ? 
&Entry.getPPSkippedRangeMapping()296
:
nullptr320
;
205
616
  }
206
};
207
208
/// A virtual file system optimized for the dependency discovery.
209
///
210
/// It is primarily designed to work with source files whose contents was was
211
/// preprocessed to remove any tokens that are unlikely to affect the dependency
212
/// computation.
213
///
214
/// This is not a thread safe VFS. A single instance is meant to be used only in
215
/// one thread. Multiple instances are allowed to service multiple threads
216
/// running in parallel.
217
class DependencyScanningWorkerFilesystem : public llvm::vfs::ProxyFileSystem {
218
public:
219
  DependencyScanningWorkerFilesystem(
220
      DependencyScanningFilesystemSharedCache &SharedCache,
221
      IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
222
      ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings)
223
      : ProxyFileSystem(std::move(FS)), SharedCache(SharedCache),
224
315
        PPSkipMappings(PPSkipMappings) {}
225
226
  llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override;
227
  llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
228
  openFileForRead(const Twine &Path) override;
229
230
  /// Disable minimization of the given file.
231
  void disableMinimization(StringRef Filename);
232
  /// Enable minimization of all files.
233
137
  void enableMinimizationOfAllFiles() { NotToBeMinimized.clear(); }
234
235
private:
236
  /// Check whether the file should be minimized.
237
  bool shouldMinimize(StringRef Filename);
238
239
  llvm::ErrorOr<EntryRef> getOrCreateFileSystemEntry(StringRef Filename);
240
241
  /// The global cache shared between worker threads.
242
  DependencyScanningFilesystemSharedCache &SharedCache;
243
  /// The local cache is used by the worker thread to cache file system queries
244
  /// locally instead of querying the global cache every time.
245
  DependencyScanningFilesystemLocalCache LocalCache;
246
  /// The optional mapping structure which records information about the
247
  /// excluded conditional directive skip mappings that are used by the
248
  /// currently active preprocessor.
249
  ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings;
250
  /// The set of files that should not be minimized.
251
  llvm::StringSet<> NotToBeMinimized;
252
};
253
254
} // end namespace dependencies
255
} // end namespace tooling
256
} // end namespace clang
257
258
#endif // LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNINGFILESYSTEM_H