| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/history/core/browser/thumbnail_database.h" | 5 #include "components/history/core/browser/thumbnail_database.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <string> | 8 #include <string> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| 11 #include "base/debug/alias.h" | 11 #include "base/debug/alias.h" |
| 12 #include "base/debug/dump_without_crashing.h" | |
| 13 #include "base/files/file_util.h" | 12 #include "base/files/file_util.h" |
| 14 #include "base/format_macros.h" | |
| 15 #include "base/memory/ref_counted_memory.h" | 13 #include "base/memory/ref_counted_memory.h" |
| 16 #include "base/metrics/histogram_macros.h" | 14 #include "base/metrics/histogram_macros.h" |
| 17 #include "base/rand_util.h" | 15 #include "base/rand_util.h" |
| 18 #include "base/strings/string_util.h" | 16 #include "base/strings/string_util.h" |
| 19 #include "base/strings/stringprintf.h" | 17 #include "base/strings/stringprintf.h" |
| 20 #include "base/time/time.h" | 18 #include "base/time/time.h" |
| 21 #include "components/history/core/browser/history_backend_client.h" | 19 #include "components/history/core/browser/history_backend_client.h" |
| 22 #include "components/history/core/browser/url_database.h" | 20 #include "components/history/core/browser/url_database.h" |
| 23 #include "sql/recovery.h" | 21 #include "sql/recovery.h" |
| 24 #include "sql/statement.h" | 22 #include "sql/statement.h" |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 114 | 112 |
| 115 // Always keep this at the end. | 113 // Always keep this at the end. |
| 116 STRUCTURE_EVENT_MAX, | 114 STRUCTURE_EVENT_MAX, |
| 117 }; | 115 }; |
| 118 | 116 |
| 119 void RecordInvalidStructure(InvalidStructureType invalid_type) { | 117 void RecordInvalidStructure(InvalidStructureType invalid_type) { |
| 120 UMA_HISTOGRAM_ENUMERATION("History.InvalidFaviconsDBStructure", | 118 UMA_HISTOGRAM_ENUMERATION("History.InvalidFaviconsDBStructure", |
| 121 invalid_type, STRUCTURE_EVENT_MAX); | 119 invalid_type, STRUCTURE_EVENT_MAX); |
| 122 } | 120 } |
| 123 | 121 |
| 124 // Attempt to pass 2000 bytes of |debug_info| into a crash dump. | 122 // TODO(shess): If this proves out, move it all into sql::Connection to be |
| 125 void DumpWithoutCrashing2000(const std::string& debug_info) { | 123 // shared. |
| 126 char debug_buf[2000]; | |
| 127 base::strlcpy(debug_buf, debug_info.c_str(), arraysize(debug_buf)); | |
| 128 base::debug::Alias(&debug_buf); | |
| 129 | |
| 130 base::debug::DumpWithoutCrashing(); | |
| 131 } | |
| 132 | |
| 133 void ReportCorrupt(sql::Connection* db, size_t startup_kb) { | |
| 134 // Buffer for accumulating debugging info about the error. Place | |
| 135 // more-relevant information earlier, in case things overflow the | |
| 136 // fixed-size buffer. | |
| 137 std::string debug_info; | |
| 138 | |
| 139 base::StringAppendF(&debug_info, "SQLITE_CORRUPT, integrity_check:\n"); | |
| 140 | |
| 141 // Check files up to 8M to keep things from blocking too long. | |
| 142 const size_t kMaxIntegrityCheckSize = 8192; | |
| 143 if (startup_kb > kMaxIntegrityCheckSize) { | |
| 144 base::StringAppendF(&debug_info, "too big %" PRIuS "\n", startup_kb); | |
| 145 } else { | |
| 146 std::vector<std::string> messages; | |
| 147 | |
| 148 const base::TimeTicks before = base::TimeTicks::Now(); | |
| 149 db->FullIntegrityCheck(&messages); | |
| 150 base::StringAppendF(&debug_info, "# %" PRIx64 " ms, %" PRIuS " records\n", | |
| 151 (base::TimeTicks::Now() - before).InMilliseconds(), | |
| 152 messages.size()); | |
| 153 | |
| 154 // SQLite returns up to 100 messages by default, trim deeper to | |
| 155 // keep close to the 2000-character size limit for dumping. | |
| 156 // | |
| 157 // TODO(shess): If the first 20 tend to be actionable, test if | |
| 158 // passing the count to integrity_check makes it exit earlier. In | |
| 159 // that case it may be possible to greatly ease the size | |
| 160 // restriction. | |
| 161 const size_t kMaxMessages = 20; | |
| 162 for (size_t i = 0; i < kMaxMessages && i < messages.size(); ++i) { | |
| 163 base::StringAppendF(&debug_info, "%s\n", messages[i].c_str()); | |
| 164 } | |
| 165 } | |
| 166 | |
| 167 DumpWithoutCrashing2000(debug_info); | |
| 168 } | |
| 169 | |
| 170 void ReportError(sql::Connection* db, int error) { | |
| 171 // Buffer for accumulating debugging info about the error. Place | |
| 172 // more-relevant information earlier, in case things overflow the | |
| 173 // fixed-size buffer. | |
| 174 std::string debug_info; | |
| 175 | |
| 176 // The error message from the failed operation. | |
| 177 base::StringAppendF(&debug_info, "db error: %d/%s\n", | |
| 178 db->GetErrorCode(), db->GetErrorMessage()); | |
| 179 | |
| 180 // System errno information. | |
| 181 base::StringAppendF(&debug_info, "errno: %d\n", db->GetLastErrno()); | |
| 182 | |
| 183 // SQLITE_ERROR reports seem to be attempts to upgrade invalid | |
| 184 // schema, try to log that info. | |
| 185 if (error == SQLITE_ERROR) { | |
| 186 const char* kVersionSql = "SELECT value FROM meta WHERE key = 'version'"; | |
| 187 if (db->IsSQLValid(kVersionSql)) { | |
| 188 sql::Statement statement(db->GetUniqueStatement(kVersionSql)); | |
| 189 if (statement.Step()) { | |
| 190 debug_info += "version: "; | |
| 191 debug_info += statement.ColumnString(0); | |
| 192 debug_info += '\n'; | |
| 193 } else if (statement.Succeeded()) { | |
| 194 debug_info += "version: none\n"; | |
| 195 } else { | |
| 196 debug_info += "version: error\n"; | |
| 197 } | |
| 198 } else { | |
| 199 debug_info += "version: invalid\n"; | |
| 200 } | |
| 201 | |
| 202 debug_info += "schema:\n"; | |
| 203 | |
| 204 // sqlite_master has columns: | |
| 205 // type - "index" or "table". | |
| 206 // name - name of created element. | |
| 207 // tbl_name - name of element, or target table in case of index. | |
| 208 // rootpage - root page of the element in database file. | |
| 209 // sql - SQL to create the element. | |
| 210 // In general, the |sql| column is sufficient to derive the other | |
| 211 // columns. |rootpage| is not interesting for debugging, without | |
| 212 // the contents of the database. The COALESCE is because certain | |
| 213 // automatic elements will have a |name| but no |sql|, | |
| 214 const char* kSchemaSql = "SELECT COALESCE(sql, name) FROM sqlite_master"; | |
| 215 sql::Statement statement(db->GetUniqueStatement(kSchemaSql)); | |
| 216 while (statement.Step()) { | |
| 217 debug_info += statement.ColumnString(0); | |
| 218 debug_info += '\n'; | |
| 219 } | |
| 220 if (!statement.Succeeded()) | |
| 221 debug_info += "error\n"; | |
| 222 } | |
| 223 | |
| 224 // TODO(shess): Think of other things to log. Not logging the | |
| 225 // statement text because the backtrace should suffice in most | |
| 226 // cases. The database schema is a possibility, but the | |
| 227 // likelihood of recursive error callbacks makes that risky (same | |
| 228 // reasoning applies to other data fetched from the database). | |
| 229 | |
| 230 DumpWithoutCrashing2000(debug_info); | |
| 231 } | |
| 232 | |
| 233 // TODO(shess): If this proves out, perhaps lift the code out to | |
| 234 // chrome/browser/diagnostics/sqlite_diagnostics.{h,cc}. | |
| 235 void GenerateDiagnostics(sql::Connection* db, | 124 void GenerateDiagnostics(sql::Connection* db, |
| 236 size_t startup_kb, | 125 int extended_error, |
| 237 int extended_error) { | 126 sql::Statement* stmt) { |
| 238 int error = (extended_error & 0xFF); | |
| 239 | |
| 240 // Infrequently report information about the error up to the crash | |
| 241 // server. | |
| 242 static const uint64 kReportsPerMillion = 50000; | |
| 243 | |
| 244 // Since some/most errors will not resolve themselves, only report | 127 // Since some/most errors will not resolve themselves, only report |
| 245 // once per Chrome run. | 128 // once per Chrome run. |
| 246 static bool reported = false; | 129 static bool reported = false; |
| 247 if (reported) | 130 if (reported) |
| 248 return; | 131 return; |
| 132 reported = true; |
| 249 | 133 |
| 250 uint64 rand = base::RandGenerator(1000000); | 134 // Only pass 5% of new reports to prevent a thundering herd of dumps. |
| 251 if (error == SQLITE_CORRUPT) { | 135 // TODO(shess): If this could be related to the time in the channel, then the |
| 252 // Once the database is known to be corrupt, it will generate a | 136 // rate could ramp up over time. Perhaps could remember the timestamp the |
| 253 // stream of errors until someone fixes it, so give one chance. | 137 // first time upload is considered, and ramp up 1% per day? |
| 254 // Set first in case of errors in generating the report. | 138 static const uint64 kReportPercent = 5; |
| 255 reported = true; | 139 uint64 rand = base::RandGenerator(100); |
| 256 | 140 if (rand <= kReportPercent) |
| 257 // Corrupt cases currently dominate, report them very infrequently. | 141 db->ReportDiagnosticInfo(extended_error, stmt); |
| 258 static const uint64 kCorruptReportsPerMillion = 10000; | |
| 259 if (rand < kCorruptReportsPerMillion) | |
| 260 ReportCorrupt(db, startup_kb); | |
| 261 } else if (error == SQLITE_READONLY) { | |
| 262 // SQLITE_READONLY appears similar to SQLITE_CORRUPT - once it | |
| 263 // is seen, it is almost guaranteed to be seen again. | |
| 264 reported = true; | |
| 265 | |
| 266 if (rand < kReportsPerMillion) | |
| 267 ReportError(db, extended_error); | |
| 268 } else { | |
| 269 // Only set the flag when making a report. This should allow | |
| 270 // later (potentially different) errors in a stream of errors to | |
| 271 // be reported. | |
| 272 // | |
| 273 // TODO(shess): Would it be worthwile to audit for which cases | |
| 274 // want once-only handling? Sqlite.Error.Thumbnail shows | |
| 275 // CORRUPT and READONLY as almost 95% of all reports on these | |
| 276 // channels, so probably easier to just harvest from the field. | |
| 277 if (rand < kReportsPerMillion) { | |
| 278 reported = true; | |
| 279 ReportError(db, extended_error); | |
| 280 } | |
| 281 } | |
| 282 } | 142 } |
| 283 | 143 |
| 284 // NOTE(shess): Schema modifications must consider initial creation in | 144 // NOTE(shess): Schema modifications must consider initial creation in |
| 285 // |InitImpl()|, recovery in |RecoverDatabaseOrRaze()|, and history pruning in | 145 // |InitImpl()|, recovery in |RecoverDatabaseOrRaze()|, and history pruning in |
| 286 // |RetainDataForPageUrls()|. | 146 // |RetainDataForPageUrls()|. |
| 287 bool InitTables(sql::Connection* db) { | 147 bool InitTables(sql::Connection* db) { |
| 288 const char kIconMappingSql[] = | 148 const char kIconMappingSql[] = |
| 289 "CREATE TABLE IF NOT EXISTS icon_mapping" | 149 "CREATE TABLE IF NOT EXISTS icon_mapping" |
| 290 "(" | 150 "(" |
| 291 "id INTEGER PRIMARY KEY," | 151 "id INTEGER PRIMARY KEY," |
| (...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 542 UMA_HISTOGRAM_COUNTS_10000("History.FaviconsRecoveredRowsFaviconBitmaps", | 402 UMA_HISTOGRAM_COUNTS_10000("History.FaviconsRecoveredRowsFaviconBitmaps", |
| 543 static_cast<int>(favicon_bitmaps_rows_recovered)); | 403 static_cast<int>(favicon_bitmaps_rows_recovered)); |
| 544 UMA_HISTOGRAM_COUNTS_10000("History.FaviconsRecoveredRowsIconMapping", | 404 UMA_HISTOGRAM_COUNTS_10000("History.FaviconsRecoveredRowsIconMapping", |
| 545 static_cast<int>(icon_mapping_rows_recovered)); | 405 static_cast<int>(icon_mapping_rows_recovered)); |
| 546 | 406 |
| 547 RecordRecoveryEvent(RECOVERY_EVENT_RECOVERED); | 407 RecordRecoveryEvent(RECOVERY_EVENT_RECOVERED); |
| 548 } | 408 } |
| 549 | 409 |
| 550 void DatabaseErrorCallback(sql::Connection* db, | 410 void DatabaseErrorCallback(sql::Connection* db, |
| 551 const base::FilePath& db_path, | 411 const base::FilePath& db_path, |
| 552 size_t startup_kb, | |
| 553 HistoryBackendClient* backend_client, | 412 HistoryBackendClient* backend_client, |
| 554 int extended_error, | 413 int extended_error, |
| 555 sql::Statement* stmt) { | 414 sql::Statement* stmt) { |
| 556 // TODO(shess): Assert that this is running on a safe thread. | 415 // TODO(shess): Assert that this is running on a safe thread. |
| 557 // AFAICT, should be the history thread, but at this level I can't | 416 // AFAICT, should be the history thread, but at this level I can't |
| 558 // see how to reach that. | 417 // see how to reach that. |
| 559 | 418 |
| 560 if (backend_client && backend_client->ShouldReportDatabaseError()) { | 419 if (backend_client && backend_client->ShouldReportDatabaseError()) { |
| 561 GenerateDiagnostics(db, startup_kb, extended_error); | 420 GenerateDiagnostics(db, extended_error, stmt); |
| 562 } | 421 } |
| 563 | 422 |
| 564 // Attempt to recover corrupt databases. | 423 // Attempt to recover corrupt databases. |
| 565 int error = (extended_error & 0xFF); | 424 int error = (extended_error & 0xFF); |
| 566 if (error == SQLITE_CORRUPT || | 425 if (error == SQLITE_CORRUPT || |
| 567 error == SQLITE_CANTOPEN || | 426 error == SQLITE_CANTOPEN || |
| 568 error == SQLITE_NOTADB) { | 427 error == SQLITE_NOTADB) { |
| 569 RecoverDatabaseOrRaze(db, db_path); | 428 RecoverDatabaseOrRaze(db, db_path); |
| 570 } | 429 } |
| 571 | 430 |
| (...skipping 626 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1198 const char kIconMappingDrop[] = "DROP TABLE temp.icon_id_mapping"; | 1057 const char kIconMappingDrop[] = "DROP TABLE temp.icon_id_mapping"; |
| 1199 const char kRetainedUrlsDrop[] = "DROP TABLE temp.retained_urls"; | 1058 const char kRetainedUrlsDrop[] = "DROP TABLE temp.retained_urls"; |
| 1200 if (!db_.Execute(kIconMappingDrop) || !db_.Execute(kRetainedUrlsDrop)) | 1059 if (!db_.Execute(kIconMappingDrop) || !db_.Execute(kRetainedUrlsDrop)) |
| 1201 return false; | 1060 return false; |
| 1202 | 1061 |
| 1203 return transaction.Commit(); | 1062 return transaction.Commit(); |
| 1204 } | 1063 } |
| 1205 | 1064 |
| 1206 sql::InitStatus ThumbnailDatabase::OpenDatabase(sql::Connection* db, | 1065 sql::InitStatus ThumbnailDatabase::OpenDatabase(sql::Connection* db, |
| 1207 const base::FilePath& db_name) { | 1066 const base::FilePath& db_name) { |
| 1208 size_t startup_kb = 0; | |
| 1209 int64 size_64; | |
| 1210 if (base::GetFileSize(db_name, &size_64)) | |
| 1211 startup_kb = static_cast<size_t>(size_64 / 1024); | |
| 1212 | |
| 1213 db->set_histogram_tag("Thumbnail"); | 1067 db->set_histogram_tag("Thumbnail"); |
| 1214 db->set_error_callback(base::Bind(&DatabaseErrorCallback, | 1068 db->set_error_callback(base::Bind(&DatabaseErrorCallback, |
| 1215 db, db_name, startup_kb, backend_client_)); | 1069 db, db_name, backend_client_)); |
| 1216 | 1070 |
| 1217 // Thumbnails db now only stores favicons, so we don't need that big a page | 1071 // Thumbnails db now only stores favicons, so we don't need that big a page |
| 1218 // size or cache. | 1072 // size or cache. |
| 1219 db->set_page_size(2048); | 1073 db->set_page_size(2048); |
| 1220 db->set_cache_size(32); | 1074 db->set_cache_size(32); |
| 1221 | 1075 |
| 1222 // Run the database in exclusive mode. Nobody else should be accessing the | 1076 // Run the database in exclusive mode. Nobody else should be accessing the |
| 1223 // database while we're running, and this will give somewhat improved perf. | 1077 // database while we're running, and this will give somewhat improved perf. |
| 1224 db->set_exclusive_locking(); | 1078 db->set_exclusive_locking(); |
| 1225 | 1079 |
| (...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1378 meta_table_.SetVersionNumber(8); | 1232 meta_table_.SetVersionNumber(8); |
| 1379 meta_table_.SetCompatibleVersionNumber(std::min(8, kCompatibleVersionNumber)); | 1233 meta_table_.SetCompatibleVersionNumber(std::min(8, kCompatibleVersionNumber)); |
| 1380 return true; | 1234 return true; |
| 1381 } | 1235 } |
| 1382 | 1236 |
| 1383 bool ThumbnailDatabase::IsFaviconDBStructureIncorrect() { | 1237 bool ThumbnailDatabase::IsFaviconDBStructureIncorrect() { |
| 1384 return !db_.IsSQLValid("SELECT id, url, icon_type FROM favicons"); | 1238 return !db_.IsSQLValid("SELECT id, url, icon_type FROM favicons"); |
| 1385 } | 1239 } |
| 1386 | 1240 |
| 1387 } // namespace history | 1241 } // namespace history |
| OLD | NEW |