Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / diagnostics / sqlite_diagnostics.cc
blob046dc29ec0aba45dc04f2a17bad5f995e6c39c7e
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
3 // found in the LICENSE file.
5 #include "chrome/browser/diagnostics/sqlite_diagnostics.h"
7 #include "base/file_util.h"
8 #include "base/logging.h"
9 #include "base/memory/ref_counted.h"
10 #include "base/memory/singleton.h"
11 #include "base/memory/weak_ptr.h"
12 #include "base/metrics/histogram.h"
13 #include "base/path_service.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "chrome/common/chrome_constants.h"
18 #include "chrome/common/chrome_paths.h"
19 #include "chromeos/chromeos_constants.h"
20 #include "components/webdata/common/webdata_constants.h"
21 #include "content/public/common/content_constants.h"
22 #include "sql/connection.h"
23 #include "sql/statement.h"
24 #include "third_party/sqlite/sqlite3.h"
25 #include "webkit/browser/database/database_tracker.h"
26 #include "webkit/common/appcache/appcache_interfaces.h"
28 namespace diagnostics {
30 namespace {
32 // Generic diagnostic test class for checking SQLite database integrity.
33 class SqliteIntegrityTest : public DiagnosticsTest {
34 public:
35 // These are bit flags, so each value should be a power of two.
36 enum Flags {
37 NO_FLAGS_SET = 0,
38 CRITICAL = 0x01,
39 REMOVE_IF_CORRUPT = 0x02,
42 SqliteIntegrityTest(uint32 flags,
43 DiagnosticsTestId id,
44 const base::FilePath& db_path)
45 : DiagnosticsTest(id), flags_(flags), db_path_(db_path) {}
47 virtual bool RecoveryImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
48 int outcome_code = GetOutcomeCode();
49 if (flags_ & REMOVE_IF_CORRUPT) {
50 switch (outcome_code) {
51 case DIAG_SQLITE_ERROR_HANDLER_CALLED:
52 case DIAG_SQLITE_CANNOT_OPEN_DB:
53 case DIAG_SQLITE_DB_LOCKED:
54 case DIAG_SQLITE_PRAGMA_FAILED:
55 case DIAG_SQLITE_DB_CORRUPTED:
56 LOG(WARNING) << "Removing broken SQLite database: "
57 << db_path_.value();
58 base::DeleteFile(db_path_, false);
59 break;
60 case DIAG_SQLITE_SUCCESS:
61 case DIAG_SQLITE_FILE_NOT_FOUND_OK:
62 case DIAG_SQLITE_FILE_NOT_FOUND:
63 break;
64 default:
65 DCHECK(false) << "Invalid outcome code: " << outcome_code;
66 break;
69 return true;
72 virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
73 // If we're given an absolute path, use it. If not, then assume it's under
74 // the profile directory.
75 base::FilePath path;
76 if (!db_path_.IsAbsolute())
77 path = GetUserDefaultProfileDir().Append(db_path_);
78 else
79 path = db_path_;
81 if (!base::PathExists(path)) {
82 if (flags_ & CRITICAL) {
83 RecordOutcome(DIAG_SQLITE_FILE_NOT_FOUND,
84 "File not found",
85 DiagnosticsModel::TEST_FAIL_CONTINUE);
86 } else {
87 RecordOutcome(DIAG_SQLITE_FILE_NOT_FOUND_OK,
88 "File not found (but that is OK)",
89 DiagnosticsModel::TEST_OK);
91 return true;
94 int errors = 0;
95 { // Scope the statement and database so they close properly.
96 sql::Connection database;
97 database.set_exclusive_locking();
98 scoped_refptr<ErrorRecorder> recorder(new ErrorRecorder);
100 // Set the error callback so that we can get useful results in a debug
101 // build for a corrupted database. Without setting the error callback,
102 // sql::Connection will just DCHECK.
103 database.set_error_callback(
104 base::Bind(&SqliteIntegrityTest::ErrorRecorder::RecordSqliteError,
105 recorder->AsWeakPtr(),
106 &database));
107 if (!database.Open(path)) {
108 RecordFailure(DIAG_SQLITE_CANNOT_OPEN_DB,
109 "Cannot open DB. Possibly corrupted");
110 return true;
112 if (recorder->has_error()) {
113 RecordFailure(DIAG_SQLITE_ERROR_HANDLER_CALLED,
114 recorder->FormatError());
115 return true;
117 sql::Statement statement(
118 database.GetUniqueStatement("PRAGMA integrity_check;"));
119 if (recorder->has_error()) {
120 RecordFailure(DIAG_SQLITE_ERROR_HANDLER_CALLED,
121 recorder->FormatError());
122 return true;
124 if (!statement.is_valid()) {
125 int error = database.GetErrorCode();
126 if (SQLITE_BUSY == error) {
127 RecordFailure(DIAG_SQLITE_DB_LOCKED,
128 "Database locked by another process");
129 } else {
130 std::string str("Pragma failed. Error: ");
131 str += base::IntToString(error);
132 RecordFailure(DIAG_SQLITE_PRAGMA_FAILED, str);
134 return false;
137 while (statement.Step()) {
138 std::string result(statement.ColumnString(0));
139 if ("ok" != result)
140 ++errors;
142 if (recorder->has_error()) {
143 RecordFailure(DIAG_SQLITE_ERROR_HANDLER_CALLED,
144 recorder->FormatError());
145 return true;
149 // All done. Report to the user.
150 if (errors != 0) {
151 std::string str("Database corruption detected: ");
152 str += base::IntToString(errors) + " errors";
153 RecordFailure(DIAG_SQLITE_DB_CORRUPTED, str);
154 return true;
156 RecordSuccess("No corruption detected");
157 return true;
160 private:
161 class ErrorRecorder : public base::RefCounted<ErrorRecorder>,
162 public base::SupportsWeakPtr<ErrorRecorder> {
163 public:
164 ErrorRecorder() : has_error_(false), sqlite_error_(0), last_errno_(0) {}
166 void RecordSqliteError(sql::Connection* connection,
167 int sqlite_error,
168 sql::Statement* statement) {
169 has_error_ = true;
170 sqlite_error_ = sqlite_error;
171 last_errno_ = connection->GetLastErrno();
172 message_ = connection->GetErrorMessage();
175 bool has_error() const { return has_error_; }
177 std::string FormatError() {
178 return base::StringPrintf("SQLite error: %d, Last Errno: %d: %s",
179 sqlite_error_,
180 last_errno_,
181 message_.c_str());
184 private:
185 friend class base::RefCounted<ErrorRecorder>;
186 ~ErrorRecorder() {}
188 bool has_error_;
189 int sqlite_error_;
190 int last_errno_;
191 std::string message_;
193 DISALLOW_COPY_AND_ASSIGN(ErrorRecorder);
196 uint32 flags_;
197 base::FilePath db_path_;
198 DISALLOW_COPY_AND_ASSIGN(SqliteIntegrityTest);
201 } // namespace
203 DiagnosticsTest* MakeSqliteAppCacheDbTest() {
204 base::FilePath appcache_dir(content::kAppCacheDirname);
205 base::FilePath appcache_db =
206 appcache_dir.Append(appcache::kAppCacheDatabaseName);
207 return new SqliteIntegrityTest(SqliteIntegrityTest::NO_FLAGS_SET,
208 DIAGNOSTICS_SQLITE_INTEGRITY_APP_CACHE_TEST,
209 appcache_db);
212 DiagnosticsTest* MakeSqliteArchivedHistoryDbTest() {
213 return new SqliteIntegrityTest(
214 SqliteIntegrityTest::NO_FLAGS_SET,
215 DIAGNOSTICS_SQLITE_INTEGRITY_ARCHIVED_HISTORY_TEST,
216 base::FilePath(chrome::kArchivedHistoryFilename));
219 DiagnosticsTest* MakeSqliteCookiesDbTest() {
220 return new SqliteIntegrityTest(SqliteIntegrityTest::CRITICAL,
221 DIAGNOSTICS_SQLITE_INTEGRITY_COOKIE_TEST,
222 base::FilePath(chrome::kCookieFilename));
225 DiagnosticsTest* MakeSqliteWebDatabaseTrackerDbTest() {
226 base::FilePath databases_dir(webkit_database::kDatabaseDirectoryName);
227 base::FilePath tracker_db =
228 databases_dir.Append(webkit_database::kTrackerDatabaseFileName);
229 return new SqliteIntegrityTest(
230 SqliteIntegrityTest::NO_FLAGS_SET,
231 DIAGNOSTICS_SQLITE_INTEGRITY_DATABASE_TRACKER_TEST,
232 tracker_db);
235 DiagnosticsTest* MakeSqliteHistoryDbTest() {
236 return new SqliteIntegrityTest(SqliteIntegrityTest::CRITICAL,
237 DIAGNOSTICS_SQLITE_INTEGRITY_HISTORY_TEST,
238 base::FilePath(chrome::kHistoryFilename));
241 #if defined(OS_CHROMEOS)
242 DiagnosticsTest* MakeSqliteNssCertDbTest() {
243 base::FilePath home_dir = base::GetHomeDir();
244 return new SqliteIntegrityTest(SqliteIntegrityTest::REMOVE_IF_CORRUPT,
245 DIAGNOSTICS_SQLITE_INTEGRITY_NSS_CERT_TEST,
246 home_dir.Append(chromeos::kNssCertDbPath));
249 DiagnosticsTest* MakeSqliteNssKeyDbTest() {
250 base::FilePath home_dir = base::GetHomeDir();
251 return new SqliteIntegrityTest(SqliteIntegrityTest::REMOVE_IF_CORRUPT,
252 DIAGNOSTICS_SQLITE_INTEGRITY_NSS_KEY_TEST,
253 home_dir.Append(chromeos::kNssKeyDbPath));
255 #endif // defined(OS_CHROMEOS)
257 DiagnosticsTest* MakeSqliteThumbnailsDbTest() {
258 return new SqliteIntegrityTest(SqliteIntegrityTest::NO_FLAGS_SET,
259 DIAGNOSTICS_SQLITE_INTEGRITY_THUMBNAILS_TEST,
260 base::FilePath(chrome::kThumbnailsFilename));
263 DiagnosticsTest* MakeSqliteWebDataDbTest() {
264 return new SqliteIntegrityTest(SqliteIntegrityTest::CRITICAL,
265 DIAGNOSTICS_SQLITE_INTEGRITY_WEB_DATA_TEST,
266 base::FilePath(kWebDataFilename));
269 } // namespace diagnostics