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