llvm.org GIT mirror llvm / 11da4cf
Reimplement MemoryBuffer::getFile with three enhancements: 1) stop using MappedFile. 2) if profitable use the sys::path::MapInFilePages api to read the file. 3) otherwise fallback to read. When sys::path::MapInFilePages is implemented, this provides several benefits: #1: this avoids fragmenting memory for small files. #2: this avoids extraneous stat calls when the file size is known. #3: this only keeps the file descriptor open while reading the file, not for the duration of the lifetime of the memory buffer. This fixes a serious clang FD 'leak' problem. I believe that this will work on a win32 machine, but I don't have one to test on. I'd appreciate it if someone could check. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@49031 91177308-0d34-0410-b5e6-96231b3b80d8 Chris Lattner 11 years ago
1 changed file(s) with 71 addition(s) and 92 deletion(s). Raw diff Collapse all Expand all
1111 //===----------------------------------------------------------------------===//
1212
1313 #include "llvm/Support/MemoryBuffer.h"
14 #include "llvm/System/MappedFile.h"
14 #include "llvm/ADT/OwningPtr.h"
15 #include "llvm/ADT/SmallString.h"
16 #include "llvm/System/Path.h"
1517 #include "llvm/System/Process.h"
1618 #include "llvm/System/Program.h"
1719 #include
1820 #include
1921 #include
2022 #include
23 #include
24 #include
25 #if !defined(_MSC_VER) && !defined(__MINGW32__)
26 #include
27 #include
28 #include
29 #else
30 #include
31 #endif
2132 using namespace llvm;
2233
2334 //===----------------------------------------------------------------------===//
135146 }
136147
137148 //===----------------------------------------------------------------------===//
138 // MemoryBufferMMapFile implementation.
149 // MemoryBuffer::getFile implementation.
139150 //===----------------------------------------------------------------------===//
140151
141152 namespace {
153 /// MemoryBufferMMapFile - This represents a file that was mapped in with the
154 /// sys::Path::MapInFilePages method. When destroyed, it calls the
155 /// sys::Path::UnMapFilePages method.
142156 class MemoryBufferMMapFile : public MemoryBuffer {
143 sys::MappedFile File;
157 std::string Filename;
144158 public:
145 MemoryBufferMMapFile() {}
146
147 bool open(const sys::Path &Filename, std::string *ErrStr);
159 MemoryBufferMMapFile(const char *filename, const char *Pages, uint64_t Size)
160 : Filename(filename) {
161 init(Pages, Pages+Size);
162 }
148163
149164 virtual const char *getBufferIdentifier() const {
150 return File.path().c_str();
165 return Filename.c_str();
151166 }
152167
153 ~MemoryBufferMMapFile();
168 ~MemoryBufferMMapFile() {
169 sys::Path::UnMapFilePages(getBufferStart(), getBufferSize());
170 }
154171 };
155172 }
156173
157 bool MemoryBufferMMapFile::open(const sys::Path &Filename,
158 std::string *ErrStr) {
159 // FIXME: This does an extra stat syscall to figure out the size, but we
160 // already know the size!
161 bool Failure = File.open(Filename, ErrStr);
162 if (Failure) return true;
163
164 if (!File.map(ErrStr))
165 return true;
166
167 size_t Size = File.size();
168
169 static unsigned PageSize = sys::Process::GetPageSize();
170 assert(((PageSize & (PageSize-1)) == 0) && PageSize &&
171 "Page size is not a power of 2!");
172
173 // If this file is not an exact multiple of the system page size (common
174 // case), then the OS has zero terminated the buffer for us.
175 const char *FileBase = static_cast(File.getBase());
176 if ((Size & (PageSize-1)) != 0) {
177 init(FileBase, FileBase+Size);
178 } else {
179 // Otherwise, we allocate a new memory buffer and copy the data over
180 initCopyOf(FileBase, FileBase+Size);
181
182 // No need to keep the file mapped any longer.
183 File.unmap();
184 }
185 return false;
186 }
187
188 MemoryBufferMMapFile::~MemoryBufferMMapFile() {
189 if (File.isMapped())
190 File.unmap();
191 }
192
193 //===----------------------------------------------------------------------===//
194 // MemoryBuffer::getFile implementation.
195 //===----------------------------------------------------------------------===//
196
197174 MemoryBuffer *MemoryBuffer::getFile(const char *FilenameStart, unsigned FnSize,
198 std::string *ErrStr, int64_t FileSize){
199 // FIXME: it would be nice if PathWithStatus didn't copy the filename into a
200 // temporary string. :(
201 sys::PathWithStatus P(FilenameStart, FnSize);
202 #if 1
203 MemoryBufferMMapFile *M = new MemoryBufferMMapFile();
204 if (!M->open(P, ErrStr))
205 return M;
206 delete M;
207 return 0;
208 #else
209 // FIXME: We need an efficient and portable method to open a file and then use
210 // 'read' to copy the bits out. The unix implementation is below. This is
211 // an important optimization for clients that want to open large numbers of
212 // small files (using mmap on everything can easily exhaust address space!).
213
214 // If the user didn't specify a filesize, do a stat to find it.
175 std::string *ErrStr, int64_t FileSize) {
176 // Null terminate the filename.
177 SmallString<1000> Filename(FilenameStart, FilenameStart+FnSize);
178 Filename.push_back(0);
179
180 int OpenFlags = 0;
181 #ifdef O_BINARY
182 Flags |= O_BINARY; // Open input file in binary mode on win32.
183 #endif
184 int FD = ::open(&Filename[0], O_RDONLY|OpenFlags);
185 if (FD == -1) {
186 if (ErrStr) *ErrStr = "could not open file";
187 return 0;
188 }
189
190 // If we don't know the file size, use fstat to find out. fstat on an open
191 // file descriptor is cheaper than stat on a random path.
215192 if (FileSize == -1) {
216 const sys::FileStatus *FS = P.getFileStatus();
217 if (FS == 0) return 0; // Error stat'ing file.
218
219 FileSize = FS->fileSize;
220 }
221
222 // If the file is larger than some threshold, use mmap, otherwise use 'read'.
223 if (FileSize >= 4096*4) {
224 MemoryBufferMMapFile *M = new MemoryBufferMMapFile();
225 if (!M->open(P, ErrStr))
226 return M;
227 delete M;
228 return 0;
229 }
230
231 MemoryBuffer *SB = getNewUninitMemBuffer(FileSize, FilenameStart);
193 struct stat FileInfo;
194 // TODO: This should use fstat64 when available.
195 if (fstat(FD, &FileInfo) == -1) {
196 if (ErrStr) *ErrStr = "could not get file length";
197 ::close(FD);
198 return 0;
199 }
200 FileSize = FileInfo.st_size;
201 }
202
203
204 // If the file is large, try to use mmap to read it in. We don't use mmap
205 // for small files, because this can severely fragment our address space. Also
206 // don't try to map files that are exactly a multiple of the system page size,
207 // as the file would not have the required null terminator.
208 if (FileSize >= 4096*4 &&
209 (FileSize & (sys::Process::GetPageSize()-1)) != 0) {
210 if (const char *Pages = sys::Path::MapInFilePages(FD, FileSize)) {
211 // Close the file descriptor, now that the whole file is in memory.
212 ::close(FD);
213 return new MemoryBufferMMapFile(&Filename[0], Pages, FileSize);
214 }
215 }
216
217 OwningPtr SB;
218 SB.reset(MemoryBuffer::getNewUninitMemBuffer(FileSize, &Filename[0]));
232219 char *BufPtr = const_cast(SB->getBufferStart());
233
234 int FD = ::open(FilenameStart, O_RDONLY);
235 if (FD == -1) {
236 delete SB;
237 return 0;
238 }
239220
240221 unsigned BytesLeft = FileSize;
241222 while (BytesLeft) {
248229 } else {
249230 // error reading.
250231 close(FD);
251 delete SB;
232 if (ErrStr) *ErrStr = "error reading file data";
252233 return 0;
253234 }
254235 }
255236 close(FD);
256237
257 return SB;
258 #endif
259 }
260
238 return SB.take();
239 }
261240
262241 //===----------------------------------------------------------------------===//
263242 // MemoryBuffer::getSTDIN implementation.