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:
SqliteIntegrityTest(bool critical,const string16 & title,const FilePath & profile_relative_db_path)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
GetId()35 virtual int GetId() { return 0; }
36
ExecuteImpl(DiagnosticsModel::Observer * observer)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:
name()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
GetErrorHandlerForCookieDb()109 sql::ErrorDelegate* GetErrorHandlerForCookieDb() {
110 return new sql::DiagnosticErrorDelegate<HistogramUniquifier<0> >();
111 }
112
GetErrorHandlerForHistoryDb()113 sql::ErrorDelegate* GetErrorHandlerForHistoryDb() {
114 return new sql::DiagnosticErrorDelegate<HistogramUniquifier<1> >();
115 }
116
GetErrorHandlerForThumbnailDb()117 sql::ErrorDelegate* GetErrorHandlerForThumbnailDb() {
118 return new sql::DiagnosticErrorDelegate<HistogramUniquifier<2> >();
119 }
120
GetErrorHandlerForTextDb()121 sql::ErrorDelegate* GetErrorHandlerForTextDb() {
122 return new sql::DiagnosticErrorDelegate<HistogramUniquifier<3> >();
123 }
124
GetErrorHandlerForWebDb()125 sql::ErrorDelegate* GetErrorHandlerForWebDb() {
126 return new sql::DiagnosticErrorDelegate<HistogramUniquifier<4> >();
127 }
128
MakeSqliteWebDbTest()129 DiagnosticTest* MakeSqliteWebDbTest() {
130 return new SqliteIntegrityTest(true, ASCIIToUTF16("Web DB"),
131 FilePath(chrome::kWebDataFilename));
132 }
133
MakeSqliteCookiesDbTest()134 DiagnosticTest* MakeSqliteCookiesDbTest() {
135 return new SqliteIntegrityTest(true, ASCIIToUTF16("Cookies DB"),
136 FilePath(chrome::kCookieFilename));
137 }
138
MakeSqliteHistoryDbTest()139 DiagnosticTest* MakeSqliteHistoryDbTest() {
140 return new SqliteIntegrityTest(true, ASCIIToUTF16("History DB"),
141 FilePath(chrome::kHistoryFilename));
142 }
143
MakeSqliteArchivedHistoryDbTest()144 DiagnosticTest* MakeSqliteArchivedHistoryDbTest() {
145 return new SqliteIntegrityTest(false, ASCIIToUTF16("Archived History DB"),
146 FilePath(chrome::kArchivedHistoryFilename));
147 }
148
MakeSqliteThumbnailsDbTest()149 DiagnosticTest* MakeSqliteThumbnailsDbTest() {
150 return new SqliteIntegrityTest(false, ASCIIToUTF16("Thumbnails DB"),
151 FilePath(chrome::kThumbnailsFilename));
152 }
153
MakeSqliteAppCacheDbTest()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
MakeSqliteWebDatabaseTrackerDbTest()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