1 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. See the AUTHORS file for names of contributors.
5 #include "third_party/leveldatabase/env_chromium.h"
12 #include "base/debug/trace_event.h"
13 #include "base/files/file_util.h"
14 #include "base/lazy_instance.h"
15 #include "base/memory/shared_memory.h"
16 #include "base/metrics/histogram.h"
17 #include "base/process/process_metrics.h"
18 #include "base/stl_util.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "third_party/leveldatabase/chromium_logger.h"
21 #include "third_party/re2/re2/re2.h"
23 using leveldb::FileLock
;
25 using leveldb::Status
;
27 namespace leveldb_env
{
31 const base::FilePath::CharType backup_table_extension
[] =
32 FILE_PATH_LITERAL(".bak");
33 const base::FilePath::CharType table_extension
[] = FILE_PATH_LITERAL(".ldb");
35 static const base::FilePath::CharType kLevelDBTestDirectoryPrefix
[]
36 = FILE_PATH_LITERAL("leveldb-test-");
38 static base::File::Error
LastFileError() {
40 return base::File::OSErrorToFileError(GetLastError());
42 return base::File::OSErrorToFileError(errno
);
46 // Making direct platform in lieu of using base::FileEnumerator because the
47 // latter can fail quietly without return an error result.
48 static base::File::Error
GetDirectoryEntries(
49 const base::FilePath
& dir_param
,
50 std::vector
<base::FilePath
>* result
) {
53 base::FilePath dir_filepath
= dir_param
.Append(FILE_PATH_LITERAL("*"));
54 WIN32_FIND_DATA find_data
;
55 HANDLE find_handle
= FindFirstFile(dir_filepath
.value().c_str(), &find_data
);
56 if (find_handle
== INVALID_HANDLE_VALUE
) {
57 DWORD last_error
= GetLastError();
58 if (last_error
== ERROR_FILE_NOT_FOUND
)
59 return base::File::FILE_OK
;
60 return base::File::OSErrorToFileError(last_error
);
63 base::FilePath
filepath(find_data
.cFileName
);
64 base::FilePath::StringType basename
= filepath
.BaseName().value();
65 if (basename
== FILE_PATH_LITERAL(".") ||
66 basename
== FILE_PATH_LITERAL(".."))
68 result
->push_back(filepath
.BaseName());
69 } while (FindNextFile(find_handle
, &find_data
));
70 DWORD last_error
= GetLastError();
71 base::File::Error return_value
= base::File::FILE_OK
;
72 if (last_error
!= ERROR_NO_MORE_FILES
)
73 return_value
= base::File::OSErrorToFileError(last_error
);
74 FindClose(find_handle
);
77 const std::string dir_string
= FilePathToString(dir_param
);
78 DIR* dir
= opendir(dir_string
.c_str());
80 return base::File::OSErrorToFileError(errno
);
81 struct dirent dent_buf
;
84 while ((readdir_result
= readdir_r(dir
, &dent_buf
, &dent
)) == 0 && dent
) {
85 if (strcmp(dent
->d_name
, ".") == 0 || strcmp(dent
->d_name
, "..") == 0)
87 result
->push_back(ChromiumEnv::CreateFilePath(dent
->d_name
));
89 int saved_errno
= errno
;
91 if (readdir_result
!= 0)
92 return base::File::OSErrorToFileError(saved_errno
);
93 return base::File::FILE_OK
;
97 class ChromiumFileLock
: public FileLock
{
105 Retrier(MethodID method
, RetrierProvider
* provider
)
106 : start_(base::TimeTicks::Now()),
107 limit_(start_
+ base::TimeDelta::FromMilliseconds(
108 provider
->MaxRetryTimeMillis())),
110 time_to_sleep_(base::TimeDelta::FromMilliseconds(10)),
113 last_error_(base::File::FILE_OK
),
114 provider_(provider
) {}
117 provider_
->GetRetryTimeHistogram(method_
)->AddTime(last_
- start_
);
118 if (last_error_
!= base::File::FILE_OK
) {
119 DCHECK_LT(last_error_
, 0);
120 provider_
->GetRecoveredFromErrorHistogram(method_
)->Add(-last_error_
);
124 bool ShouldKeepTrying(base::File::Error last_error
) {
125 DCHECK_NE(last_error
, base::File::FILE_OK
);
126 last_error_
= last_error
;
127 if (last_
< limit_
) {
128 base::PlatformThread::Sleep(time_to_sleep_
);
129 last_
= base::TimeTicks::Now();
137 base::TimeTicks start_
;
138 base::TimeTicks limit_
;
139 base::TimeTicks last_
;
140 base::TimeDelta time_to_sleep_
;
143 base::File::Error last_error_
;
144 RetrierProvider
* provider_
;
147 class ChromiumSequentialFile
: public leveldb::SequentialFile
{
149 ChromiumSequentialFile(const std::string
& fname
,
151 const UMALogger
* uma_logger
)
152 : filename_(fname
), file_(f
), uma_logger_(uma_logger
) {}
153 virtual ~ChromiumSequentialFile() {}
155 Status
Read(size_t n
, Slice
* result
, char* scratch
) override
{
156 int bytes_read
= file_
->ReadAtCurrentPosNoBestEffort(scratch
, n
);
157 if (bytes_read
== -1) {
158 base::File::Error error
= LastFileError();
159 uma_logger_
->RecordErrorAt(kSequentialFileRead
);
160 return MakeIOError(filename_
, base::File::ErrorToString(error
),
161 kSequentialFileRead
, error
);
163 *result
= Slice(scratch
, bytes_read
);
168 Status
Skip(uint64_t n
) override
{
169 if (file_
->Seek(base::File::FROM_CURRENT
, n
) == -1) {
170 base::File::Error error
= LastFileError();
171 uma_logger_
->RecordErrorAt(kSequentialFileSkip
);
172 return MakeIOError(filename_
, base::File::ErrorToString(error
),
173 kSequentialFileSkip
, error
);
180 std::string filename_
;
181 scoped_ptr
<base::File
> file_
;
182 const UMALogger
* uma_logger_
;
185 class ChromiumRandomAccessFile
: public leveldb::RandomAccessFile
{
187 ChromiumRandomAccessFile(const std::string
& fname
,
189 const UMALogger
* uma_logger
)
190 : filename_(fname
), file_(file
.Pass()), uma_logger_(uma_logger
) {}
191 virtual ~ChromiumRandomAccessFile() {}
193 Status
Read(uint64_t offset
,
196 char* scratch
) const override
{
198 int r
= file_
.Read(offset
, scratch
, n
);
199 *result
= Slice(scratch
, (r
< 0) ? 0 : r
);
201 // An error: return a non-ok status
202 s
= MakeIOError(filename_
, "Could not perform read",
203 kRandomAccessFileRead
);
204 uma_logger_
->RecordErrorAt(kRandomAccessFileRead
);
210 std::string filename_
;
211 mutable ::base::File file_
;
212 const UMALogger
* uma_logger_
;
215 class ChromiumWritableFile
: public leveldb::WritableFile
{
217 ChromiumWritableFile(const std::string
& fname
,
219 const UMALogger
* uma_logger
,
220 WriteTracker
* tracker
,
222 virtual ~ChromiumWritableFile() {}
223 leveldb::Status
Append(const leveldb::Slice
& data
) override
;
224 leveldb::Status
Close() override
;
225 leveldb::Status
Flush() override
;
226 leveldb::Status
Sync() override
;
229 enum Type
{ kManifest
, kTable
, kOther
};
230 leveldb::Status
SyncParent();
232 std::string filename_
;
233 scoped_ptr
<base::File
> file_
;
234 const UMALogger
* uma_logger_
;
235 WriteTracker
* tracker_
;
237 std::string parent_dir_
;
241 ChromiumWritableFile::ChromiumWritableFile(const std::string
& fname
,
243 const UMALogger
* uma_logger
,
244 WriteTracker
* tracker
,
248 uma_logger_(uma_logger
),
251 make_backup_(make_backup
) {
252 base::FilePath path
= base::FilePath::FromUTF8Unsafe(fname
);
253 if (FilePathToString(path
.BaseName()).find("MANIFEST") == 0)
254 file_type_
= kManifest
;
255 else if (ChromiumEnv::HasTableExtension(path
))
257 if (file_type_
!= kManifest
)
258 tracker_
->DidCreateNewFile(filename_
);
259 parent_dir_
= FilePathToString(ChromiumEnv::CreateFilePath(fname
).DirName());
262 Status
ChromiumWritableFile::SyncParent() {
263 TRACE_EVENT0("leveldb", "SyncParent");
264 #if defined(OS_POSIX)
265 base::FilePath path
= base::FilePath::FromUTF8Unsafe(parent_dir_
);
266 base::File
f(path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
);
268 return MakeIOError(parent_dir_
, "Unable to open directory", kSyncParent
,
272 base::File::Error error
= LastFileError();
273 return MakeIOError(parent_dir_
, base::File::ErrorToString(error
),
280 Status
ChromiumWritableFile::Append(const Slice
& data
) {
281 if (file_type_
== kManifest
&& tracker_
->DoesDirNeedSync(filename_
)) {
282 Status s
= SyncParent();
285 tracker_
->DidSyncDir(filename_
);
288 int bytes_written
= file_
->WriteAtCurrentPos(data
.data(), data
.size());
289 if (bytes_written
!= data
.size()) {
290 base::File::Error error
= LastFileError();
291 uma_logger_
->RecordOSError(kWritableFileAppend
, error
);
292 return MakeIOError(filename_
, base::File::ErrorToString(error
),
293 kWritableFileAppend
, error
);
299 Status
ChromiumWritableFile::Close() {
304 Status
ChromiumWritableFile::Flush() {
305 // base::File doesn't do buffered I/O (i.e. POSIX FILE streams) so nothing to
310 Status
ChromiumWritableFile::Sync() {
311 TRACE_EVENT0("leveldb", "WritableFile::Sync");
313 if (!file_
->Flush()) {
314 base::File::Error error
= LastFileError();
315 uma_logger_
->RecordErrorAt(kWritableFileSync
);
316 return MakeIOError(filename_
, base::File::ErrorToString(error
),
317 kWritableFileSync
, error
);
320 if (make_backup_
&& file_type_
== kTable
)
321 uma_logger_
->RecordBackupResult(ChromiumEnv::MakeBackup(filename_
));
326 class IDBEnv
: public ChromiumEnv
{
328 IDBEnv() : ChromiumEnv() {
329 name_
= "LevelDBEnv.IDB";
334 ::base::LazyInstance
<IDBEnv
>::Leaky idb_env
= LAZY_INSTANCE_INITIALIZER
;
336 ::base::LazyInstance
<ChromiumEnv
>::Leaky default_env
=
337 LAZY_INSTANCE_INITIALIZER
;
339 } // unnamed namespace
341 const char* MethodIDToString(MethodID method
) {
343 case kSequentialFileRead
:
344 return "SequentialFileRead";
345 case kSequentialFileSkip
:
346 return "SequentialFileSkip";
347 case kRandomAccessFileRead
:
348 return "RandomAccessFileRead";
349 case kWritableFileAppend
:
350 return "WritableFileAppend";
351 case kWritableFileClose
:
352 return "WritableFileClose";
353 case kWritableFileFlush
:
354 return "WritableFileFlush";
355 case kWritableFileSync
:
356 return "WritableFileSync";
357 case kNewSequentialFile
:
358 return "NewSequentialFile";
359 case kNewRandomAccessFile
:
360 return "NewRandomAccessFile";
361 case kNewWritableFile
:
362 return "NewWritableFile";
370 return "GetFileSize";
377 case kGetTestDirectory
:
378 return "GetTestDirectory";
384 return "GetChildren";
387 return "kNumEntries";
393 Status
MakeIOError(Slice filename
,
394 const std::string
& message
,
396 base::File::Error error
) {
399 snprintf(buf
, sizeof(buf
), "%s (ChromeMethodPFE: %d::%s::%d)",
400 message
.c_str(), method
, MethodIDToString(method
), -error
);
401 return Status::IOError(filename
, buf
);
404 Status
MakeIOError(Slice filename
,
405 const std::string
& message
,
408 snprintf(buf
, sizeof(buf
), "%s (ChromeMethodOnly: %d::%s)", message
.c_str(),
409 method
, MethodIDToString(method
));
410 return Status::IOError(filename
, buf
);
413 ErrorParsingResult
ParseMethodAndError(const char* string
,
414 MethodID
* method_param
,
417 if (RE2::PartialMatch(string
, "ChromeMethodOnly: (\\d+)", &method
)) {
418 *method_param
= static_cast<MethodID
>(method
);
421 if (RE2::PartialMatch(
422 string
, "ChromeMethodPFE: (\\d+)::.*::(\\d+)", &method
, error
)) {
424 *method_param
= static_cast<MethodID
>(method
);
425 return METHOD_AND_PFE
;
427 if (RE2::PartialMatch(
428 string
, "ChromeMethodErrno: (\\d+)::.*::(\\d+)", &method
, error
)) {
429 *method_param
= static_cast<MethodID
>(method
);
430 return METHOD_AND_ERRNO
;
435 // Keep in sync with LevelDBCorruptionTypes in histograms.xml. Also, don't
436 // change the order because indices into this array have been recorded in uma
438 const char* patterns
[] = {
440 "log record too small",
441 "corrupted internal key",
443 "missing start of fragmented record",
444 "error in middle of record",
445 "unknown record type",
446 "truncated record at end",
449 "FileReader invoked with unexpected value",
451 "CURRENT file does not end with newline",
452 "no meta-nextfile entry",
453 "no meta-lognumber entry",
454 "no last-sequence-number entry",
455 "malformed WriteBatch",
456 "bad WriteBatch Put",
457 "bad WriteBatch Delete",
458 "unknown WriteBatch tag",
459 "WriteBatch has wrong count",
460 "bad entry in block",
461 "bad block contents",
463 "truncated block read",
464 "block checksum mismatch",
466 "corrupted compressed block contents",
472 // Returns 1-based index into the above array or 0 if nothing matches.
473 int GetCorruptionCode(const leveldb::Status
& status
) {
474 DCHECK(!status
.IsIOError());
475 DCHECK(!status
.ok());
476 const int kOtherError
= 0;
477 int error
= kOtherError
;
478 const std::string
& str_error
= status
.ToString();
479 const size_t kNumPatterns
= arraysize(patterns
);
480 for (size_t i
= 0; i
< kNumPatterns
; ++i
) {
481 if (str_error
.find(patterns
[i
]) != std::string::npos
) {
489 int GetNumCorruptionCodes() {
490 // + 1 for the "other" error that is returned when a corruption message
491 // doesn't match any of the patterns.
492 return arraysize(patterns
) + 1;
495 std::string
GetCorruptionMessage(const leveldb::Status
& status
) {
496 int code
= GetCorruptionCode(status
);
498 return "Unknown corruption";
499 return patterns
[code
- 1];
502 bool IndicatesDiskFull(const leveldb::Status
& status
) {
505 leveldb_env::MethodID method
;
507 leveldb_env::ErrorParsingResult result
= leveldb_env::ParseMethodAndError(
508 status
.ToString().c_str(), &method
, &error
);
509 return (result
== leveldb_env::METHOD_AND_PFE
&&
510 static_cast<base::File::Error
>(error
) ==
511 base::File::FILE_ERROR_NO_SPACE
) ||
512 (result
== leveldb_env::METHOD_AND_ERRNO
&& error
== ENOSPC
);
515 bool IsIOError(const leveldb::Status
& status
) {
516 leveldb_env::MethodID method
;
518 leveldb_env::ErrorParsingResult result
= leveldb_env::ParseMethodAndError(
519 status
.ToString().c_str(), &method
, &error
);
520 return result
!= leveldb_env::NONE
;
523 std::string
FilePathToString(const base::FilePath
& file_path
) {
525 return base::UTF16ToUTF8(file_path
.value());
527 return file_path
.value();
531 base::FilePath
ChromiumEnv::CreateFilePath(const std::string
& file_path
) {
532 return base::FilePath::FromUTF8Unsafe(file_path
);
535 bool ChromiumEnv::MakeBackup(const std::string
& fname
) {
536 base::FilePath original_table_name
= CreateFilePath(fname
);
537 base::FilePath backup_table_name
=
538 original_table_name
.ReplaceExtension(backup_table_extension
);
539 return base::CopyFile(original_table_name
, backup_table_name
);
542 bool ChromiumEnv::HasTableExtension(const base::FilePath
& path
) {
543 return path
.MatchesExtension(table_extension
);
546 ChromiumEnv::ChromiumEnv()
547 : name_("LevelDBEnv"),
550 started_bgthread_(false),
551 kMaxRetryTimeMillis(1000) {
554 ChromiumEnv::~ChromiumEnv() {
555 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
556 // ensure that behavior isn't accidentally changed, but there's an instance in
557 // a unit test that is deleted.
560 bool ChromiumEnv::FileExists(const std::string
& fname
) {
561 return ::base::PathExists(CreateFilePath(fname
));
564 const char* ChromiumEnv::FileErrorString(::base::File::Error error
) {
566 case ::base::File::FILE_ERROR_FAILED
:
567 return "No further details.";
568 case ::base::File::FILE_ERROR_IN_USE
:
569 return "File currently in use.";
570 case ::base::File::FILE_ERROR_EXISTS
:
571 return "File already exists.";
572 case ::base::File::FILE_ERROR_NOT_FOUND
:
573 return "File not found.";
574 case ::base::File::FILE_ERROR_ACCESS_DENIED
:
575 return "Access denied.";
576 case ::base::File::FILE_ERROR_TOO_MANY_OPENED
:
577 return "Too many files open.";
578 case ::base::File::FILE_ERROR_NO_MEMORY
:
579 return "Out of memory.";
580 case ::base::File::FILE_ERROR_NO_SPACE
:
581 return "No space left on drive.";
582 case ::base::File::FILE_ERROR_NOT_A_DIRECTORY
:
583 return "Not a directory.";
584 case ::base::File::FILE_ERROR_INVALID_OPERATION
:
585 return "Invalid operation.";
586 case ::base::File::FILE_ERROR_SECURITY
:
587 return "Security error.";
588 case ::base::File::FILE_ERROR_ABORT
:
589 return "File operation aborted.";
590 case ::base::File::FILE_ERROR_NOT_A_FILE
:
591 return "The supplied path was not a file.";
592 case ::base::File::FILE_ERROR_NOT_EMPTY
:
593 return "The file was not empty.";
594 case ::base::File::FILE_ERROR_INVALID_URL
:
595 return "Invalid URL.";
596 case ::base::File::FILE_ERROR_IO
:
597 return "OS or hardware error.";
598 case ::base::File::FILE_OK
:
600 case ::base::File::FILE_ERROR_MAX
:
604 return "Unknown error.";
607 base::FilePath
ChromiumEnv::RestoreFromBackup(const base::FilePath
& base_name
) {
608 base::FilePath table_name
=
609 base_name
.AddExtension(table_extension
);
610 bool result
= base::CopyFile(base_name
.AddExtension(backup_table_extension
),
612 std::string
uma_name(name_
);
613 uma_name
.append(".TableRestore");
614 base::BooleanHistogram::FactoryGet(
615 uma_name
, base::Histogram::kUmaTargetedHistogramFlag
)->AddBoolean(result
);
619 void ChromiumEnv::RestoreIfNecessary(const std::string
& dir
,
620 std::vector
<std::string
>* result
) {
621 std::set
<base::FilePath
> tables_found
;
622 std::set
<base::FilePath
> backups_found
;
623 for (std::vector
<std::string
>::iterator it
= result
->begin();
626 base::FilePath current
= CreateFilePath(*it
);
627 if (current
.MatchesExtension(table_extension
))
628 tables_found
.insert(current
.RemoveExtension());
629 if (current
.MatchesExtension(backup_table_extension
))
630 backups_found
.insert(current
.RemoveExtension());
632 std::set
<base::FilePath
> backups_only
=
633 base::STLSetDifference
<std::set
<base::FilePath
> >(backups_found
,
636 if (backups_only
.size()) {
637 std::string
uma_name(name_
);
638 uma_name
.append(".MissingFiles");
639 int num_missing_files
=
640 backups_only
.size() > INT_MAX
? INT_MAX
: backups_only
.size();
641 base::Histogram::FactoryGet(uma_name
,
645 base::Histogram::kUmaTargetedHistogramFlag
)
646 ->Add(num_missing_files
);
648 base::FilePath dir_filepath
= base::FilePath::FromUTF8Unsafe(dir
);
649 for (std::set
<base::FilePath
>::iterator it
= backups_only
.begin();
650 it
!= backups_only
.end();
652 base::FilePath restored_table_name
=
653 RestoreFromBackup(dir_filepath
.Append(*it
));
654 result
->push_back(FilePathToString(restored_table_name
.BaseName()));
658 Status
ChromiumEnv::GetChildren(const std::string
& dir_string
,
659 std::vector
<std::string
>* result
) {
660 std::vector
<base::FilePath
> entries
;
661 base::File::Error error
=
662 GetDirectoryEntries(CreateFilePath(dir_string
), &entries
);
663 if (error
!= base::File::FILE_OK
) {
664 RecordOSError(kGetChildren
, error
);
666 dir_string
, "Could not open/read directory", kGetChildren
, error
);
670 for (const auto& entry
: entries
)
671 result
->push_back(FilePathToString(entry
));
674 RestoreIfNecessary(dir_string
, result
);
679 Status
ChromiumEnv::DeleteFile(const std::string
& fname
) {
681 base::FilePath fname_filepath
= CreateFilePath(fname
);
682 // TODO(jorlow): Should we assert this is a file?
683 if (!::base::DeleteFile(fname_filepath
, false)) {
684 result
= MakeIOError(fname
, "Could not delete file.", kDeleteFile
);
685 RecordErrorAt(kDeleteFile
);
687 if (make_backup_
&& fname_filepath
.MatchesExtension(table_extension
)) {
688 base::DeleteFile(fname_filepath
.ReplaceExtension(backup_table_extension
),
694 Status
ChromiumEnv::CreateDir(const std::string
& name
) {
696 base::File::Error error
= base::File::FILE_OK
;
697 Retrier
retrier(kCreateDir
, this);
699 if (base::CreateDirectoryAndGetError(CreateFilePath(name
), &error
))
701 } while (retrier
.ShouldKeepTrying(error
));
702 result
= MakeIOError(name
, "Could not create directory.", kCreateDir
, error
);
703 RecordOSError(kCreateDir
, error
);
707 Status
ChromiumEnv::DeleteDir(const std::string
& name
) {
709 // TODO(jorlow): Should we assert this is a directory?
710 if (!::base::DeleteFile(CreateFilePath(name
), false)) {
711 result
= MakeIOError(name
, "Could not delete directory.", kDeleteDir
);
712 RecordErrorAt(kDeleteDir
);
717 Status
ChromiumEnv::GetFileSize(const std::string
& fname
, uint64_t* size
) {
720 if (!::base::GetFileSize(CreateFilePath(fname
), &signed_size
)) {
722 s
= MakeIOError(fname
, "Could not determine file size.", kGetFileSize
);
723 RecordErrorAt(kGetFileSize
);
725 *size
= static_cast<uint64_t>(signed_size
);
730 Status
ChromiumEnv::RenameFile(const std::string
& src
, const std::string
& dst
) {
732 base::FilePath src_file_path
= CreateFilePath(src
);
733 if (!::base::PathExists(src_file_path
))
735 base::FilePath destination
= CreateFilePath(dst
);
737 Retrier
retrier(kRenameFile
, this);
738 base::File::Error error
= base::File::FILE_OK
;
740 if (base::ReplaceFile(src_file_path
, destination
, &error
))
742 } while (retrier
.ShouldKeepTrying(error
));
744 DCHECK(error
!= base::File::FILE_OK
);
745 RecordOSError(kRenameFile
, error
);
749 "Could not rename file: %s",
750 FileErrorString(error
));
751 return MakeIOError(src
, buf
, kRenameFile
, error
);
754 Status
ChromiumEnv::LockFile(const std::string
& fname
, FileLock
** lock
) {
757 int flags
= ::base::File::FLAG_OPEN_ALWAYS
|
758 ::base::File::FLAG_READ
|
759 ::base::File::FLAG_WRITE
;
760 ::base::File::Error error_code
;
762 Retrier
retrier(kLockFile
, this);
764 file
.Initialize(CreateFilePath(fname
), flags
);
766 error_code
= file
.error_details();
767 } while (!file
.IsValid() && retrier
.ShouldKeepTrying(error_code
));
769 if (!file
.IsValid()) {
770 if (error_code
== ::base::File::FILE_ERROR_NOT_FOUND
) {
771 ::base::FilePath parent
= CreateFilePath(fname
).DirName();
772 ::base::FilePath last_parent
;
773 int num_missing_ancestors
= 0;
775 if (base::DirectoryExists(parent
))
777 ++num_missing_ancestors
;
778 last_parent
= parent
;
779 parent
= parent
.DirName();
780 } while (parent
!= last_parent
);
781 RecordLockFileAncestors(num_missing_ancestors
);
784 result
= MakeIOError(fname
, FileErrorString(error_code
), kLockFile
,
786 RecordOSError(kLockFile
, error_code
);
790 if (!locks_
.Insert(fname
)) {
791 result
= MakeIOError(fname
, "Lock file already locked.", kLockFile
);
795 Retrier lock_retrier
= Retrier(kLockFile
, this);
797 error_code
= file
.Lock();
798 } while (error_code
!= ::base::File::FILE_OK
&&
799 retrier
.ShouldKeepTrying(error_code
));
801 if (error_code
!= ::base::File::FILE_OK
) {
802 locks_
.Remove(fname
);
803 result
= MakeIOError(fname
, FileErrorString(error_code
), kLockFile
,
805 RecordOSError(kLockFile
, error_code
);
809 ChromiumFileLock
* my_lock
= new ChromiumFileLock
;
810 my_lock
->file_
= file
.Pass();
811 my_lock
->name_
= fname
;
816 Status
ChromiumEnv::UnlockFile(FileLock
* lock
) {
817 ChromiumFileLock
* my_lock
= reinterpret_cast<ChromiumFileLock
*>(lock
);
820 ::base::File::Error error_code
= my_lock
->file_
.Unlock();
821 if (error_code
!= ::base::File::FILE_OK
) {
823 MakeIOError(my_lock
->name_
, "Could not unlock lock file.", kUnlockFile
);
824 RecordOSError(kUnlockFile
, error_code
);
826 bool removed
= locks_
.Remove(my_lock
->name_
);
832 Status
ChromiumEnv::GetTestDirectory(std::string
* path
) {
834 if (test_directory_
.empty()) {
835 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix
,
838 RecordErrorAt(kGetTestDirectory
);
840 "Could not create temp directory.", "", kGetTestDirectory
);
843 *path
= FilePathToString(test_directory_
);
848 Status
ChromiumEnv::NewLogger(const std::string
& fname
,
849 leveldb::Logger
** result
) {
850 base::FilePath path
= CreateFilePath(fname
);
851 scoped_ptr
<base::File
> f(new base::File(
852 path
, base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
));
855 RecordOSError(kNewLogger
, f
->error_details());
856 return MakeIOError(fname
, "Unable to create log file", kNewLogger
,
859 *result
= new leveldb::ChromiumLogger(f
.release());
864 Status
ChromiumEnv::NewSequentialFile(const std::string
& fname
,
865 leveldb::SequentialFile
** result
) {
866 base::FilePath path
= CreateFilePath(fname
);
867 scoped_ptr
<base::File
> f(
868 new base::File(path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
));
871 RecordOSError(kNewSequentialFile
, f
->error_details());
872 return MakeIOError(fname
, "Unable to create sequential file",
873 kNewSequentialFile
, f
->error_details());
875 *result
= new ChromiumSequentialFile(fname
, f
.release(), this);
880 void ChromiumEnv::RecordOpenFilesLimit(const std::string
& type
) {
881 #if defined(OS_POSIX)
882 GetMaxFDHistogram(type
)->Add(base::GetMaxFds());
883 #elif defined(OS_WIN)
884 // Windows is only limited by available memory
886 #error "Need to determine limit to open files for this OS"
890 Status
ChromiumEnv::NewRandomAccessFile(const std::string
& fname
,
891 leveldb::RandomAccessFile
** result
) {
892 int flags
= ::base::File::FLAG_READ
| ::base::File::FLAG_OPEN
;
893 ::base::File
file(ChromiumEnv::CreateFilePath(fname
), flags
);
894 if (file
.IsValid()) {
895 *result
= new ChromiumRandomAccessFile(fname
, file
.Pass(), this);
896 RecordOpenFilesLimit("Success");
899 ::base::File::Error error_code
= file
.error_details();
900 if (error_code
== ::base::File::FILE_ERROR_TOO_MANY_OPENED
)
901 RecordOpenFilesLimit("TooManyOpened");
903 RecordOpenFilesLimit("OtherError");
905 RecordOSError(kNewRandomAccessFile
, error_code
);
906 return MakeIOError(fname
, FileErrorString(error_code
), kNewRandomAccessFile
,
910 Status
ChromiumEnv::NewWritableFile(const std::string
& fname
,
911 leveldb::WritableFile
** result
) {
913 base::FilePath path
= CreateFilePath(fname
);
914 scoped_ptr
<base::File
> f(new base::File(
915 path
, base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
));
917 RecordErrorAt(kNewWritableFile
);
918 return MakeIOError(fname
, "Unable to create writable file",
919 kNewWritableFile
, f
->error_details());
922 new ChromiumWritableFile(fname
, f
.release(), this, this, make_backup_
);
927 uint64_t ChromiumEnv::NowMicros() {
928 return ::base::TimeTicks::Now().ToInternalValue();
931 void ChromiumEnv::SleepForMicroseconds(int micros
) {
932 // Round up to the next millisecond.
933 ::base::PlatformThread::Sleep(::base::TimeDelta::FromMicroseconds(micros
));
936 void ChromiumEnv::RecordErrorAt(MethodID method
) const {
937 GetMethodIOErrorHistogram()->Add(method
);
940 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors
) const {
941 GetLockFileAncestorHistogram()->Add(num_missing_ancestors
);
944 void ChromiumEnv::RecordOSError(MethodID method
,
945 base::File::Error error
) const {
947 RecordErrorAt(method
);
948 GetOSErrorHistogram(method
, -base::File::FILE_ERROR_MAX
)->Add(-error
);
951 void ChromiumEnv::RecordBackupResult(bool result
) const {
952 std::string
uma_name(name_
);
953 uma_name
.append(".TableBackup");
954 base::BooleanHistogram::FactoryGet(
955 uma_name
, base::Histogram::kUmaTargetedHistogramFlag
)->AddBoolean(result
);
958 base::HistogramBase
* ChromiumEnv::GetOSErrorHistogram(MethodID method
,
960 std::string
uma_name(name_
);
961 // TODO(dgrogan): This is probably not the best way to concatenate strings.
962 uma_name
.append(".IOError.").append(MethodIDToString(method
));
963 return base::LinearHistogram::FactoryGet(uma_name
, 1, limit
, limit
+ 1,
964 base::Histogram::kUmaTargetedHistogramFlag
);
967 base::HistogramBase
* ChromiumEnv::GetMethodIOErrorHistogram() const {
968 std::string
uma_name(name_
);
969 uma_name
.append(".IOError");
970 return base::LinearHistogram::FactoryGet(uma_name
, 1, kNumEntries
,
971 kNumEntries
+ 1, base::Histogram::kUmaTargetedHistogramFlag
);
974 base::HistogramBase
* ChromiumEnv::GetMaxFDHistogram(
975 const std::string
& type
) const {
976 std::string
uma_name(name_
);
977 uma_name
.append(".MaxFDs.").append(type
);
978 // These numbers make each bucket twice as large as the previous bucket.
979 const int kFirstEntry
= 1;
980 const int kLastEntry
= 65536;
981 const int kNumBuckets
= 18;
982 return base::Histogram::FactoryGet(
983 uma_name
, kFirstEntry
, kLastEntry
, kNumBuckets
,
984 base::Histogram::kUmaTargetedHistogramFlag
);
987 base::HistogramBase
* ChromiumEnv::GetLockFileAncestorHistogram() const {
988 std::string
uma_name(name_
);
989 uma_name
.append(".LockFileAncestorsNotFound");
992 const int kNumBuckets
= 11;
993 return base::LinearHistogram::FactoryGet(
994 uma_name
, kMin
, kMax
, kNumBuckets
,
995 base::Histogram::kUmaTargetedHistogramFlag
);
998 base::HistogramBase
* ChromiumEnv::GetRetryTimeHistogram(MethodID method
) const {
999 std::string
uma_name(name_
);
1000 // TODO(dgrogan): This is probably not the best way to concatenate strings.
1001 uma_name
.append(".TimeUntilSuccessFor").append(MethodIDToString(method
));
1003 const int kBucketSizeMillis
= 25;
1004 // Add 2, 1 for each of the buckets <1 and >max.
1005 const int kNumBuckets
= kMaxRetryTimeMillis
/ kBucketSizeMillis
+ 2;
1006 return base::Histogram::FactoryTimeGet(
1007 uma_name
, base::TimeDelta::FromMilliseconds(1),
1008 base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis
+ 1),
1010 base::Histogram::kUmaTargetedHistogramFlag
);
1013 base::HistogramBase
* ChromiumEnv::GetRecoveredFromErrorHistogram(
1014 MethodID method
) const {
1015 std::string
uma_name(name_
);
1016 uma_name
.append(".RetryRecoveredFromErrorIn")
1017 .append(MethodIDToString(method
));
1018 return base::LinearHistogram::FactoryGet(uma_name
, 1, kNumEntries
,
1019 kNumEntries
+ 1, base::Histogram::kUmaTargetedHistogramFlag
);
1022 class Thread
: public ::base::PlatformThread::Delegate
{
1024 Thread(void (*function
)(void* arg
), void* arg
)
1025 : function_(function
), arg_(arg
) {
1026 ::base::PlatformThreadHandle handle
;
1027 bool success
= ::base::PlatformThread::Create(0, this, &handle
);
1030 virtual ~Thread() {}
1031 void ThreadMain() override
{
1037 void (*function_
)(void* arg
);
1041 void ChromiumEnv::Schedule(ScheduleFunc
* function
, void* arg
) {
1044 // Start background thread if necessary
1045 if (!started_bgthread_
) {
1046 started_bgthread_
= true;
1047 StartThread(&ChromiumEnv::BGThreadWrapper
, this);
1050 // If the queue is currently empty, the background thread may currently be
1052 if (queue_
.empty()) {
1056 // Add to priority queue
1057 queue_
.push_back(BGItem());
1058 queue_
.back().function
= function
;
1059 queue_
.back().arg
= arg
;
1064 void ChromiumEnv::BGThread() {
1065 base::PlatformThread::SetName(name_
.c_str());
1068 // Wait until there is an item that is ready to run
1070 while (queue_
.empty()) {
1074 void (*function
)(void*) = queue_
.front().function
;
1075 void* arg
= queue_
.front().arg
;
1079 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
1084 void ChromiumEnv::StartThread(void (*function
)(void* arg
), void* arg
) {
1085 new Thread(function
, arg
); // Will self-delete.
1088 static std::string
GetDirName(const std::string
& filename
) {
1089 base::FilePath file
= base::FilePath::FromUTF8Unsafe(filename
);
1090 return FilePathToString(file
.DirName());
1093 void ChromiumEnv::DidCreateNewFile(const std::string
& filename
) {
1094 base::AutoLock
auto_lock(directory_sync_lock_
);
1095 directories_needing_sync_
.insert(GetDirName(filename
));
1098 bool ChromiumEnv::DoesDirNeedSync(const std::string
& filename
) {
1099 base::AutoLock
auto_lock(directory_sync_lock_
);
1100 return ContainsKey(directories_needing_sync_
, GetDirName(filename
));
1103 void ChromiumEnv::DidSyncDir(const std::string
& filename
) {
1104 base::AutoLock
auto_lock(directory_sync_lock_
);
1105 directories_needing_sync_
.erase(GetDirName(filename
));
1108 } // namespace leveldb_env
1113 return leveldb_env::idb_env
.Pointer();
1116 Env
* Env::Default() {
1117 return leveldb_env::default_env
.Pointer();
1120 } // namespace leveldb