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: 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) {} 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 } 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: 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: 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 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 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 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 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 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. 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 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 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 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 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 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 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 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 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 354 bool ChromiumEnv::HasTableExtension(const base::FilePath& path) { 355 return path.MatchesExtension(table_extension); 356 } 357 358 ChromiumEnv::ChromiumEnv() 359 : name_("LevelDBEnv"), 360 make_backup_(false), 361 bgsignal_(&mu_), 362 started_bgthread_(false), 363 kMaxRetryTimeMillis(1000) { 364 } 365 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 372 bool ChromiumEnv::FileExists(const std::string& fname) { 373 return ::base::PathExists(CreateFilePath(fname)); 374 } 375 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 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 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 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 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 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 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 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 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 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 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 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 661 uint64_t ChromiumEnv::NowMicros() { 662 return ::base::TimeTicks::Now().ToInternalValue(); 663 } 664 665 void ChromiumEnv::SleepForMicroseconds(int micros) { 666 // Round up to the next millisecond. 667 ::base::PlatformThread::Sleep(::base::TimeDelta::FromMicroseconds(micros)); 668 } 669 670 void ChromiumEnv::RecordErrorAt(MethodID method) const { 671 GetMethodIOErrorHistogram()->Add(method); 672 } 673 674 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors) const { 675 GetLockFileAncestorHistogram()->Add(num_missing_ancestors); 676 } 677 678 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 685 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 691 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 698 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 707 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 714 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 727 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 738 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 753 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: 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 } 770 virtual ~Thread() {} 771 virtual void ThreadMain() { 772 (*function_)(arg_); 773 delete this; 774 } 775 776 private: 777 void (*function_)(void* arg); 778 void* arg_; 779 }; 780 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 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 824 void ChromiumEnv::StartThread(void (*function)(void* arg), void* arg) { 825 new Thread(function, arg); // Will self-delete. 826 } 827 828 static std::string GetDirName(const std::string& filename) { 829 base::FilePath file = base::FilePath::FromUTF8Unsafe(filename); 830 return FilePathToString(file.DirName()); 831 } 832 833 void ChromiumEnv::DidCreateNewFile(const std::string& filename) { 834 base::AutoLock auto_lock(map_lock_); 835 needs_sync_map_[GetDirName(filename)] = true; 836 } 837 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 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 852 Env* IDBEnv() { 853 return leveldb_env::idb_env.Pointer(); 854 } 855 856 Env* Env::Default() { 857 return leveldb_env::default_env.Pointer(); 858 } 859 860 } // namespace leveldb 861