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("LevelDBEnv.IDB", true /* make_backup */) {}
326 base::LazyInstance
<IDBEnv
>::Leaky idb_env
= LAZY_INSTANCE_INITIALIZER
;
328 base::LazyInstance
<ChromiumEnv
>::Leaky default_env
= LAZY_INSTANCE_INITIALIZER
;
330 } // unnamed namespace
332 const char* MethodIDToString(MethodID method
) {
334 case kSequentialFileRead
:
335 return "SequentialFileRead";
336 case kSequentialFileSkip
:
337 return "SequentialFileSkip";
338 case kRandomAccessFileRead
:
339 return "RandomAccessFileRead";
340 case kWritableFileAppend
:
341 return "WritableFileAppend";
342 case kWritableFileClose
:
343 return "WritableFileClose";
344 case kWritableFileFlush
:
345 return "WritableFileFlush";
346 case kWritableFileSync
:
347 return "WritableFileSync";
348 case kNewSequentialFile
:
349 return "NewSequentialFile";
350 case kNewRandomAccessFile
:
351 return "NewRandomAccessFile";
352 case kNewWritableFile
:
353 return "NewWritableFile";
354 case kNewAppendableFile
:
355 return "NewAppendableFile";
363 return "GetFileSize";
370 case kGetTestDirectory
:
371 return "GetTestDirectory";
377 return "GetChildren";
380 return "kNumEntries";
386 Status
MakeIOError(Slice filename
,
387 const std::string
& message
,
389 base::File::Error error
) {
392 snprintf(buf
, sizeof(buf
), "%s (ChromeMethodBFE: %d::%s::%d)",
393 message
.c_str(), method
, MethodIDToString(method
), -error
);
394 return Status::IOError(filename
, buf
);
397 Status
MakeIOError(Slice filename
,
398 const std::string
& message
,
401 snprintf(buf
, sizeof(buf
), "%s (ChromeMethodOnly: %d::%s)", message
.c_str(),
402 method
, MethodIDToString(method
));
403 return Status::IOError(filename
, buf
);
406 ErrorParsingResult
ParseMethodAndError(const leveldb::Status
& status
,
407 MethodID
* method_param
,
408 base::File::Error
* error
) {
409 const std::string status_string
= status
.ToString();
411 if (RE2::PartialMatch(status_string
.c_str(), "ChromeMethodOnly: (\\d+)",
413 *method_param
= static_cast<MethodID
>(method
);
417 if (RE2::PartialMatch(status_string
.c_str(),
418 "ChromeMethodBFE: (\\d+)::.*::(\\d+)", &method
,
420 *method_param
= static_cast<MethodID
>(method
);
421 *error
= static_cast<base::File::Error
>(-parsed_error
);
422 DCHECK_LT(*error
, base::File::FILE_OK
);
423 DCHECK_GT(*error
, base::File::FILE_ERROR_MAX
);
424 return METHOD_AND_BFE
;
429 // Keep in sync with LevelDBCorruptionTypes in histograms.xml. Also, don't
430 // change the order because indices into this array have been recorded in uma
432 const char* patterns
[] = {
434 "log record too small",
435 "corrupted internal key",
437 "missing start of fragmented record",
438 "error in middle of record",
439 "unknown record type",
440 "truncated record at end",
443 "FileReader invoked with unexpected value",
445 "CURRENT file does not end with newline",
446 "no meta-nextfile entry",
447 "no meta-lognumber entry",
448 "no last-sequence-number entry",
449 "malformed WriteBatch",
450 "bad WriteBatch Put",
451 "bad WriteBatch Delete",
452 "unknown WriteBatch tag",
453 "WriteBatch has wrong count",
454 "bad entry in block",
455 "bad block contents",
457 "truncated block read",
458 "block checksum mismatch",
460 "corrupted compressed block contents",
466 // Returns 1-based index into the above array or 0 if nothing matches.
467 int GetCorruptionCode(const leveldb::Status
& status
) {
468 DCHECK(!status
.IsIOError());
469 DCHECK(!status
.ok());
470 const int kOtherError
= 0;
471 int error
= kOtherError
;
472 const std::string
& str_error
= status
.ToString();
473 const size_t kNumPatterns
= arraysize(patterns
);
474 for (size_t i
= 0; i
< kNumPatterns
; ++i
) {
475 if (str_error
.find(patterns
[i
]) != std::string::npos
) {
483 int GetNumCorruptionCodes() {
484 // + 1 for the "other" error that is returned when a corruption message
485 // doesn't match any of the patterns.
486 return arraysize(patterns
) + 1;
489 std::string
GetCorruptionMessage(const leveldb::Status
& status
) {
490 int code
= GetCorruptionCode(status
);
492 return "Unknown corruption";
493 return patterns
[code
- 1];
496 bool IndicatesDiskFull(const leveldb::Status
& status
) {
499 leveldb_env::MethodID method
;
500 base::File::Error error
= base::File::FILE_OK
;
501 leveldb_env::ErrorParsingResult result
=
502 leveldb_env::ParseMethodAndError(status
, &method
, &error
);
503 return (result
== leveldb_env::METHOD_AND_BFE
&&
504 static_cast<base::File::Error
>(error
) ==
505 base::File::FILE_ERROR_NO_SPACE
);
508 bool ChromiumEnv::MakeBackup(const std::string
& fname
) {
509 FilePath original_table_name
= FilePath::FromUTF8Unsafe(fname
);
510 FilePath backup_table_name
=
511 original_table_name
.ReplaceExtension(backup_table_extension
);
512 return base::CopyFile(original_table_name
, backup_table_name
);
515 ChromiumEnv::ChromiumEnv()
516 : ChromiumEnv("LevelDBEnv", false /* make_backup */) {}
518 ChromiumEnv::ChromiumEnv(const std::string
& name
, bool make_backup
)
520 make_backup_(make_backup
),
522 started_bgthread_(false),
523 kMaxRetryTimeMillis(1000) {
524 uma_ioerror_base_name_
= name_
+ ".IOError.BFE";
527 ChromiumEnv::~ChromiumEnv() {
528 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
529 // ensure that behavior isn't accidentally changed, but there's an instance in
530 // a unit test that is deleted.
533 bool ChromiumEnv::FileExists(const std::string
& fname
) {
534 return base::PathExists(FilePath::FromUTF8Unsafe(fname
));
537 const char* ChromiumEnv::FileErrorString(base::File::Error error
) {
539 case base::File::FILE_ERROR_FAILED
:
540 return "No further details.";
541 case base::File::FILE_ERROR_IN_USE
:
542 return "File currently in use.";
543 case base::File::FILE_ERROR_EXISTS
:
544 return "File already exists.";
545 case base::File::FILE_ERROR_NOT_FOUND
:
546 return "File not found.";
547 case base::File::FILE_ERROR_ACCESS_DENIED
:
548 return "Access denied.";
549 case base::File::FILE_ERROR_TOO_MANY_OPENED
:
550 return "Too many files open.";
551 case base::File::FILE_ERROR_NO_MEMORY
:
552 return "Out of memory.";
553 case base::File::FILE_ERROR_NO_SPACE
:
554 return "No space left on drive.";
555 case base::File::FILE_ERROR_NOT_A_DIRECTORY
:
556 return "Not a directory.";
557 case base::File::FILE_ERROR_INVALID_OPERATION
:
558 return "Invalid operation.";
559 case base::File::FILE_ERROR_SECURITY
:
560 return "Security error.";
561 case base::File::FILE_ERROR_ABORT
:
562 return "File operation aborted.";
563 case base::File::FILE_ERROR_NOT_A_FILE
:
564 return "The supplied path was not a file.";
565 case base::File::FILE_ERROR_NOT_EMPTY
:
566 return "The file was not empty.";
567 case base::File::FILE_ERROR_INVALID_URL
:
568 return "Invalid URL.";
569 case base::File::FILE_ERROR_IO
:
570 return "OS or hardware error.";
571 case base::File::FILE_OK
:
573 case base::File::FILE_ERROR_MAX
:
577 return "Unknown error.";
580 FilePath
ChromiumEnv::RestoreFromBackup(const FilePath
& base_name
) {
581 FilePath table_name
= base_name
.AddExtension(table_extension
);
582 bool result
= base::CopyFile(base_name
.AddExtension(backup_table_extension
),
584 std::string
uma_name(name_
);
585 uma_name
.append(".TableRestore");
586 base::BooleanHistogram::FactoryGet(
587 uma_name
, base::Histogram::kUmaTargetedHistogramFlag
)->AddBoolean(result
);
591 void ChromiumEnv::RestoreIfNecessary(const std::string
& dir
,
592 std::vector
<std::string
>* dir_entries
) {
593 std::set
<FilePath
> tables_found
;
594 std::set
<FilePath
> backups_found
;
595 for (const std::string
& entry
: *dir_entries
) {
596 FilePath current
= FilePath::FromUTF8Unsafe(entry
);
597 if (current
.MatchesExtension(table_extension
))
598 tables_found
.insert(current
.RemoveExtension());
599 if (current
.MatchesExtension(backup_table_extension
))
600 backups_found
.insert(current
.RemoveExtension());
602 std::set
<FilePath
> backups_only
=
603 base::STLSetDifference
<std::set
<FilePath
>>(backups_found
, tables_found
);
605 if (backups_only
.size()) {
606 std::string
uma_name(name_
);
607 uma_name
.append(".MissingFiles");
608 int num_missing_files
=
609 backups_only
.size() > INT_MAX
? INT_MAX
: backups_only
.size();
610 base::Histogram::FactoryGet(uma_name
,
614 base::Histogram::kUmaTargetedHistogramFlag
)
615 ->Add(num_missing_files
);
617 FilePath dir_path
= FilePath::FromUTF8Unsafe(dir
);
618 for (const FilePath
& backup
: backups_only
) {
619 FilePath restored_table_name
= RestoreFromBackup(dir_path
.Append(backup
));
620 dir_entries
->push_back(restored_table_name
.BaseName().AsUTF8Unsafe());
624 Status
ChromiumEnv::GetChildren(const std::string
& dir
,
625 std::vector
<std::string
>* result
) {
626 std::vector
<FilePath
> entries
;
627 base::File::Error error
=
628 GetDirectoryEntries(FilePath::FromUTF8Unsafe(dir
), &entries
);
629 if (error
!= base::File::FILE_OK
) {
630 RecordOSError(kGetChildren
, error
);
631 return MakeIOError(dir
, "Could not open/read directory", kGetChildren
,
636 for (const auto& entry
: entries
)
637 result
->push_back(entry
.BaseName().AsUTF8Unsafe());
640 RestoreIfNecessary(dir
, result
);
645 Status
ChromiumEnv::DeleteFile(const std::string
& fname
) {
647 FilePath fname_filepath
= FilePath::FromUTF8Unsafe(fname
);
648 // TODO(jorlow): Should we assert this is a file?
649 if (!base::DeleteFile(fname_filepath
, false)) {
650 result
= MakeIOError(fname
, "Could not delete file.", kDeleteFile
);
651 RecordErrorAt(kDeleteFile
);
653 if (make_backup_
&& fname_filepath
.MatchesExtension(table_extension
)) {
654 base::DeleteFile(fname_filepath
.ReplaceExtension(backup_table_extension
),
660 Status
ChromiumEnv::CreateDir(const std::string
& name
) {
662 base::File::Error error
= base::File::FILE_OK
;
663 Retrier
retrier(kCreateDir
, this);
665 if (base::CreateDirectoryAndGetError(FilePath::FromUTF8Unsafe(name
),
668 } while (retrier
.ShouldKeepTrying(error
));
669 result
= MakeIOError(name
, "Could not create directory.", kCreateDir
, error
);
670 RecordOSError(kCreateDir
, error
);
674 Status
ChromiumEnv::DeleteDir(const std::string
& name
) {
676 // TODO(jorlow): Should we assert this is a directory?
677 if (!base::DeleteFile(FilePath::FromUTF8Unsafe(name
), false)) {
678 result
= MakeIOError(name
, "Could not delete directory.", kDeleteDir
);
679 RecordErrorAt(kDeleteDir
);
684 Status
ChromiumEnv::GetFileSize(const std::string
& fname
, uint64_t* size
) {
687 if (!base::GetFileSize(FilePath::FromUTF8Unsafe(fname
), &signed_size
)) {
689 s
= MakeIOError(fname
, "Could not determine file size.", kGetFileSize
);
690 RecordErrorAt(kGetFileSize
);
692 *size
= static_cast<uint64_t>(signed_size
);
697 Status
ChromiumEnv::RenameFile(const std::string
& src
, const std::string
& dst
) {
699 FilePath src_file_path
= FilePath::FromUTF8Unsafe(src
);
700 if (!base::PathExists(src_file_path
))
702 FilePath destination
= FilePath::FromUTF8Unsafe(dst
);
704 Retrier
retrier(kRenameFile
, this);
705 base::File::Error error
= base::File::FILE_OK
;
707 if (base::ReplaceFile(src_file_path
, destination
, &error
))
709 } while (retrier
.ShouldKeepTrying(error
));
711 DCHECK(error
!= base::File::FILE_OK
);
712 RecordOSError(kRenameFile
, error
);
716 "Could not rename file: %s",
717 FileErrorString(error
));
718 return MakeIOError(src
, buf
, kRenameFile
, error
);
721 Status
ChromiumEnv::LockFile(const std::string
& fname
, FileLock
** lock
) {
724 int flags
= base::File::FLAG_OPEN_ALWAYS
|
725 base::File::FLAG_READ
|
726 base::File::FLAG_WRITE
;
727 base::File::Error error_code
;
729 Retrier
retrier(kLockFile
, this);
731 file
.Initialize(FilePath::FromUTF8Unsafe(fname
), flags
);
733 error_code
= file
.error_details();
734 } while (!file
.IsValid() && retrier
.ShouldKeepTrying(error_code
));
736 if (!file
.IsValid()) {
737 if (error_code
== base::File::FILE_ERROR_NOT_FOUND
) {
738 FilePath parent
= FilePath::FromUTF8Unsafe(fname
).DirName();
739 FilePath last_parent
;
740 int num_missing_ancestors
= 0;
742 if (base::DirectoryExists(parent
))
744 ++num_missing_ancestors
;
745 last_parent
= parent
;
746 parent
= parent
.DirName();
747 } while (parent
!= last_parent
);
748 RecordLockFileAncestors(num_missing_ancestors
);
751 result
= MakeIOError(fname
, FileErrorString(error_code
), kLockFile
,
753 RecordOSError(kLockFile
, error_code
);
757 if (!locks_
.Insert(fname
)) {
758 result
= MakeIOError(fname
, "Lock file already locked.", kLockFile
);
762 Retrier lock_retrier
= Retrier(kLockFile
, this);
764 error_code
= file
.Lock();
765 } while (error_code
!= base::File::FILE_OK
&&
766 retrier
.ShouldKeepTrying(error_code
));
768 if (error_code
!= base::File::FILE_OK
) {
769 locks_
.Remove(fname
);
770 result
= MakeIOError(fname
, FileErrorString(error_code
), kLockFile
,
772 RecordOSError(kLockFile
, error_code
);
776 ChromiumFileLock
* my_lock
= new ChromiumFileLock
;
777 my_lock
->file_
= file
.Pass();
778 my_lock
->name_
= fname
;
783 Status
ChromiumEnv::UnlockFile(FileLock
* lock
) {
784 ChromiumFileLock
* my_lock
= reinterpret_cast<ChromiumFileLock
*>(lock
);
787 base::File::Error error_code
= my_lock
->file_
.Unlock();
788 if (error_code
!= base::File::FILE_OK
) {
790 MakeIOError(my_lock
->name_
, "Could not unlock lock file.", kUnlockFile
);
791 RecordOSError(kUnlockFile
, error_code
);
793 bool removed
= locks_
.Remove(my_lock
->name_
);
799 Status
ChromiumEnv::GetTestDirectory(std::string
* path
) {
801 if (test_directory_
.empty()) {
802 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix
,
805 RecordErrorAt(kGetTestDirectory
);
807 "Could not create temp directory.", "", kGetTestDirectory
);
810 *path
= test_directory_
.AsUTF8Unsafe();
815 Status
ChromiumEnv::NewLogger(const std::string
& fname
,
816 leveldb::Logger
** result
) {
817 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
818 scoped_ptr
<base::File
> f(new base::File(
819 path
, base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
));
822 RecordOSError(kNewLogger
, f
->error_details());
823 return MakeIOError(fname
, "Unable to create log file", kNewLogger
,
826 *result
= new leveldb::ChromiumLogger(f
.release());
831 Status
ChromiumEnv::NewSequentialFile(const std::string
& fname
,
832 leveldb::SequentialFile
** result
) {
833 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
834 scoped_ptr
<base::File
> f(
835 new base::File(path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
));
838 RecordOSError(kNewSequentialFile
, f
->error_details());
839 return MakeIOError(fname
, "Unable to create sequential file",
840 kNewSequentialFile
, f
->error_details());
842 *result
= new ChromiumSequentialFile(fname
, f
.release(), this);
847 void ChromiumEnv::RecordOpenFilesLimit(const std::string
& type
) {
848 #if defined(OS_POSIX)
849 GetMaxFDHistogram(type
)->Add(base::GetMaxFds());
850 #elif defined(OS_WIN)
851 // Windows is only limited by available memory
853 #error "Need to determine limit to open files for this OS"
857 Status
ChromiumEnv::NewRandomAccessFile(const std::string
& fname
,
858 leveldb::RandomAccessFile
** result
) {
859 int flags
= base::File::FLAG_READ
| base::File::FLAG_OPEN
;
860 base::File
file(FilePath::FromUTF8Unsafe(fname
), flags
);
861 if (file
.IsValid()) {
862 *result
= new ChromiumRandomAccessFile(fname
, file
.Pass(), this);
863 RecordOpenFilesLimit("Success");
866 base::File::Error error_code
= file
.error_details();
867 if (error_code
== base::File::FILE_ERROR_TOO_MANY_OPENED
)
868 RecordOpenFilesLimit("TooManyOpened");
870 RecordOpenFilesLimit("OtherError");
872 RecordOSError(kNewRandomAccessFile
, error_code
);
873 return MakeIOError(fname
, FileErrorString(error_code
), kNewRandomAccessFile
,
877 Status
ChromiumEnv::NewWritableFile(const std::string
& fname
,
878 leveldb::WritableFile
** result
) {
880 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
881 scoped_ptr
<base::File
> f(new base::File(
882 path
, base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
));
884 RecordErrorAt(kNewWritableFile
);
885 return MakeIOError(fname
, "Unable to create writable file",
886 kNewWritableFile
, f
->error_details());
888 *result
= new ChromiumWritableFile(fname
, f
.release(), this, make_backup_
);
893 Status
ChromiumEnv::NewAppendableFile(const std::string
& fname
,
894 leveldb::WritableFile
** result
) {
896 FilePath path
= FilePath::FromUTF8Unsafe(fname
);
897 scoped_ptr
<base::File
> f(new base::File(
898 path
, base::File::FLAG_OPEN_ALWAYS
| base::File::FLAG_APPEND
));
900 RecordErrorAt(kNewAppendableFile
);
901 return MakeIOError(fname
, "Unable to create appendable file",
902 kNewAppendableFile
, f
->error_details());
904 *result
= new ChromiumWritableFile(fname
, f
.release(), this, make_backup_
);
908 uint64_t ChromiumEnv::NowMicros() {
909 return base::TimeTicks::Now().ToInternalValue();
912 void ChromiumEnv::SleepForMicroseconds(int micros
) {
913 // Round up to the next millisecond.
914 base::PlatformThread::Sleep(base::TimeDelta::FromMicroseconds(micros
));
917 void ChromiumEnv::RecordErrorAt(MethodID method
) const {
918 GetMethodIOErrorHistogram()->Add(method
);
921 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors
) const {
922 GetLockFileAncestorHistogram()->Add(num_missing_ancestors
);
925 void ChromiumEnv::RecordOSError(MethodID method
,
926 base::File::Error error
) const {
928 RecordErrorAt(method
);
929 GetOSErrorHistogram(method
, -base::File::FILE_ERROR_MAX
)->Add(-error
);
932 void ChromiumEnv::RecordBackupResult(bool result
) const {
933 std::string
uma_name(name_
);
934 uma_name
.append(".TableBackup");
935 base::BooleanHistogram::FactoryGet(
936 uma_name
, base::Histogram::kUmaTargetedHistogramFlag
)->AddBoolean(result
);
939 base::HistogramBase
* ChromiumEnv::GetOSErrorHistogram(MethodID method
,
941 std::string uma_name
;
942 base::StringAppendF(&uma_name
, "%s.%s", uma_ioerror_base_name_
.c_str(),
943 MethodIDToString(method
));
944 return base::LinearHistogram::FactoryGet(uma_name
, 1, limit
, limit
+ 1,
945 base::Histogram::kUmaTargetedHistogramFlag
);
948 base::HistogramBase
* ChromiumEnv::GetMethodIOErrorHistogram() const {
949 std::string
uma_name(name_
);
950 uma_name
.append(".IOError");
951 return base::LinearHistogram::FactoryGet(uma_name
, 1, kNumEntries
,
952 kNumEntries
+ 1, base::Histogram::kUmaTargetedHistogramFlag
);
955 base::HistogramBase
* ChromiumEnv::GetMaxFDHistogram(
956 const std::string
& type
) const {
957 std::string
uma_name(name_
);
958 uma_name
.append(".MaxFDs.").append(type
);
959 // These numbers make each bucket twice as large as the previous bucket.
960 const int kFirstEntry
= 1;
961 const int kLastEntry
= 65536;
962 const int kNumBuckets
= 18;
963 return base::Histogram::FactoryGet(
964 uma_name
, kFirstEntry
, kLastEntry
, kNumBuckets
,
965 base::Histogram::kUmaTargetedHistogramFlag
);
968 base::HistogramBase
* ChromiumEnv::GetLockFileAncestorHistogram() const {
969 std::string
uma_name(name_
);
970 uma_name
.append(".LockFileAncestorsNotFound");
973 const int kNumBuckets
= 11;
974 return base::LinearHistogram::FactoryGet(
975 uma_name
, kMin
, kMax
, kNumBuckets
,
976 base::Histogram::kUmaTargetedHistogramFlag
);
979 base::HistogramBase
* ChromiumEnv::GetRetryTimeHistogram(MethodID method
) const {
980 std::string
uma_name(name_
);
981 // TODO(dgrogan): This is probably not the best way to concatenate strings.
982 uma_name
.append(".TimeUntilSuccessFor").append(MethodIDToString(method
));
984 const int kBucketSizeMillis
= 25;
985 // Add 2, 1 for each of the buckets <1 and >max.
986 const int kNumBuckets
= kMaxRetryTimeMillis
/ kBucketSizeMillis
+ 2;
987 return base::Histogram::FactoryTimeGet(
988 uma_name
, base::TimeDelta::FromMilliseconds(1),
989 base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis
+ 1),
991 base::Histogram::kUmaTargetedHistogramFlag
);
994 base::HistogramBase
* ChromiumEnv::GetRecoveredFromErrorHistogram(
995 MethodID method
) const {
996 std::string
uma_name(name_
);
997 uma_name
.append(".RetryRecoveredFromErrorIn")
998 .append(MethodIDToString(method
));
999 return base::LinearHistogram::FactoryGet(uma_name
, 1, kNumEntries
,
1000 kNumEntries
+ 1, base::Histogram::kUmaTargetedHistogramFlag
);
1003 class Thread
: public base::PlatformThread::Delegate
{
1005 Thread(void (*function
)(void* arg
), void* arg
)
1006 : function_(function
), arg_(arg
) {
1007 base::PlatformThreadHandle handle
;
1008 bool success
= base::PlatformThread::Create(0, this, &handle
);
1011 virtual ~Thread() {}
1012 void ThreadMain() override
{
1018 void (*function_
)(void* arg
);
1022 void ChromiumEnv::Schedule(ScheduleFunc
* function
, void* arg
) {
1025 // Start background thread if necessary
1026 if (!started_bgthread_
) {
1027 started_bgthread_
= true;
1028 StartThread(&ChromiumEnv::BGThreadWrapper
, this);
1031 // If the queue is currently empty, the background thread may currently be
1033 if (queue_
.empty()) {
1037 // Add to priority queue
1038 queue_
.push_back(BGItem());
1039 queue_
.back().function
= function
;
1040 queue_
.back().arg
= arg
;
1045 void ChromiumEnv::BGThread() {
1046 base::PlatformThread::SetName(name_
.c_str());
1049 // Wait until there is an item that is ready to run
1051 while (queue_
.empty()) {
1055 void (*function
)(void*) = queue_
.front().function
;
1056 void* arg
= queue_
.front().arg
;
1060 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
1065 void ChromiumEnv::StartThread(void (*function
)(void* arg
), void* arg
) {
1066 new Thread(function
, arg
); // Will self-delete.
1069 } // namespace leveldb_env
1074 return leveldb_env::idb_env
.Pointer();
1077 Env
* Env::Default() {
1078 return leveldb_env::default_env
.Pointer();
1081 } // namespace leveldb