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/files/file_util.h"
13 #include "base/lazy_instance.h"
14 #include "base/memory/shared_memory.h"
15 #include "base/metrics/histogram.h"
16 #include "base/process/process_metrics.h"
17 #include "base/stl_util.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/trace_event/trace_event.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
,
415 base::File::Error
* error
) {
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
);
424 if (RE2::PartialMatch(status_string
.c_str(),
425 "ChromeMethodPFE: (\\d+)::.*::(\\d+)", &method
,
427 *method_param
= static_cast<MethodID
>(method
);
428 *error
= static_cast<base::File::Error
>(-parsed_error
);
429 DCHECK_LT(*error
, base::File::FILE_OK
);
430 DCHECK_GT(*error
, base::File::FILE_ERROR_MAX
);
431 return METHOD_AND_PFE
;
436 // Keep in sync with LevelDBCorruptionTypes in histograms.xml. Also, don't
437 // change the order because indices into this array have been recorded in uma
439 const char* patterns
[] = {
441 "log record too small",
442 "corrupted internal key",
444 "missing start of fragmented record",
445 "error in middle of record",
446 "unknown record type",
447 "truncated record at end",
450 "FileReader invoked with unexpected value",
452 "CURRENT file does not end with newline",
453 "no meta-nextfile entry",
454 "no meta-lognumber entry",
455 "no last-sequence-number entry",
456 "malformed WriteBatch",
457 "bad WriteBatch Put",
458 "bad WriteBatch Delete",
459 "unknown WriteBatch tag",
460 "WriteBatch has wrong count",
461 "bad entry in block",
462 "bad block contents",
464 "truncated block read",
465 "block checksum mismatch",
467 "corrupted compressed block contents",
473 // Returns 1-based index into the above array or 0 if nothing matches.
474 int GetCorruptionCode(const leveldb::Status
& status
) {
475 DCHECK(!status
.IsIOError());
476 DCHECK(!status
.ok());
477 const int kOtherError
= 0;
478 int error
= kOtherError
;
479 const std::string
& str_error
= status
.ToString();
480 const size_t kNumPatterns
= arraysize(patterns
);
481 for (size_t i
= 0; i
< kNumPatterns
; ++i
) {
482 if (str_error
.find(patterns
[i
]) != std::string::npos
) {
490 int GetNumCorruptionCodes() {
491 // + 1 for the "other" error that is returned when a corruption message
492 // doesn't match any of the patterns.
493 return arraysize(patterns
) + 1;
496 std::string
GetCorruptionMessage(const leveldb::Status
& status
) {
497 int code
= GetCorruptionCode(status
);
499 return "Unknown corruption";
500 return patterns
[code
- 1];
503 bool IndicatesDiskFull(const leveldb::Status
& status
) {
506 leveldb_env::MethodID method
;
507 base::File::Error error
= base::File::FILE_OK
;
508 leveldb_env::ErrorParsingResult result
=
509 leveldb_env::ParseMethodAndError(status
, &method
, &error
);
510 return (result
== leveldb_env::METHOD_AND_PFE
&&
511 static_cast<base::File::Error
>(error
) ==
512 base::File::FILE_ERROR_NO_SPACE
);
515 bool ChromiumEnv::MakeBackup(const std::string
& fname
) {
516 FilePath original_table_name
= FilePath::FromUTF8Unsafe(fname
);
517 FilePath backup_table_name
=
518 original_table_name
.ReplaceExtension(backup_table_extension
);
519 return base::CopyFile(original_table_name
, backup_table_name
);
522 ChromiumEnv::ChromiumEnv()
523 : name_("LevelDBEnv"),
526 started_bgthread_(false),
527 kMaxRetryTimeMillis(1000) {
530 ChromiumEnv::~ChromiumEnv() {
531 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
532 // ensure that behavior isn't accidentally changed, but there's an instance in
533 // a unit test that is deleted.
536 bool ChromiumEnv::FileExists(const std::string
& fname
) {
537 return base::PathExists(FilePath::FromUTF8Unsafe(fname
));
540 const char* ChromiumEnv::FileErrorString(base::File::Error error
) {
542 case base::File::FILE_ERROR_FAILED
:
543 return "No further details.";
544 case base::File::FILE_ERROR_IN_USE
:
545 return "File currently in use.";
546 case base::File::FILE_ERROR_EXISTS
:
547 return "File already exists.";
548 case base::File::FILE_ERROR_NOT_FOUND
:
549 return "File not found.";
550 case base::File::FILE_ERROR_ACCESS_DENIED
:
551 return "Access denied.";
552 case base::File::FILE_ERROR_TOO_MANY_OPENED
:
553 return "Too many files open.";
554 case base::File::FILE_ERROR_NO_MEMORY
:
555 return "Out of memory.";
556 case base::File::FILE_ERROR_NO_SPACE
:
557 return "No space left on drive.";
558 case base::File::FILE_ERROR_NOT_A_DIRECTORY
:
559 return "Not a directory.";
560 case base::File::FILE_ERROR_INVALID_OPERATION
:
561 return "Invalid operation.";
562 case base::File::FILE_ERROR_SECURITY
:
563 return "Security error.";
564 case base::File::FILE_ERROR_ABORT
:
565 return "File operation aborted.";
566 case base::File::FILE_ERROR_NOT_A_FILE
:
567 return "The supplied path was not a file.";
568 case base::File::FILE_ERROR_NOT_EMPTY
:
569 return "The file was not empty.";
570 case base::File::FILE_ERROR_INVALID_URL
:
571 return "Invalid URL.";
572 case base::File::FILE_ERROR_IO
:
573 return "OS or hardware error.";
574 case base::File::FILE_OK
:
576 case base::File::FILE_ERROR_MAX
:
580 return "Unknown error.";
583 FilePath
ChromiumEnv::RestoreFromBackup(const FilePath
& base_name
) {
584 FilePath table_name
= base_name
.AddExtension(table_extension
);
585 bool result
= base::CopyFile(base_name
.AddExtension(backup_table_extension
),
587 std::string
uma_name(name_
);
588 uma_name
.append(".TableRestore");
589 base::BooleanHistogram::FactoryGet(
590 uma_name
, base::Histogram::kUmaTargetedHistogramFlag
)->AddBoolean(result
);
594 void ChromiumEnv::RestoreIfNecessary(const std::string
& dir
,
595 std::vector
<std::string
>* dir_entries
) {
596 std::set
<FilePath
> tables_found
;
597 std::set
<FilePath
> backups_found
;
598 for (const std::string
& entry
: *dir_entries
) {
599 FilePath current
= FilePath::FromUTF8Unsafe(entry
);
600 if (current
.MatchesExtension(table_extension
))
601 tables_found
.insert(current
.RemoveExtension());
602 if (current
.MatchesExtension(backup_table_extension
))
603 backups_found
.insert(current
.RemoveExtension());
605 std::set
<FilePath
> backups_only
=
606 base::STLSetDifference
<std::set
<FilePath
>>(backups_found
, tables_found
);
608 if (backups_only
.size()) {
609 std::string
uma_name(name_
);
610 uma_name
.append(".MissingFiles");
611 int num_missing_files
=
612 backups_only
.size() > INT_MAX
? INT_MAX
: backups_only
.size();
613 base::Histogram::FactoryGet(uma_name
,
617 base::Histogram::kUmaTargetedHistogramFlag
)
618 ->Add(num_missing_files
);
620 FilePath dir_path
= FilePath::FromUTF8Unsafe(dir
);
621 for (const FilePath
& backup
: backups_only
) {
622 FilePath restored_table_name
= RestoreFromBackup(dir_path
.Append(backup
));
623 dir_entries
->push_back(restored_table_name
.BaseName().AsUTF8Unsafe());
627 Status
ChromiumEnv::GetChildren(const std::string
& dir
,
628 std::vector
<std::string
>* result
) {
629 std::vector
<FilePath
> entries
;
630 base::File::Error error
=
631 GetDirectoryEntries(FilePath::FromUTF8Unsafe(dir
), &entries
);
632 if (error
!= base::File::FILE_OK
) {
633 RecordOSError(kGetChildren
, error
);
634 return MakeIOError(dir
, "Could not open/read directory", kGetChildren
,
639 for (const auto& entry
: entries
)
640 result
->push_back(entry
.BaseName().AsUTF8Unsafe());
643 RestoreIfNecessary(dir
, result
);
648 Status
ChromiumEnv::DeleteFile(const std::string
& fname
) {
650 FilePath fname_filepath
= FilePath::FromUTF8Unsafe(fname
);
651 // TODO(jorlow): Should we assert this is a file?
652 if (!base::DeleteFile(fname_filepath
, false)) {
653 result
= MakeIOError(fname
, "Could not delete file.", kDeleteFile
);
654 RecordErrorAt(kDeleteFile
);
656 if (make_backup_
&& fname_filepath
.MatchesExtension(table_extension
)) {
657 base::DeleteFile(fname_filepath
.ReplaceExtension(backup_table_extension
),
663 Status
ChromiumEnv::CreateDir(const std::string
& name
) {
665 base::File::Error error
= base::File::FILE_OK
;
666 Retrier
retrier(kCreateDir
, this);
668 if (base::CreateDirectoryAndGetError(FilePath::FromUTF8Unsafe(name
),
671 } while (retrier
.ShouldKeepTrying(error
));
672 result
= MakeIOError(name
, "Could not create directory.", kCreateDir
, error
);
673 RecordOSError(kCreateDir
, error
);
677 Status
ChromiumEnv::DeleteDir(const std::string
& name
) {
679 // TODO(jorlow): Should we assert this is a directory?
680 if (!base::DeleteFile(FilePath::FromUTF8Unsafe(name
), false)) {
681 result
= MakeIOError(name
, "Could not delete directory.", kDeleteDir
);
682 RecordErrorAt(kDeleteDir
);
687 Status
ChromiumEnv::GetFileSize(const std::string
& fname
, uint64_t* size
) {
690 if (!base::GetFileSize(FilePath::FromUTF8Unsafe(fname
), &signed_size
)) {
692 s
= MakeIOError(fname
, "Could not determine file size.", kGetFileSize
);
693 RecordErrorAt(kGetFileSize
);
695 *size
= static_cast<uint64_t>(signed_size
);
700 Status
ChromiumEnv::RenameFile(const std::string
& src
, const std::string
& dst
) {
702 FilePath src_file_path
= FilePath::FromUTF8Unsafe(src
);
703 if (!base::PathExists(src_file_path
))
705 FilePath destination
= FilePath::FromUTF8Unsafe(dst
);
707 Retrier
retrier(kRenameFile
, this);
708 base::File::Error error
= base::File::FILE_OK
;
710 if (base::ReplaceFile(src_file_path
, destination
, &error
))
712 } while (retrier
.ShouldKeepTrying(error
));
714 DCHECK(error
!= base::File::FILE_OK
);
715 RecordOSError(kRenameFile
, error
);
719 "Could not rename file: %s",
720 FileErrorString(error
));
721 return MakeIOError(src
, buf
, kRenameFile
, error
);
724 Status
ChromiumEnv::LockFile(const std::string
& fname
, FileLock
** lock
) {
727 int flags
= base::File::FLAG_OPEN_ALWAYS
|
728 base::File::FLAG_READ
|
729 base::File::FLAG_WRITE
;
730 base::File::Error error_code
;
732 Retrier
retrier(kLockFile
, this);
734 file
.Initialize(FilePath::FromUTF8Unsafe(fname
), flags
);
736 error_code
= file
.error_details();
737 } while (!file
.IsValid() && retrier
.ShouldKeepTrying(error_code
));
739 if (!file
.IsValid()) {
740 if (error_code
== base::File::FILE_ERROR_NOT_FOUND
) {
741 FilePath parent
= FilePath::FromUTF8Unsafe(fname
).DirName();
742 FilePath last_parent
;
743 int num_missing_ancestors
= 0;
745 if (base::DirectoryExists(parent
))
747 ++num_missing_ancestors
;
748 last_parent
= parent
;
749 parent
= parent
.DirName();
750 } while (parent
!= last_parent
);
751 RecordLockFileAncestors(num_missing_ancestors
);
754 result
= MakeIOError(fname
, FileErrorString(error_code
), kLockFile
,
756 RecordOSError(kLockFile
, error_code
);
760 if (!locks_
.Insert(fname
)) {
761 result
= MakeIOError(fname
, "Lock file already locked.", kLockFile
);
765 Retrier lock_retrier
= Retrier(kLockFile
, this);
767 error_code
= file
.Lock();
768 } while (error_code
!= base::File::FILE_OK
&&
769 retrier
.ShouldKeepTrying(error_code
));
771 if (error_code
!= base::File::FILE_OK
) {
772 locks_
.Remove(fname
);
773 result
= MakeIOError(fname
, FileErrorString(error_code
), kLockFile
,
775 RecordOSError(kLockFile
, error_code
);
779 ChromiumFileLock
* my_lock
= new ChromiumFileLock
;
780 my_lock
->file_
= file
.Pass();
781 my_lock
->name_
= fname
;
786 Status
ChromiumEnv::UnlockFile(FileLock
* lock
) {
787 ChromiumFileLock
* my_lock
= reinterpret_cast<ChromiumFileLock
*>(lock
);
790 base::File::Error error_code
= my_lock
->file_
.Unlock();
791 if (error_code
!= base::File::FILE_OK
) {
793 MakeIOError(my_lock
->name_
, "Could not unlock lock file.", kUnlockFile
);
794 RecordOSError(kUnlockFile
, error_code
);
796 bool removed
= locks_
.Remove(my_lock
->name_
);
802 Status
ChromiumEnv::GetTestDirectory(std::string
* path
) {
804 if (test_directory_
.empty()) {
805 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix
,
808 RecordErrorAt(kGetTestDirectory
);
810 "Could not create temp directory.", "", kGetTestDirectory
);
813 *path
= test_directory_
.AsUTF8Unsafe();
818 Status
ChromiumEnv::NewLogger(const std::string
& fname
,
819 leveldb::Logger
** result
) {
820 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
821 scoped_ptr
<base::File
> f(new base::File(
822 path
, base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
));
825 RecordOSError(kNewLogger
, f
->error_details());
826 return MakeIOError(fname
, "Unable to create log file", kNewLogger
,
829 *result
= new leveldb::ChromiumLogger(f
.release());
834 Status
ChromiumEnv::NewSequentialFile(const std::string
& fname
,
835 leveldb::SequentialFile
** result
) {
836 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
837 scoped_ptr
<base::File
> f(
838 new base::File(path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
));
841 RecordOSError(kNewSequentialFile
, f
->error_details());
842 return MakeIOError(fname
, "Unable to create sequential file",
843 kNewSequentialFile
, f
->error_details());
845 *result
= new ChromiumSequentialFile(fname
, f
.release(), this);
850 void ChromiumEnv::RecordOpenFilesLimit(const std::string
& type
) {
851 #if defined(OS_POSIX)
852 GetMaxFDHistogram(type
)->Add(base::GetMaxFds());
853 #elif defined(OS_WIN)
854 // Windows is only limited by available memory
856 #error "Need to determine limit to open files for this OS"
860 Status
ChromiumEnv::NewRandomAccessFile(const std::string
& fname
,
861 leveldb::RandomAccessFile
** result
) {
862 int flags
= base::File::FLAG_READ
| base::File::FLAG_OPEN
;
863 base::File
file(FilePath::FromUTF8Unsafe(fname
), flags
);
864 if (file
.IsValid()) {
865 *result
= new ChromiumRandomAccessFile(fname
, file
.Pass(), this);
866 RecordOpenFilesLimit("Success");
869 base::File::Error error_code
= file
.error_details();
870 if (error_code
== base::File::FILE_ERROR_TOO_MANY_OPENED
)
871 RecordOpenFilesLimit("TooManyOpened");
873 RecordOpenFilesLimit("OtherError");
875 RecordOSError(kNewRandomAccessFile
, error_code
);
876 return MakeIOError(fname
, FileErrorString(error_code
), kNewRandomAccessFile
,
880 Status
ChromiumEnv::NewWritableFile(const std::string
& fname
,
881 leveldb::WritableFile
** result
) {
883 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
884 scoped_ptr
<base::File
> f(new base::File(
885 path
, base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
));
887 RecordErrorAt(kNewWritableFile
);
888 return MakeIOError(fname
, "Unable to create writable file",
889 kNewWritableFile
, f
->error_details());
892 new ChromiumWritableFile(fname
, f
.release(), this, this, make_backup_
);
897 Status
ChromiumEnv::NewAppendableFile(const std::string
& fname
,
898 leveldb::WritableFile
** result
) {
899 #if defined(OS_CHROMEOS)
900 // Disabled until crbug.com/460568 is fixed. Technically this method shouldn't
901 // be called if reuse_logs is false, but a leveldb bug (fixed, but not yet in
902 // Chrome) still calls this function. Using default leveldb Env implementation
903 // to workaround this bug.
904 return Env::NewAppendableFile(fname
, result
);
908 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
909 scoped_ptr
<base::File
> f(new base::File(
910 path
, base::File::FLAG_OPEN_ALWAYS
| base::File::FLAG_APPEND
));
912 RecordErrorAt(kNewAppendableFile
);
913 return MakeIOError(fname
, "Unable to create appendable file",
914 kNewAppendableFile
, f
->error_details());
917 new ChromiumWritableFile(fname
, f
.release(), this, this, make_backup_
);
921 uint64_t ChromiumEnv::NowMicros() {
922 return base::TimeTicks::Now().ToInternalValue();
925 void ChromiumEnv::SleepForMicroseconds(int micros
) {
926 // Round up to the next millisecond.
927 base::PlatformThread::Sleep(base::TimeDelta::FromMicroseconds(micros
));
930 void ChromiumEnv::RecordErrorAt(MethodID method
) const {
931 GetMethodIOErrorHistogram()->Add(method
);
934 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors
) const {
935 GetLockFileAncestorHistogram()->Add(num_missing_ancestors
);
938 void ChromiumEnv::RecordOSError(MethodID method
,
939 base::File::Error error
) const {
941 RecordErrorAt(method
);
942 GetOSErrorHistogram(method
, -base::File::FILE_ERROR_MAX
)->Add(-error
);
945 void ChromiumEnv::RecordBackupResult(bool result
) const {
946 std::string
uma_name(name_
);
947 uma_name
.append(".TableBackup");
948 base::BooleanHistogram::FactoryGet(
949 uma_name
, base::Histogram::kUmaTargetedHistogramFlag
)->AddBoolean(result
);
952 base::HistogramBase
* ChromiumEnv::GetOSErrorHistogram(MethodID method
,
954 std::string
uma_name(name_
);
955 // TODO(dgrogan): This is probably not the best way to concatenate strings.
956 uma_name
.append(".IOError.").append(MethodIDToString(method
));
957 return base::LinearHistogram::FactoryGet(uma_name
, 1, limit
, limit
+ 1,
958 base::Histogram::kUmaTargetedHistogramFlag
);
961 base::HistogramBase
* ChromiumEnv::GetMethodIOErrorHistogram() const {
962 std::string
uma_name(name_
);
963 uma_name
.append(".IOError");
964 return base::LinearHistogram::FactoryGet(uma_name
, 1, kNumEntries
,
965 kNumEntries
+ 1, base::Histogram::kUmaTargetedHistogramFlag
);
968 base::HistogramBase
* ChromiumEnv::GetMaxFDHistogram(
969 const std::string
& type
) const {
970 std::string
uma_name(name_
);
971 uma_name
.append(".MaxFDs.").append(type
);
972 // These numbers make each bucket twice as large as the previous bucket.
973 const int kFirstEntry
= 1;
974 const int kLastEntry
= 65536;
975 const int kNumBuckets
= 18;
976 return base::Histogram::FactoryGet(
977 uma_name
, kFirstEntry
, kLastEntry
, kNumBuckets
,
978 base::Histogram::kUmaTargetedHistogramFlag
);
981 base::HistogramBase
* ChromiumEnv::GetLockFileAncestorHistogram() const {
982 std::string
uma_name(name_
);
983 uma_name
.append(".LockFileAncestorsNotFound");
986 const int kNumBuckets
= 11;
987 return base::LinearHistogram::FactoryGet(
988 uma_name
, kMin
, kMax
, kNumBuckets
,
989 base::Histogram::kUmaTargetedHistogramFlag
);
992 base::HistogramBase
* ChromiumEnv::GetRetryTimeHistogram(MethodID method
) const {
993 std::string
uma_name(name_
);
994 // TODO(dgrogan): This is probably not the best way to concatenate strings.
995 uma_name
.append(".TimeUntilSuccessFor").append(MethodIDToString(method
));
997 const int kBucketSizeMillis
= 25;
998 // Add 2, 1 for each of the buckets <1 and >max.
999 const int kNumBuckets
= kMaxRetryTimeMillis
/ kBucketSizeMillis
+ 2;
1000 return base::Histogram::FactoryTimeGet(
1001 uma_name
, base::TimeDelta::FromMilliseconds(1),
1002 base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis
+ 1),
1004 base::Histogram::kUmaTargetedHistogramFlag
);
1007 base::HistogramBase
* ChromiumEnv::GetRecoveredFromErrorHistogram(
1008 MethodID method
) const {
1009 std::string
uma_name(name_
);
1010 uma_name
.append(".RetryRecoveredFromErrorIn")
1011 .append(MethodIDToString(method
));
1012 return base::LinearHistogram::FactoryGet(uma_name
, 1, kNumEntries
,
1013 kNumEntries
+ 1, base::Histogram::kUmaTargetedHistogramFlag
);
1016 class Thread
: public base::PlatformThread::Delegate
{
1018 Thread(void (*function
)(void* arg
), void* arg
)
1019 : function_(function
), arg_(arg
) {
1020 base::PlatformThreadHandle handle
;
1021 bool success
= base::PlatformThread::Create(0, this, &handle
);
1024 virtual ~Thread() {}
1025 void ThreadMain() override
{
1031 void (*function_
)(void* arg
);
1035 void ChromiumEnv::Schedule(ScheduleFunc
* function
, void* arg
) {
1038 // Start background thread if necessary
1039 if (!started_bgthread_
) {
1040 started_bgthread_
= true;
1041 StartThread(&ChromiumEnv::BGThreadWrapper
, this);
1044 // If the queue is currently empty, the background thread may currently be
1046 if (queue_
.empty()) {
1050 // Add to priority queue
1051 queue_
.push_back(BGItem());
1052 queue_
.back().function
= function
;
1053 queue_
.back().arg
= arg
;
1058 void ChromiumEnv::BGThread() {
1059 base::PlatformThread::SetName(name_
.c_str());
1062 // Wait until there is an item that is ready to run
1064 while (queue_
.empty()) {
1068 void (*function
)(void*) = queue_
.front().function
;
1069 void* arg
= queue_
.front().arg
;
1073 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
1078 void ChromiumEnv::StartThread(void (*function
)(void* arg
), void* arg
) {
1079 new Thread(function
, arg
); // Will self-delete.
1082 static std::string
GetDirName(const std::string
& filename
) {
1083 return FilePath::FromUTF8Unsafe(filename
).DirName().AsUTF8Unsafe();
1086 void ChromiumEnv::DidCreateNewFile(const std::string
& filename
) {
1087 base::AutoLock
auto_lock(directory_sync_lock_
);
1088 directories_needing_sync_
.insert(GetDirName(filename
));
1091 bool ChromiumEnv::DoesDirNeedSync(const std::string
& filename
) {
1092 base::AutoLock
auto_lock(directory_sync_lock_
);
1093 return ContainsKey(directories_needing_sync_
, GetDirName(filename
));
1096 void ChromiumEnv::DidSyncDir(const std::string
& filename
) {
1097 base::AutoLock
auto_lock(directory_sync_lock_
);
1098 directories_needing_sync_
.erase(GetDirName(filename
));
1101 } // namespace leveldb_env
1106 return leveldb_env::idb_env
.Pointer();
1109 Env
* Env::Default() {
1110 return leveldb_env::default_env
.Pointer();
1113 } // namespace leveldb