Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit 4c3cdee

Browse files
committed
Modules: Cache PCMs in memory and avoid a use-after-free
Clang's internal build system for implicit modules uses lock files to ensure that after a process writes a PCM it will read the same one back in (without contention from other -cc1 commands). Since PCMs are read from disk repeatedly while invalidating, building, and importing, the lock is not released quickly. Furthermore, the LockFileManager is not robust in every environment. Other -cc1 commands can stall until timeout (after about eight minutes). This commit changes the lock file from being necessary for correctness to a (possibly dubious) performance hack. The remaining benefit is to reduce duplicate work in competing -cc1 commands which depend on the same module. Follow-up commits will change the internal build system to continue after a timeout, and reduce the timeout. Perhaps we should reconsider blocking at all. This also fixes a use-after-free, when one part of a compilation validates a PCM and starts using it, and another tries to swap out the PCM for something new. The PCMCache is a new type called MemoryBufferCache, which saves memory buffers based on their filename. Its ownership is shared by the CompilerInstance and ModuleManager. - The ModuleManager stores PCMs there that it loads from disk, never touching the disk if the cache is hot. - When modules fail to validate, they're removed from the cache. - When a CompilerInstance is spawned to build a new module, each already-loaded PCM is assumed to be valid, and is frozen to avoid the use-after-free. - Any newly-built module is written directly to the cache to avoid the round-trip to the filesystem, making lock files unnecessary for correctness. Original patch by Manman Ren; most testcases by Adrian Prantl! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@298165 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 594a067 commit 4c3cdee

34 files changed

+475
-57
lines changed

Diff for: include/clang/Basic/DiagnosticSerializationKinds.td

+5
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,11 @@ def warn_duplicate_module_file_extension : Warning<
176176
"duplicate module file extension block name '%0'">,
177177
InGroup<ModuleFileExtension>;
178178

179+
def warn_module_system_bit_conflict : Warning<
180+
"module file '%0' was validated as a system module and is now being imported "
181+
"as a non-system module; any difference in diagnostic options will be ignored">,
182+
InGroup<ModuleConflict>;
183+
179184
} // let CategoryName
180185
} // let Component
181186

Diff for: include/clang/Basic/MemoryBufferCache.h

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//===- MemoryBufferCache.h - Cache for loaded memory buffers ----*- 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+
#ifndef LLVM_CLANG_BASIC_MEMORYBUFFERCACHE_H
11+
#define LLVM_CLANG_BASIC_MEMORYBUFFERCACHE_H
12+
13+
#include "llvm/ADT/IntrusiveRefCntPtr.h"
14+
#include "llvm/ADT/StringMap.h"
15+
#include <memory>
16+
17+
namespace llvm {
18+
class MemoryBuffer;
19+
} // end namespace llvm
20+
21+
namespace clang {
22+
23+
/// Manage memory buffers across multiple users.
24+
///
25+
/// Ensures that multiple users have a consistent view of each buffer. This is
26+
/// used by \a CompilerInstance when building PCMs to ensure that each \a
27+
/// ModuleManager sees the same files.
28+
///
29+
/// \a finalizeCurrentBuffers() should be called before creating a new user.
30+
/// This locks in the current buffers, ensuring that no buffer that has already
31+
/// been accessed can be purged, preventing use-after-frees.
32+
class MemoryBufferCache : public llvm::RefCountedBase<MemoryBufferCache> {
33+
struct BufferEntry {
34+
std::unique_ptr<llvm::MemoryBuffer> Buffer;
35+
36+
/// Track the timeline of when this was added to the cache.
37+
unsigned Index;
38+
};
39+
40+
/// Cache of buffers.
41+
llvm::StringMap<BufferEntry> Buffers;
42+
43+
/// Monotonically increasing index.
44+
unsigned NextIndex = 0;
45+
46+
/// Bumped to prevent "older" buffers from being removed.
47+
unsigned FirstRemovableIndex = 0;
48+
49+
public:
50+
/// Store the Buffer under the Filename.
51+
///
52+
/// \pre There is not already buffer is not already in the cache.
53+
/// \return a reference to the buffer as a convenience.
54+
llvm::MemoryBuffer &addBuffer(llvm::StringRef Filename,
55+
std::unique_ptr<llvm::MemoryBuffer> Buffer);
56+
57+
/// Try to remove a buffer from the cache.
58+
///
59+
/// \return false on success, iff \c !isBufferFinal().
60+
bool tryToRemoveBuffer(llvm::StringRef Filename);
61+
62+
/// Get a pointer to the buffer if it exists; else nullptr.
63+
llvm::MemoryBuffer *lookupBuffer(llvm::StringRef Filename);
64+
65+
/// Check whether the buffer is final.
66+
///
67+
/// \return true iff \a finalizeCurrentBuffers() has been called since the
68+
/// buffer was added. This prevents buffers from being removed.
69+
bool isBufferFinal(llvm::StringRef Filename);
70+
71+
/// Finalize the current buffers in the cache.
72+
///
73+
/// Should be called when creating a new user to ensure previous uses aren't
74+
/// invalidated.
75+
void finalizeCurrentBuffers();
76+
};
77+
78+
} // end namespace clang
79+
80+
#endif // LLVM_CLANG_BASIC_MEMORYBUFFERCACHE_H

