14ba319b5SDimitry Andric //===- FileSystemStatCache.cpp - Caching for 'stat' calls -----------------===//
22754fe60SDimitry Andric //
32754fe60SDimitry Andric // The LLVM Compiler Infrastructure
42754fe60SDimitry Andric //
52754fe60SDimitry Andric // This file is distributed under the University of Illinois Open Source
62754fe60SDimitry Andric // License. See LICENSE.TXT for details.
72754fe60SDimitry Andric //
82754fe60SDimitry Andric //===----------------------------------------------------------------------===//
92754fe60SDimitry Andric //
102754fe60SDimitry Andric // This file defines the FileSystemStatCache interface.
112754fe60SDimitry Andric //
122754fe60SDimitry Andric //===----------------------------------------------------------------------===//
132754fe60SDimitry Andric
142754fe60SDimitry Andric #include "clang/Basic/FileSystemStatCache.h"
154ba319b5SDimitry Andric #include "llvm/Support/Chrono.h"
164ba319b5SDimitry Andric #include "llvm/Support/ErrorOr.h"
172754fe60SDimitry Andric #include "llvm/Support/Path.h"
18*b5893f02SDimitry Andric #include "llvm/Support/VirtualFileSystem.h"
194ba319b5SDimitry Andric #include <utility>
202754fe60SDimitry Andric
212754fe60SDimitry Andric using namespace clang;
222754fe60SDimitry Andric
anchor()23dff0c46cSDimitry Andric void FileSystemStatCache::anchor() {}
24dff0c46cSDimitry Andric
copyStatusToFileData(const llvm::vfs::Status & Status,FileData & Data)25*b5893f02SDimitry Andric static void copyStatusToFileData(const llvm::vfs::Status &Status,
26f785676fSDimitry Andric FileData &Data) {
2759d1ed5bSDimitry Andric Data.Name = Status.getName();
28f785676fSDimitry Andric Data.Size = Status.getSize();
2944290647SDimitry Andric Data.ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
30f785676fSDimitry Andric Data.UniqueID = Status.getUniqueID();
3159d1ed5bSDimitry Andric Data.IsDirectory = Status.isDirectory();
3259d1ed5bSDimitry Andric Data.IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;
33f785676fSDimitry Andric Data.InPCH = false;
3459d1ed5bSDimitry Andric Data.IsVFSMapped = Status.IsVFSMapped;
35f785676fSDimitry Andric }
36f785676fSDimitry Andric
372754fe60SDimitry Andric /// FileSystemStatCache::get - Get the 'stat' information for the specified
382754fe60SDimitry Andric /// path, using the cache to accelerate it if possible. This returns true if
392754fe60SDimitry Andric /// the path does not exist or false if it exists.
402754fe60SDimitry Andric ///
41139f7f9bSDimitry Andric /// If isFile is true, then this lookup should only return success for files
42139f7f9bSDimitry Andric /// (not directories). If it is false this lookup should only return
432754fe60SDimitry Andric /// success for directories (not files). On a successful file lookup, the
442754fe60SDimitry Andric /// implementation can optionally fill in FileDescriptor with a valid
452754fe60SDimitry Andric /// descriptor and the client guarantees that it will close it.
get(StringRef Path,FileData & Data,bool isFile,std::unique_ptr<llvm::vfs::File> * F,FileSystemStatCache * Cache,llvm::vfs::FileSystem & FS)4644290647SDimitry Andric bool FileSystemStatCache::get(StringRef Path, FileData &Data, bool isFile,
47*b5893f02SDimitry Andric std::unique_ptr<llvm::vfs::File> *F,
48*b5893f02SDimitry Andric FileSystemStatCache *Cache,
49*b5893f02SDimitry Andric llvm::vfs::FileSystem &FS) {
502754fe60SDimitry Andric LookupResult R;
51139f7f9bSDimitry Andric bool isForDir = !isFile;
522754fe60SDimitry Andric
532754fe60SDimitry Andric // If we have a cache, use it to resolve the stat query.
542754fe60SDimitry Andric if (Cache)
5559d1ed5bSDimitry Andric R = Cache->getStat(Path, Data, isFile, F, FS);
5659d1ed5bSDimitry Andric else if (isForDir || !F) {
57139f7f9bSDimitry Andric // If this is a directory or a file descriptor is not needed and we have
58139f7f9bSDimitry Andric // no cache, just go to the file system.
59*b5893f02SDimitry Andric llvm::ErrorOr<llvm::vfs::Status> Status = FS.status(Path);
6059d1ed5bSDimitry Andric if (!Status) {
61f785676fSDimitry Andric R = CacheMissing;
62f785676fSDimitry Andric } else {
63f785676fSDimitry Andric R = CacheExists;
6459d1ed5bSDimitry Andric copyStatusToFileData(*Status, Data);
65f785676fSDimitry Andric }
662754fe60SDimitry Andric } else {
672754fe60SDimitry Andric // Otherwise, we have to go to the filesystem. We can always just use
682754fe60SDimitry Andric // 'stat' here, but (for files) the client is asking whether the file exists
692754fe60SDimitry Andric // because it wants to turn around and *open* it. It is more efficient to
702754fe60SDimitry Andric // do "open+fstat" on success than it is to do "stat+open".
712754fe60SDimitry Andric //
722754fe60SDimitry Andric // Because of this, check to see if the file exists with 'open'. If the
732754fe60SDimitry Andric // open succeeds, use fstat to get the stat info.
7439d628a0SDimitry Andric auto OwnedFile = FS.openFileForRead(Path);
752754fe60SDimitry Andric
7639d628a0SDimitry Andric if (!OwnedFile) {
772754fe60SDimitry Andric // If the open fails, our "stat" fails.
782754fe60SDimitry Andric R = CacheMissing;
792754fe60SDimitry Andric } else {
802754fe60SDimitry Andric // Otherwise, the open succeeded. Do an fstat to get the information
812754fe60SDimitry Andric // about the file. We'll end up returning the open file descriptor to the
822754fe60SDimitry Andric // client to do what they please with it.
83*b5893f02SDimitry Andric llvm::ErrorOr<llvm::vfs::Status> Status = (*OwnedFile)->status();
8459d1ed5bSDimitry Andric if (Status) {
852754fe60SDimitry Andric R = CacheExists;
8659d1ed5bSDimitry Andric copyStatusToFileData(*Status, Data);
8739d628a0SDimitry Andric *F = std::move(*OwnedFile);
88f785676fSDimitry Andric } else {
892754fe60SDimitry Andric // fstat rarely fails. If it does, claim the initial open didn't
902754fe60SDimitry Andric // succeed.
912754fe60SDimitry Andric R = CacheMissing;
9259d1ed5bSDimitry Andric *F = nullptr;
932754fe60SDimitry Andric }
942754fe60SDimitry Andric }
952754fe60SDimitry Andric }
962754fe60SDimitry Andric
972754fe60SDimitry Andric // If the path doesn't exist, return failure.
982754fe60SDimitry Andric if (R == CacheMissing) return true;
992754fe60SDimitry Andric
1002754fe60SDimitry Andric // If the path exists, make sure that its "directoryness" matches the clients
1012754fe60SDimitry Andric // demands.
102f785676fSDimitry Andric if (Data.IsDirectory != isForDir) {
1032754fe60SDimitry Andric // If not, close the file if opened.
10459d1ed5bSDimitry Andric if (F)
10559d1ed5bSDimitry Andric *F = nullptr;
1062754fe60SDimitry Andric
1072754fe60SDimitry Andric return true;
1082754fe60SDimitry Andric }
1092754fe60SDimitry Andric
1102754fe60SDimitry Andric return false;
1112754fe60SDimitry Andric }
1122754fe60SDimitry Andric
1132754fe60SDimitry Andric MemorizeStatCalls::LookupResult
getStat(StringRef Path,FileData & Data,bool isFile,std::unique_ptr<llvm::vfs::File> * F,llvm::vfs::FileSystem & FS)11444290647SDimitry Andric MemorizeStatCalls::getStat(StringRef Path, FileData &Data, bool isFile,
115*b5893f02SDimitry Andric std::unique_ptr<llvm::vfs::File> *F,
116*b5893f02SDimitry Andric llvm::vfs::FileSystem &FS) {
117*b5893f02SDimitry Andric if (get(Path, Data, isFile, F, nullptr, FS)) {
1182754fe60SDimitry Andric // Do not cache failed stats, it is easy to construct common inconsistent
119*b5893f02SDimitry Andric // situations if we do, and they are not important for PCH performance
120*b5893f02SDimitry Andric // (which currently only needs the stats to construct the initial
121*b5893f02SDimitry Andric // FileManager entries).
122*b5893f02SDimitry Andric return CacheMissing;
123*b5893f02SDimitry Andric }
1242754fe60SDimitry Andric
1252754fe60SDimitry Andric // Cache file 'stat' results and directories with absolutely paths.
126f785676fSDimitry Andric if (!Data.IsDirectory || llvm::sys::path::is_absolute(Path))
127f785676fSDimitry Andric StatCalls[Path] = Data;
1282754fe60SDimitry Andric
129*b5893f02SDimitry Andric return CacheExists;
1302754fe60SDimitry Andric }
131