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/metrics/histogram.h"
15 #include "base/process/process_metrics.h"
16 #include "base/stl_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/threading/thread_restrictions.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
) {
51 base::ThreadRestrictions::AssertIOAllowed();
54 FilePath dir_filepath
= dir_param
.Append(FILE_PATH_LITERAL("*"));
55 WIN32_FIND_DATA find_data
;
56 HANDLE find_handle
= FindFirstFile(dir_filepath
.value().c_str(), &find_data
);
57 if (find_handle
== INVALID_HANDLE_VALUE
) {
58 DWORD last_error
= GetLastError();
59 if (last_error
== ERROR_FILE_NOT_FOUND
)
60 return base::File::FILE_OK
;
61 return base::File::OSErrorToFileError(last_error
);
64 FilePath
filepath(find_data
.cFileName
);
65 FilePath::StringType basename
= filepath
.BaseName().value();
66 if (basename
== FILE_PATH_LITERAL(".") ||
67 basename
== FILE_PATH_LITERAL(".."))
69 result
->push_back(filepath
.BaseName());
70 } while (FindNextFile(find_handle
, &find_data
));
71 DWORD last_error
= GetLastError();
72 base::File::Error return_value
= base::File::FILE_OK
;
73 if (last_error
!= ERROR_NO_MORE_FILES
)
74 return_value
= base::File::OSErrorToFileError(last_error
);
75 FindClose(find_handle
);
78 const std::string dir_string
= dir_param
.AsUTF8Unsafe();
79 DIR* dir
= opendir(dir_string
.c_str());
81 return base::File::OSErrorToFileError(errno
);
82 struct dirent dent_buf
;
85 while ((readdir_result
= readdir_r(dir
, &dent_buf
, &dent
)) == 0 && dent
) {
86 if (strcmp(dent
->d_name
, ".") == 0 || strcmp(dent
->d_name
, "..") == 0)
88 result
->push_back(FilePath::FromUTF8Unsafe(dent
->d_name
));
90 int saved_errno
= errno
;
92 if (readdir_result
!= 0)
93 return base::File::OSErrorToFileError(saved_errno
);
94 return base::File::FILE_OK
;
98 class ChromiumFileLock
: public FileLock
{
106 Retrier(MethodID method
, RetrierProvider
* provider
)
107 : start_(base::TimeTicks::Now()),
108 limit_(start_
+ base::TimeDelta::FromMilliseconds(
109 provider
->MaxRetryTimeMillis())),
111 time_to_sleep_(base::TimeDelta::FromMilliseconds(10)),
114 last_error_(base::File::FILE_OK
),
115 provider_(provider
) {}
118 provider_
->GetRetryTimeHistogram(method_
)->AddTime(last_
- start_
);
119 if (last_error_
!= base::File::FILE_OK
) {
120 DCHECK_LT(last_error_
, 0);
121 provider_
->GetRecoveredFromErrorHistogram(method_
)->Add(-last_error_
);
125 bool ShouldKeepTrying(base::File::Error last_error
) {
126 DCHECK_NE(last_error
, base::File::FILE_OK
);
127 last_error_
= last_error
;
128 if (last_
< limit_
) {
129 base::PlatformThread::Sleep(time_to_sleep_
);
130 last_
= base::TimeTicks::Now();
138 base::TimeTicks start_
;
139 base::TimeTicks limit_
;
140 base::TimeTicks last_
;
141 base::TimeDelta time_to_sleep_
;
144 base::File::Error last_error_
;
145 RetrierProvider
* provider_
;
148 class ChromiumSequentialFile
: public leveldb::SequentialFile
{
150 ChromiumSequentialFile(const std::string
& fname
,
152 const UMALogger
* uma_logger
)
153 : filename_(fname
), file_(f
), uma_logger_(uma_logger
) {}
154 virtual ~ChromiumSequentialFile() {}
156 Status
Read(size_t n
, Slice
* result
, char* scratch
) override
{
157 int bytes_read
= file_
->ReadAtCurrentPosNoBestEffort(scratch
, n
);
158 if (bytes_read
== -1) {
159 base::File::Error error
= LastFileError();
160 uma_logger_
->RecordErrorAt(kSequentialFileRead
);
161 return MakeIOError(filename_
, base::File::ErrorToString(error
),
162 kSequentialFileRead
, error
);
164 *result
= Slice(scratch
, bytes_read
);
169 Status
Skip(uint64_t n
) override
{
170 if (file_
->Seek(base::File::FROM_CURRENT
, n
) == -1) {
171 base::File::Error error
= LastFileError();
172 uma_logger_
->RecordErrorAt(kSequentialFileSkip
);
173 return MakeIOError(filename_
, base::File::ErrorToString(error
),
174 kSequentialFileSkip
, error
);
181 std::string filename_
;
182 scoped_ptr
<base::File
> file_
;
183 const UMALogger
* uma_logger_
;
186 class ChromiumRandomAccessFile
: public leveldb::RandomAccessFile
{
188 ChromiumRandomAccessFile(const std::string
& fname
,
190 const UMALogger
* uma_logger
)
191 : filename_(fname
), file_(file
.Pass()), uma_logger_(uma_logger
) {}
192 virtual ~ChromiumRandomAccessFile() {}
194 Status
Read(uint64_t offset
,
197 char* scratch
) const override
{
199 int r
= file_
.Read(offset
, scratch
, n
);
200 *result
= Slice(scratch
, (r
< 0) ? 0 : r
);
202 // An error: return a non-ok status
203 s
= MakeIOError(filename_
, "Could not perform read",
204 kRandomAccessFileRead
);
205 uma_logger_
->RecordErrorAt(kRandomAccessFileRead
);
211 std::string filename_
;
212 mutable base::File file_
;
213 const UMALogger
* uma_logger_
;
216 class ChromiumWritableFile
: public leveldb::WritableFile
{
218 ChromiumWritableFile(const std::string
& fname
,
220 const UMALogger
* uma_logger
,
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_
;
236 std::string parent_dir_
;
240 ChromiumWritableFile::ChromiumWritableFile(const std::string
& fname
,
242 const UMALogger
* uma_logger
,
246 uma_logger_(uma_logger
),
248 make_backup_(make_backup
) {
249 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
250 if (path
.BaseName().AsUTF8Unsafe().find("MANIFEST") == 0)
251 file_type_
= kManifest
;
252 else if (path
.MatchesExtension(table_extension
))
254 parent_dir_
= FilePath::FromUTF8Unsafe(fname
).DirName().AsUTF8Unsafe();
257 Status
ChromiumWritableFile::SyncParent() {
258 TRACE_EVENT0("leveldb", "SyncParent");
259 #if defined(OS_POSIX)
260 FilePath path
= FilePath::FromUTF8Unsafe(parent_dir_
);
261 base::File
f(path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
);
263 return MakeIOError(parent_dir_
, "Unable to open directory", kSyncParent
,
267 base::File::Error error
= LastFileError();
268 return MakeIOError(parent_dir_
, base::File::ErrorToString(error
),
275 Status
ChromiumWritableFile::Append(const Slice
& data
) {
276 int bytes_written
= file_
->WriteAtCurrentPos(data
.data(), data
.size());
277 if (bytes_written
!= data
.size()) {
278 base::File::Error error
= LastFileError();
279 uma_logger_
->RecordOSError(kWritableFileAppend
, error
);
280 return MakeIOError(filename_
, base::File::ErrorToString(error
),
281 kWritableFileAppend
, error
);
287 Status
ChromiumWritableFile::Close() {
292 Status
ChromiumWritableFile::Flush() {
293 // base::File doesn't do buffered I/O (i.e. POSIX FILE streams) so nothing to
298 Status
ChromiumWritableFile::Sync() {
299 TRACE_EVENT0("leveldb", "WritableFile::Sync");
301 if (!file_
->Flush()) {
302 base::File::Error error
= LastFileError();
303 uma_logger_
->RecordErrorAt(kWritableFileSync
);
304 return MakeIOError(filename_
, base::File::ErrorToString(error
),
305 kWritableFileSync
, error
);
308 if (make_backup_
&& file_type_
== kTable
)
309 uma_logger_
->RecordBackupResult(ChromiumEnv::MakeBackup(filename_
));
311 // leveldb's implicit contract for Sync() is that if this instance is for a
312 // manifest file then the directory is also sync'ed. See leveldb's
314 if (file_type_
== kManifest
)
320 base::LazyInstance
<ChromiumEnv
>::Leaky default_env
= LAZY_INSTANCE_INITIALIZER
;
322 } // unnamed namespace
324 const char* MethodIDToString(MethodID method
) {
326 case kSequentialFileRead
:
327 return "SequentialFileRead";
328 case kSequentialFileSkip
:
329 return "SequentialFileSkip";
330 case kRandomAccessFileRead
:
331 return "RandomAccessFileRead";
332 case kWritableFileAppend
:
333 return "WritableFileAppend";
334 case kWritableFileClose
:
335 return "WritableFileClose";
336 case kWritableFileFlush
:
337 return "WritableFileFlush";
338 case kWritableFileSync
:
339 return "WritableFileSync";
340 case kNewSequentialFile
:
341 return "NewSequentialFile";
342 case kNewRandomAccessFile
:
343 return "NewRandomAccessFile";
344 case kNewWritableFile
:
345 return "NewWritableFile";
346 case kNewAppendableFile
:
347 return "NewAppendableFile";
355 return "GetFileSize";
362 case kGetTestDirectory
:
363 return "GetTestDirectory";
369 return "GetChildren";
372 return "kNumEntries";
378 Status
MakeIOError(Slice filename
,
379 const std::string
& message
,
381 base::File::Error error
) {
384 snprintf(buf
, sizeof(buf
), "%s (ChromeMethodBFE: %d::%s::%d)",
385 message
.c_str(), method
, MethodIDToString(method
), -error
);
386 return Status::IOError(filename
, buf
);
389 Status
MakeIOError(Slice filename
,
390 const std::string
& message
,
393 snprintf(buf
, sizeof(buf
), "%s (ChromeMethodOnly: %d::%s)", message
.c_str(),
394 method
, MethodIDToString(method
));
395 return Status::IOError(filename
, buf
);
398 ErrorParsingResult
ParseMethodAndError(const leveldb::Status
& status
,
399 MethodID
* method_param
,
400 base::File::Error
* error
) {
401 const std::string status_string
= status
.ToString();
403 if (RE2::PartialMatch(status_string
.c_str(), "ChromeMethodOnly: (\\d+)",
405 *method_param
= static_cast<MethodID
>(method
);
409 if (RE2::PartialMatch(status_string
.c_str(),
410 "ChromeMethodBFE: (\\d+)::.*::(\\d+)", &method
,
412 *method_param
= static_cast<MethodID
>(method
);
413 *error
= static_cast<base::File::Error
>(-parsed_error
);
414 DCHECK_LT(*error
, base::File::FILE_OK
);
415 DCHECK_GT(*error
, base::File::FILE_ERROR_MAX
);
416 return METHOD_AND_BFE
;
421 // Keep in sync with LevelDBCorruptionTypes in histograms.xml. Also, don't
422 // change the order because indices into this array have been recorded in uma
424 const char* patterns
[] = {
426 "log record too small",
427 "corrupted internal key",
429 "missing start of fragmented record",
430 "error in middle of record",
431 "unknown record type",
432 "truncated record at end",
435 "FileReader invoked with unexpected value",
437 "CURRENT file does not end with newline",
438 "no meta-nextfile entry",
439 "no meta-lognumber entry",
440 "no last-sequence-number entry",
441 "malformed WriteBatch",
442 "bad WriteBatch Put",
443 "bad WriteBatch Delete",
444 "unknown WriteBatch tag",
445 "WriteBatch has wrong count",
446 "bad entry in block",
447 "bad block contents",
449 "truncated block read",
450 "block checksum mismatch",
452 "corrupted compressed block contents",
458 // Returns 1-based index into the above array or 0 if nothing matches.
459 int GetCorruptionCode(const leveldb::Status
& status
) {
460 DCHECK(!status
.IsIOError());
461 DCHECK(!status
.ok());
462 const int kOtherError
= 0;
463 int error
= kOtherError
;
464 const std::string
& str_error
= status
.ToString();
465 const size_t kNumPatterns
= arraysize(patterns
);
466 for (size_t i
= 0; i
< kNumPatterns
; ++i
) {
467 if (str_error
.find(patterns
[i
]) != std::string::npos
) {
475 int GetNumCorruptionCodes() {
476 // + 1 for the "other" error that is returned when a corruption message
477 // doesn't match any of the patterns.
478 return arraysize(patterns
) + 1;
481 std::string
GetCorruptionMessage(const leveldb::Status
& status
) {
482 int code
= GetCorruptionCode(status
);
484 return "Unknown corruption";
485 return patterns
[code
- 1];
488 bool IndicatesDiskFull(const leveldb::Status
& status
) {
491 leveldb_env::MethodID method
;
492 base::File::Error error
= base::File::FILE_OK
;
493 leveldb_env::ErrorParsingResult result
=
494 leveldb_env::ParseMethodAndError(status
, &method
, &error
);
495 return (result
== leveldb_env::METHOD_AND_BFE
&&
496 static_cast<base::File::Error
>(error
) ==
497 base::File::FILE_ERROR_NO_SPACE
);
500 bool ChromiumEnv::MakeBackup(const std::string
& fname
) {
501 FilePath original_table_name
= FilePath::FromUTF8Unsafe(fname
);
502 FilePath backup_table_name
=
503 original_table_name
.ReplaceExtension(backup_table_extension
);
504 return base::CopyFile(original_table_name
, backup_table_name
);
507 ChromiumEnv::ChromiumEnv()
508 : ChromiumEnv("LevelDBEnv", false /* make_backup */) {}
510 ChromiumEnv::ChromiumEnv(const std::string
& name
, bool make_backup
)
511 : kMaxRetryTimeMillis(1000),
513 make_backup_(make_backup
),
515 started_bgthread_(false) {
516 uma_ioerror_base_name_
= name_
+ ".IOError.BFE";
519 ChromiumEnv::~ChromiumEnv() {
520 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
521 // ensure that behavior isn't accidentally changed, but there's an instance in
522 // a unit test that is deleted.
525 bool ChromiumEnv::FileExists(const std::string
& fname
) {
526 return base::PathExists(FilePath::FromUTF8Unsafe(fname
));
529 const char* ChromiumEnv::FileErrorString(base::File::Error error
) {
531 case base::File::FILE_ERROR_FAILED
:
532 return "No further details.";
533 case base::File::FILE_ERROR_IN_USE
:
534 return "File currently in use.";
535 case base::File::FILE_ERROR_EXISTS
:
536 return "File already exists.";
537 case base::File::FILE_ERROR_NOT_FOUND
:
538 return "File not found.";
539 case base::File::FILE_ERROR_ACCESS_DENIED
:
540 return "Access denied.";
541 case base::File::FILE_ERROR_TOO_MANY_OPENED
:
542 return "Too many files open.";
543 case base::File::FILE_ERROR_NO_MEMORY
:
544 return "Out of memory.";
545 case base::File::FILE_ERROR_NO_SPACE
:
546 return "No space left on drive.";
547 case base::File::FILE_ERROR_NOT_A_DIRECTORY
:
548 return "Not a directory.";
549 case base::File::FILE_ERROR_INVALID_OPERATION
:
550 return "Invalid operation.";
551 case base::File::FILE_ERROR_SECURITY
:
552 return "Security error.";
553 case base::File::FILE_ERROR_ABORT
:
554 return "File operation aborted.";
555 case base::File::FILE_ERROR_NOT_A_FILE
:
556 return "The supplied path was not a file.";
557 case base::File::FILE_ERROR_NOT_EMPTY
:
558 return "The file was not empty.";
559 case base::File::FILE_ERROR_INVALID_URL
:
560 return "Invalid URL.";
561 case base::File::FILE_ERROR_IO
:
562 return "OS or hardware error.";
563 case base::File::FILE_OK
:
565 case base::File::FILE_ERROR_MAX
:
569 return "Unknown error.";
572 FilePath
ChromiumEnv::RestoreFromBackup(const FilePath
& base_name
) {
573 FilePath table_name
= base_name
.AddExtension(table_extension
);
574 bool result
= base::CopyFile(base_name
.AddExtension(backup_table_extension
),
576 std::string
uma_name(name_
);
577 uma_name
.append(".TableRestore");
578 base::BooleanHistogram::FactoryGet(
579 uma_name
, base::Histogram::kUmaTargetedHistogramFlag
)->AddBoolean(result
);
583 void ChromiumEnv::RestoreIfNecessary(const std::string
& dir
,
584 std::vector
<std::string
>* dir_entries
) {
585 std::set
<FilePath
> tables_found
;
586 std::set
<FilePath
> backups_found
;
587 for (const std::string
& entry
: *dir_entries
) {
588 FilePath current
= FilePath::FromUTF8Unsafe(entry
);
589 if (current
.MatchesExtension(table_extension
))
590 tables_found
.insert(current
.RemoveExtension());
591 if (current
.MatchesExtension(backup_table_extension
))
592 backups_found
.insert(current
.RemoveExtension());
594 std::set
<FilePath
> backups_only
=
595 base::STLSetDifference
<std::set
<FilePath
>>(backups_found
, tables_found
);
597 if (backups_only
.size()) {
598 std::string
uma_name(name_
);
599 uma_name
.append(".MissingFiles");
600 int num_missing_files
=
601 backups_only
.size() > INT_MAX
? INT_MAX
: backups_only
.size();
602 base::Histogram::FactoryGet(uma_name
,
606 base::Histogram::kUmaTargetedHistogramFlag
)
607 ->Add(num_missing_files
);
609 FilePath dir_path
= FilePath::FromUTF8Unsafe(dir
);
610 for (const FilePath
& backup
: backups_only
) {
611 FilePath restored_table_name
= RestoreFromBackup(dir_path
.Append(backup
));
612 dir_entries
->push_back(restored_table_name
.BaseName().AsUTF8Unsafe());
616 Status
ChromiumEnv::GetChildren(const std::string
& dir
,
617 std::vector
<std::string
>* result
) {
618 std::vector
<FilePath
> entries
;
619 base::File::Error error
=
620 GetDirectoryEntries(FilePath::FromUTF8Unsafe(dir
), &entries
);
621 if (error
!= base::File::FILE_OK
) {
622 RecordOSError(kGetChildren
, error
);
623 return MakeIOError(dir
, "Could not open/read directory", kGetChildren
,
628 for (const auto& entry
: entries
)
629 result
->push_back(entry
.BaseName().AsUTF8Unsafe());
632 RestoreIfNecessary(dir
, result
);
637 Status
ChromiumEnv::DeleteFile(const std::string
& fname
) {
639 FilePath fname_filepath
= FilePath::FromUTF8Unsafe(fname
);
640 // TODO(jorlow): Should we assert this is a file?
641 if (!base::DeleteFile(fname_filepath
, false)) {
642 result
= MakeIOError(fname
, "Could not delete file.", kDeleteFile
);
643 RecordErrorAt(kDeleteFile
);
645 if (make_backup_
&& fname_filepath
.MatchesExtension(table_extension
)) {
646 base::DeleteFile(fname_filepath
.ReplaceExtension(backup_table_extension
),
652 Status
ChromiumEnv::CreateDir(const std::string
& name
) {
654 base::File::Error error
= base::File::FILE_OK
;
655 Retrier
retrier(kCreateDir
, this);
657 if (base::CreateDirectoryAndGetError(FilePath::FromUTF8Unsafe(name
),
660 } while (retrier
.ShouldKeepTrying(error
));
661 result
= MakeIOError(name
, "Could not create directory.", kCreateDir
, error
);
662 RecordOSError(kCreateDir
, error
);
666 Status
ChromiumEnv::DeleteDir(const std::string
& name
) {
668 // TODO(jorlow): Should we assert this is a directory?
669 if (!base::DeleteFile(FilePath::FromUTF8Unsafe(name
), false)) {
670 result
= MakeIOError(name
, "Could not delete directory.", kDeleteDir
);
671 RecordErrorAt(kDeleteDir
);
676 Status
ChromiumEnv::GetFileSize(const std::string
& fname
, uint64_t* size
) {
679 if (!base::GetFileSize(FilePath::FromUTF8Unsafe(fname
), &signed_size
)) {
681 s
= MakeIOError(fname
, "Could not determine file size.", kGetFileSize
);
682 RecordErrorAt(kGetFileSize
);
684 *size
= static_cast<uint64_t>(signed_size
);
689 Status
ChromiumEnv::RenameFile(const std::string
& src
, const std::string
& dst
) {
691 FilePath src_file_path
= FilePath::FromUTF8Unsafe(src
);
692 if (!base::PathExists(src_file_path
))
694 FilePath destination
= FilePath::FromUTF8Unsafe(dst
);
696 Retrier
retrier(kRenameFile
, this);
697 base::File::Error error
= base::File::FILE_OK
;
699 if (base::ReplaceFile(src_file_path
, destination
, &error
))
701 } while (retrier
.ShouldKeepTrying(error
));
703 DCHECK(error
!= base::File::FILE_OK
);
704 RecordOSError(kRenameFile
, error
);
708 "Could not rename file: %s",
709 FileErrorString(error
));
710 return MakeIOError(src
, buf
, kRenameFile
, error
);
713 Status
ChromiumEnv::LockFile(const std::string
& fname
, FileLock
** lock
) {
716 int flags
= base::File::FLAG_OPEN_ALWAYS
|
717 base::File::FLAG_READ
|
718 base::File::FLAG_WRITE
;
719 base::File::Error error_code
;
721 Retrier
retrier(kLockFile
, this);
723 file
.Initialize(FilePath::FromUTF8Unsafe(fname
), flags
);
725 error_code
= file
.error_details();
726 } while (!file
.IsValid() && retrier
.ShouldKeepTrying(error_code
));
728 if (!file
.IsValid()) {
729 if (error_code
== base::File::FILE_ERROR_NOT_FOUND
) {
730 FilePath parent
= FilePath::FromUTF8Unsafe(fname
).DirName();
731 FilePath last_parent
;
732 int num_missing_ancestors
= 0;
734 if (base::DirectoryExists(parent
))
736 ++num_missing_ancestors
;
737 last_parent
= parent
;
738 parent
= parent
.DirName();
739 } while (parent
!= last_parent
);
740 RecordLockFileAncestors(num_missing_ancestors
);
743 result
= MakeIOError(fname
, FileErrorString(error_code
), kLockFile
,
745 RecordOSError(kLockFile
, error_code
);
749 if (!locks_
.Insert(fname
)) {
750 result
= MakeIOError(fname
, "Lock file already locked.", kLockFile
);
754 Retrier lock_retrier
= Retrier(kLockFile
, this);
756 error_code
= file
.Lock();
757 } while (error_code
!= base::File::FILE_OK
&&
758 retrier
.ShouldKeepTrying(error_code
));
760 if (error_code
!= base::File::FILE_OK
) {
761 locks_
.Remove(fname
);
762 result
= MakeIOError(fname
, FileErrorString(error_code
), kLockFile
,
764 RecordOSError(kLockFile
, error_code
);
768 ChromiumFileLock
* my_lock
= new ChromiumFileLock
;
769 my_lock
->file_
= file
.Pass();
770 my_lock
->name_
= fname
;
775 Status
ChromiumEnv::UnlockFile(FileLock
* lock
) {
776 ChromiumFileLock
* my_lock
= reinterpret_cast<ChromiumFileLock
*>(lock
);
779 base::File::Error error_code
= my_lock
->file_
.Unlock();
780 if (error_code
!= base::File::FILE_OK
) {
782 MakeIOError(my_lock
->name_
, "Could not unlock lock file.", kUnlockFile
);
783 RecordOSError(kUnlockFile
, error_code
);
785 bool removed
= locks_
.Remove(my_lock
->name_
);
791 Status
ChromiumEnv::GetTestDirectory(std::string
* path
) {
793 if (test_directory_
.empty()) {
794 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix
,
797 RecordErrorAt(kGetTestDirectory
);
799 "Could not create temp directory.", "", kGetTestDirectory
);
802 *path
= test_directory_
.AsUTF8Unsafe();
807 Status
ChromiumEnv::NewLogger(const std::string
& fname
,
808 leveldb::Logger
** result
) {
809 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
810 scoped_ptr
<base::File
> f(new base::File(
811 path
, base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
));
814 RecordOSError(kNewLogger
, f
->error_details());
815 return MakeIOError(fname
, "Unable to create log file", kNewLogger
,
818 *result
= new leveldb::ChromiumLogger(f
.release());
823 Status
ChromiumEnv::NewSequentialFile(const std::string
& fname
,
824 leveldb::SequentialFile
** result
) {
825 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
826 scoped_ptr
<base::File
> f(
827 new base::File(path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
));
830 RecordOSError(kNewSequentialFile
, f
->error_details());
831 return MakeIOError(fname
, "Unable to create sequential file",
832 kNewSequentialFile
, f
->error_details());
834 *result
= new ChromiumSequentialFile(fname
, f
.release(), this);
839 void ChromiumEnv::RecordOpenFilesLimit(const std::string
& type
) {
840 #if defined(OS_POSIX)
841 GetMaxFDHistogram(type
)->Add(base::GetMaxFds());
842 #elif defined(OS_WIN)
843 // Windows is only limited by available memory
845 #error "Need to determine limit to open files for this OS"
849 Status
ChromiumEnv::NewRandomAccessFile(const std::string
& fname
,
850 leveldb::RandomAccessFile
** result
) {
851 int flags
= base::File::FLAG_READ
| base::File::FLAG_OPEN
;
852 base::File
file(FilePath::FromUTF8Unsafe(fname
), flags
);
853 if (file
.IsValid()) {
854 *result
= new ChromiumRandomAccessFile(fname
, file
.Pass(), this);
855 RecordOpenFilesLimit("Success");
858 base::File::Error error_code
= file
.error_details();
859 if (error_code
== base::File::FILE_ERROR_TOO_MANY_OPENED
)
860 RecordOpenFilesLimit("TooManyOpened");
862 RecordOpenFilesLimit("OtherError");
864 RecordOSError(kNewRandomAccessFile
, error_code
);
865 return MakeIOError(fname
, FileErrorString(error_code
), kNewRandomAccessFile
,
869 Status
ChromiumEnv::NewWritableFile(const std::string
& fname
,
870 leveldb::WritableFile
** result
) {
872 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
873 scoped_ptr
<base::File
> f(new base::File(
874 path
, base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
));
876 RecordErrorAt(kNewWritableFile
);
877 return MakeIOError(fname
, "Unable to create writable file",
878 kNewWritableFile
, f
->error_details());
880 *result
= new ChromiumWritableFile(fname
, f
.release(), this, make_backup_
);
885 Status
ChromiumEnv::NewAppendableFile(const std::string
& fname
,
886 leveldb::WritableFile
** result
) {
888 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
889 scoped_ptr
<base::File
> f(new base::File(
890 path
, base::File::FLAG_OPEN_ALWAYS
| base::File::FLAG_APPEND
));
892 RecordErrorAt(kNewAppendableFile
);
893 return MakeIOError(fname
, "Unable to create appendable file",
894 kNewAppendableFile
, f
->error_details());
896 *result
= new ChromiumWritableFile(fname
, f
.release(), this, make_backup_
);
900 uint64_t ChromiumEnv::NowMicros() {
901 return base::TimeTicks::Now().ToInternalValue();
904 void ChromiumEnv::SleepForMicroseconds(int micros
) {
905 // Round up to the next millisecond.
906 base::PlatformThread::Sleep(base::TimeDelta::FromMicroseconds(micros
));
909 void ChromiumEnv::RecordErrorAt(MethodID method
) const {
910 GetMethodIOErrorHistogram()->Add(method
);
913 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors
) const {
914 GetLockFileAncestorHistogram()->Add(num_missing_ancestors
);
917 void ChromiumEnv::RecordOSError(MethodID method
,
918 base::File::Error error
) const {
920 RecordErrorAt(method
);
921 GetOSErrorHistogram(method
, -base::File::FILE_ERROR_MAX
)->Add(-error
);
924 void ChromiumEnv::RecordBackupResult(bool result
) const {
925 std::string
uma_name(name_
);
926 uma_name
.append(".TableBackup");
927 base::BooleanHistogram::FactoryGet(
928 uma_name
, base::Histogram::kUmaTargetedHistogramFlag
)->AddBoolean(result
);
931 base::HistogramBase
* ChromiumEnv::GetOSErrorHistogram(MethodID method
,
933 std::string uma_name
;
934 base::StringAppendF(&uma_name
, "%s.%s", uma_ioerror_base_name_
.c_str(),
935 MethodIDToString(method
));
936 return base::LinearHistogram::FactoryGet(uma_name
, 1, limit
, limit
+ 1,
937 base::Histogram::kUmaTargetedHistogramFlag
);
940 base::HistogramBase
* ChromiumEnv::GetMethodIOErrorHistogram() const {
941 std::string
uma_name(name_
);
942 uma_name
.append(".IOError");
943 return base::LinearHistogram::FactoryGet(uma_name
, 1, kNumEntries
,
944 kNumEntries
+ 1, base::Histogram::kUmaTargetedHistogramFlag
);
947 base::HistogramBase
* ChromiumEnv::GetMaxFDHistogram(
948 const std::string
& type
) const {
949 std::string
uma_name(name_
);
950 uma_name
.append(".MaxFDs.").append(type
);
951 // These numbers make each bucket twice as large as the previous bucket.
952 const int kFirstEntry
= 1;
953 const int kLastEntry
= 65536;
954 const int kNumBuckets
= 18;
955 return base::Histogram::FactoryGet(
956 uma_name
, kFirstEntry
, kLastEntry
, kNumBuckets
,
957 base::Histogram::kUmaTargetedHistogramFlag
);
960 base::HistogramBase
* ChromiumEnv::GetLockFileAncestorHistogram() const {
961 std::string
uma_name(name_
);
962 uma_name
.append(".LockFileAncestorsNotFound");
965 const int kNumBuckets
= 11;
966 return base::LinearHistogram::FactoryGet(
967 uma_name
, kMin
, kMax
, kNumBuckets
,
968 base::Histogram::kUmaTargetedHistogramFlag
);
971 base::HistogramBase
* ChromiumEnv::GetRetryTimeHistogram(MethodID method
) const {
972 std::string
uma_name(name_
);
973 // TODO(dgrogan): This is probably not the best way to concatenate strings.
974 uma_name
.append(".TimeUntilSuccessFor").append(MethodIDToString(method
));
976 const int kBucketSizeMillis
= 25;
977 // Add 2, 1 for each of the buckets <1 and >max.
978 const int kNumBuckets
= kMaxRetryTimeMillis
/ kBucketSizeMillis
+ 2;
979 return base::Histogram::FactoryTimeGet(
980 uma_name
, base::TimeDelta::FromMilliseconds(1),
981 base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis
+ 1),
983 base::Histogram::kUmaTargetedHistogramFlag
);
986 base::HistogramBase
* ChromiumEnv::GetRecoveredFromErrorHistogram(
987 MethodID method
) const {
988 std::string
uma_name(name_
);
989 uma_name
.append(".RetryRecoveredFromErrorIn")
990 .append(MethodIDToString(method
));
991 return base::LinearHistogram::FactoryGet(uma_name
, 1, kNumEntries
,
992 kNumEntries
+ 1, base::Histogram::kUmaTargetedHistogramFlag
);
995 class Thread
: public base::PlatformThread::Delegate
{
997 Thread(void (*function
)(void* arg
), void* arg
)
998 : function_(function
), arg_(arg
) {
999 base::PlatformThreadHandle handle
;
1000 bool success
= base::PlatformThread::Create(0, this, &handle
);
1003 virtual ~Thread() {}
1004 void ThreadMain() override
{
1010 void (*function_
)(void* arg
);
1014 void ChromiumEnv::Schedule(ScheduleFunc
* function
, void* arg
) {
1017 // Start background thread if necessary
1018 if (!started_bgthread_
) {
1019 started_bgthread_
= true;
1020 StartThread(&ChromiumEnv::BGThreadWrapper
, this);
1023 // If the queue is currently empty, the background thread may currently be
1025 if (queue_
.empty()) {
1029 // Add to priority queue
1030 queue_
.push_back(BGItem());
1031 queue_
.back().function
= function
;
1032 queue_
.back().arg
= arg
;
1037 void ChromiumEnv::BGThread() {
1038 base::PlatformThread::SetName(name_
.c_str());
1041 // Wait until there is an item that is ready to run
1043 while (queue_
.empty()) {
1047 void (*function
)(void*) = queue_
.front().function
;
1048 void* arg
= queue_
.front().arg
;
1052 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
1057 void ChromiumEnv::StartThread(void (*function
)(void* arg
), void* arg
) {
1058 new Thread(function
, arg
); // Will self-delete.
1061 LevelDBStatusValue
GetLevelDBStatusUMAValue(const leveldb::Status
& s
) {
1063 return LEVELDB_STATUS_OK
;
1065 return LEVELDB_STATUS_NOT_FOUND
;
1066 if (s
.IsCorruption())
1067 return LEVELDB_STATUS_CORRUPTION
;
1068 if (s
.IsNotSupportedError())
1069 return LEVELDB_STATUS_NOT_SUPPORTED
;
1071 return LEVELDB_STATUS_IO_ERROR
;
1072 // TODO(cmumford): IsInvalidArgument() was just added to leveldb. Use this
1073 // function once that change goes to the public repository.
1074 return LEVELDB_STATUS_INVALID_ARGUMENT
;
1077 } // namespace leveldb_env
1081 Env
* Env::Default() {
1082 return leveldb_env::default_env
.Pointer();
1085 } // namespace leveldb