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/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/trace_event/trace_event.h"
21 #include "third_party/leveldatabase/chromium_logger.h"
22 #include "third_party/re2/re2/re2.h"
25 using leveldb::FileLock
;
27 using leveldb::Status
;
29 namespace leveldb_env
{
33 const FilePath::CharType backup_table_extension
[] = FILE_PATH_LITERAL(".bak");
34 const FilePath::CharType table_extension
[] = FILE_PATH_LITERAL(".ldb");
36 static const FilePath::CharType kLevelDBTestDirectoryPrefix
[] =
37 FILE_PATH_LITERAL("leveldb-test-");
39 static base::File::Error
LastFileError() {
41 return base::File::OSErrorToFileError(GetLastError());
43 return base::File::OSErrorToFileError(errno
);
47 // Making direct platform in lieu of using base::FileEnumerator because the
48 // latter can fail quietly without return an error result.
49 static base::File::Error
GetDirectoryEntries(const FilePath
& dir_param
,
50 std::vector
<FilePath
>* result
) {
53 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 FilePath
filepath(find_data
.cFileName
);
64 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
= dir_param
.AsUTF8Unsafe();
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(FilePath::FromUTF8Unsafe(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
,
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_
;
235 std::string parent_dir_
;
239 ChromiumWritableFile::ChromiumWritableFile(const std::string
& fname
,
241 const UMALogger
* uma_logger
,
245 uma_logger_(uma_logger
),
247 make_backup_(make_backup
) {
248 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
249 if (path
.BaseName().AsUTF8Unsafe().find("MANIFEST") == 0)
250 file_type_
= kManifest
;
251 else if (path
.MatchesExtension(table_extension
))
253 parent_dir_
= FilePath::FromUTF8Unsafe(fname
).DirName().AsUTF8Unsafe();
256 Status
ChromiumWritableFile::SyncParent() {
257 TRACE_EVENT0("leveldb", "SyncParent");
258 #if defined(OS_POSIX)
259 FilePath path
= FilePath::FromUTF8Unsafe(parent_dir_
);
260 base::File
f(path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
);
262 return MakeIOError(parent_dir_
, "Unable to open directory", kSyncParent
,
266 base::File::Error error
= LastFileError();
267 return MakeIOError(parent_dir_
, base::File::ErrorToString(error
),
274 Status
ChromiumWritableFile::Append(const Slice
& data
) {
275 int bytes_written
= file_
->WriteAtCurrentPos(data
.data(), data
.size());
276 if (bytes_written
!= data
.size()) {
277 base::File::Error error
= LastFileError();
278 uma_logger_
->RecordOSError(kWritableFileAppend
, error
);
279 return MakeIOError(filename_
, base::File::ErrorToString(error
),
280 kWritableFileAppend
, error
);
286 Status
ChromiumWritableFile::Close() {
291 Status
ChromiumWritableFile::Flush() {
292 // base::File doesn't do buffered I/O (i.e. POSIX FILE streams) so nothing to
297 Status
ChromiumWritableFile::Sync() {
298 TRACE_EVENT0("leveldb", "WritableFile::Sync");
300 if (!file_
->Flush()) {
301 base::File::Error error
= LastFileError();
302 uma_logger_
->RecordErrorAt(kWritableFileSync
);
303 return MakeIOError(filename_
, base::File::ErrorToString(error
),
304 kWritableFileSync
, error
);
307 if (make_backup_
&& file_type_
== kTable
)
308 uma_logger_
->RecordBackupResult(ChromiumEnv::MakeBackup(filename_
));
310 // leveldb's implicit contract for Sync() is that if this instance is for a
311 // manifest file then the directory is also sync'ed. See leveldb's
313 if (file_type_
== kManifest
)
319 class IDBEnv
: public ChromiumEnv
{
321 IDBEnv() : ChromiumEnv() {
322 name_
= "LevelDBEnv.IDB";
323 uma_ioerror_base_name_
= name_
+ ".IOError.BFE";
328 base::LazyInstance
<IDBEnv
>::Leaky idb_env
= LAZY_INSTANCE_INITIALIZER
;
330 base::LazyInstance
<ChromiumEnv
>::Leaky default_env
= LAZY_INSTANCE_INITIALIZER
;
332 } // unnamed namespace
334 const char* MethodIDToString(MethodID method
) {
336 case kSequentialFileRead
:
337 return "SequentialFileRead";
338 case kSequentialFileSkip
:
339 return "SequentialFileSkip";
340 case kRandomAccessFileRead
:
341 return "RandomAccessFileRead";
342 case kWritableFileAppend
:
343 return "WritableFileAppend";
344 case kWritableFileClose
:
345 return "WritableFileClose";
346 case kWritableFileFlush
:
347 return "WritableFileFlush";
348 case kWritableFileSync
:
349 return "WritableFileSync";
350 case kNewSequentialFile
:
351 return "NewSequentialFile";
352 case kNewRandomAccessFile
:
353 return "NewRandomAccessFile";
354 case kNewWritableFile
:
355 return "NewWritableFile";
356 case kNewAppendableFile
:
357 return "NewAppendableFile";
365 return "GetFileSize";
372 case kGetTestDirectory
:
373 return "GetTestDirectory";
379 return "GetChildren";
382 return "kNumEntries";
388 Status
MakeIOError(Slice filename
,
389 const std::string
& message
,
391 base::File::Error error
) {
394 snprintf(buf
, sizeof(buf
), "%s (ChromeMethodBFE: %d::%s::%d)",
395 message
.c_str(), method
, MethodIDToString(method
), -error
);
396 return Status::IOError(filename
, buf
);
399 Status
MakeIOError(Slice filename
,
400 const std::string
& message
,
403 snprintf(buf
, sizeof(buf
), "%s (ChromeMethodOnly: %d::%s)", message
.c_str(),
404 method
, MethodIDToString(method
));
405 return Status::IOError(filename
, buf
);
408 ErrorParsingResult
ParseMethodAndError(const leveldb::Status
& status
,
409 MethodID
* method_param
,
410 base::File::Error
* error
) {
411 const std::string status_string
= status
.ToString();
413 if (RE2::PartialMatch(status_string
.c_str(), "ChromeMethodOnly: (\\d+)",
415 *method_param
= static_cast<MethodID
>(method
);
419 if (RE2::PartialMatch(status_string
.c_str(),
420 "ChromeMethodBFE: (\\d+)::.*::(\\d+)", &method
,
422 *method_param
= static_cast<MethodID
>(method
);
423 *error
= static_cast<base::File::Error
>(-parsed_error
);
424 DCHECK_LT(*error
, base::File::FILE_OK
);
425 DCHECK_GT(*error
, base::File::FILE_ERROR_MAX
);
426 return METHOD_AND_BFE
;
431 // Keep in sync with LevelDBCorruptionTypes in histograms.xml. Also, don't
432 // change the order because indices into this array have been recorded in uma
434 const char* patterns
[] = {
436 "log record too small",
437 "corrupted internal key",
439 "missing start of fragmented record",
440 "error in middle of record",
441 "unknown record type",
442 "truncated record at end",
445 "FileReader invoked with unexpected value",
447 "CURRENT file does not end with newline",
448 "no meta-nextfile entry",
449 "no meta-lognumber entry",
450 "no last-sequence-number entry",
451 "malformed WriteBatch",
452 "bad WriteBatch Put",
453 "bad WriteBatch Delete",
454 "unknown WriteBatch tag",
455 "WriteBatch has wrong count",
456 "bad entry in block",
457 "bad block contents",
459 "truncated block read",
460 "block checksum mismatch",
462 "corrupted compressed block contents",
468 // Returns 1-based index into the above array or 0 if nothing matches.
469 int GetCorruptionCode(const leveldb::Status
& status
) {
470 DCHECK(!status
.IsIOError());
471 DCHECK(!status
.ok());
472 const int kOtherError
= 0;
473 int error
= kOtherError
;
474 const std::string
& str_error
= status
.ToString();
475 const size_t kNumPatterns
= arraysize(patterns
);
476 for (size_t i
= 0; i
< kNumPatterns
; ++i
) {
477 if (str_error
.find(patterns
[i
]) != std::string::npos
) {
485 int GetNumCorruptionCodes() {
486 // + 1 for the "other" error that is returned when a corruption message
487 // doesn't match any of the patterns.
488 return arraysize(patterns
) + 1;
491 std::string
GetCorruptionMessage(const leveldb::Status
& status
) {
492 int code
= GetCorruptionCode(status
);
494 return "Unknown corruption";
495 return patterns
[code
- 1];
498 bool IndicatesDiskFull(const leveldb::Status
& status
) {
501 leveldb_env::MethodID method
;
502 base::File::Error error
= base::File::FILE_OK
;
503 leveldb_env::ErrorParsingResult result
=
504 leveldb_env::ParseMethodAndError(status
, &method
, &error
);
505 return (result
== leveldb_env::METHOD_AND_BFE
&&
506 static_cast<base::File::Error
>(error
) ==
507 base::File::FILE_ERROR_NO_SPACE
);
510 bool ChromiumEnv::MakeBackup(const std::string
& fname
) {
511 FilePath original_table_name
= FilePath::FromUTF8Unsafe(fname
);
512 FilePath backup_table_name
=
513 original_table_name
.ReplaceExtension(backup_table_extension
);
514 return base::CopyFile(original_table_name
, backup_table_name
);
517 ChromiumEnv::ChromiumEnv()
518 : name_("LevelDBEnv"),
521 started_bgthread_(false),
522 kMaxRetryTimeMillis(1000) {
523 uma_ioerror_base_name_
= name_
+ ".IOError.BFE";
526 ChromiumEnv::~ChromiumEnv() {
527 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
528 // ensure that behavior isn't accidentally changed, but there's an instance in
529 // a unit test that is deleted.
532 bool ChromiumEnv::FileExists(const std::string
& fname
) {
533 return base::PathExists(FilePath::FromUTF8Unsafe(fname
));
536 const char* ChromiumEnv::FileErrorString(base::File::Error error
) {
538 case base::File::FILE_ERROR_FAILED
:
539 return "No further details.";
540 case base::File::FILE_ERROR_IN_USE
:
541 return "File currently in use.";
542 case base::File::FILE_ERROR_EXISTS
:
543 return "File already exists.";
544 case base::File::FILE_ERROR_NOT_FOUND
:
545 return "File not found.";
546 case base::File::FILE_ERROR_ACCESS_DENIED
:
547 return "Access denied.";
548 case base::File::FILE_ERROR_TOO_MANY_OPENED
:
549 return "Too many files open.";
550 case base::File::FILE_ERROR_NO_MEMORY
:
551 return "Out of memory.";
552 case base::File::FILE_ERROR_NO_SPACE
:
553 return "No space left on drive.";
554 case base::File::FILE_ERROR_NOT_A_DIRECTORY
:
555 return "Not a directory.";
556 case base::File::FILE_ERROR_INVALID_OPERATION
:
557 return "Invalid operation.";
558 case base::File::FILE_ERROR_SECURITY
:
559 return "Security error.";
560 case base::File::FILE_ERROR_ABORT
:
561 return "File operation aborted.";
562 case base::File::FILE_ERROR_NOT_A_FILE
:
563 return "The supplied path was not a file.";
564 case base::File::FILE_ERROR_NOT_EMPTY
:
565 return "The file was not empty.";
566 case base::File::FILE_ERROR_INVALID_URL
:
567 return "Invalid URL.";
568 case base::File::FILE_ERROR_IO
:
569 return "OS or hardware error.";
570 case base::File::FILE_OK
:
572 case base::File::FILE_ERROR_MAX
:
576 return "Unknown error.";
579 FilePath
ChromiumEnv::RestoreFromBackup(const FilePath
& base_name
) {
580 FilePath table_name
= base_name
.AddExtension(table_extension
);
581 bool result
= base::CopyFile(base_name
.AddExtension(backup_table_extension
),
583 std::string
uma_name(name_
);
584 uma_name
.append(".TableRestore");
585 base::BooleanHistogram::FactoryGet(
586 uma_name
, base::Histogram::kUmaTargetedHistogramFlag
)->AddBoolean(result
);
590 void ChromiumEnv::RestoreIfNecessary(const std::string
& dir
,
591 std::vector
<std::string
>* dir_entries
) {
592 std::set
<FilePath
> tables_found
;
593 std::set
<FilePath
> backups_found
;
594 for (const std::string
& entry
: *dir_entries
) {
595 FilePath current
= FilePath::FromUTF8Unsafe(entry
);
596 if (current
.MatchesExtension(table_extension
))
597 tables_found
.insert(current
.RemoveExtension());
598 if (current
.MatchesExtension(backup_table_extension
))
599 backups_found
.insert(current
.RemoveExtension());
601 std::set
<FilePath
> backups_only
=
602 base::STLSetDifference
<std::set
<FilePath
>>(backups_found
, tables_found
);
604 if (backups_only
.size()) {
605 std::string
uma_name(name_
);
606 uma_name
.append(".MissingFiles");
607 int num_missing_files
=
608 backups_only
.size() > INT_MAX
? INT_MAX
: backups_only
.size();
609 base::Histogram::FactoryGet(uma_name
,
613 base::Histogram::kUmaTargetedHistogramFlag
)
614 ->Add(num_missing_files
);
616 FilePath dir_path
= FilePath::FromUTF8Unsafe(dir
);
617 for (const FilePath
& backup
: backups_only
) {
618 FilePath restored_table_name
= RestoreFromBackup(dir_path
.Append(backup
));
619 dir_entries
->push_back(restored_table_name
.BaseName().AsUTF8Unsafe());
623 Status
ChromiumEnv::GetChildren(const std::string
& dir
,
624 std::vector
<std::string
>* result
) {
625 std::vector
<FilePath
> entries
;
626 base::File::Error error
=
627 GetDirectoryEntries(FilePath::FromUTF8Unsafe(dir
), &entries
);
628 if (error
!= base::File::FILE_OK
) {
629 RecordOSError(kGetChildren
, error
);
630 return MakeIOError(dir
, "Could not open/read directory", kGetChildren
,
635 for (const auto& entry
: entries
)
636 result
->push_back(entry
.BaseName().AsUTF8Unsafe());
639 RestoreIfNecessary(dir
, result
);
644 Status
ChromiumEnv::DeleteFile(const std::string
& fname
) {
646 FilePath fname_filepath
= FilePath::FromUTF8Unsafe(fname
);
647 // TODO(jorlow): Should we assert this is a file?
648 if (!base::DeleteFile(fname_filepath
, false)) {
649 result
= MakeIOError(fname
, "Could not delete file.", kDeleteFile
);
650 RecordErrorAt(kDeleteFile
);
652 if (make_backup_
&& fname_filepath
.MatchesExtension(table_extension
)) {
653 base::DeleteFile(fname_filepath
.ReplaceExtension(backup_table_extension
),
659 Status
ChromiumEnv::CreateDir(const std::string
& name
) {
661 base::File::Error error
= base::File::FILE_OK
;
662 Retrier
retrier(kCreateDir
, this);
664 if (base::CreateDirectoryAndGetError(FilePath::FromUTF8Unsafe(name
),
667 } while (retrier
.ShouldKeepTrying(error
));
668 result
= MakeIOError(name
, "Could not create directory.", kCreateDir
, error
);
669 RecordOSError(kCreateDir
, error
);
673 Status
ChromiumEnv::DeleteDir(const std::string
& name
) {
675 // TODO(jorlow): Should we assert this is a directory?
676 if (!base::DeleteFile(FilePath::FromUTF8Unsafe(name
), false)) {
677 result
= MakeIOError(name
, "Could not delete directory.", kDeleteDir
);
678 RecordErrorAt(kDeleteDir
);
683 Status
ChromiumEnv::GetFileSize(const std::string
& fname
, uint64_t* size
) {
686 if (!base::GetFileSize(FilePath::FromUTF8Unsafe(fname
), &signed_size
)) {
688 s
= MakeIOError(fname
, "Could not determine file size.", kGetFileSize
);
689 RecordErrorAt(kGetFileSize
);
691 *size
= static_cast<uint64_t>(signed_size
);
696 Status
ChromiumEnv::RenameFile(const std::string
& src
, const std::string
& dst
) {
698 FilePath src_file_path
= FilePath::FromUTF8Unsafe(src
);
699 if (!base::PathExists(src_file_path
))
701 FilePath destination
= FilePath::FromUTF8Unsafe(dst
);
703 Retrier
retrier(kRenameFile
, this);
704 base::File::Error error
= base::File::FILE_OK
;
706 if (base::ReplaceFile(src_file_path
, destination
, &error
))
708 } while (retrier
.ShouldKeepTrying(error
));
710 DCHECK(error
!= base::File::FILE_OK
);
711 RecordOSError(kRenameFile
, error
);
715 "Could not rename file: %s",
716 FileErrorString(error
));
717 return MakeIOError(src
, buf
, kRenameFile
, error
);
720 Status
ChromiumEnv::LockFile(const std::string
& fname
, FileLock
** lock
) {
723 int flags
= base::File::FLAG_OPEN_ALWAYS
|
724 base::File::FLAG_READ
|
725 base::File::FLAG_WRITE
;
726 base::File::Error error_code
;
728 Retrier
retrier(kLockFile
, this);
730 file
.Initialize(FilePath::FromUTF8Unsafe(fname
), flags
);
732 error_code
= file
.error_details();
733 } while (!file
.IsValid() && retrier
.ShouldKeepTrying(error_code
));
735 if (!file
.IsValid()) {
736 if (error_code
== base::File::FILE_ERROR_NOT_FOUND
) {
737 FilePath parent
= FilePath::FromUTF8Unsafe(fname
).DirName();
738 FilePath last_parent
;
739 int num_missing_ancestors
= 0;
741 if (base::DirectoryExists(parent
))
743 ++num_missing_ancestors
;
744 last_parent
= parent
;
745 parent
= parent
.DirName();
746 } while (parent
!= last_parent
);
747 RecordLockFileAncestors(num_missing_ancestors
);
750 result
= MakeIOError(fname
, FileErrorString(error_code
), kLockFile
,
752 RecordOSError(kLockFile
, error_code
);
756 if (!locks_
.Insert(fname
)) {
757 result
= MakeIOError(fname
, "Lock file already locked.", kLockFile
);
761 Retrier lock_retrier
= Retrier(kLockFile
, this);
763 error_code
= file
.Lock();
764 } while (error_code
!= base::File::FILE_OK
&&
765 retrier
.ShouldKeepTrying(error_code
));
767 if (error_code
!= base::File::FILE_OK
) {
768 locks_
.Remove(fname
);
769 result
= MakeIOError(fname
, FileErrorString(error_code
), kLockFile
,
771 RecordOSError(kLockFile
, error_code
);
775 ChromiumFileLock
* my_lock
= new ChromiumFileLock
;
776 my_lock
->file_
= file
.Pass();
777 my_lock
->name_
= fname
;
782 Status
ChromiumEnv::UnlockFile(FileLock
* lock
) {
783 ChromiumFileLock
* my_lock
= reinterpret_cast<ChromiumFileLock
*>(lock
);
786 base::File::Error error_code
= my_lock
->file_
.Unlock();
787 if (error_code
!= base::File::FILE_OK
) {
789 MakeIOError(my_lock
->name_
, "Could not unlock lock file.", kUnlockFile
);
790 RecordOSError(kUnlockFile
, error_code
);
792 bool removed
= locks_
.Remove(my_lock
->name_
);
798 Status
ChromiumEnv::GetTestDirectory(std::string
* path
) {
800 if (test_directory_
.empty()) {
801 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix
,
804 RecordErrorAt(kGetTestDirectory
);
806 "Could not create temp directory.", "", kGetTestDirectory
);
809 *path
= test_directory_
.AsUTF8Unsafe();
814 Status
ChromiumEnv::NewLogger(const std::string
& fname
,
815 leveldb::Logger
** result
) {
816 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
817 scoped_ptr
<base::File
> f(new base::File(
818 path
, base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
));
821 RecordOSError(kNewLogger
, f
->error_details());
822 return MakeIOError(fname
, "Unable to create log file", kNewLogger
,
825 *result
= new leveldb::ChromiumLogger(f
.release());
830 Status
ChromiumEnv::NewSequentialFile(const std::string
& fname
,
831 leveldb::SequentialFile
** result
) {
832 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
833 scoped_ptr
<base::File
> f(
834 new base::File(path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
));
837 RecordOSError(kNewSequentialFile
, f
->error_details());
838 return MakeIOError(fname
, "Unable to create sequential file",
839 kNewSequentialFile
, f
->error_details());
841 *result
= new ChromiumSequentialFile(fname
, f
.release(), this);
846 void ChromiumEnv::RecordOpenFilesLimit(const std::string
& type
) {
847 #if defined(OS_POSIX)
848 GetMaxFDHistogram(type
)->Add(base::GetMaxFds());
849 #elif defined(OS_WIN)
850 // Windows is only limited by available memory
852 #error "Need to determine limit to open files for this OS"
856 Status
ChromiumEnv::NewRandomAccessFile(const std::string
& fname
,
857 leveldb::RandomAccessFile
** result
) {
858 int flags
= base::File::FLAG_READ
| base::File::FLAG_OPEN
;
859 base::File
file(FilePath::FromUTF8Unsafe(fname
), flags
);
860 if (file
.IsValid()) {
861 *result
= new ChromiumRandomAccessFile(fname
, file
.Pass(), this);
862 RecordOpenFilesLimit("Success");
865 base::File::Error error_code
= file
.error_details();
866 if (error_code
== base::File::FILE_ERROR_TOO_MANY_OPENED
)
867 RecordOpenFilesLimit("TooManyOpened");
869 RecordOpenFilesLimit("OtherError");
871 RecordOSError(kNewRandomAccessFile
, error_code
);
872 return MakeIOError(fname
, FileErrorString(error_code
), kNewRandomAccessFile
,
876 Status
ChromiumEnv::NewWritableFile(const std::string
& fname
,
877 leveldb::WritableFile
** result
) {
879 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
880 scoped_ptr
<base::File
> f(new base::File(
881 path
, base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
));
883 RecordErrorAt(kNewWritableFile
);
884 return MakeIOError(fname
, "Unable to create writable file",
885 kNewWritableFile
, f
->error_details());
887 *result
= new ChromiumWritableFile(fname
, f
.release(), this, make_backup_
);
892 Status
ChromiumEnv::NewAppendableFile(const std::string
& fname
,
893 leveldb::WritableFile
** result
) {
895 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
896 scoped_ptr
<base::File
> f(new base::File(
897 path
, base::File::FLAG_OPEN_ALWAYS
| base::File::FLAG_APPEND
));
899 RecordErrorAt(kNewAppendableFile
);
900 return MakeIOError(fname
, "Unable to create appendable file",
901 kNewAppendableFile
, f
->error_details());
903 *result
= new ChromiumWritableFile(fname
, f
.release(), this, make_backup_
);
907 uint64_t ChromiumEnv::NowMicros() {
908 return base::TimeTicks::Now().ToInternalValue();
911 void ChromiumEnv::SleepForMicroseconds(int micros
) {
912 // Round up to the next millisecond.
913 base::PlatformThread::Sleep(base::TimeDelta::FromMicroseconds(micros
));
916 void ChromiumEnv::RecordErrorAt(MethodID method
) const {
917 GetMethodIOErrorHistogram()->Add(method
);
920 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors
) const {
921 GetLockFileAncestorHistogram()->Add(num_missing_ancestors
);
924 void ChromiumEnv::RecordOSError(MethodID method
,
925 base::File::Error error
) const {
927 RecordErrorAt(method
);
928 GetOSErrorHistogram(method
, -base::File::FILE_ERROR_MAX
)->Add(-error
);
931 void ChromiumEnv::RecordBackupResult(bool result
) const {
932 std::string
uma_name(name_
);
933 uma_name
.append(".TableBackup");
934 base::BooleanHistogram::FactoryGet(
935 uma_name
, base::Histogram::kUmaTargetedHistogramFlag
)->AddBoolean(result
);
938 base::HistogramBase
* ChromiumEnv::GetOSErrorHistogram(MethodID method
,
940 std::string uma_name
;
941 base::StringAppendF(&uma_name
, "%s.%s", uma_ioerror_base_name_
.c_str(),
942 MethodIDToString(method
));
943 return base::LinearHistogram::FactoryGet(uma_name
, 1, limit
, limit
+ 1,
944 base::Histogram::kUmaTargetedHistogramFlag
);
947 base::HistogramBase
* ChromiumEnv::GetMethodIOErrorHistogram() const {
948 std::string
uma_name(name_
);
949 uma_name
.append(".IOError");
950 return base::LinearHistogram::FactoryGet(uma_name
, 1, kNumEntries
,
951 kNumEntries
+ 1, base::Histogram::kUmaTargetedHistogramFlag
);
954 base::HistogramBase
* ChromiumEnv::GetMaxFDHistogram(
955 const std::string
& type
) const {
956 std::string
uma_name(name_
);
957 uma_name
.append(".MaxFDs.").append(type
);
958 // These numbers make each bucket twice as large as the previous bucket.
959 const int kFirstEntry
= 1;
960 const int kLastEntry
= 65536;
961 const int kNumBuckets
= 18;
962 return base::Histogram::FactoryGet(
963 uma_name
, kFirstEntry
, kLastEntry
, kNumBuckets
,
964 base::Histogram::kUmaTargetedHistogramFlag
);
967 base::HistogramBase
* ChromiumEnv::GetLockFileAncestorHistogram() const {
968 std::string
uma_name(name_
);
969 uma_name
.append(".LockFileAncestorsNotFound");
972 const int kNumBuckets
= 11;
973 return base::LinearHistogram::FactoryGet(
974 uma_name
, kMin
, kMax
, kNumBuckets
,
975 base::Histogram::kUmaTargetedHistogramFlag
);
978 base::HistogramBase
* ChromiumEnv::GetRetryTimeHistogram(MethodID method
) const {
979 std::string
uma_name(name_
);
980 // TODO(dgrogan): This is probably not the best way to concatenate strings.
981 uma_name
.append(".TimeUntilSuccessFor").append(MethodIDToString(method
));
983 const int kBucketSizeMillis
= 25;
984 // Add 2, 1 for each of the buckets <1 and >max.
985 const int kNumBuckets
= kMaxRetryTimeMillis
/ kBucketSizeMillis
+ 2;
986 return base::Histogram::FactoryTimeGet(
987 uma_name
, base::TimeDelta::FromMilliseconds(1),
988 base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis
+ 1),
990 base::Histogram::kUmaTargetedHistogramFlag
);
993 base::HistogramBase
* ChromiumEnv::GetRecoveredFromErrorHistogram(
994 MethodID method
) const {
995 std::string
uma_name(name_
);
996 uma_name
.append(".RetryRecoveredFromErrorIn")
997 .append(MethodIDToString(method
));
998 return base::LinearHistogram::FactoryGet(uma_name
, 1, kNumEntries
,
999 kNumEntries
+ 1, base::Histogram::kUmaTargetedHistogramFlag
);
1002 class Thread
: public base::PlatformThread::Delegate
{
1004 Thread(void (*function
)(void* arg
), void* arg
)
1005 : function_(function
), arg_(arg
) {
1006 base::PlatformThreadHandle handle
;
1007 bool success
= base::PlatformThread::Create(0, this, &handle
);
1010 virtual ~Thread() {}
1011 void ThreadMain() override
{
1017 void (*function_
)(void* arg
);
1021 void ChromiumEnv::Schedule(ScheduleFunc
* function
, void* arg
) {
1024 // Start background thread if necessary
1025 if (!started_bgthread_
) {
1026 started_bgthread_
= true;
1027 StartThread(&ChromiumEnv::BGThreadWrapper
, this);
1030 // If the queue is currently empty, the background thread may currently be
1032 if (queue_
.empty()) {
1036 // Add to priority queue
1037 queue_
.push_back(BGItem());
1038 queue_
.back().function
= function
;
1039 queue_
.back().arg
= arg
;
1044 void ChromiumEnv::BGThread() {
1045 base::PlatformThread::SetName(name_
.c_str());
1048 // Wait until there is an item that is ready to run
1050 while (queue_
.empty()) {
1054 void (*function
)(void*) = queue_
.front().function
;
1055 void* arg
= queue_
.front().arg
;
1059 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
1064 void ChromiumEnv::StartThread(void (*function
)(void* arg
), void* arg
) {
1065 new Thread(function
, arg
); // Will self-delete.
1068 } // namespace leveldb_env
1073 return leveldb_env::idb_env
.Pointer();
1076 Env
* Env::Default() {
1077 return leveldb_env::default_env
.Pointer();
1080 } // namespace leveldb