1 // Copyright (c) 2011 The Chromium 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. 4 5 #include "chrome/browser/diagnostics/sqlite_diagnostics.h" 6 7 #include "app/sql/connection.h" 8 #include "app/sql/diagnostic_error_delegate.h" 9 #include "app/sql/statement.h" 10 #include "base/file_util.h" 11 #include "base/logging.h" 12 #include "base/memory/singleton.h" 13 #include "base/metrics/histogram.h" 14 #include "base/path_service.h" 15 #include "base/string_number_conversions.h" 16 #include "base/utf_string_conversions.h" 17 #include "chrome/common/chrome_constants.h" 18 #include "chrome/common/chrome_paths.h" 19 #include "third_party/sqlite/sqlite3.h" 20 #include "webkit/appcache/appcache_interfaces.h" 21 #include "webkit/database/database_tracker.h" 22 23 namespace { 24 25 // Generic diagnostic test class for checking sqlite db integrity. 26 class SqliteIntegrityTest : public DiagnosticTest { 27 public: 28 SqliteIntegrityTest(bool critical, const string16& title, 29 const FilePath& profile_relative_db_path) 30 : DiagnosticTest(title), 31 critical_(critical), 32 db_path_(profile_relative_db_path) { 33 } 34 35 virtual int GetId() { return 0; } 36 37 virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) { 38 FilePath path = GetUserDefaultProfileDir(); 39 path = path.Append(db_path_); 40 if (!file_util::PathExists(path)) { 41 RecordOutcome(ASCIIToUTF16("File not found"), 42 critical_ ? DiagnosticsModel::TEST_FAIL_CONTINUE : 43 DiagnosticsModel::TEST_OK); 44 return true; 45 } 46 47 int errors = 0; 48 { // This block scopes the lifetime of the db objects. 49 sql::Connection db; 50 db.set_exclusive_locking(); 51 if (!db.Open(path)) { 52 RecordFailure(ASCIIToUTF16("Cannot open DB. Possibly corrupted")); 53 return true; 54 } 55 sql::Statement s(db.GetUniqueStatement("PRAGMA integrity_check;")); 56 if (!s) { 57 int error = db.GetErrorCode(); 58 if (SQLITE_BUSY == error) { 59 RecordFailure(ASCIIToUTF16("DB locked by another process")); 60 } else { 61 string16 str(ASCIIToUTF16("Pragma failed. Error: ")); 62 str += base::IntToString16(error); 63 RecordFailure(str); 64 } 65 return false; 66 } 67 while (s.Step()) { 68 std::string result(s.ColumnString(0)); 69 if ("ok" != result) 70 ++errors; 71 } 72 } 73 // All done. Report to the user. 74 if (errors != 0) { 75 string16 str(ASCIIToUTF16("Database corruption detected :")); 76 str += base::IntToString16(errors) + ASCIIToUTF16(" errors"); 77 RecordFailure(str); 78 return true; 79 } 80 RecordSuccess(ASCIIToUTF16("no corruption detected")); 81 return true; 82 } 83 84 private: 85 bool critical_; 86 FilePath db_path_; 87 DISALLOW_COPY_AND_ASSIGN(SqliteIntegrityTest); 88 }; 89 90 // Uniquifier to use the sql::DiagnosticErrorDelegate template which 91 // requires a static name() method. 92 template <size_t unique> 93 class HistogramUniquifier { 94 public: 95 static const char* name() { 96 const char* kHistogramNames[] = { 97 "Sqlite.Cookie.Error", 98 "Sqlite.History.Error", 99 "Sqlite.Thumbnail.Error", 100 "Sqlite.Text.Error", 101 "Sqlite.Web.Error" 102 }; 103 return kHistogramNames[unique]; 104 } 105 }; 106 107 } // namespace 108 109 sql::ErrorDelegate* GetErrorHandlerForCookieDb() { 110 return new sql::DiagnosticErrorDelegate<HistogramUniquifier<0> >(); 111 } 112 113 sql::ErrorDelegate* GetErrorHandlerForHistoryDb() { 114 return new sql::DiagnosticErrorDelegate<HistogramUniquifier<1> >(); 115 } 116 117 sql::ErrorDelegate* GetErrorHandlerForThumbnailDb() { 118 return new sql::DiagnosticErrorDelegate<HistogramUniquifier<2> >(); 119 } 120 121 sql::ErrorDelegate* GetErrorHandlerForTextDb() { 122 return new sql::DiagnosticErrorDelegate<HistogramUniquifier<3> >(); 123 } 124 125 sql::ErrorDelegate* GetErrorHandlerForWebDb() { 126 return new sql::DiagnosticErrorDelegate<HistogramUniquifier<4> >(); 127 } 128 129 DiagnosticTest* MakeSqliteWebDbTest() { 130 return new SqliteIntegrityTest(true, ASCIIToUTF16("Web DB"), 131 FilePath(chrome::kWebDataFilename)); 132 } 133 134 DiagnosticTest* MakeSqliteCookiesDbTest() { 135 return new SqliteIntegrityTest(true, ASCIIToUTF16("Cookies DB"), 136 FilePath(chrome::kCookieFilename)); 137 } 138 139 DiagnosticTest* MakeSqliteHistoryDbTest() { 140 return new SqliteIntegrityTest(true, ASCIIToUTF16("History DB"), 141 FilePath(chrome::kHistoryFilename)); 142 } 143 144 DiagnosticTest* MakeSqliteArchivedHistoryDbTest() { 145 return new SqliteIntegrityTest(false, ASCIIToUTF16("Archived History DB"), 146 FilePath(chrome::kArchivedHistoryFilename)); 147 } 148 149 DiagnosticTest* MakeSqliteThumbnailsDbTest() { 150 return new SqliteIntegrityTest(false, ASCIIToUTF16("Thumbnails DB"), 151 FilePath(chrome::kThumbnailsFilename)); 152 } 153 154 DiagnosticTest* MakeSqliteAppCacheDbTest() { 155 FilePath appcache_dir(chrome::kAppCacheDirname); 156 FilePath appcache_db = appcache_dir.Append(appcache::kAppCacheDatabaseName); 157 return new SqliteIntegrityTest(false, ASCIIToUTF16("AppCache DB"), 158 appcache_db); 159 } 160 161 DiagnosticTest* MakeSqliteWebDatabaseTrackerDbTest() { 162 FilePath databases_dir(webkit_database::kDatabaseDirectoryName); 163 FilePath tracker_db = 164 databases_dir.Append(webkit_database::kTrackerDatabaseFileName); 165 return new SqliteIntegrityTest(false, ASCIIToUTF16("DatabaseTracker DB"), 166 tracker_db); 167 } 168