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.
4
5 #include "third_party/leveldatabase/env_chromium.h"
6
7 #if defined(OS_WIN)
8 #include <io.h>
9 #endif
10
11 #include "base/debug/trace_event.h"
12 #include "base/files/file_util.h"
13 #include "base/lazy_instance.h"
14 #include "base/metrics/histogram.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "third_party/leveldatabase/env_chromium_stdio.h"
17 #include "third_party/re2/re2/re2.h"
18
19 #if defined(OS_WIN)
20 #include "base/command_line.h"
21 #include "base/win/win_util.h"
22 #include "third_party/leveldatabase/env_chromium_win.h"
23 #endif
24
25 using leveldb::FileLock;
26 using leveldb::Slice;
27 using leveldb::Status;
28
29 namespace leveldb_env {
30
31 namespace {
32
33 const base::FilePath::CharType backup_table_extension[] =
34 FILE_PATH_LITERAL(".bak");
35 const base::FilePath::CharType table_extension[] = FILE_PATH_LITERAL(".ldb");
36
37 static const base::FilePath::CharType kLevelDBTestDirectoryPrefix[]
38 = FILE_PATH_LITERAL("leveldb-test-");
39
40 class ChromiumFileLock : public FileLock {
41 public:
42 ::base::File file_;
43 std::string name_;
44 };
45
46 class Retrier {
47 public:
Retrier(MethodID method,RetrierProvider * provider)48 Retrier(MethodID method, RetrierProvider* provider)
49 : start_(base::TimeTicks::Now()),
50 limit_(start_ + base::TimeDelta::FromMilliseconds(
51 provider->MaxRetryTimeMillis())),
52 last_(start_),
53 time_to_sleep_(base::TimeDelta::FromMilliseconds(10)),
54 success_(true),
55 method_(method),
56 last_error_(base::File::FILE_OK),
57 provider_(provider) {}
~Retrier()58 ~Retrier() {
59 if (success_) {
60 provider_->GetRetryTimeHistogram(method_)->AddTime(last_ - start_);
61 if (last_error_ != base::File::FILE_OK) {
62 DCHECK_LT(last_error_, 0);
63 provider_->GetRecoveredFromErrorHistogram(method_)->Add(-last_error_);
64 }
65 }
66 }
ShouldKeepTrying(base::File::Error last_error)67 bool ShouldKeepTrying(base::File::Error last_error) {
68 DCHECK_NE(last_error, base::File::FILE_OK);
69 last_error_ = last_error;
70 if (last_ < limit_) {
71 base::PlatformThread::Sleep(time_to_sleep_);
72 last_ = base::TimeTicks::Now();
73 return true;
74 }
75 success_ = false;
76 return false;
77 }
78
79 private:
80 base::TimeTicks start_;
81 base::TimeTicks limit_;
82 base::TimeTicks last_;
83 base::TimeDelta time_to_sleep_;
84 bool success_;
85 MethodID method_;
86 base::File::Error last_error_;
87 RetrierProvider* provider_;
88 };
89
90 class IDBEnvStdio : public ChromiumEnvStdio {
91 public:
IDBEnvStdio()92 IDBEnvStdio() : ChromiumEnvStdio() {
93 name_ = "LevelDBEnv.IDB";
94 make_backup_ = true;
95 }
96 };
97
98 #if defined(OS_WIN)
99 class IDBEnvWin : public ChromiumEnvWin {
100 public:
IDBEnvWin()101 IDBEnvWin() : ChromiumEnvWin() {
102 name_ = "LevelDBEnv.IDB";
103 make_backup_ = true;
104 }
105 };
106 #endif
107
108 #if defined(OS_WIN)
109 ::base::LazyInstance<IDBEnvWin>::Leaky idb_env =
110 LAZY_INSTANCE_INITIALIZER;
111 #else
112 ::base::LazyInstance<IDBEnvStdio>::Leaky idb_env =
113 LAZY_INSTANCE_INITIALIZER;
114 #endif
115
116 ::base::LazyInstance<ChromiumEnvStdio>::Leaky default_env =
117 LAZY_INSTANCE_INITIALIZER;
118
119 } // unnamed namespace
120
MethodIDToString(MethodID method)121 const char* MethodIDToString(MethodID method) {
122 switch (method) {
123 case kSequentialFileRead:
124 return "SequentialFileRead";
125 case kSequentialFileSkip:
126 return "SequentialFileSkip";
127 case kRandomAccessFileRead:
128 return "RandomAccessFileRead";
129 case kWritableFileAppend:
130 return "WritableFileAppend";
131 case kWritableFileClose:
132 return "WritableFileClose";
133 case kWritableFileFlush:
134 return "WritableFileFlush";
135 case kWritableFileSync:
136 return "WritableFileSync";
137 case kNewSequentialFile:
138 return "NewSequentialFile";
139 case kNewRandomAccessFile:
140 return "NewRandomAccessFile";
141 case kNewWritableFile:
142 return "NewWritableFile";
143 case kDeleteFile:
144 return "DeleteFile";
145 case kCreateDir:
146 return "CreateDir";
147 case kDeleteDir:
148 return "DeleteDir";
149 case kGetFileSize:
150 return "GetFileSize";
151 case kRenameFile:
152 return "RenameFile";
153 case kLockFile:
154 return "LockFile";
155 case kUnlockFile:
156 return "UnlockFile";
157 case kGetTestDirectory:
158 return "GetTestDirectory";
159 case kNewLogger:
160 return "NewLogger";
161 case kSyncParent:
162 return "SyncParent";
163 case kGetChildren:
164 return "GetChildren";
165 case kNumEntries:
166 NOTREACHED();
167 return "kNumEntries";
168 }
169 NOTREACHED();
170 return "Unknown";
171 }
172
MakeIOError(Slice filename,const char * message,MethodID method,int saved_errno)173 Status MakeIOError(Slice filename,
174 const char* message,
175 MethodID method,
176 int saved_errno) {
177 char buf[512];
178 snprintf(buf,
179 sizeof(buf),
180 "%s (ChromeMethodErrno: %d::%s::%d)",
181 message,
182 method,
183 MethodIDToString(method),
184 saved_errno);
185 return Status::IOError(filename, buf);
186 }
187
MakeIOError(Slice filename,const char * message,MethodID method,base::File::Error error)188 Status MakeIOError(Slice filename,
189 const char* message,
190 MethodID method,
191 base::File::Error error) {
192 DCHECK_LT(error, 0);
193 char buf[512];
194 snprintf(buf,
195 sizeof(buf),
196 "%s (ChromeMethodPFE: %d::%s::%d)",
197 message,
198 method,
199 MethodIDToString(method),
200 -error);
201 return Status::IOError(filename, buf);
202 }
203
MakeIOError(Slice filename,const char * message,MethodID method)204 Status MakeIOError(Slice filename, const char* message, MethodID method) {
205 char buf[512];
206 snprintf(buf,
207 sizeof(buf),
208 "%s (ChromeMethodOnly: %d::%s)",
209 message,
210 method,
211 MethodIDToString(method));
212 return Status::IOError(filename, buf);
213 }
214
ParseMethodAndError(const char * string,MethodID * method_param,int * error)215 ErrorParsingResult ParseMethodAndError(const char* string,
216 MethodID* method_param,
217 int* error) {
218 int method;
219 if (RE2::PartialMatch(string, "ChromeMethodOnly: (\\d+)", &method)) {
220 *method_param = static_cast<MethodID>(method);
221 return METHOD_ONLY;
222 }
223 if (RE2::PartialMatch(
224 string, "ChromeMethodPFE: (\\d+)::.*::(\\d+)", &method, error)) {
225 *error = -*error;
226 *method_param = static_cast<MethodID>(method);
227 return METHOD_AND_PFE;
228 }
229 if (RE2::PartialMatch(
230 string, "ChromeMethodErrno: (\\d+)::.*::(\\d+)", &method, error)) {
231 *method_param = static_cast<MethodID>(method);
232 return METHOD_AND_ERRNO;
233 }
234 return NONE;
235 }
236
237 // Keep in sync with LevelDBCorruptionTypes in histograms.xml. Also, don't
238 // change the order because indices into this array have been recorded in uma
239 // histograms.
240 const char* patterns[] = {
241 "missing files",
242 "log record too small",
243 "corrupted internal key",
244 "partial record",
245 "missing start of fragmented record",
246 "error in middle of record",
247 "unknown record type",
248 "truncated record at end",
249 "bad record length",
250 "VersionEdit",
251 "FileReader invoked with unexpected value",
252 "corrupted key",
253 "CURRENT file does not end with newline",
254 "no meta-nextfile entry",
255 "no meta-lognumber entry",
256 "no last-sequence-number entry",
257 "malformed WriteBatch",
258 "bad WriteBatch Put",
259 "bad WriteBatch Delete",
260 "unknown WriteBatch tag",
261 "WriteBatch has wrong count",
262 "bad entry in block",
263 "bad block contents",
264 "bad block handle",
265 "truncated block read",
266 "block checksum mismatch",
267 "checksum mismatch",
268 "corrupted compressed block contents",
269 "bad block type",
270 "bad magic number",
271 "file is too short",
272 };
273
274 // Returns 1-based index into the above array or 0 if nothing matches.
GetCorruptionCode(const leveldb::Status & status)275 int GetCorruptionCode(const leveldb::Status& status) {
276 DCHECK(!status.IsIOError());
277 DCHECK(!status.ok());
278 const int kOtherError = 0;
279 int error = kOtherError;
280 const std::string& str_error = status.ToString();
281 const size_t kNumPatterns = arraysize(patterns);
282 for (size_t i = 0; i < kNumPatterns; ++i) {
283 if (str_error.find(patterns[i]) != std::string::npos) {
284 error = i + 1;
285 break;
286 }
287 }
288 return error;
289 }
290
GetNumCorruptionCodes()291 int GetNumCorruptionCodes() {
292 // + 1 for the "other" error that is returned when a corruption message
293 // doesn't match any of the patterns.
294 return arraysize(patterns) + 1;
295 }
296
GetCorruptionMessage(const leveldb::Status & status)297 std::string GetCorruptionMessage(const leveldb::Status& status) {
298 int code = GetCorruptionCode(status);
299 if (code == 0)
300 return "Unknown corruption";
301 return patterns[code - 1];
302 }
303
IndicatesDiskFull(const leveldb::Status & status)304 bool IndicatesDiskFull(const leveldb::Status& status) {
305 if (status.ok())
306 return false;
307 leveldb_env::MethodID method;
308 int error = -1;
309 leveldb_env::ErrorParsingResult result = leveldb_env::ParseMethodAndError(
310 status.ToString().c_str(), &method, &error);
311 return (result == leveldb_env::METHOD_AND_PFE &&
312 static_cast<base::File::Error>(error) ==
313 base::File::FILE_ERROR_NO_SPACE) ||
314 (result == leveldb_env::METHOD_AND_ERRNO && error == ENOSPC);
315 }
316
IsIOError(const leveldb::Status & status)317 bool IsIOError(const leveldb::Status& status) {
318 leveldb_env::MethodID method;
319 int error = -1;
320 leveldb_env::ErrorParsingResult result = leveldb_env::ParseMethodAndError(
321 status.ToString().c_str(), &method, &error);
322 return result != leveldb_env::NONE;
323 }
324
IsCorruption(const leveldb::Status & status)325 bool IsCorruption(const leveldb::Status& status) {
326 // LevelDB returns InvalidArgument when an sst file is truncated but there is
327 // no IsInvalidArgument() accessor defined.
328 return status.IsCorruption() || (!status.ok() && !IsIOError(status));
329 }
330
FilePathToString(const base::FilePath & file_path)331 std::string FilePathToString(const base::FilePath& file_path) {
332 #if defined(OS_WIN)
333 return base::UTF16ToUTF8(file_path.value());
334 #else
335 return file_path.value();
336 #endif
337 }
338
CreateFilePath(const std::string & file_path)339 base::FilePath ChromiumEnv::CreateFilePath(const std::string& file_path) {
340 #if defined(OS_WIN)
341 return base::FilePath(base::UTF8ToUTF16(file_path));
342 #else
343 return base::FilePath(file_path);
344 #endif
345 }
346
MakeBackup(const std::string & fname)347 bool ChromiumEnv::MakeBackup(const std::string& fname) {
348 base::FilePath original_table_name = CreateFilePath(fname);
349 base::FilePath backup_table_name =
350 original_table_name.ReplaceExtension(backup_table_extension);
351 return base::CopyFile(original_table_name, backup_table_name);
352 }
353
HasTableExtension(const base::FilePath & path)354 bool ChromiumEnv::HasTableExtension(const base::FilePath& path) {
355 return path.MatchesExtension(table_extension);
356 }
357
ChromiumEnv()358 ChromiumEnv::ChromiumEnv()
359 : name_("LevelDBEnv"),
360 make_backup_(false),
361 bgsignal_(&mu_),
362 started_bgthread_(false),
363 kMaxRetryTimeMillis(1000) {
364 }
365
~ChromiumEnv()366 ChromiumEnv::~ChromiumEnv() {
367 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
368 // ensure that behavior isn't accidentally changed, but there's an instance in
369 // a unit test that is deleted.
370 }
371
FileExists(const std::string & fname)372 bool ChromiumEnv::FileExists(const std::string& fname) {
373 return ::base::PathExists(CreateFilePath(fname));
374 }
375
FileErrorString(::base::File::Error error)376 const char* ChromiumEnv::FileErrorString(::base::File::Error error) {
377 switch (error) {
378 case ::base::File::FILE_ERROR_FAILED:
379 return "No further details.";
380 case ::base::File::FILE_ERROR_IN_USE:
381 return "File currently in use.";
382 case ::base::File::FILE_ERROR_EXISTS:
383 return "File already exists.";
384 case ::base::File::FILE_ERROR_NOT_FOUND:
385 return "File not found.";
386 case ::base::File::FILE_ERROR_ACCESS_DENIED:
387 return "Access denied.";
388 case ::base::File::FILE_ERROR_TOO_MANY_OPENED:
389 return "Too many files open.";
390 case ::base::File::FILE_ERROR_NO_MEMORY:
391 return "Out of memory.";
392 case ::base::File::FILE_ERROR_NO_SPACE:
393 return "No space left on drive.";
394 case ::base::File::FILE_ERROR_NOT_A_DIRECTORY:
395 return "Not a directory.";
396 case ::base::File::FILE_ERROR_INVALID_OPERATION:
397 return "Invalid operation.";
398 case ::base::File::FILE_ERROR_SECURITY:
399 return "Security error.";
400 case ::base::File::FILE_ERROR_ABORT:
401 return "File operation aborted.";
402 case ::base::File::FILE_ERROR_NOT_A_FILE:
403 return "The supplied path was not a file.";
404 case ::base::File::FILE_ERROR_NOT_EMPTY:
405 return "The file was not empty.";
406 case ::base::File::FILE_ERROR_INVALID_URL:
407 return "Invalid URL.";
408 case ::base::File::FILE_ERROR_IO:
409 return "OS or hardware error.";
410 case ::base::File::FILE_OK:
411 return "OK.";
412 case ::base::File::FILE_ERROR_MAX:
413 NOTREACHED();
414 }
415 NOTIMPLEMENTED();
416 return "Unknown error.";
417 }
418
RestoreFromBackup(const base::FilePath & base_name)419 base::FilePath ChromiumEnv::RestoreFromBackup(const base::FilePath& base_name) {
420 base::FilePath table_name =
421 base_name.AddExtension(table_extension);
422 bool result = base::CopyFile(base_name.AddExtension(backup_table_extension),
423 table_name);
424 std::string uma_name(name_);
425 uma_name.append(".TableRestore");
426 base::BooleanHistogram::FactoryGet(
427 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
428 return table_name;
429 }
430
RestoreIfNecessary(const std::string & dir,std::vector<std::string> * result)431 void ChromiumEnv::RestoreIfNecessary(const std::string& dir,
432 std::vector<std::string>* result) {
433 std::set<base::FilePath> tables_found;
434 std::set<base::FilePath> backups_found;
435 for (std::vector<std::string>::iterator it = result->begin();
436 it != result->end();
437 ++it) {
438 base::FilePath current = CreateFilePath(*it);
439 if (current.MatchesExtension(table_extension))
440 tables_found.insert(current.RemoveExtension());
441 if (current.MatchesExtension(backup_table_extension))
442 backups_found.insert(current.RemoveExtension());
443 }
444 std::set<base::FilePath> backups_only =
445 base::STLSetDifference<std::set<base::FilePath> >(backups_found,
446 tables_found);
447
448 if (backups_only.size()) {
449 std::string uma_name(name_);
450 uma_name.append(".MissingFiles");
451 int num_missing_files =
452 backups_only.size() > INT_MAX ? INT_MAX : backups_only.size();
453 base::Histogram::FactoryGet(uma_name,
454 1 /*min*/,
455 100 /*max*/,
456 8 /*num_buckets*/,
457 base::Histogram::kUmaTargetedHistogramFlag)
458 ->Add(num_missing_files);
459 }
460 base::FilePath dir_filepath = base::FilePath::FromUTF8Unsafe(dir);
461 for (std::set<base::FilePath>::iterator it = backups_only.begin();
462 it != backups_only.end();
463 ++it) {
464 base::FilePath restored_table_name =
465 RestoreFromBackup(dir_filepath.Append(*it));
466 result->push_back(FilePathToString(restored_table_name.BaseName()));
467 }
468 }
469
GetChildren(const std::string & dir_string,std::vector<std::string> * result)470 Status ChromiumEnv::GetChildren(const std::string& dir_string,
471 std::vector<std::string>* result) {
472 std::vector<base::FilePath> entries;
473 base::File::Error error =
474 GetDirectoryEntries(CreateFilePath(dir_string), &entries);
475 if (error != base::File::FILE_OK) {
476 RecordOSError(kGetChildren, error);
477 return MakeIOError(
478 dir_string, "Could not open/read directory", kGetChildren, error);
479 }
480 result->clear();
481 for (std::vector<base::FilePath>::iterator it = entries.begin();
482 it != entries.end();
483 ++it) {
484 result->push_back(FilePathToString(*it));
485 }
486
487 if (make_backup_)
488 RestoreIfNecessary(dir_string, result);
489 return Status::OK();
490 }
491
DeleteFile(const std::string & fname)492 Status ChromiumEnv::DeleteFile(const std::string& fname) {
493 Status result;
494 base::FilePath fname_filepath = CreateFilePath(fname);
495 // TODO(jorlow): Should we assert this is a file?
496 if (!::base::DeleteFile(fname_filepath, false)) {
497 result = MakeIOError(fname, "Could not delete file.", kDeleteFile);
498 RecordErrorAt(kDeleteFile);
499 }
500 if (make_backup_ && fname_filepath.MatchesExtension(table_extension)) {
501 base::DeleteFile(fname_filepath.ReplaceExtension(backup_table_extension),
502 false);
503 }
504 return result;
505 }
506
CreateDir(const std::string & name)507 Status ChromiumEnv::CreateDir(const std::string& name) {
508 Status result;
509 base::File::Error error = base::File::FILE_OK;
510 Retrier retrier(kCreateDir, this);
511 do {
512 if (base::CreateDirectoryAndGetError(CreateFilePath(name), &error))
513 return result;
514 } while (retrier.ShouldKeepTrying(error));
515 result = MakeIOError(name, "Could not create directory.", kCreateDir, error);
516 RecordOSError(kCreateDir, error);
517 return result;
518 }
519
DeleteDir(const std::string & name)520 Status ChromiumEnv::DeleteDir(const std::string& name) {
521 Status result;
522 // TODO(jorlow): Should we assert this is a directory?
523 if (!::base::DeleteFile(CreateFilePath(name), false)) {
524 result = MakeIOError(name, "Could not delete directory.", kDeleteDir);
525 RecordErrorAt(kDeleteDir);
526 }
527 return result;
528 }
529
GetFileSize(const std::string & fname,uint64_t * size)530 Status ChromiumEnv::GetFileSize(const std::string& fname, uint64_t* size) {
531 Status s;
532 int64_t signed_size;
533 if (!::base::GetFileSize(CreateFilePath(fname), &signed_size)) {
534 *size = 0;
535 s = MakeIOError(fname, "Could not determine file size.", kGetFileSize);
536 RecordErrorAt(kGetFileSize);
537 } else {
538 *size = static_cast<uint64_t>(signed_size);
539 }
540 return s;
541 }
542
RenameFile(const std::string & src,const std::string & dst)543 Status ChromiumEnv::RenameFile(const std::string& src, const std::string& dst) {
544 Status result;
545 base::FilePath src_file_path = CreateFilePath(src);
546 if (!::base::PathExists(src_file_path))
547 return result;
548 base::FilePath destination = CreateFilePath(dst);
549
550 Retrier retrier(kRenameFile, this);
551 base::File::Error error = base::File::FILE_OK;
552 do {
553 if (base::ReplaceFile(src_file_path, destination, &error))
554 return result;
555 } while (retrier.ShouldKeepTrying(error));
556
557 DCHECK(error != base::File::FILE_OK);
558 RecordOSError(kRenameFile, error);
559 char buf[100];
560 snprintf(buf,
561 sizeof(buf),
562 "Could not rename file: %s",
563 FileErrorString(error));
564 return MakeIOError(src, buf, kRenameFile, error);
565 }
566
LockFile(const std::string & fname,FileLock ** lock)567 Status ChromiumEnv::LockFile(const std::string& fname, FileLock** lock) {
568 *lock = NULL;
569 Status result;
570 int flags = ::base::File::FLAG_OPEN_ALWAYS |
571 ::base::File::FLAG_READ |
572 ::base::File::FLAG_WRITE;
573 ::base::File::Error error_code;
574 ::base::File file;
575 Retrier retrier(kLockFile, this);
576 do {
577 file.Initialize(CreateFilePath(fname), flags);
578 if (!file.IsValid())
579 error_code = file.error_details();
580 } while (!file.IsValid() && retrier.ShouldKeepTrying(error_code));
581
582 if (!file.IsValid()) {
583 if (error_code == ::base::File::FILE_ERROR_NOT_FOUND) {
584 ::base::FilePath parent = CreateFilePath(fname).DirName();
585 ::base::FilePath last_parent;
586 int num_missing_ancestors = 0;
587 do {
588 if (base::DirectoryExists(parent))
589 break;
590 ++num_missing_ancestors;
591 last_parent = parent;
592 parent = parent.DirName();
593 } while (parent != last_parent);
594 RecordLockFileAncestors(num_missing_ancestors);
595 }
596
597 result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
598 error_code);
599 RecordOSError(kLockFile, error_code);
600 return result;
601 }
602
603 if (!locks_.Insert(fname)) {
604 result = MakeIOError(fname, "Lock file already locked.", kLockFile);
605 return result;
606 }
607
608 Retrier lock_retrier = Retrier(kLockFile, this);
609 do {
610 error_code = file.Lock();
611 } while (error_code != ::base::File::FILE_OK &&
612 retrier.ShouldKeepTrying(error_code));
613
614 if (error_code != ::base::File::FILE_OK) {
615 locks_.Remove(fname);
616 result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
617 error_code);
618 RecordOSError(kLockFile, error_code);
619 return result;
620 }
621
622 ChromiumFileLock* my_lock = new ChromiumFileLock;
623 my_lock->file_ = file.Pass();
624 my_lock->name_ = fname;
625 *lock = my_lock;
626 return result;
627 }
628
UnlockFile(FileLock * lock)629 Status ChromiumEnv::UnlockFile(FileLock* lock) {
630 ChromiumFileLock* my_lock = reinterpret_cast<ChromiumFileLock*>(lock);
631 Status result;
632
633 ::base::File::Error error_code = my_lock->file_.Unlock();
634 if (error_code != ::base::File::FILE_OK) {
635 result =
636 MakeIOError(my_lock->name_, "Could not unlock lock file.", kUnlockFile);
637 RecordOSError(kUnlockFile, error_code);
638 }
639 bool removed = locks_.Remove(my_lock->name_);
640 DCHECK(removed);
641 delete my_lock;
642 return result;
643 }
644
GetTestDirectory(std::string * path)645 Status ChromiumEnv::GetTestDirectory(std::string* path) {
646 mu_.Acquire();
647 if (test_directory_.empty()) {
648 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix,
649 &test_directory_)) {
650 mu_.Release();
651 RecordErrorAt(kGetTestDirectory);
652 return MakeIOError(
653 "Could not create temp directory.", "", kGetTestDirectory);
654 }
655 }
656 *path = FilePathToString(test_directory_);
657 mu_.Release();
658 return Status::OK();
659 }
660
NowMicros()661 uint64_t ChromiumEnv::NowMicros() {
662 return ::base::TimeTicks::Now().ToInternalValue();
663 }
664
SleepForMicroseconds(int micros)665 void ChromiumEnv::SleepForMicroseconds(int micros) {
666 // Round up to the next millisecond.
667 ::base::PlatformThread::Sleep(::base::TimeDelta::FromMicroseconds(micros));
668 }
669
RecordErrorAt(MethodID method) const670 void ChromiumEnv::RecordErrorAt(MethodID method) const {
671 GetMethodIOErrorHistogram()->Add(method);
672 }
673
RecordLockFileAncestors(int num_missing_ancestors) const674 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors) const {
675 GetLockFileAncestorHistogram()->Add(num_missing_ancestors);
676 }
677
RecordOSError(MethodID method,base::File::Error error) const678 void ChromiumEnv::RecordOSError(MethodID method,
679 base::File::Error error) const {
680 DCHECK_LT(error, 0);
681 RecordErrorAt(method);
682 GetOSErrorHistogram(method, -base::File::FILE_ERROR_MAX)->Add(-error);
683 }
684
RecordOSError(MethodID method,int error) const685 void ChromiumEnv::RecordOSError(MethodID method, int error) const {
686 DCHECK_GT(error, 0);
687 RecordErrorAt(method);
688 GetOSErrorHistogram(method, ERANGE + 1)->Add(error);
689 }
690
RecordBackupResult(bool result) const691 void ChromiumEnv::RecordBackupResult(bool result) const {
692 std::string uma_name(name_);
693 uma_name.append(".TableBackup");
694 base::BooleanHistogram::FactoryGet(
695 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
696 }
697
GetOSErrorHistogram(MethodID method,int limit) const698 base::HistogramBase* ChromiumEnv::GetOSErrorHistogram(MethodID method,
699 int limit) const {
700 std::string uma_name(name_);
701 // TODO(dgrogan): This is probably not the best way to concatenate strings.
702 uma_name.append(".IOError.").append(MethodIDToString(method));
703 return base::LinearHistogram::FactoryGet(uma_name, 1, limit, limit + 1,
704 base::Histogram::kUmaTargetedHistogramFlag);
705 }
706
GetMethodIOErrorHistogram() const707 base::HistogramBase* ChromiumEnv::GetMethodIOErrorHistogram() const {
708 std::string uma_name(name_);
709 uma_name.append(".IOError");
710 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
711 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
712 }
713
GetMaxFDHistogram(const std::string & type) const714 base::HistogramBase* ChromiumEnv::GetMaxFDHistogram(
715 const std::string& type) const {
716 std::string uma_name(name_);
717 uma_name.append(".MaxFDs.").append(type);
718 // These numbers make each bucket twice as large as the previous bucket.
719 const int kFirstEntry = 1;
720 const int kLastEntry = 65536;
721 const int kNumBuckets = 18;
722 return base::Histogram::FactoryGet(
723 uma_name, kFirstEntry, kLastEntry, kNumBuckets,
724 base::Histogram::kUmaTargetedHistogramFlag);
725 }
726
GetLockFileAncestorHistogram() const727 base::HistogramBase* ChromiumEnv::GetLockFileAncestorHistogram() const {
728 std::string uma_name(name_);
729 uma_name.append(".LockFileAncestorsNotFound");
730 const int kMin = 1;
731 const int kMax = 10;
732 const int kNumBuckets = 11;
733 return base::LinearHistogram::FactoryGet(
734 uma_name, kMin, kMax, kNumBuckets,
735 base::Histogram::kUmaTargetedHistogramFlag);
736 }
737
GetRetryTimeHistogram(MethodID method) const738 base::HistogramBase* ChromiumEnv::GetRetryTimeHistogram(MethodID method) const {
739 std::string uma_name(name_);
740 // TODO(dgrogan): This is probably not the best way to concatenate strings.
741 uma_name.append(".TimeUntilSuccessFor").append(MethodIDToString(method));
742
743 const int kBucketSizeMillis = 25;
744 // Add 2, 1 for each of the buckets <1 and >max.
745 const int kNumBuckets = kMaxRetryTimeMillis / kBucketSizeMillis + 2;
746 return base::Histogram::FactoryTimeGet(
747 uma_name, base::TimeDelta::FromMilliseconds(1),
748 base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis + 1),
749 kNumBuckets,
750 base::Histogram::kUmaTargetedHistogramFlag);
751 }
752
GetRecoveredFromErrorHistogram(MethodID method) const753 base::HistogramBase* ChromiumEnv::GetRecoveredFromErrorHistogram(
754 MethodID method) const {
755 std::string uma_name(name_);
756 uma_name.append(".RetryRecoveredFromErrorIn")
757 .append(MethodIDToString(method));
758 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
759 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
760 }
761
762 class Thread : public ::base::PlatformThread::Delegate {
763 public:
Thread(void (* function)(void * arg),void * arg)764 Thread(void (*function)(void* arg), void* arg)
765 : function_(function), arg_(arg) {
766 ::base::PlatformThreadHandle handle;
767 bool success = ::base::PlatformThread::Create(0, this, &handle);
768 DCHECK(success);
769 }
~Thread()770 virtual ~Thread() {}
ThreadMain()771 virtual void ThreadMain() {
772 (*function_)(arg_);
773 delete this;
774 }
775
776 private:
777 void (*function_)(void* arg);
778 void* arg_;
779 };
780
Schedule(ScheduleFunc * function,void * arg)781 void ChromiumEnv::Schedule(ScheduleFunc* function, void* arg) {
782 mu_.Acquire();
783
784 // Start background thread if necessary
785 if (!started_bgthread_) {
786 started_bgthread_ = true;
787 StartThread(&ChromiumEnv::BGThreadWrapper, this);
788 }
789
790 // If the queue is currently empty, the background thread may currently be
791 // waiting.
792 if (queue_.empty()) {
793 bgsignal_.Signal();
794 }
795
796 // Add to priority queue
797 queue_.push_back(BGItem());
798 queue_.back().function = function;
799 queue_.back().arg = arg;
800
801 mu_.Release();
802 }
803
BGThread()804 void ChromiumEnv::BGThread() {
805 base::PlatformThread::SetName(name_.c_str());
806
807 while (true) {
808 // Wait until there is an item that is ready to run
809 mu_.Acquire();
810 while (queue_.empty()) {
811 bgsignal_.Wait();
812 }
813
814 void (*function)(void*) = queue_.front().function;
815 void* arg = queue_.front().arg;
816 queue_.pop_front();
817
818 mu_.Release();
819 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
820 (*function)(arg);
821 }
822 }
823
StartThread(void (* function)(void * arg),void * arg)824 void ChromiumEnv::StartThread(void (*function)(void* arg), void* arg) {
825 new Thread(function, arg); // Will self-delete.
826 }
827
GetDirName(const std::string & filename)828 static std::string GetDirName(const std::string& filename) {
829 base::FilePath file = base::FilePath::FromUTF8Unsafe(filename);
830 return FilePathToString(file.DirName());
831 }
832
DidCreateNewFile(const std::string & filename)833 void ChromiumEnv::DidCreateNewFile(const std::string& filename) {
834 base::AutoLock auto_lock(map_lock_);
835 needs_sync_map_[GetDirName(filename)] = true;
836 }
837
DoesDirNeedSync(const std::string & filename)838 bool ChromiumEnv::DoesDirNeedSync(const std::string& filename) {
839 base::AutoLock auto_lock(map_lock_);
840 return needs_sync_map_.find(GetDirName(filename)) != needs_sync_map_.end();
841 }
842
DidSyncDir(const std::string & filename)843 void ChromiumEnv::DidSyncDir(const std::string& filename) {
844 base::AutoLock auto_lock(map_lock_);
845 needs_sync_map_.erase(GetDirName(filename));
846 }
847
848 } // namespace leveldb_env
849
850 namespace leveldb {
851
IDBEnv()852 Env* IDBEnv() {
853 return leveldb_env::idb_env.Pointer();
854 }
855
Default()856 Env* Env::Default() {
857 return leveldb_env::default_env.Pointer();
858 }
859
860 } // namespace leveldb
861