Diff for: include/clang/Frontend/ASTUnit.h

+2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class DiagnosticsEngine;
5151
class FileEntry;
5252
class FileManager;
5353
class HeaderSearch;
54+
class MemoryBufferCache;
5455
class Preprocessor;
5556
class PCHContainerOperations;
5657
class PCHContainerReader;
@@ -84,6 +85,7 @@ class ASTUnit : public ModuleLoader {
8485
IntrusiveRefCntPtr<DiagnosticsEngine> Diagnostics;
8586
IntrusiveRefCntPtr<FileManager> FileMgr;
8687
IntrusiveRefCntPtr<SourceManager> SourceMgr;
88+
IntrusiveRefCntPtr<MemoryBufferCache> PCMCache;
8789
std::unique_ptr<HeaderSearch> HeaderInfo;
8890
IntrusiveRefCntPtr<TargetInfo> Target;
8991
std::shared_ptr<Preprocessor> PP;

Diff for: include/clang/Frontend/CompilerInstance.h

+7-1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class ExternalASTSource;
4444
class FileEntry;
4545
class FileManager;
4646
class FrontendAction;
47+
class MemoryBufferCache;
4748
class Module;
4849
class Preprocessor;
4950
class Sema;
@@ -90,6 +91,9 @@ class CompilerInstance : public ModuleLoader {
9091
/// The source manager.
9192
IntrusiveRefCntPtr<SourceManager> SourceMgr;
9293

94+
/// The cache of PCM files.
95+
IntrusiveRefCntPtr<MemoryBufferCache> PCMCache;
96+
9397
/// The preprocessor.
9498
std::shared_ptr<Preprocessor> PP;
9599

@@ -178,7 +182,7 @@ class CompilerInstance : public ModuleLoader {
178182
explicit CompilerInstance(
179183
std::shared_ptr<PCHContainerOperations> PCHContainerOps =
180184
std::make_shared<PCHContainerOperations>(),
181-
bool BuildingModule = false);
185+
MemoryBufferCache *SharedPCMCache = nullptr);
182186
~CompilerInstance() override;
183187

184188
/// @name High-Level Operations
@@ -783,6 +787,8 @@ class CompilerInstance : public ModuleLoader {
783787
}
784788

785789
void setExternalSemaSource(IntrusiveRefCntPtr<ExternalSemaSource> ESS);
790+
791+
MemoryBufferCache &getPCMCache() const { return *PCMCache; }
786792
};
787793

788794
} // end namespace clang

Diff for: include/clang/Lex/Preprocessor.h

