// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/diagnostics/sqlite_diagnostics.h" #include "app/sql/connection.h" #include "app/sql/diagnostic_error_delegate.h" #include "app/sql/statement.h" #include "base/file_util.h" #include "base/logging.h" #include "base/memory/singleton.h" #include "base/metrics/histogram.h" #include "base/path_service.h" #include "base/string_number_conversions.h" #include "base/utf_string_conversions.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths.h" #include "third_party/sqlite/sqlite3.h" #include "webkit/appcache/appcache_interfaces.h" #include "webkit/database/database_tracker.h" namespace { // Generic diagnostic test class for checking sqlite db integrity. class SqliteIntegrityTest : public DiagnosticTest { public: SqliteIntegrityTest(bool critical, const string16& title, const FilePath& profile_relative_db_path) : DiagnosticTest(title), critical_(critical), db_path_(profile_relative_db_path) { } virtual int GetId() { return 0; } virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) { FilePath path = GetUserDefaultProfileDir(); path = path.Append(db_path_); if (!file_util::PathExists(path)) { RecordOutcome(ASCIIToUTF16("File not found"), critical_ ? DiagnosticsModel::TEST_FAIL_CONTINUE : DiagnosticsModel::TEST_OK); return true; } int errors = 0; { // This block scopes the lifetime of the db objects. sql::Connection db; db.set_exclusive_locking(); if (!db.Open(path)) { RecordFailure(ASCIIToUTF16("Cannot open DB. Possibly corrupted")); return true; } sql::Statement s(db.GetUniqueStatement("PRAGMA integrity_check;")); if (!s) { int error = db.GetErrorCode(); if (SQLITE_BUSY == error) { RecordFailure(ASCIIToUTF16("DB locked by another process")); } else { string16 str(ASCIIToUTF16("Pragma failed. Error: ")); str += base::IntToString16(error); RecordFailure(str); } return false; } while (s.Step()) { std::string result(s.ColumnString(0)); if ("ok" != result) ++errors; } } // All done. Report to the user. if (errors != 0) { string16 str(ASCIIToUTF16("Database corruption detected :")); str += base::IntToString16(errors) + ASCIIToUTF16(" errors"); RecordFailure(str); return true; } RecordSuccess(ASCIIToUTF16("no corruption detected")); return true; } private: bool critical_; FilePath db_path_; DISALLOW_COPY_AND_ASSIGN(SqliteIntegrityTest); }; // Uniquifier to use the sql::DiagnosticErrorDelegate template which // requires a static name() method. template <size_t unique> class HistogramUniquifier { public: static const char* name() { const char* kHistogramNames[] = { "Sqlite.Cookie.Error", "Sqlite.History.Error", "Sqlite.Thumbnail.Error", "Sqlite.Text.Error", "Sqlite.Web.Error" }; return kHistogramNames[unique]; } }; } // namespace sql::ErrorDelegate* GetErrorHandlerForCookieDb() { return new sql::DiagnosticErrorDelegate<HistogramUniquifier<0> >(); } sql::ErrorDelegate* GetErrorHandlerForHistoryDb() { return new sql::DiagnosticErrorDelegate<HistogramUniquifier<1> >(); } sql::ErrorDelegate* GetErrorHandlerForThumbnailDb() { return new sql::DiagnosticErrorDelegate<HistogramUniquifier<2> >(); } sql::ErrorDelegate* GetErrorHandlerForTextDb() { return new sql::DiagnosticErrorDelegate<HistogramUniquifier<3> >(); } sql::ErrorDelegate* GetErrorHandlerForWebDb() { return new sql::DiagnosticErrorDelegate<HistogramUniquifier<4> >(); } DiagnosticTest* MakeSqliteWebDbTest() { return new SqliteIntegrityTest(true, ASCIIToUTF16("Web DB"), FilePath(chrome::kWebDataFilename)); } DiagnosticTest* MakeSqliteCookiesDbTest() { return new SqliteIntegrityTest(true, ASCIIToUTF16("Cookies DB"), FilePath(chrome::kCookieFilename)); } DiagnosticTest* MakeSqliteHistoryDbTest() { return new SqliteIntegrityTest(true, ASCIIToUTF16("History DB"), FilePath(chrome::kHistoryFilename)); } DiagnosticTest* MakeSqliteArchivedHistoryDbTest() { return new SqliteIntegrityTest(false, ASCIIToUTF16("Archived History DB"), FilePath(chrome::kArchivedHistoryFilename)); } DiagnosticTest* MakeSqliteThumbnailsDbTest() { return new SqliteIntegrityTest(false, ASCIIToUTF16("Thumbnails DB"), FilePath(chrome::kThumbnailsFilename)); } DiagnosticTest* MakeSqliteAppCacheDbTest() { FilePath appcache_dir(chrome::kAppCacheDirname); FilePath appcache_db = appcache_dir.Append(appcache::kAppCacheDatabaseName); return new SqliteIntegrityTest(false, ASCIIToUTF16("AppCache DB"), appcache_db); } DiagnosticTest* MakeSqliteWebDatabaseTrackerDbTest() { FilePath databases_dir(webkit_database::kDatabaseDirectoryName); FilePath tracker_db = databases_dir.Append(webkit_database::kTrackerDatabaseFileName); return new SqliteIntegrityTest(false, ASCIIToUTF16("DatabaseTracker DB"), tracker_db); }