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"
24 using leveldb::FileLock
;
26 using leveldb::Status
;
28 namespace leveldb_env
{
32 const FilePath::CharType backup_table_extension
[] = FILE_PATH_LITERAL(".bak");
33 const FilePath::CharType table_extension
[] = FILE_PATH_LITERAL(".ldb");
35 static const 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(const FilePath
& dir_param
,
49 std::vector
<FilePath
>* result
) {
52 FilePath dir_filepath
= dir_param
.Append(FILE_PATH_LITERAL("*"));
53 WIN32_FIND_DATA find_data
;
54 HANDLE find_handle
= FindFirstFile(dir_filepath
.value().c_str(), &find_data
);
55 if (find_handle
== INVALID_HANDLE_VALUE
) {
56 DWORD last_error
= GetLastError();
57 if (last_error
== ERROR_FILE_NOT_FOUND
)
58 return base::File::FILE_OK
;
59 return base::File::OSErrorToFileError(last_error
);
62 FilePath
filepath(find_data
.cFileName
);
63 FilePath::StringType basename
= filepath
.BaseName().value();
64 if (basename
== FILE_PATH_LITERAL(".") ||
65 basename
== FILE_PATH_LITERAL(".."))
67 result
->push_back(filepath
.BaseName());
68 } while (FindNextFile(find_handle
, &find_data
));
69 DWORD last_error
= GetLastError();
70 base::File::Error return_value
= base::File::FILE_OK
;
71 if (last_error
!= ERROR_NO_MORE_FILES
)
72 return_value
= base::File::OSErrorToFileError(last_error
);
73 FindClose(find_handle
);
76 const std::string dir_string
= dir_param
.AsUTF8Unsafe();
77 DIR* dir
= opendir(dir_string
.c_str());
79 return base::File::OSErrorToFileError(errno
);
80 struct dirent dent_buf
;
83 while ((readdir_result
= readdir_r(dir
, &dent_buf
, &dent
)) == 0 && dent
) {
84 if (strcmp(dent
->d_name
, ".") == 0 || strcmp(dent
->d_name
, "..") == 0)
86 result
->push_back(FilePath::FromUTF8Unsafe(dent
->d_name
));
88 int saved_errno
= errno
;
90 if (readdir_result
!= 0)
91 return base::File::OSErrorToFileError(saved_errno
);
92 return base::File::FILE_OK
;
96 class ChromiumFileLock
: public FileLock
{
104 Retrier(MethodID method
, RetrierProvider
* provider
)
105 : start_(base::TimeTicks::Now()),
106 limit_(start_
+ base::TimeDelta::FromMilliseconds(
107 provider
->MaxRetryTimeMillis())),
109 time_to_sleep_(base::TimeDelta::FromMilliseconds(10)),
112 last_error_(base::File::FILE_OK
),
113 provider_(provider
) {}
116 provider_
->GetRetryTimeHistogram(method_
)->AddTime(last_
- start_
);
117 if (last_error_
!= base::File::FILE_OK
) {
118 DCHECK_LT(last_error_
, 0);
119 provider_
->GetRecoveredFromErrorHistogram(method_
)->Add(-last_error_
);
123 bool ShouldKeepTrying(base::File::Error last_error
) {
124 DCHECK_NE(last_error
, base::File::FILE_OK
);
125 last_error_
= last_error
;
126 if (last_
< limit_
) {
127 base::PlatformThread::Sleep(time_to_sleep_
);
128 last_
= base::TimeTicks::Now();
136 base::TimeTicks start_
;
137 base::TimeTicks limit_
;
138 base::TimeTicks last_
;
139 base::TimeDelta time_to_sleep_
;
142 base::File::Error last_error_
;
143 RetrierProvider
* provider_
;
146 class ChromiumSequentialFile
: public leveldb::SequentialFile
{
148 ChromiumSequentialFile(const std::string
& fname
,
150 const UMALogger
* uma_logger
)
151 : filename_(fname
), file_(f
), uma_logger_(uma_logger
) {}
152 virtual ~ChromiumSequentialFile() {}
154 Status
Read(size_t n
, Slice
* result
, char* scratch
) override
{
155 int bytes_read
= file_
->ReadAtCurrentPosNoBestEffort(scratch
, n
);
156 if (bytes_read
== -1) {
157 base::File::Error error
= LastFileError();
158 uma_logger_
->RecordErrorAt(kSequentialFileRead
);
159 return MakeIOError(filename_
, base::File::ErrorToString(error
),
160 kSequentialFileRead
, error
);
162 *result
= Slice(scratch
, bytes_read
);
167 Status
Skip(uint64_t n
) override
{
168 if (file_
->Seek(base::File::FROM_CURRENT
, n
) == -1) {
169 base::File::Error error
= LastFileError();
170 uma_logger_
->RecordErrorAt(kSequentialFileSkip
);
171 return MakeIOError(filename_
, base::File::ErrorToString(error
),
172 kSequentialFileSkip
, error
);
179 std::string filename_
;
180 scoped_ptr
<base::File
> file_
;
181 const UMALogger
* uma_logger_
;
184 class ChromiumRandomAccessFile
: public leveldb::RandomAccessFile
{
186 ChromiumRandomAccessFile(const std::string
& fname
,
188 const UMALogger
* uma_logger
)
189 : filename_(fname
), file_(file
.Pass()), uma_logger_(uma_logger
) {}
190 virtual ~ChromiumRandomAccessFile() {}
192 Status
Read(uint64_t offset
,
195 char* scratch
) const override
{
197 int r
= file_
.Read(offset
, scratch
, n
);
198 *result
= Slice(scratch
, (r
< 0) ? 0 : r
);
200 // An error: return a non-ok status
201 s
= MakeIOError(filename_
, "Could not perform read",
202 kRandomAccessFileRead
);
203 uma_logger_
->RecordErrorAt(kRandomAccessFileRead
);
209 std::string filename_
;
210 mutable base::File file_
;
211 const UMALogger
* uma_logger_
;
214 class ChromiumWritableFile
: public leveldb::WritableFile
{
216 ChromiumWritableFile(const std::string
& fname
,
218 const UMALogger
* uma_logger
,
219 WriteTracker
* tracker
,
221 virtual ~ChromiumWritableFile() {}
222 leveldb::Status
Append(const leveldb::Slice
& data
) override
;
223 leveldb::Status
Close() override
;
224 leveldb::Status
Flush() override
;
225 leveldb::Status
Sync() override
;
228 enum Type
{ kManifest
, kTable
, kOther
};
229 leveldb::Status
SyncParent();
231 std::string filename_
;
232 scoped_ptr
<base::File
> file_
;
233 const UMALogger
* uma_logger_
;
234 WriteTracker
* tracker_
;
236 std::string parent_dir_
;
240 ChromiumWritableFile::ChromiumWritableFile(const std::string
& fname
,
242 const UMALogger
* uma_logger
,
243 WriteTracker
* tracker
,
247 uma_logger_(uma_logger
),
250 make_backup_(make_backup
) {
251 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
252 if (path
.BaseName().AsUTF8Unsafe().find("MANIFEST") == 0)
253 file_type_
= kManifest
;
254 else if (path
.MatchesExtension(table_extension
))
256 if (file_type_
!= kManifest
)
257 tracker_
->DidCreateNewFile(filename_
);
258 parent_dir_
= FilePath::FromUTF8Unsafe(fname
).DirName().AsUTF8Unsafe();
261 Status
ChromiumWritableFile::SyncParent() {
262 TRACE_EVENT0("leveldb", "SyncParent");
263 #if defined(OS_POSIX)
264 FilePath path
= FilePath::FromUTF8Unsafe(parent_dir_
);
265 base::File
f(path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
);
267 return MakeIOError(parent_dir_
, "Unable to open directory", kSyncParent
,
271 base::File::Error error
= LastFileError();
272 return MakeIOError(parent_dir_
, base::File::ErrorToString(error
),
279 Status
ChromiumWritableFile::Append(const Slice
& data
) {
280 if (file_type_
== kManifest
&& tracker_
->DoesDirNeedSync(filename_
)) {
281 Status s
= SyncParent();
284 tracker_
->DidSyncDir(filename_
);
287 int bytes_written
= file_
->WriteAtCurrentPos(data
.data(), data
.size());
288 if (bytes_written
!= data
.size()) {
289 base::File::Error error
= LastFileError();
290 uma_logger_
->RecordOSError(kWritableFileAppend
, error
);
291 return MakeIOError(filename_
, base::File::ErrorToString(error
),
292 kWritableFileAppend
, error
);
298 Status
ChromiumWritableFile::Close() {
303 Status
ChromiumWritableFile::Flush() {
304 // base::File doesn't do buffered I/O (i.e. POSIX FILE streams) so nothing to
309 Status
ChromiumWritableFile::Sync() {
310 TRACE_EVENT0("leveldb", "WritableFile::Sync");
312 if (!file_
->Flush()) {
313 base::File::Error error
= LastFileError();
314 uma_logger_
->RecordErrorAt(kWritableFileSync
);
315 return MakeIOError(filename_
, base::File::ErrorToString(error
),
316 kWritableFileSync
, error
);
319 if (make_backup_
&& file_type_
== kTable
)
320 uma_logger_
->RecordBackupResult(ChromiumEnv::MakeBackup(filename_
));
325 class IDBEnv
: public ChromiumEnv
{
327 IDBEnv() : ChromiumEnv() {
328 name_
= "LevelDBEnv.IDB";
333 base::LazyInstance
<IDBEnv
>::Leaky idb_env
= LAZY_INSTANCE_INITIALIZER
;
335 base::LazyInstance
<ChromiumEnv
>::Leaky default_env
= LAZY_INSTANCE_INITIALIZER
;
337 } // unnamed namespace
339 const char* MethodIDToString(MethodID method
) {
341 case kSequentialFileRead
:
342 return "SequentialFileRead";
343 case kSequentialFileSkip
:
344 return "SequentialFileSkip";
345 case kRandomAccessFileRead
:
346 return "RandomAccessFileRead";
347 case kWritableFileAppend
:
348 return "WritableFileAppend";
349 case kWritableFileClose
:
350 return "WritableFileClose";
351 case kWritableFileFlush
:
352 return "WritableFileFlush";
353 case kWritableFileSync
:
354 return "WritableFileSync";
355 case kNewSequentialFile
:
356 return "NewSequentialFile";
357 case kNewRandomAccessFile
:
358 return "NewRandomAccessFile";
359 case kNewWritableFile
:
360 return "NewWritableFile";
361 case kNewAppendableFile
:
362 return "NewAppendableFile";
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 leveldb::Status
& status
,
414 MethodID
* method_param
,
416 const std::string status_string
= status
.ToString();
418 if (RE2::PartialMatch(status_string
.c_str(), "ChromeMethodOnly: (\\d+)",
420 *method_param
= static_cast<MethodID
>(method
);
423 if (RE2::PartialMatch(status_string
.c_str(),
424 "ChromeMethodPFE: (\\d+)::.*::(\\d+)", &method
,
427 *method_param
= static_cast<MethodID
>(method
);
428 return METHOD_AND_PFE
;
430 if (RE2::PartialMatch(status_string
.c_str(),
431 "ChromeMethodErrno: (\\d+)::.*::(\\d+)", &method
,
433 *method_param
= static_cast<MethodID
>(method
);
434 return METHOD_AND_ERRNO
;
439 // Keep in sync with LevelDBCorruptionTypes in histograms.xml. Also, don't
440 // change the order because indices into this array have been recorded in uma
442 const char* patterns
[] = {
444 "log record too small",
445 "corrupted internal key",
447 "missing start of fragmented record",
448 "error in middle of record",
449 "unknown record type",
450 "truncated record at end",
453 "FileReader invoked with unexpected value",
455 "CURRENT file does not end with newline",
456 "no meta-nextfile entry",
457 "no meta-lognumber entry",
458 "no last-sequence-number entry",
459 "malformed WriteBatch",
460 "bad WriteBatch Put",
461 "bad WriteBatch Delete",
462 "unknown WriteBatch tag",
463 "WriteBatch has wrong count",
464 "bad entry in block",
465 "bad block contents",
467 "truncated block read",
468 "block checksum mismatch",
470 "corrupted compressed block contents",
476 // Returns 1-based index into the above array or 0 if nothing matches.
477 int GetCorruptionCode(const leveldb::Status
& status
) {
478 DCHECK(!status
.IsIOError());
479 DCHECK(!status
.ok());
480 const int kOtherError
= 0;
481 int error
= kOtherError
;
482 const std::string
& str_error
= status
.ToString();
483 const size_t kNumPatterns
= arraysize(patterns
);
484 for (size_t i
= 0; i
< kNumPatterns
; ++i
) {
485 if (str_error
.find(patterns
[i
]) != std::string::npos
) {
493 int GetNumCorruptionCodes() {
494 // + 1 for the "other" error that is returned when a corruption message
495 // doesn't match any of the patterns.
496 return arraysize(patterns
) + 1;
499 std::string
GetCorruptionMessage(const leveldb::Status
& status
) {
500 int code
= GetCorruptionCode(status
);
502 return "Unknown corruption";
503 return patterns
[code
- 1];
506 bool IndicatesDiskFull(const leveldb::Status
& status
) {
509 leveldb_env::MethodID method
;
511 leveldb_env::ErrorParsingResult result
=
512 leveldb_env::ParseMethodAndError(status
, &method
, &error
);
513 return (result
== leveldb_env::METHOD_AND_PFE
&&
514 static_cast<base::File::Error
>(error
) ==
515 base::File::FILE_ERROR_NO_SPACE
) ||
516 (result
== leveldb_env::METHOD_AND_ERRNO
&& error
== ENOSPC
);
519 bool ChromiumEnv::MakeBackup(const std::string
& fname
) {
520 FilePath original_table_name
= FilePath::FromUTF8Unsafe(fname
);
521 FilePath backup_table_name
=
522 original_table_name
.ReplaceExtension(backup_table_extension
);
523 return base::CopyFile(original_table_name
, backup_table_name
);
526 ChromiumEnv::ChromiumEnv()
527 : name_("LevelDBEnv"),
530 started_bgthread_(false),
531 kMaxRetryTimeMillis(1000) {
534 ChromiumEnv::~ChromiumEnv() {
535 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
536 // ensure that behavior isn't accidentally changed, but there's an instance in
537 // a unit test that is deleted.
540 bool ChromiumEnv::FileExists(const std::string
& fname
) {
541 return base::PathExists(FilePath::FromUTF8Unsafe(fname
));
544 const char* ChromiumEnv::FileErrorString(base::File::Error error
) {
546 case base::File::FILE_ERROR_FAILED
:
547 return "No further details.";
548 case base::File::FILE_ERROR_IN_USE
:
549 return "File currently in use.";
550 case base::File::FILE_ERROR_EXISTS
:
551 return "File already exists.";
552 case base::File::FILE_ERROR_NOT_FOUND
:
553 return "File not found.";
554 case base::File::FILE_ERROR_ACCESS_DENIED
:
555 return "Access denied.";
556 case base::File::FILE_ERROR_TOO_MANY_OPENED
:
557 return "Too many files open.";
558 case base::File::FILE_ERROR_NO_MEMORY
:
559 return "Out of memory.";
560 case base::File::FILE_ERROR_NO_SPACE
:
561 return "No space left on drive.";
562 case base::File::FILE_ERROR_NOT_A_DIRECTORY
:
563 return "Not a directory.";
564 case base::File::FILE_ERROR_INVALID_OPERATION
:
565 return "Invalid operation.";
566 case base::File::FILE_ERROR_SECURITY
:
567 return "Security error.";
568 case base::File::FILE_ERROR_ABORT
:
569 return "File operation aborted.";
570 case base::File::FILE_ERROR_NOT_A_FILE
:
571 return "The supplied path was not a file.";
572 case base::File::FILE_ERROR_NOT_EMPTY
:
573 return "The file was not empty.";
574 case base::File::FILE_ERROR_INVALID_URL
:
575 return "Invalid URL.";
576 case base::File::FILE_ERROR_IO
:
577 return "OS or hardware error.";
578 case base::File::FILE_OK
:
580 case base::File::FILE_ERROR_MAX
:
584 return "Unknown error.";
587 FilePath
ChromiumEnv::RestoreFromBackup(const FilePath
& base_name
) {
588 FilePath table_name
= base_name
.AddExtension(table_extension
);
589 bool result
= base::CopyFile(base_name
.AddExtension(backup_table_extension
),
591 std::string
uma_name(name_
);
592 uma_name
.append(".TableRestore");
593 base::BooleanHistogram::FactoryGet(
594 uma_name
, base::Histogram::kUmaTargetedHistogramFlag
)->AddBoolean(result
);
598 void ChromiumEnv::RestoreIfNecessary(const std::string
& dir
,
599 std::vector
<std::string
>* dir_entries
) {
600 std::set
<FilePath
> tables_found
;
601 std::set
<FilePath
> backups_found
;
602 for (const std::string
& entry
: *dir_entries
) {
603 FilePath current
= FilePath::FromUTF8Unsafe(entry
);
604 if (current
.MatchesExtension(table_extension
))
605 tables_found
.insert(current
.RemoveExtension());
606 if (current
.MatchesExtension(backup_table_extension
))
607 backups_found
.insert(current
.RemoveExtension());
609 std::set
<FilePath
> backups_only
=
610 base::STLSetDifference
<std::set
<FilePath
>>(backups_found
, tables_found
);
612 if (backups_only
.size()) {
613 std::string
uma_name(name_
);
614 uma_name
.append(".MissingFiles");
615 int num_missing_files
=
616 backups_only
.size() > INT_MAX
? INT_MAX
: backups_only
.size();
617 base::Histogram::FactoryGet(uma_name
,
621 base::Histogram::kUmaTargetedHistogramFlag
)
622 ->Add(num_missing_files
);
624 FilePath dir_path
= FilePath::FromUTF8Unsafe(dir
);
625 for (const FilePath
& backup
: backups_only
) {
626 FilePath restored_table_name
= RestoreFromBackup(dir_path
.Append(backup
));
627 dir_entries
->push_back(restored_table_name
.BaseName().AsUTF8Unsafe());
631 Status
ChromiumEnv::GetChildren(const std::string
& dir
,
632 std::vector
<std::string
>* result
) {
633 std::vector
<FilePath
> entries
;
634 base::File::Error error
=
635 GetDirectoryEntries(FilePath::FromUTF8Unsafe(dir
), &entries
);
636 if (error
!= base::File::FILE_OK
) {
637 RecordOSError(kGetChildren
, error
);
638 return MakeIOError(dir
, "Could not open/read directory", kGetChildren
,
643 for (const auto& entry
: entries
)
644 result
->push_back(entry
.BaseName().AsUTF8Unsafe());
647 RestoreIfNecessary(dir
, result
);
652 Status
ChromiumEnv::DeleteFile(const std::string
& fname
) {
654 FilePath fname_filepath
= FilePath::FromUTF8Unsafe(fname
);
655 // TODO(jorlow): Should we assert this is a file?
656 if (!base::DeleteFile(fname_filepath
, false)) {
657 result
= MakeIOError(fname
, "Could not delete file.", kDeleteFile
);
658 RecordErrorAt(kDeleteFile
);
660 if (make_backup_
&& fname_filepath
.MatchesExtension(table_extension
)) {
661 base::DeleteFile(fname_filepath
.ReplaceExtension(backup_table_extension
),
667 Status
ChromiumEnv::CreateDir(const std::string
& name
) {
669 base::File::Error error
= base::File::FILE_OK
;
670 Retrier
retrier(kCreateDir
, this);
672 if (base::CreateDirectoryAndGetError(FilePath::FromUTF8Unsafe(name
),
675 } while (retrier
.ShouldKeepTrying(error
));
676 result
= MakeIOError(name
, "Could not create directory.", kCreateDir
, error
);
677 RecordOSError(kCreateDir
, error
);
681 Status
ChromiumEnv::DeleteDir(const std::string
& name
) {
683 // TODO(jorlow): Should we assert this is a directory?
684 if (!base::DeleteFile(FilePath::FromUTF8Unsafe(name
), false)) {
685 result
= MakeIOError(name
, "Could not delete directory.", kDeleteDir
);
686 RecordErrorAt(kDeleteDir
);
691 Status
ChromiumEnv::GetFileSize(const std::string
& fname
, uint64_t* size
) {
694 if (!base::GetFileSize(FilePath::FromUTF8Unsafe(fname
), &signed_size
)) {
696 s
= MakeIOError(fname
, "Could not determine file size.", kGetFileSize
);
697 RecordErrorAt(kGetFileSize
);
699 *size
= static_cast<uint64_t>(signed_size
);
704 Status
ChromiumEnv::RenameFile(const std::string
& src
, const std::string
& dst
) {
706 FilePath src_file_path
= FilePath::FromUTF8Unsafe(src
);
707 if (!base::PathExists(src_file_path
))
709 FilePath destination
= FilePath::FromUTF8Unsafe(dst
);
711 Retrier
retrier(kRenameFile
, this);
712 base::File::Error error
= base::File::FILE_OK
;
714 if (base::ReplaceFile(src_file_path
, destination
, &error
))
716 } while (retrier
.ShouldKeepTrying(error
));
718 DCHECK(error
!= base::File::FILE_OK
);
719 RecordOSError(kRenameFile
, error
);
723 "Could not rename file: %s",
724 FileErrorString(error
));
725 return MakeIOError(src
, buf
, kRenameFile
, error
);
728 Status
ChromiumEnv::LockFile(const std::string
& fname
, FileLock
** lock
) {
731 int flags
= base::File::FLAG_OPEN_ALWAYS
|
732 base::File::FLAG_READ
|
733 base::File::FLAG_WRITE
;
734 base::File::Error error_code
;
736 Retrier
retrier(kLockFile
, this);
738 file
.Initialize(FilePath::FromUTF8Unsafe(fname
), flags
);
740 error_code
= file
.error_details();
741 } while (!file
.IsValid() && retrier
.ShouldKeepTrying(error_code
));
743 if (!file
.IsValid()) {
744 if (error_code
== base::File::FILE_ERROR_NOT_FOUND
) {
745 FilePath parent
= FilePath::FromUTF8Unsafe(fname
).DirName();
746 FilePath last_parent
;
747 int num_missing_ancestors
= 0;
749 if (base::DirectoryExists(parent
))
751 ++num_missing_ancestors
;
752 last_parent
= parent
;
753 parent
= parent
.DirName();
754 } while (parent
!= last_parent
);
755 RecordLockFileAncestors(num_missing_ancestors
);
758 result
= MakeIOError(fname
, FileErrorString(error_code
), kLockFile
,
760 RecordOSError(kLockFile
, error_code
);
764 if (!locks_
.Insert(fname
)) {
765 result
= MakeIOError(fname
, "Lock file already locked.", kLockFile
);
769 Retrier lock_retrier
= Retrier(kLockFile
, this);
771 error_code
= file
.Lock();
772 } while (error_code
!= base::File::FILE_OK
&&
773 retrier
.ShouldKeepTrying(error_code
));
775 if (error_code
!= base::File::FILE_OK
) {
776 locks_
.Remove(fname
);
777 result
= MakeIOError(fname
, FileErrorString(error_code
), kLockFile
,
779 RecordOSError(kLockFile
, error_code
);
783 ChromiumFileLock
* my_lock
= new ChromiumFileLock
;
784 my_lock
->file_
= file
.Pass();
785 my_lock
->name_
= fname
;
790 Status
ChromiumEnv::UnlockFile(FileLock
* lock
) {
791 ChromiumFileLock
* my_lock
= reinterpret_cast<ChromiumFileLock
*>(lock
);
794 base::File::Error error_code
= my_lock
->file_
.Unlock();
795 if (error_code
!= base::File::FILE_OK
) {
797 MakeIOError(my_lock
->name_
, "Could not unlock lock file.", kUnlockFile
);
798 RecordOSError(kUnlockFile
, error_code
);
800 bool removed
= locks_
.Remove(my_lock
->name_
);
806 Status
ChromiumEnv::GetTestDirectory(std::string
* path
) {
808 if (test_directory_
.empty()) {
809 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix
,
812 RecordErrorAt(kGetTestDirectory
);
814 "Could not create temp directory.", "", kGetTestDirectory
);
817 *path
= test_directory_
.AsUTF8Unsafe();
822 Status
ChromiumEnv::NewLogger(const std::string
& fname
,
823 leveldb::Logger
** result
) {
824 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
825 scoped_ptr
<base::File
> f(new base::File(
826 path
, base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
));
829 RecordOSError(kNewLogger
, f
->error_details());
830 return MakeIOError(fname
, "Unable to create log file", kNewLogger
,
833 *result
= new leveldb::ChromiumLogger(f
.release());
838 Status
ChromiumEnv::NewSequentialFile(const std::string
& fname
,
839 leveldb::SequentialFile
** result
) {
840 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
841 scoped_ptr
<base::File
> f(
842 new base::File(path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
));
845 RecordOSError(kNewSequentialFile
, f
->error_details());
846 return MakeIOError(fname
, "Unable to create sequential file",
847 kNewSequentialFile
, f
->error_details());
849 *result
= new ChromiumSequentialFile(fname
, f
.release(), this);
854 void ChromiumEnv::RecordOpenFilesLimit(const std::string
& type
) {
855 #if defined(OS_POSIX)
856 GetMaxFDHistogram(type
)->Add(base::GetMaxFds());
857 #elif defined(OS_WIN)
858 // Windows is only limited by available memory
860 #error "Need to determine limit to open files for this OS"
864 Status
ChromiumEnv::NewRandomAccessFile(const std::string
& fname
,
865 leveldb::RandomAccessFile
** result
) {
866 int flags
= base::File::FLAG_READ
| base::File::FLAG_OPEN
;
867 base::File
file(FilePath::FromUTF8Unsafe(fname
), flags
);
868 if (file
.IsValid()) {
869 *result
= new ChromiumRandomAccessFile(fname
, file
.Pass(), this);
870 RecordOpenFilesLimit("Success");
873 base::File::Error error_code
= file
.error_details();
874 if (error_code
== base::File::FILE_ERROR_TOO_MANY_OPENED
)
875 RecordOpenFilesLimit("TooManyOpened");
877 RecordOpenFilesLimit("OtherError");
879 RecordOSError(kNewRandomAccessFile
, error_code
);
880 return MakeIOError(fname
, FileErrorString(error_code
), kNewRandomAccessFile
,
884 Status
ChromiumEnv::NewWritableFile(const std::string
& fname
,
885 leveldb::WritableFile
** result
) {
887 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
888 scoped_ptr
<base::File
> f(new base::File(
889 path
, base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
));
891 RecordErrorAt(kNewWritableFile
);
892 return MakeIOError(fname
, "Unable to create writable file",
893 kNewWritableFile
, f
->error_details());
896 new ChromiumWritableFile(fname
, f
.release(), this, this, make_backup_
);
901 uint64_t ChromiumEnv::NowMicros() {
902 return base::TimeTicks::Now().ToInternalValue();
905 void ChromiumEnv::SleepForMicroseconds(int micros
) {
906 // Round up to the next millisecond.
907 base::PlatformThread::Sleep(base::TimeDelta::FromMicroseconds(micros
));
910 void ChromiumEnv::RecordErrorAt(MethodID method
) const {
911 GetMethodIOErrorHistogram()->Add(method
);
914 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors
) const {
915 GetLockFileAncestorHistogram()->Add(num_missing_ancestors
);
918 void ChromiumEnv::RecordOSError(MethodID method
,
919 base::File::Error error
) const {
921 RecordErrorAt(method
);
922 GetOSErrorHistogram(method
, -base::File::FILE_ERROR_MAX
)->Add(-error
);
925 void ChromiumEnv::RecordBackupResult(bool result
) const {
926 std::string
uma_name(name_
);
927 uma_name
.append(".TableBackup");
928 base::BooleanHistogram::FactoryGet(
929 uma_name
, base::Histogram::kUmaTargetedHistogramFlag
)->AddBoolean(result
);
932 base::HistogramBase
* ChromiumEnv::GetOSErrorHistogram(MethodID method
,
934 std::string
uma_name(name_
);
935 // TODO(dgrogan): This is probably not the best way to concatenate strings.
936 uma_name
.append(".IOError.").append(MethodIDToString(method
));
937 return base::LinearHistogram::FactoryGet(uma_name
, 1, limit
, limit
+ 1,
938 base::Histogram::kUmaTargetedHistogramFlag
);
941 base::HistogramBase
* ChromiumEnv::GetMethodIOErrorHistogram() const {
942 std::string
uma_name(name_
);
943 uma_name
.append(".IOError");
944 return base::LinearHistogram::FactoryGet(uma_name
, 1, kNumEntries
,
945 kNumEntries
+ 1, base::Histogram::kUmaTargetedHistogramFlag
);
948 base::HistogramBase
* ChromiumEnv::GetMaxFDHistogram(
949 const std::string
& type
) const {
950 std::string
uma_name(name_
);
951 uma_name
.append(".MaxFDs.").append(type
);
952 // These numbers make each bucket twice as large as the previous bucket.
953 const int kFirstEntry
= 1;
954 const int kLastEntry
= 65536;
955 const int kNumBuckets
= 18;
956 return base::Histogram::FactoryGet(
957 uma_name
, kFirstEntry
, kLastEntry
, kNumBuckets
,
958 base::Histogram::kUmaTargetedHistogramFlag
);
961 base::HistogramBase
* ChromiumEnv::GetLockFileAncestorHistogram() const {
962 std::string
uma_name(name_
);
963 uma_name
.append(".LockFileAncestorsNotFound");
966 const int kNumBuckets
= 11;
967 return base::LinearHistogram::FactoryGet(
968 uma_name
, kMin
, kMax
, kNumBuckets
,
969 base::Histogram::kUmaTargetedHistogramFlag
);
972 base::HistogramBase
* ChromiumEnv::GetRetryTimeHistogram(MethodID method
) const {
973 std::string
uma_name(name_
);
974 // TODO(dgrogan): This is probably not the best way to concatenate strings.
975 uma_name
.append(".TimeUntilSuccessFor").append(MethodIDToString(method
));
977 const int kBucketSizeMillis
= 25;
978 // Add 2, 1 for each of the buckets <1 and >max.
979 const int kNumBuckets
= kMaxRetryTimeMillis
/ kBucketSizeMillis
+ 2;
980 return base::Histogram::FactoryTimeGet(
981 uma_name
, base::TimeDelta::FromMilliseconds(1),
982 base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis
+ 1),
984 base::Histogram::kUmaTargetedHistogramFlag
);
987 base::HistogramBase
* ChromiumEnv::GetRecoveredFromErrorHistogram(
988 MethodID method
) const {
989 std::string
uma_name(name_
);
990 uma_name
.append(".RetryRecoveredFromErrorIn")
991 .append(MethodIDToString(method
));
992 return base::LinearHistogram::FactoryGet(uma_name
, 1, kNumEntries
,
993 kNumEntries
+ 1, base::Histogram::kUmaTargetedHistogramFlag
);
996 class Thread
: public base::PlatformThread::Delegate
{
998 Thread(void (*function
)(void* arg
), void* arg
)
999 : function_(function
), arg_(arg
) {
1000 base::PlatformThreadHandle handle
;
1001 bool success
= base::PlatformThread::Create(0, this, &handle
);
1004 virtual ~Thread() {}
1005 void ThreadMain() override
{
1011 void (*function_
)(void* arg
);
1015 void ChromiumEnv::Schedule(ScheduleFunc
* function
, void* arg
) {
1018 // Start background thread if necessary
1019 if (!started_bgthread_
) {
1020 started_bgthread_
= true;
1021 StartThread(&ChromiumEnv::BGThreadWrapper
, this);
1024 // If the queue is currently empty, the background thread may currently be
1026 if (queue_
.empty()) {
1030 // Add to priority queue
1031 queue_
.push_back(BGItem());
1032 queue_
.back().function
= function
;
1033 queue_
.back().arg
= arg
;
1038 void ChromiumEnv::BGThread() {
1039 base::PlatformThread::SetName(name_
.c_str());
1042 // Wait until there is an item that is ready to run
1044 while (queue_
.empty()) {
1048 void (*function
)(void*) = queue_
.front().function
;
1049 void* arg
= queue_
.front().arg
;
1053 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
1058 void ChromiumEnv::StartThread(void (*function
)(void* arg
), void* arg
) {
1059 new Thread(function
, arg
); // Will self-delete.
1062 static std::string
GetDirName(const std::string
& filename
) {
1063 return FilePath::FromUTF8Unsafe(filename
).DirName().AsUTF8Unsafe();
1066 void ChromiumEnv::DidCreateNewFile(const std::string
& filename
) {
1067 base::AutoLock
auto_lock(directory_sync_lock_
);
1068 directories_needing_sync_
.insert(GetDirName(filename
));
1071 bool ChromiumEnv::DoesDirNeedSync(const std::string
& filename
) {
1072 base::AutoLock
auto_lock(directory_sync_lock_
);
1073 return ContainsKey(directories_needing_sync_
, GetDirName(filename
));
1076 void ChromiumEnv::DidSyncDir(const std::string
& filename
) {
1077 base::AutoLock
auto_lock(directory_sync_lock_
);
1078 directories_needing_sync_
.erase(GetDirName(filename
));
1081 } // namespace leveldb_env
1086 return leveldb_env::idb_env
.Pointer();
1089 Env
* Env::Default() {
1090 return leveldb_env::default_env
.Pointer();
1093 } // namespace leveldb