+4
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class ExternalPreprocessorSource;
4747
class FileManager;
4848
class FileEntry;
4949
class HeaderSearch;
50+
class MemoryBufferCache;
5051
class PragmaNamespace;
5152
class PragmaHandler;
5253
class CommentHandler;
@@ -102,6 +103,7 @@ class Preprocessor {
102103
const TargetInfo *AuxTarget;
103104
FileManager &FileMgr;
104105
SourceManager &SourceMgr;
106+
MemoryBufferCache &PCMCache;
105107
std::unique_ptr<ScratchBuffer> ScratchBuf;
106108
HeaderSearch &HeaderInfo;
107109
ModuleLoader &TheModuleLoader;
@@ -652,6 +654,7 @@ class Preprocessor {
652654
public:
653655
Preprocessor(std::shared_ptr<PreprocessorOptions> PPOpts,
654656
DiagnosticsEngine &diags, LangOptions &opts, SourceManager &SM,
657+
MemoryBufferCache &PCMCache,
655658
HeaderSearch &Headers, ModuleLoader &TheModuleLoader,
656659
IdentifierInfoLookup *IILookup = nullptr,
657660
bool OwnsHeaderSearch = false,
@@ -691,6 +694,7 @@ class Preprocessor {
691694
const TargetInfo *getAuxTargetInfo() const { return AuxTarget; }
692695
FileManager &getFileManager() const { return FileMgr; }
693696
SourceManager &getSourceManager() const { return SourceMgr; }
697+
MemoryBufferCache &getPCMCache() const { return PCMCache; }
694698
HeaderSearch &getHeaderSearchInfo() const { return HeaderInfo; }
695699

696700
IdentifierTable &getIdentifierTable() { return Identifiers; }

Diff for: include/clang/Serialization/ASTReader.h

+3
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,9 @@ class ASTReader
408408
/// \brief The module manager which manages modules and their dependencies
409409
ModuleManager ModuleMgr;
410410

411+
/// The cache that manages memory buffers for PCM files.
412+
MemoryBufferCache &PCMCache;
413+
411414
/// \brief A dummy identifier resolver used to merge TU-scope declarations in
412415
/// C, for the cases where we don't have a Sema object to provide a real
413416
/// identifier resolver.

Diff for: include/clang/Serialization/ASTWriter.h

+5
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class MacroInfo;
5454
class OpaqueValueExpr;
5555
class OpenCLOptions;
5656
class ASTReader;
57+
class MemoryBufferCache;
5758
class Module;
5859
class ModuleFileExtension;
5960
class ModuleFileExtensionWriter;
@@ -109,6 +110,9 @@ class ASTWriter : public ASTDeserializationListener,
109110
/// The buffer associated with the bitstream.
110111
const SmallVectorImpl<char> &Buffer;
111112

113+
/// \brief The PCM manager which manages memory buffers for pcm files.
114+
MemoryBufferCache &PCMCache;
115+
112116
/// \brief The ASTContext we're writing.
113117
ASTContext *Context = nullptr;
114118

@@ -512,6 +516,7 @@ class ASTWriter : public ASTDeserializationListener,
512516
/// \brief Create a new precompiled header writer that outputs to
513517
/// the given bitstream.
514518
ASTWriter(llvm::BitstreamWriter &Stream, SmallVectorImpl<char> &Buffer,
519+
MemoryBufferCache &PCMCache,
515520
ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions,
516521
bool IncludeTimestamps = true);
517522
~ASTWriter() override;

Diff for: include/clang/Serialization/Module.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,9 @@ class ModuleFile {
163163
/// \brief The generation of which this module file is a part.
164164
unsigned Generation;
165165

166-
/// \brief The memory buffer that stores the data associated with
167-
/// this AST file.
168-
std::unique_ptr<llvm::MemoryBuffer> Buffer;
166+
/// The memory buffer that stores the data associated with
167+
/// this AST file, owned by the PCMCache in the ModuleManager.
168+
llvm::MemoryBuffer *Buffer;
169169

170170
/// \brief The size of this file, in bits.
171171
uint64_t SizeInBits = 0;

Diff for: include/clang/Serialization/ModuleManager.h

+7-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
namespace clang {
2525

2626
class GlobalModuleIndex;
27+
class MemoryBufferCache;
2728
class ModuleMap;
2829
class PCHContainerReader;
2930

@@ -51,6 +52,9 @@ class ModuleManager {
5152
/// FileEntry *.
5253
FileManager &FileMgr;
5354

55+
/// Cache of PCM files.
56+
IntrusiveRefCntPtr<MemoryBufferCache> PCMCache;
57+
5458
/// \brief Knows how to unwrap module containers.
5559
const PCHContainerReader &PCHContainerRdr;
5660

@@ -123,7 +127,7 @@ class ModuleManager {
123127
ModuleReverseIterator;
124128
typedef std::pair<uint32_t, StringRef> ModuleOffset;
125129

126-
explicit ModuleManager(FileManager &FileMgr,
130+
explicit ModuleManager(FileManager &FileMgr, MemoryBufferCache &PCMCache,
127131
const PCHContainerReader &PCHContainerRdr);
128132
~ModuleManager();
129133

@@ -290,6 +294,8 @@ class ModuleManager {
290294

291295
/// \brief View the graphviz representation of the module graph.
292296
void viewGraph();
297+
298+
MemoryBufferCache &getPCMCache() const { return *PCMCache; }
293299
};
294300

295301
} } // end namespace clang::serialization

Diff for: lib/Basic/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ add_clang_library(clangBasic
7474
FileSystemStatCache.cpp
7575
IdentifierTable.cpp
7676
LangOptions.cpp
77+
MemoryBufferCache.cpp
7778
Module.cpp
7879
ObjCRuntime.cpp
7980
OpenMPKinds.cpp

Diff for: lib/Basic/MemoryBufferCache.cpp

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//===- MemoryBufferCache.cpp - Cache for loaded memory buffers ------------===//
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+
#include "clang/Basic/MemoryBufferCache.h"
11+
#include "llvm/Support/MemoryBuffer.h"
12+
13+
using namespace clang;
14+
15+
llvm::MemoryBuffer &
16+
MemoryBufferCache::addBuffer(llvm::StringRef Filename,
17+
std::unique_ptr<llvm::MemoryBuffer> Buffer) {
18+
auto Insertion =
19+
Buffers.insert({Filename, BufferEntry{std::move(Buffer), NextIndex++}});
20+
assert(Insertion.second && "Already has a buffer");
21+
return *Insertion.first->second.Buffer;
22+
}
23+
24+
llvm::MemoryBuffer *MemoryBufferCache::lookupBuffer(llvm::StringRef Filename) {
25+
auto I = Buffers.find(Filename);
26+
if (I == Buffers.end())
27+
return nullptr;
28+
return I->second.Buffer.get();
29+
}
30+
31+
bool MemoryBufferCache::isBufferFinal(llvm::StringRef Filename) {
32+
auto I = Buffers.find(Filename);
33+
if (I == Buffers.end())
34+
return false;
35+
return I->second.Index < FirstRemovableIndex;
36+
}
37+
38+
bool MemoryBufferCache::tryToRemoveBuffer(llvm::StringRef Filename) {
39+
auto I = Buffers.find(Filename);
40+
assert(I != Buffers.end() && "No buffer to remove...");
41+
if (I->second.Index < FirstRemovableIndex)
42+
return true;
43+
44+
Buffers.erase(I);
45+
return false;
46+
}
47+
48+
void MemoryBufferCache::finalizeCurrentBuffers() { FirstRemovableIndex = NextIndex; }

Diff for: lib/Frontend/ASTUnit.cpp

+10-4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "clang/AST/StmtVisitor.h"
1919
#include "clang/AST/TypeOrdering.h"
2020
#include "clang/Basic/Diagnostic.h"
21+
#include "clang/Basic/MemoryBufferCache.h"
2122
#include "clang/Basic/TargetInfo.h"
2223
#include "clang/Basic/TargetOptions.h"
2324
#include "clang/Basic/VirtualFileSystem.h"
@@ -185,7 +186,8 @@ struct ASTUnit::ASTWriterData {
185186
llvm::BitstreamWriter Stream;
186187
ASTWriter Writer;
187188

188-
ASTWriterData() : Stream(Buffer), Writer(Stream, Buffer, {}) {}
189+
ASTWriterData(MemoryBufferCache &PCMCache)
190+
: Stream(Buffer), Writer(Stream, Buffer, PCMCache, {}) {}
189191
};
190192

191193
void ASTUnit::clearFileLevelDecls() {
@@ -681,6 +683,7 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile(
681683
AST->SourceMgr = new SourceManager(AST->getDiagnostics(),
682684
AST->getFileManager(),
683685
UserFilesAreVolatile);
686+
AST->PCMCache = new MemoryBufferCache;
684687
AST->HSOpts = std::make_shared<HeaderSearchOptions>();
685688
AST->HSOpts->ModuleFormat = PCHContainerRdr.getFormat();
686689
AST->HeaderInfo.reset(new HeaderSearch(AST->HSOpts,
@@ -701,7 +704,7 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile(
701704

702705
AST->PP = std::make_shared<Preprocessor>(
703706
std::move(PPOpts), AST->getDiagnostics(), AST->ASTFileLangOpts,
704-
AST->getSourceManager(), HeaderInfo, *AST,
707+
AST->getSourceManager(), *AST->PCMCache, HeaderInfo, *AST,
705708
/*IILookup=*/nullptr,
706709
/*OwnsHeaderSearch=*/false);
707710
Preprocessor &PP = *AST->PP;
@@ -1727,6 +1730,7 @@ ASTUnit::create(std::shared_ptr<CompilerInvocation> CI,
17271730
AST->UserFilesAreVolatile = UserFilesAreVolatile;
17281731
AST->SourceMgr = new SourceManager(AST->getDiagnostics(), *AST->FileMgr,
17291732
UserFilesAreVolatile);
1733+
AST->PCMCache = new MemoryBufferCache;
17301734

17311735
return AST;
17321736
}
@@ -1997,6 +2001,7 @@ ASTUnit *ASTUnit::LoadFromCommandLine(
19972001
if (!VFS)
19982002
return nullptr;
19992003
AST->FileMgr = new FileManager(AST->FileSystemOpts, VFS);
2004+
AST->PCMCache = new MemoryBufferCache;
20002005
AST->OnlyLocalDecls = OnlyLocalDecls;
20012006
AST->CaptureDiagnostics = CaptureDiagnostics;
20022007
AST->TUKind = TUKind;
@@ -2008,7 +2013,7 @@ ASTUnit *ASTUnit::LoadFromCommandLine(
20082013
AST->StoredDiagnostics.swap(StoredDiagnostics);
20092014
AST->Invocation = CI;
20102015
if (ForSerialization)
2011-
AST->WriterData.reset(new ASTWriterData());
2016+
AST->WriterData.reset(new ASTWriterData(*AST->PCMCache));
20122017
// Zero out now to ease cleanup during crash recovery.
20132018
CI = nullptr;
20142019
Diags = nullptr;
@@ -2523,7 +2528,8 @@ bool ASTUnit::serialize(raw_ostream &OS) {
25232528

25242529
SmallString<128> Buffer;
25252530
llvm::BitstreamWriter Stream(Buffer);
2526-
ASTWriter Writer(Stream, Buffer, {});
2531+
MemoryBufferCache PCMCache;
2532+
ASTWriter Writer(Stream, Buffer, PCMCache, {});
25272533
return serializeUnit(Writer, Buffer, getSema(), hasErrors, OS);
25282534
}
25292535

0 commit comments

Comments
 (0)