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 class IDBEnv
: public ChromiumEnv
{
323 IDBEnv() : ChromiumEnv() {
324 name_
= "LevelDBEnv.IDB";
325 uma_ioerror_base_name_
= name_
+ ".IOError.BFE";
330 base::LazyInstance
<IDBEnv
>::Leaky idb_env
= LAZY_INSTANCE_INITIALIZER
;
332 base::LazyInstance
<ChromiumEnv
>::Leaky default_env
= LAZY_INSTANCE_INITIALIZER
;
334 } // unnamed namespace
336 const char* MethodIDToString(MethodID method
) {
338 case kSequentialFileRead
:
339 return "SequentialFileRead";
340 case kSequentialFileSkip
:
341 return "SequentialFileSkip";
342 case kRandomAccessFileRead
:
343 return "RandomAccessFileRead";
344 case kWritableFileAppend
:
345 return "WritableFileAppend";
346 case kWritableFileClose
:
347 return "WritableFileClose";
348 case kWritableFileFlush
:
349 return "WritableFileFlush";
350 case kWritableFileSync
:
351 return "WritableFileSync";
352 case kNewSequentialFile
:
353 return "NewSequentialFile";
354 case kNewRandomAccessFile
:
355 return "NewRandomAccessFile";
356 case kNewWritableFile
:
357 return "NewWritableFile";
358 case kNewAppendableFile
:
359 return "NewAppendableFile";
367 return "GetFileSize";
374 case kGetTestDirectory
:
375 return "GetTestDirectory";
381 return "GetChildren";
384 return "kNumEntries";
390 Status
MakeIOError(Slice filename
,
391 const std::string
& message
,
393 base::File::Error error
) {
396 snprintf(buf
, sizeof(buf
), "%s (ChromeMethodBFE: %d::%s::%d)",
397 message
.c_str(), method
, MethodIDToString(method
), -error
);
398 return Status::IOError(filename
, buf
);
401 Status
MakeIOError(Slice filename
,
402 const std::string
& message
,
405 snprintf(buf
, sizeof(buf
), "%s (ChromeMethodOnly: %d::%s)", message
.c_str(),
406 method
, MethodIDToString(method
));
407 return Status::IOError(filename
, buf
);
410 ErrorParsingResult
ParseMethodAndError(const leveldb::Status
& status
,
411 MethodID
* method_param
,
412 base::File::Error
* error
) {
413 const std::string status_string
= status
.ToString();
415 if (RE2::PartialMatch(status_string
.c_str(), "ChromeMethodOnly: (\\d+)",
417 *method_param
= static_cast<MethodID
>(method
);
421 if (RE2::PartialMatch(status_string
.c_str(),
422 "ChromeMethodBFE: (\\d+)::.*::(\\d+)", &method
,
424 *method_param
= static_cast<MethodID
>(method
);
425 *error
= static_cast<base::File::Error
>(-parsed_error
);
426 DCHECK_LT(*error
, base::File::FILE_OK
);
427 DCHECK_GT(*error
, base::File::FILE_ERROR_MAX
);
428 return METHOD_AND_BFE
;
433 // Keep in sync with LevelDBCorruptionTypes in histograms.xml. Also, don't
434 // change the order because indices into this array have been recorded in uma
436 const char* patterns
[] = {
438 "log record too small",
439 "corrupted internal key",
441 "missing start of fragmented record",
442 "error in middle of record",
443 "unknown record type",
444 "truncated record at end",
447 "FileReader invoked with unexpected value",
449 "CURRENT file does not end with newline",
450 "no meta-nextfile entry",
451 "no meta-lognumber entry",
452 "no last-sequence-number entry",
453 "malformed WriteBatch",
454 "bad WriteBatch Put",
455 "bad WriteBatch Delete",
456 "unknown WriteBatch tag",
457 "WriteBatch has wrong count",
458 "bad entry in block",
459 "bad block contents",
461 "truncated block read",
462 "block checksum mismatch",
464 "corrupted compressed block contents",
470 // Returns 1-based index into the above array or 0 if nothing matches.
471 int GetCorruptionCode(const leveldb::Status
& status
) {
472 DCHECK(!status
.IsIOError());
473 DCHECK(!status
.ok());
474 const int kOtherError
= 0;
475 int error
= kOtherError
;
476 const std::string
& str_error
= status
.ToString();
477 const size_t kNumPatterns
= arraysize(patterns
);
478 for (size_t i
= 0; i
< kNumPatterns
; ++i
) {
479 if (str_error
.find(patterns
[i
]) != std::string::npos
) {
487 int GetNumCorruptionCodes() {
488 // + 1 for the "other" error that is returned when a corruption message
489 // doesn't match any of the patterns.
490 return arraysize(patterns
) + 1;
493 std::string
GetCorruptionMessage(const leveldb::Status
& status
) {
494 int code
= GetCorruptionCode(status
);
496 return "Unknown corruption";
497 return patterns
[code
- 1];
500 bool IndicatesDiskFull(const leveldb::Status
& status
) {
503 leveldb_env::MethodID method
;
504 base::File::Error error
= base::File::FILE_OK
;
505 leveldb_env::ErrorParsingResult result
=
506 leveldb_env::ParseMethodAndError(status
, &method
, &error
);
507 return (result
== leveldb_env::METHOD_AND_BFE
&&
508 static_cast<base::File::Error
>(error
) ==
509 base::File::FILE_ERROR_NO_SPACE
);
512 bool ChromiumEnv::MakeBackup(const std::string
& fname
) {
513 FilePath original_table_name
= FilePath::FromUTF8Unsafe(fname
);
514 FilePath backup_table_name
=
515 original_table_name
.ReplaceExtension(backup_table_extension
);
516 return base::CopyFile(original_table_name
, backup_table_name
);
519 ChromiumEnv::ChromiumEnv()
520 : name_("LevelDBEnv"),
523 started_bgthread_(false),
524 kMaxRetryTimeMillis(1000) {
525 uma_ioerror_base_name_
= name_
+ ".IOError.BFE";
528 ChromiumEnv::~ChromiumEnv() {
529 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
530 // ensure that behavior isn't accidentally changed, but there's an instance in
531 // a unit test that is deleted.
534 bool ChromiumEnv::FileExists(const std::string
& fname
) {
535 return base::PathExists(FilePath::FromUTF8Unsafe(fname
));
538 const char* ChromiumEnv::FileErrorString(base::File::Error error
) {
540 case base::File::FILE_ERROR_FAILED
:
541 return "No further details.";
542 case base::File::FILE_ERROR_IN_USE
:
543 return "File currently in use.";
544 case base::File::FILE_ERROR_EXISTS
:
545 return "File already exists.";
546 case base::File::FILE_ERROR_NOT_FOUND
:
547 return "File not found.";
548 case base::File::FILE_ERROR_ACCESS_DENIED
:
549 return "Access denied.";
550 case base::File::FILE_ERROR_TOO_MANY_OPENED
:
551 return "Too many files open.";
552 case base::File::FILE_ERROR_NO_MEMORY
:
553 return "Out of memory.";
554 case base::File::FILE_ERROR_NO_SPACE
:
555 return "No space left on drive.";
556 case base::File::FILE_ERROR_NOT_A_DIRECTORY
:
557 return "Not a directory.";
558 case base::File::FILE_ERROR_INVALID_OPERATION
:
559 return "Invalid operation.";
560 case base::File::FILE_ERROR_SECURITY
:
561 return "Security error.";
562 case base::File::FILE_ERROR_ABORT
:
563 return "File operation aborted.";
564 case base::File::FILE_ERROR_NOT_A_FILE
:
565 return "The supplied path was not a file.";
566 case base::File::FILE_ERROR_NOT_EMPTY
:
567 return "The file was not empty.";
568 case base::File::FILE_ERROR_INVALID_URL
:
569 return "Invalid URL.";
570 case base::File::FILE_ERROR_IO
:
571 return "OS or hardware error.";
572 case base::File::FILE_OK
:
574 case base::File::FILE_ERROR_MAX
:
578 return "Unknown error.";
581 FilePath
ChromiumEnv::RestoreFromBackup(const FilePath
& base_name
) {
582 FilePath table_name
= base_name
.AddExtension(table_extension
);
583 bool result
= base::CopyFile(base_name
.AddExtension(backup_table_extension
),
585 std::string
uma_name(name_
);
586 uma_name
.append(".TableRestore");
587 base::BooleanHistogram::FactoryGet(
588 uma_name
, base::Histogram::kUmaTargetedHistogramFlag
)->AddBoolean(result
);
592 void ChromiumEnv::RestoreIfNecessary(const std::string
& dir
,
593 std::vector
<std::string
>* dir_entries
) {
594 std::set
<FilePath
> tables_found
;
595 std::set
<FilePath
> backups_found
;
596 for (const std::string
& entry
: *dir_entries
) {
597 FilePath current
= FilePath::FromUTF8Unsafe(entry
);
598 if (current
.MatchesExtension(table_extension
))
599 tables_found
.insert(current
.RemoveExtension());
600 if (current
.MatchesExtension(backup_table_extension
))
601 backups_found
.insert(current
.RemoveExtension());
603 std::set
<FilePath
> backups_only
=
604 base::STLSetDifference
<std::set
<FilePath
>>(backups_found
, tables_found
);
606 if (backups_only
.size()) {
607 std::string
uma_name(name_
);
608 uma_name
.append(".MissingFiles");
609 int num_missing_files
=
610 backups_only
.size() > INT_MAX
? INT_MAX
: backups_only
.size();
611 base::Histogram::FactoryGet(uma_name
,
615 base::Histogram::kUmaTargetedHistogramFlag
)
616 ->Add(num_missing_files
);
618 FilePath dir_path
= FilePath::FromUTF8Unsafe(dir
);
619 for (const FilePath
& backup
: backups_only
) {
620 FilePath restored_table_name
= RestoreFromBackup(dir_path
.Append(backup
));
621 dir_entries
->push_back(restored_table_name
.BaseName().AsUTF8Unsafe());
625 Status
ChromiumEnv::GetChildren(const std::string
& dir
,
626 std::vector
<std::string
>* result
) {
627 std::vector
<FilePath
> entries
;
628 base::File::Error error
=
629 GetDirectoryEntries(FilePath::FromUTF8Unsafe(dir
), &entries
);
630 if (error
!= base::File::FILE_OK
) {
631 RecordOSError(kGetChildren
, error
);
632 return MakeIOError(dir
, "Could not open/read directory", kGetChildren
,
637 for (const auto& entry
: entries
)
638 result
->push_back(entry
.BaseName().AsUTF8Unsafe());
641 RestoreIfNecessary(dir
, result
);
646 Status
ChromiumEnv::DeleteFile(const std::string
& fname
) {
648 FilePath fname_filepath
= FilePath::FromUTF8Unsafe(fname
);
649 // TODO(jorlow): Should we assert this is a file?
650 if (!base::DeleteFile(fname_filepath
, false)) {
651 result
= MakeIOError(fname
, "Could not delete file.", kDeleteFile
);
652 RecordErrorAt(kDeleteFile
);
654 if (make_backup_
&& fname_filepath
.MatchesExtension(table_extension
)) {
655 base::DeleteFile(fname_filepath
.ReplaceExtension(backup_table_extension
),
661 Status
ChromiumEnv::CreateDir(const std::string
& name
) {
663 base::File::Error error
= base::File::FILE_OK
;
664 Retrier
retrier(kCreateDir
, this);
666 if (base::CreateDirectoryAndGetError(FilePath::FromUTF8Unsafe(name
),
669 } while (retrier
.ShouldKeepTrying(error
));
670 result
= MakeIOError(name
, "Could not create directory.", kCreateDir
, error
);
671 RecordOSError(kCreateDir
, error
);
675 Status
ChromiumEnv::DeleteDir(const std::string
& name
) {
677 // TODO(jorlow): Should we assert this is a directory?
678 if (!base::DeleteFile(FilePath::FromUTF8Unsafe(name
), false)) {
679 result
= MakeIOError(name
, "Could not delete directory.", kDeleteDir
);
680 RecordErrorAt(kDeleteDir
);
685 Status
ChromiumEnv::GetFileSize(const std::string
& fname
, uint64_t* size
) {
688 if (!base::GetFileSize(FilePath::FromUTF8Unsafe(fname
), &signed_size
)) {
690 s
= MakeIOError(fname
, "Could not determine file size.", kGetFileSize
);
691 RecordErrorAt(kGetFileSize
);
693 *size
= static_cast<uint64_t>(signed_size
);
698 Status
ChromiumEnv::RenameFile(const std::string
& src
, const std::string
& dst
) {
700 FilePath src_file_path
= FilePath::FromUTF8Unsafe(src
);
701 if (!base::PathExists(src_file_path
))
703 FilePath destination
= FilePath::FromUTF8Unsafe(dst
);
705 Retrier
retrier(kRenameFile
, this);
706 base::File::Error error
= base::File::FILE_OK
;
708 if (base::ReplaceFile(src_file_path
, destination
, &error
))
710 } while (retrier
.ShouldKeepTrying(error
));
712 DCHECK(error
!= base::File::FILE_OK
);
713 RecordOSError(kRenameFile
, error
);
717 "Could not rename file: %s",
718 FileErrorString(error
));
719 return MakeIOError(src
, buf
, kRenameFile
, error
);
722 Status
ChromiumEnv::LockFile(const std::string
& fname
, FileLock
** lock
) {
725 int flags
= base::File::FLAG_OPEN_ALWAYS
|
726 base::File::FLAG_READ
|
727 base::File::FLAG_WRITE
;
728 base::File::Error error_code
;
730 Retrier
retrier(kLockFile
, this);
732 file
.Initialize(FilePath::FromUTF8Unsafe(fname
), flags
);
734 error_code
= file
.error_details();
735 } while (!file
.IsValid() && retrier
.ShouldKeepTrying(error_code
));
737 if (!file
.IsValid()) {
738 if (error_code
== base::File::FILE_ERROR_NOT_FOUND
) {
739 FilePath parent
= FilePath::FromUTF8Unsafe(fname
).DirName();
740 FilePath last_parent
;
741 int num_missing_ancestors
= 0;
743 if (base::DirectoryExists(parent
))
745 ++num_missing_ancestors
;
746 last_parent
= parent
;
747 parent
= parent
.DirName();
748 } while (parent
!= last_parent
);
749 RecordLockFileAncestors(num_missing_ancestors
);
752 result
= MakeIOError(fname
, FileErrorString(error_code
), kLockFile
,
754 RecordOSError(kLockFile
, error_code
);
758 if (!locks_
.Insert(fname
)) {
759 result
= MakeIOError(fname
, "Lock file already locked.", kLockFile
);
763 Retrier lock_retrier
= Retrier(kLockFile
, this);
765 error_code
= file
.Lock();
766 } while (error_code
!= base::File::FILE_OK
&&
767 retrier
.ShouldKeepTrying(error_code
));
769 if (error_code
!= base::File::FILE_OK
) {
770 locks_
.Remove(fname
);
771 result
= MakeIOError(fname
, FileErrorString(error_code
), kLockFile
,
773 RecordOSError(kLockFile
, error_code
);
777 ChromiumFileLock
* my_lock
= new ChromiumFileLock
;
778 my_lock
->file_
= file
.Pass();
779 my_lock
->name_
= fname
;
784 Status
ChromiumEnv::UnlockFile(FileLock
* lock
) {
785 ChromiumFileLock
* my_lock
= reinterpret_cast<ChromiumFileLock
*>(lock
);
788 base::File::Error error_code
= my_lock
->file_
.Unlock();
789 if (error_code
!= base::File::FILE_OK
) {
791 MakeIOError(my_lock
->name_
, "Could not unlock lock file.", kUnlockFile
);
792 RecordOSError(kUnlockFile
, error_code
);
794 bool removed
= locks_
.Remove(my_lock
->name_
);
800 Status
ChromiumEnv::GetTestDirectory(std::string
* path
) {
802 if (test_directory_
.empty()) {
803 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix
,
806 RecordErrorAt(kGetTestDirectory
);
808 "Could not create temp directory.", "", kGetTestDirectory
);
811 *path
= test_directory_
.AsUTF8Unsafe();
816 Status
ChromiumEnv::NewLogger(const std::string
& fname
,
817 leveldb::Logger
** result
) {
818 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
819 scoped_ptr
<base::File
> f(new base::File(
820 path
, base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
));
823 RecordOSError(kNewLogger
, f
->error_details());
824 return MakeIOError(fname
, "Unable to create log file", kNewLogger
,
827 *result
= new leveldb::ChromiumLogger(f
.release());
832 Status
ChromiumEnv::NewSequentialFile(const std::string
& fname
,
833 leveldb::SequentialFile
** result
) {
834 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
835 scoped_ptr
<base::File
> f(
836 new base::File(path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
));
839 RecordOSError(kNewSequentialFile
, f
->error_details());
840 return MakeIOError(fname
, "Unable to create sequential file",
841 kNewSequentialFile
, f
->error_details());
843 *result
= new ChromiumSequentialFile(fname
, f
.release(), this);
848 void ChromiumEnv::RecordOpenFilesLimit(const std::string
& type
) {
849 #if defined(OS_POSIX)
850 GetMaxFDHistogram(type
)->Add(base::GetMaxFds());
851 #elif defined(OS_WIN)
852 // Windows is only limited by available memory
854 #error "Need to determine limit to open files for this OS"
858 Status
ChromiumEnv::NewRandomAccessFile(const std::string
& fname
,
859 leveldb::RandomAccessFile
** result
) {
860 int flags
= base::File::FLAG_READ
| base::File::FLAG_OPEN
;
861 base::File
file(FilePath::FromUTF8Unsafe(fname
), flags
);
862 if (file
.IsValid()) {
863 *result
= new ChromiumRandomAccessFile(fname
, file
.Pass(), this);
864 RecordOpenFilesLimit("Success");
867 base::File::Error error_code
= file
.error_details();
868 if (error_code
== base::File::FILE_ERROR_TOO_MANY_OPENED
)
869 RecordOpenFilesLimit("TooManyOpened");
871 RecordOpenFilesLimit("OtherError");
873 RecordOSError(kNewRandomAccessFile
, error_code
);
874 return MakeIOError(fname
, FileErrorString(error_code
), kNewRandomAccessFile
,
878 Status
ChromiumEnv::NewWritableFile(const std::string
& fname
,
879 leveldb::WritableFile
** result
) {
881 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
882 scoped_ptr
<base::File
> f(new base::File(
883 path
, base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
));
885 RecordErrorAt(kNewWritableFile
);
886 return MakeIOError(fname
, "Unable to create writable file",
887 kNewWritableFile
, f
->error_details());
889 *result
= new ChromiumWritableFile(fname
, f
.release(), this, make_backup_
);
894 Status
ChromiumEnv::NewAppendableFile(const std::string
& fname
,
895 leveldb::WritableFile
** result
) {
897 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
898 scoped_ptr
<base::File
> f(new base::File(
899 path
, base::File::FLAG_OPEN_ALWAYS
| base::File::FLAG_APPEND
));
901 RecordErrorAt(kNewAppendableFile
);
902 return MakeIOError(fname
, "Unable to create appendable file",
903 kNewAppendableFile
, f
->error_details());
905 *result
= new ChromiumWritableFile(fname
, f
.release(), this, make_backup_
);
909 uint64_t ChromiumEnv::NowMicros() {
910 return base::TimeTicks::Now().ToInternalValue();
913 void ChromiumEnv::SleepForMicroseconds(int micros
) {
914 // Round up to the next millisecond.
915 base::PlatformThread::Sleep(base::TimeDelta::FromMicroseconds(micros
));
918 void ChromiumEnv::RecordErrorAt(MethodID method
) const {
919 GetMethodIOErrorHistogram()->Add(method
);
922 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors
) const {
923 GetLockFileAncestorHistogram()->Add(num_missing_ancestors
);
926 void ChromiumEnv::RecordOSError(MethodID method
,
927 base::File::Error error
) const {
929 RecordErrorAt(method
);
930 GetOSErrorHistogram(method
, -base::File::FILE_ERROR_MAX
)->Add(-error
);
933 void ChromiumEnv::RecordBackupResult(bool result
) const {
934 std::string
uma_name(name_
);
935 uma_name
.append(".TableBackup");
936 base::BooleanHistogram::FactoryGet(
937 uma_name
, base::Histogram::kUmaTargetedHistogramFlag
)->AddBoolean(result
);
940 base::HistogramBase
* ChromiumEnv::GetOSErrorHistogram(MethodID method
,
942 std::string uma_name
;
943 base::StringAppendF(&uma_name
, "%s.%s", uma_ioerror_base_name_
.c_str(),
944 MethodIDToString(method
));
945 return base::LinearHistogram::FactoryGet(uma_name
, 1, limit
, limit
+ 1,
946 base::Histogram::kUmaTargetedHistogramFlag
);
949 base::HistogramBase
* ChromiumEnv::GetMethodIOErrorHistogram() const {
950 std::string
uma_name(name_
);
951 uma_name
.append(".IOError");
952 return base::LinearHistogram::FactoryGet(uma_name
, 1, kNumEntries
,
953 kNumEntries
+ 1, base::Histogram::kUmaTargetedHistogramFlag
);
956 base::HistogramBase
* ChromiumEnv::GetMaxFDHistogram(
957 const std::string
& type
) const {
958 std::string
uma_name(name_
);
959 uma_name
.append(".MaxFDs.").append(type
);
960 // These numbers make each bucket twice as large as the previous bucket.
961 const int kFirstEntry
= 1;
962 const int kLastEntry
= 65536;
963 const int kNumBuckets
= 18;
964 return base::Histogram::FactoryGet(
965 uma_name
, kFirstEntry
, kLastEntry
, kNumBuckets
,
966 base::Histogram::kUmaTargetedHistogramFlag
);
969 base::HistogramBase
* ChromiumEnv::GetLockFileAncestorHistogram() const {
970 std::string
uma_name(name_
);
971 uma_name
.append(".LockFileAncestorsNotFound");
974 const int kNumBuckets
= 11;
975 return base::LinearHistogram::FactoryGet(
976 uma_name
, kMin
, kMax
, kNumBuckets
,
977 base::Histogram::kUmaTargetedHistogramFlag
);
980 base::HistogramBase
* ChromiumEnv::GetRetryTimeHistogram(MethodID method
) const {
981 std::string
uma_name(name_
);
982 // TODO(dgrogan): This is probably not the best way to concatenate strings.
983 uma_name
.append(".TimeUntilSuccessFor").append(MethodIDToString(method
));
985 const int kBucketSizeMillis
= 25;
986 // Add 2, 1 for each of the buckets <1 and >max.
987 const int kNumBuckets
= kMaxRetryTimeMillis
/ kBucketSizeMillis
+ 2;
988 return base::Histogram::FactoryTimeGet(
989 uma_name
, base::TimeDelta::FromMilliseconds(1),
990 base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis
+ 1),
992 base::Histogram::kUmaTargetedHistogramFlag
);
995 base::HistogramBase
* ChromiumEnv::GetRecoveredFromErrorHistogram(
996 MethodID method
) const {
997 std::string
uma_name(name_
);
998 uma_name
.append(".RetryRecoveredFromErrorIn")
999 .append(MethodIDToString(method
));
1000 return base::LinearHistogram::FactoryGet(uma_name
, 1, kNumEntries
,
1001 kNumEntries
+ 1, base::Histogram::kUmaTargetedHistogramFlag
);
1004 class Thread
: public base::PlatformThread::Delegate
{
1006 Thread(void (*function
)(void* arg
), void* arg
)
1007 : function_(function
), arg_(arg
) {
1008 base::PlatformThreadHandle handle
;
1009 bool success
= base::PlatformThread::Create(0, this, &handle
);
1012 virtual ~Thread() {}
1013 void ThreadMain() override
{
1019 void (*function_
)(void* arg
);
1023 void ChromiumEnv::Schedule(ScheduleFunc
* function
, void* arg
) {
1026 // Start background thread if necessary
1027 if (!started_bgthread_
) {
1028 started_bgthread_
= true;
1029 StartThread(&ChromiumEnv::BGThreadWrapper
, this);
1032 // If the queue is currently empty, the background thread may currently be
1034 if (queue_
.empty()) {
1038 // Add to priority queue
1039 queue_
.push_back(BGItem());
1040 queue_
.back().function
= function
;
1041 queue_
.back().arg
= arg
;
1046 void ChromiumEnv::BGThread() {
1047 base::PlatformThread::SetName(name_
.c_str());
1050 // Wait until there is an item that is ready to run
1052 while (queue_
.empty()) {
1056 void (*function
)(void*) = queue_
.front().function
;
1057 void* arg
= queue_
.front().arg
;
1061 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
1066 void ChromiumEnv::StartThread(void (*function
)(void* arg
), void* arg
) {
1067 new Thread(function
, arg
); // Will self-delete.
1070 } // namespace leveldb_env
1075 return leveldb_env::idb_env
.Pointer();
1078 Env
* Env::Default() {
1079 return leveldb_env::default_env
.Pointer();
1082 } // namespace leveldb