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/threading/thread_restrictions.h"
21 #include "base/trace_event/trace_event.h"
22 #include "third_party/leveldatabase/chromium_logger.h"
23 #include "third_party/re2/re2/re2.h"
26 using leveldb::FileLock
;
28 using leveldb::Status
;
30 namespace leveldb_env
{
34 const FilePath::CharType backup_table_extension
[] = FILE_PATH_LITERAL(".bak");
35 const FilePath::CharType table_extension
[] = FILE_PATH_LITERAL(".ldb");
37 static const FilePath::CharType kLevelDBTestDirectoryPrefix
[] =
38 FILE_PATH_LITERAL("leveldb-test-");
40 static base::File::Error
LastFileError() {
42 return base::File::OSErrorToFileError(GetLastError());
44 return base::File::OSErrorToFileError(errno
);
48 // Making direct platform in lieu of using base::FileEnumerator because the
49 // latter can fail quietly without return an error result.
50 static base::File::Error
GetDirectoryEntries(const FilePath
& dir_param
,
51 std::vector
<FilePath
>* result
) {
52 base::ThreadRestrictions::AssertIOAllowed();
55 FilePath dir_filepath
= dir_param
.Append(FILE_PATH_LITERAL("*"));
56 WIN32_FIND_DATA find_data
;
57 HANDLE find_handle
= FindFirstFile(dir_filepath
.value().c_str(), &find_data
);
58 if (find_handle
== INVALID_HANDLE_VALUE
) {
59 DWORD last_error
= GetLastError();
60 if (last_error
== ERROR_FILE_NOT_FOUND
)
61 return base::File::FILE_OK
;
62 return base::File::OSErrorToFileError(last_error
);
65 FilePath
filepath(find_data
.cFileName
);
66 FilePath::StringType basename
= filepath
.BaseName().value();
67 if (basename
== FILE_PATH_LITERAL(".") ||
68 basename
== FILE_PATH_LITERAL(".."))
70 result
->push_back(filepath
.BaseName());
71 } while (FindNextFile(find_handle
, &find_data
));
72 DWORD last_error
= GetLastError();
73 base::File::Error return_value
= base::File::FILE_OK
;
74 if (last_error
!= ERROR_NO_MORE_FILES
)
75 return_value
= base::File::OSErrorToFileError(last_error
);
76 FindClose(find_handle
);
79 const std::string dir_string
= dir_param
.AsUTF8Unsafe();
80 DIR* dir
= opendir(dir_string
.c_str());
82 return base::File::OSErrorToFileError(errno
);
83 struct dirent dent_buf
;
86 while ((readdir_result
= readdir_r(dir
, &dent_buf
, &dent
)) == 0 && dent
) {
87 if (strcmp(dent
->d_name
, ".") == 0 || strcmp(dent
->d_name
, "..") == 0)
89 result
->push_back(FilePath::FromUTF8Unsafe(dent
->d_name
));
91 int saved_errno
= errno
;
93 if (readdir_result
!= 0)
94 return base::File::OSErrorToFileError(saved_errno
);
95 return base::File::FILE_OK
;
99 class ChromiumFileLock
: public FileLock
{
107 Retrier(MethodID method
, RetrierProvider
* provider
)
108 : start_(base::TimeTicks::Now()),
109 limit_(start_
+ base::TimeDelta::FromMilliseconds(
110 provider
->MaxRetryTimeMillis())),
112 time_to_sleep_(base::TimeDelta::FromMilliseconds(10)),
115 last_error_(base::File::FILE_OK
),
116 provider_(provider
) {}
119 provider_
->GetRetryTimeHistogram(method_
)->AddTime(last_
- start_
);
120 if (last_error_
!= base::File::FILE_OK
) {
121 DCHECK_LT(last_error_
, 0);
122 provider_
->GetRecoveredFromErrorHistogram(method_
)->Add(-last_error_
);
126 bool ShouldKeepTrying(base::File::Error last_error
) {
127 DCHECK_NE(last_error
, base::File::FILE_OK
);
128 last_error_
= last_error
;
129 if (last_
< limit_
) {
130 base::PlatformThread::Sleep(time_to_sleep_
);
131 last_
= base::TimeTicks::Now();
139 base::TimeTicks start_
;
140 base::TimeTicks limit_
;
141 base::TimeTicks last_
;
142 base::TimeDelta time_to_sleep_
;
145 base::File::Error last_error_
;
146 RetrierProvider
* provider_
;
149 class ChromiumSequentialFile
: public leveldb::SequentialFile
{
151 ChromiumSequentialFile(const std::string
& fname
,
153 const UMALogger
* uma_logger
)
154 : filename_(fname
), file_(f
), uma_logger_(uma_logger
) {}
155 virtual ~ChromiumSequentialFile() {}
157 Status
Read(size_t n
, Slice
* result
, char* scratch
) override
{
158 int bytes_read
= file_
->ReadAtCurrentPosNoBestEffort(scratch
, n
);
159 if (bytes_read
== -1) {
160 base::File::Error error
= LastFileError();
161 uma_logger_
->RecordErrorAt(kSequentialFileRead
);
162 return MakeIOError(filename_
, base::File::ErrorToString(error
),
163 kSequentialFileRead
, error
);
165 *result
= Slice(scratch
, bytes_read
);
170 Status
Skip(uint64_t n
) override
{
171 if (file_
->Seek(base::File::FROM_CURRENT
, n
) == -1) {
172 base::File::Error error
= LastFileError();
173 uma_logger_
->RecordErrorAt(kSequentialFileSkip
);
174 return MakeIOError(filename_
, base::File::ErrorToString(error
),
175 kSequentialFileSkip
, error
);
182 std::string filename_
;
183 scoped_ptr
<base::File
> file_
;
184 const UMALogger
* uma_logger_
;
187 class ChromiumRandomAccessFile
: public leveldb::RandomAccessFile
{
189 ChromiumRandomAccessFile(const std::string
& fname
,
191 const UMALogger
* uma_logger
)
192 : filename_(fname
), file_(file
.Pass()), uma_logger_(uma_logger
) {}
193 virtual ~ChromiumRandomAccessFile() {}
195 Status
Read(uint64_t offset
,
198 char* scratch
) const override
{
200 int r
= file_
.Read(offset
, scratch
, n
);
201 *result
= Slice(scratch
, (r
< 0) ? 0 : r
);
203 // An error: return a non-ok status
204 s
= MakeIOError(filename_
, "Could not perform read",
205 kRandomAccessFileRead
);
206 uma_logger_
->RecordErrorAt(kRandomAccessFileRead
);
212 std::string filename_
;
213 mutable base::File file_
;
214 const UMALogger
* uma_logger_
;
217 class ChromiumWritableFile
: public leveldb::WritableFile
{
219 ChromiumWritableFile(const std::string
& fname
,
221 const UMALogger
* uma_logger
,
223 virtual ~ChromiumWritableFile() {}
224 leveldb::Status
Append(const leveldb::Slice
& data
) override
;
225 leveldb::Status
Close() override
;
226 leveldb::Status
Flush() override
;
227 leveldb::Status
Sync() override
;
230 enum Type
{ kManifest
, kTable
, kOther
};
231 leveldb::Status
SyncParent();
233 std::string filename_
;
234 scoped_ptr
<base::File
> file_
;
235 const UMALogger
* uma_logger_
;
237 std::string parent_dir_
;
241 ChromiumWritableFile::ChromiumWritableFile(const std::string
& fname
,
243 const UMALogger
* uma_logger
,
247 uma_logger_(uma_logger
),
249 make_backup_(make_backup
) {
250 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
251 if (path
.BaseName().AsUTF8Unsafe().find("MANIFEST") == 0)
252 file_type_
= kManifest
;
253 else if (path
.MatchesExtension(table_extension
))
255 parent_dir_
= FilePath::FromUTF8Unsafe(fname
).DirName().AsUTF8Unsafe();
258 Status
ChromiumWritableFile::SyncParent() {
259 TRACE_EVENT0("leveldb", "SyncParent");
260 #if defined(OS_POSIX)
261 FilePath path
= FilePath::FromUTF8Unsafe(parent_dir_
);
262 base::File
f(path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
);
264 return MakeIOError(parent_dir_
, "Unable to open directory", kSyncParent
,
268 base::File::Error error
= LastFileError();
269 return MakeIOError(parent_dir_
, base::File::ErrorToString(error
),
276 Status
ChromiumWritableFile::Append(const Slice
& data
) {
277 int bytes_written
= file_
->WriteAtCurrentPos(data
.data(), data
.size());
278 if (bytes_written
!= data
.size()) {
279 base::File::Error error
= LastFileError();
280 uma_logger_
->RecordOSError(kWritableFileAppend
, error
);
281 return MakeIOError(filename_
, base::File::ErrorToString(error
),
282 kWritableFileAppend
, error
);
288 Status
ChromiumWritableFile::Close() {
293 Status
ChromiumWritableFile::Flush() {
294 // base::File doesn't do buffered I/O (i.e. POSIX FILE streams) so nothing to
299 Status
ChromiumWritableFile::Sync() {
300 TRACE_EVENT0("leveldb", "WritableFile::Sync");
302 if (!file_
->Flush()) {
303 base::File::Error error
= LastFileError();
304 uma_logger_
->RecordErrorAt(kWritableFileSync
);
305 return MakeIOError(filename_
, base::File::ErrorToString(error
),
306 kWritableFileSync
, error
);
309 if (make_backup_
&& file_type_
== kTable
)
310 uma_logger_
->RecordBackupResult(ChromiumEnv::MakeBackup(filename_
));
312 // leveldb's implicit contract for Sync() is that if this instance is for a
313 // manifest file then the directory is also sync'ed. See leveldb's
315 if (file_type_
== kManifest
)
321 base::LazyInstance
<ChromiumEnv
>::Leaky default_env
= LAZY_INSTANCE_INITIALIZER
;
323 } // unnamed namespace
325 const char* MethodIDToString(MethodID method
) {
327 case kSequentialFileRead
:
328 return "SequentialFileRead";
329 case kSequentialFileSkip
:
330 return "SequentialFileSkip";
331 case kRandomAccessFileRead
:
332 return "RandomAccessFileRead";
333 case kWritableFileAppend
:
334 return "WritableFileAppend";
335 case kWritableFileClose
:
336 return "WritableFileClose";
337 case kWritableFileFlush
:
338 return "WritableFileFlush";
339 case kWritableFileSync
:
340 return "WritableFileSync";
341 case kNewSequentialFile
:
342 return "NewSequentialFile";
343 case kNewRandomAccessFile
:
344 return "NewRandomAccessFile";
345 case kNewWritableFile
:
346 return "NewWritableFile";
347 case kNewAppendableFile
:
348 return "NewAppendableFile";
356 return "GetFileSize";
363 case kGetTestDirectory
:
364 return "GetTestDirectory";
370 return "GetChildren";
373 return "kNumEntries";
379 Status
MakeIOError(Slice filename
,
380 const std::string
& message
,
382 base::File::Error error
) {
385 snprintf(buf
, sizeof(buf
), "%s (ChromeMethodBFE: %d::%s::%d)",
386 message
.c_str(), method
, MethodIDToString(method
), -error
);
387 return Status::IOError(filename
, buf
);
390 Status
MakeIOError(Slice filename
,
391 const std::string
& message
,
394 snprintf(buf
, sizeof(buf
), "%s (ChromeMethodOnly: %d::%s)", message
.c_str(),
395 method
, MethodIDToString(method
));
396 return Status::IOError(filename
, buf
);
399 ErrorParsingResult
ParseMethodAndError(const leveldb::Status
& status
,
400 MethodID
* method_param
,
401 base::File::Error
* error
) {
402 const std::string status_string
= status
.ToString();
404 if (RE2::PartialMatch(status_string
.c_str(), "ChromeMethodOnly: (\\d+)",
406 *method_param
= static_cast<MethodID
>(method
);
410 if (RE2::PartialMatch(status_string
.c_str(),
411 "ChromeMethodBFE: (\\d+)::.*::(\\d+)", &method
,
413 *method_param
= static_cast<MethodID
>(method
);
414 *error
= static_cast<base::File::Error
>(-parsed_error
);
415 DCHECK_LT(*error
, base::File::FILE_OK
);
416 DCHECK_GT(*error
, base::File::FILE_ERROR_MAX
);
417 return METHOD_AND_BFE
;
422 // Keep in sync with LevelDBCorruptionTypes in histograms.xml. Also, don't
423 // change the order because indices into this array have been recorded in uma
425 const char* patterns
[] = {
427 "log record too small",
428 "corrupted internal key",
430 "missing start of fragmented record",
431 "error in middle of record",
432 "unknown record type",
433 "truncated record at end",
436 "FileReader invoked with unexpected value",
438 "CURRENT file does not end with newline",
439 "no meta-nextfile entry",
440 "no meta-lognumber entry",
441 "no last-sequence-number entry",
442 "malformed WriteBatch",
443 "bad WriteBatch Put",
444 "bad WriteBatch Delete",
445 "unknown WriteBatch tag",
446 "WriteBatch has wrong count",
447 "bad entry in block",
448 "bad block contents",
450 "truncated block read",
451 "block checksum mismatch",
453 "corrupted compressed block contents",
459 // Returns 1-based index into the above array or 0 if nothing matches.
460 int GetCorruptionCode(const leveldb::Status
& status
) {
461 DCHECK(!status
.IsIOError());
462 DCHECK(!status
.ok());
463 const int kOtherError
= 0;
464 int error
= kOtherError
;
465 const std::string
& str_error
= status
.ToString();
466 const size_t kNumPatterns
= arraysize(patterns
);
467 for (size_t i
= 0; i
< kNumPatterns
; ++i
) {
468 if (str_error
.find(patterns
[i
]) != std::string::npos
) {
476 int GetNumCorruptionCodes() {
477 // + 1 for the "other" error that is returned when a corruption message
478 // doesn't match any of the patterns.
479 return arraysize(patterns
) + 1;
482 std::string
GetCorruptionMessage(const leveldb::Status
& status
) {
483 int code
= GetCorruptionCode(status
);
485 return "Unknown corruption";
486 return patterns
[code
- 1];
489 bool IndicatesDiskFull(const leveldb::Status
& status
) {
492 leveldb_env::MethodID method
;
493 base::File::Error error
= base::File::FILE_OK
;
494 leveldb_env::ErrorParsingResult result
=
495 leveldb_env::ParseMethodAndError(status
, &method
, &error
);
496 return (result
== leveldb_env::METHOD_AND_BFE
&&
497 static_cast<base::File::Error
>(error
) ==
498 base::File::FILE_ERROR_NO_SPACE
);
501 bool ChromiumEnv::MakeBackup(const std::string
& fname
) {
502 FilePath original_table_name
= FilePath::FromUTF8Unsafe(fname
);
503 FilePath backup_table_name
=
504 original_table_name
.ReplaceExtension(backup_table_extension
);
505 return base::CopyFile(original_table_name
, backup_table_name
);
508 ChromiumEnv::ChromiumEnv()
509 : ChromiumEnv("LevelDBEnv", false /* make_backup */) {}
511 ChromiumEnv::ChromiumEnv(const std::string
& name
, bool make_backup
)
512 : kMaxRetryTimeMillis(1000),
514 make_backup_(make_backup
),
516 started_bgthread_(false) {
517 uma_ioerror_base_name_
= name_
+ ".IOError.BFE";
520 ChromiumEnv::~ChromiumEnv() {
521 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
522 // ensure that behavior isn't accidentally changed, but there's an instance in
523 // a unit test that is deleted.
526 bool ChromiumEnv::FileExists(const std::string
& fname
) {
527 return base::PathExists(FilePath::FromUTF8Unsafe(fname
));
530 const char* ChromiumEnv::FileErrorString(base::File::Error error
) {
532 case base::File::FILE_ERROR_FAILED
:
533 return "No further details.";
534 case base::File::FILE_ERROR_IN_USE
:
535 return "File currently in use.";
536 case base::File::FILE_ERROR_EXISTS
:
537 return "File already exists.";
538 case base::File::FILE_ERROR_NOT_FOUND
:
539 return "File not found.";
540 case base::File::FILE_ERROR_ACCESS_DENIED
:
541 return "Access denied.";
542 case base::File::FILE_ERROR_TOO_MANY_OPENED
:
543 return "Too many files open.";
544 case base::File::FILE_ERROR_NO_MEMORY
:
545 return "Out of memory.";
546 case base::File::FILE_ERROR_NO_SPACE
:
547 return "No space left on drive.";
548 case base::File::FILE_ERROR_NOT_A_DIRECTORY
:
549 return "Not a directory.";
550 case base::File::FILE_ERROR_INVALID_OPERATION
:
551 return "Invalid operation.";
552 case base::File::FILE_ERROR_SECURITY
:
553 return "Security error.";
554 case base::File::FILE_ERROR_ABORT
:
555 return "File operation aborted.";
556 case base::File::FILE_ERROR_NOT_A_FILE
:
557 return "The supplied path was not a file.";
558 case base::File::FILE_ERROR_NOT_EMPTY
:
559 return "The file was not empty.";
560 case base::File::FILE_ERROR_INVALID_URL
:
561 return "Invalid URL.";
562 case base::File::FILE_ERROR_IO
:
563 return "OS or hardware error.";
564 case base::File::FILE_OK
:
566 case base::File::FILE_ERROR_MAX
:
570 return "Unknown error.";
573 FilePath
ChromiumEnv::RestoreFromBackup(const FilePath
& base_name
) {
574 FilePath table_name
= base_name
.AddExtension(table_extension
);
575 bool result
= base::CopyFile(base_name
.AddExtension(backup_table_extension
),
577 std::string
uma_name(name_
);
578 uma_name
.append(".TableRestore");
579 base::BooleanHistogram::FactoryGet(
580 uma_name
, base::Histogram::kUmaTargetedHistogramFlag
)->AddBoolean(result
);
584 void ChromiumEnv::RestoreIfNecessary(const std::string
& dir
,
585 std::vector
<std::string
>* dir_entries
) {
586 std::set
<FilePath
> tables_found
;
587 std::set
<FilePath
> backups_found
;
588 for (const std::string
& entry
: *dir_entries
) {
589 FilePath current
= FilePath::FromUTF8Unsafe(entry
);
590 if (current
.MatchesExtension(table_extension
))
591 tables_found
.insert(current
.RemoveExtension());
592 if (current
.MatchesExtension(backup_table_extension
))
593 backups_found
.insert(current
.RemoveExtension());
595 std::set
<FilePath
> backups_only
=
596 base::STLSetDifference
<std::set
<FilePath
>>(backups_found
, tables_found
);
598 if (backups_only
.size()) {
599 std::string
uma_name(name_
);
600 uma_name
.append(".MissingFiles");
601 int num_missing_files
=
602 backups_only
.size() > INT_MAX
? INT_MAX
: backups_only
.size();
603 base::Histogram::FactoryGet(uma_name
,
607 base::Histogram::kUmaTargetedHistogramFlag
)
608 ->Add(num_missing_files
);
610 FilePath dir_path
= FilePath::FromUTF8Unsafe(dir
);
611 for (const FilePath
& backup
: backups_only
) {
612 FilePath restored_table_name
= RestoreFromBackup(dir_path
.Append(backup
));
613 dir_entries
->push_back(restored_table_name
.BaseName().AsUTF8Unsafe());
617 Status
ChromiumEnv::GetChildren(const std::string
& dir
,
618 std::vector
<std::string
>* result
) {
619 std::vector
<FilePath
> entries
;
620 base::File::Error error
=
621 GetDirectoryEntries(FilePath::FromUTF8Unsafe(dir
), &entries
);
622 if (error
!= base::File::FILE_OK
) {
623 RecordOSError(kGetChildren
, error
);
624 return MakeIOError(dir
, "Could not open/read directory", kGetChildren
,
629 for (const auto& entry
: entries
)
630 result
->push_back(entry
.BaseName().AsUTF8Unsafe());
633 RestoreIfNecessary(dir
, result
);
638 Status
ChromiumEnv::DeleteFile(const std::string
& fname
) {
640 FilePath fname_filepath
= FilePath::FromUTF8Unsafe(fname
);
641 // TODO(jorlow): Should we assert this is a file?
642 if (!base::DeleteFile(fname_filepath
, false)) {
643 result
= MakeIOError(fname
, "Could not delete file.", kDeleteFile
);
644 RecordErrorAt(kDeleteFile
);
646 if (make_backup_
&& fname_filepath
.MatchesExtension(table_extension
)) {
647 base::DeleteFile(fname_filepath
.ReplaceExtension(backup_table_extension
),
653 Status
ChromiumEnv::CreateDir(const std::string
& name
) {
655 base::File::Error error
= base::File::FILE_OK
;
656 Retrier
retrier(kCreateDir
, this);
658 if (base::CreateDirectoryAndGetError(FilePath::FromUTF8Unsafe(name
),
661 } while (retrier
.ShouldKeepTrying(error
));
662 result
= MakeIOError(name
, "Could not create directory.", kCreateDir
, error
);
663 RecordOSError(kCreateDir
, error
);
667 Status
ChromiumEnv::DeleteDir(const std::string
& name
) {
669 // TODO(jorlow): Should we assert this is a directory?
670 if (!base::DeleteFile(FilePath::FromUTF8Unsafe(name
), false)) {
671 result
= MakeIOError(name
, "Could not delete directory.", kDeleteDir
);
672 RecordErrorAt(kDeleteDir
);
677 Status
ChromiumEnv::GetFileSize(const std::string
& fname
, uint64_t* size
) {
680 if (!base::GetFileSize(FilePath::FromUTF8Unsafe(fname
), &signed_size
)) {
682 s
= MakeIOError(fname
, "Could not determine file size.", kGetFileSize
);
683 RecordErrorAt(kGetFileSize
);
685 *size
= static_cast<uint64_t>(signed_size
);
690 Status
ChromiumEnv::RenameFile(const std::string
& src
, const std::string
& dst
) {
692 FilePath src_file_path
= FilePath::FromUTF8Unsafe(src
);
693 if (!base::PathExists(src_file_path
))
695 FilePath destination
= FilePath::FromUTF8Unsafe(dst
);
697 Retrier
retrier(kRenameFile
, this);
698 base::File::Error error
= base::File::FILE_OK
;
700 if (base::ReplaceFile(src_file_path
, destination
, &error
))
702 } while (retrier
.ShouldKeepTrying(error
));
704 DCHECK(error
!= base::File::FILE_OK
);
705 RecordOSError(kRenameFile
, error
);
709 "Could not rename file: %s",
710 FileErrorString(error
));
711 return MakeIOError(src
, buf
, kRenameFile
, error
);
714 Status
ChromiumEnv::LockFile(const std::string
& fname
, FileLock
** lock
) {
717 int flags
= base::File::FLAG_OPEN_ALWAYS
|
718 base::File::FLAG_READ
|
719 base::File::FLAG_WRITE
;
720 base::File::Error error_code
;
722 Retrier
retrier(kLockFile
, this);
724 file
.Initialize(FilePath::FromUTF8Unsafe(fname
), flags
);
726 error_code
= file
.error_details();
727 } while (!file
.IsValid() && retrier
.ShouldKeepTrying(error_code
));
729 if (!file
.IsValid()) {
730 if (error_code
== base::File::FILE_ERROR_NOT_FOUND
) {
731 FilePath parent
= FilePath::FromUTF8Unsafe(fname
).DirName();
732 FilePath last_parent
;
733 int num_missing_ancestors
= 0;
735 if (base::DirectoryExists(parent
))
737 ++num_missing_ancestors
;
738 last_parent
= parent
;
739 parent
= parent
.DirName();
740 } while (parent
!= last_parent
);
741 RecordLockFileAncestors(num_missing_ancestors
);
744 result
= MakeIOError(fname
, FileErrorString(error_code
), kLockFile
,
746 RecordOSError(kLockFile
, error_code
);
750 if (!locks_
.Insert(fname
)) {
751 result
= MakeIOError(fname
, "Lock file already locked.", kLockFile
);
755 Retrier lock_retrier
= Retrier(kLockFile
, this);
757 error_code
= file
.Lock();
758 } while (error_code
!= base::File::FILE_OK
&&
759 retrier
.ShouldKeepTrying(error_code
));
761 if (error_code
!= base::File::FILE_OK
) {
762 locks_
.Remove(fname
);
763 result
= MakeIOError(fname
, FileErrorString(error_code
), kLockFile
,
765 RecordOSError(kLockFile
, error_code
);
769 ChromiumFileLock
* my_lock
= new ChromiumFileLock
;
770 my_lock
->file_
= file
.Pass();
771 my_lock
->name_
= fname
;
776 Status
ChromiumEnv::UnlockFile(FileLock
* lock
) {
777 ChromiumFileLock
* my_lock
= reinterpret_cast<ChromiumFileLock
*>(lock
);
780 base::File::Error error_code
= my_lock
->file_
.Unlock();
781 if (error_code
!= base::File::FILE_OK
) {
783 MakeIOError(my_lock
->name_
, "Could not unlock lock file.", kUnlockFile
);
784 RecordOSError(kUnlockFile
, error_code
);
786 bool removed
= locks_
.Remove(my_lock
->name_
);
792 Status
ChromiumEnv::GetTestDirectory(std::string
* path
) {
794 if (test_directory_
.empty()) {
795 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix
,
798 RecordErrorAt(kGetTestDirectory
);
800 "Could not create temp directory.", "", kGetTestDirectory
);
803 *path
= test_directory_
.AsUTF8Unsafe();
808 Status
ChromiumEnv::NewLogger(const std::string
& fname
,
809 leveldb::Logger
** result
) {
810 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
811 scoped_ptr
<base::File
> f(new base::File(
812 path
, base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
));
815 RecordOSError(kNewLogger
, f
->error_details());
816 return MakeIOError(fname
, "Unable to create log file", kNewLogger
,
819 *result
= new leveldb::ChromiumLogger(f
.release());
824 Status
ChromiumEnv::NewSequentialFile(const std::string
& fname
,
825 leveldb::SequentialFile
** result
) {
826 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
827 scoped_ptr
<base::File
> f(
828 new base::File(path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
));
831 RecordOSError(kNewSequentialFile
, f
->error_details());
832 return MakeIOError(fname
, "Unable to create sequential file",
833 kNewSequentialFile
, f
->error_details());
835 *result
= new ChromiumSequentialFile(fname
, f
.release(), this);
840 void ChromiumEnv::RecordOpenFilesLimit(const std::string
& type
) {
841 #if defined(OS_POSIX)
842 GetMaxFDHistogram(type
)->Add(base::GetMaxFds());
843 #elif defined(OS_WIN)
844 // Windows is only limited by available memory
846 #error "Need to determine limit to open files for this OS"
850 Status
ChromiumEnv::NewRandomAccessFile(const std::string
& fname
,
851 leveldb::RandomAccessFile
** result
) {
852 int flags
= base::File::FLAG_READ
| base::File::FLAG_OPEN
;
853 base::File
file(FilePath::FromUTF8Unsafe(fname
), flags
);
854 if (file
.IsValid()) {
855 *result
= new ChromiumRandomAccessFile(fname
, file
.Pass(), this);
856 RecordOpenFilesLimit("Success");
859 base::File::Error error_code
= file
.error_details();
860 if (error_code
== base::File::FILE_ERROR_TOO_MANY_OPENED
)
861 RecordOpenFilesLimit("TooManyOpened");
863 RecordOpenFilesLimit("OtherError");
865 RecordOSError(kNewRandomAccessFile
, error_code
);
866 return MakeIOError(fname
, FileErrorString(error_code
), kNewRandomAccessFile
,
870 Status
ChromiumEnv::NewWritableFile(const std::string
& fname
,
871 leveldb::WritableFile
** result
) {
873 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
874 scoped_ptr
<base::File
> f(new base::File(
875 path
, base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
));
877 RecordErrorAt(kNewWritableFile
);
878 return MakeIOError(fname
, "Unable to create writable file",
879 kNewWritableFile
, f
->error_details());
881 *result
= new ChromiumWritableFile(fname
, f
.release(), this, make_backup_
);
886 Status
ChromiumEnv::NewAppendableFile(const std::string
& fname
,
887 leveldb::WritableFile
** result
) {
889 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
890 scoped_ptr
<base::File
> f(new base::File(
891 path
, base::File::FLAG_OPEN_ALWAYS
| base::File::FLAG_APPEND
));
893 RecordErrorAt(kNewAppendableFile
);
894 return MakeIOError(fname
, "Unable to create appendable file",
895 kNewAppendableFile
, f
->error_details());
897 *result
= new ChromiumWritableFile(fname
, f
.release(), 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
;
935 base::StringAppendF(&uma_name
, "%s.%s", uma_ioerror_base_name_
.c_str(),
936 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 LevelDBStatusValue
GetLevelDBStatusUMAValue(const leveldb::Status
& s
) {
1064 return LEVELDB_STATUS_OK
;
1066 return LEVELDB_STATUS_NOT_FOUND
;
1067 if (s
.IsCorruption())
1068 return LEVELDB_STATUS_CORRUPTION
;
1069 if (s
.IsNotSupportedError())
1070 return LEVELDB_STATUS_NOT_SUPPORTED
;
1072 return LEVELDB_STATUS_IO_ERROR
;
1073 // TODO(cmumford): IsInvalidArgument() was just added to leveldb. Use this
1074 // function once that change goes to the public repository.
1075 return LEVELDB_STATUS_INVALID_ARGUMENT
;
1078 } // namespace leveldb_env
1082 Env
* Env::Default() {
1083 return leveldb_env::default_env
.Pointer();
1086 } // namespace leveldb