sqlite_diagnostics.cc revision 731df977c0511bca2206b5f333555b1205ff1f43
1// Copyright (c) 2010 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/metrics/histogram.h"
13#include "base/path_service.h"
14#include "base/singleton.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
23namespace {
24
25// Generic diagnostic test class for checking sqlite db integrity.
26class 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.
92template <size_t unique>
93class 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
109sql::ErrorDelegate* GetErrorHandlerForCookieDb() {
110  return new sql::DiagnosticErrorDelegate<HistogramUniquifier<0> >();
111}
112
113sql::ErrorDelegate* GetErrorHandlerForHistoryDb() {
114  return new sql::DiagnosticErrorDelegate<HistogramUniquifier<1> >();
115}
116
117sql::ErrorDelegate* GetErrorHandlerForThumbnailDb() {
118  return new sql::DiagnosticErrorDelegate<HistogramUniquifier<2> >();
119}
120
121sql::ErrorDelegate* GetErrorHandlerForTextDb() {
122  return new sql::DiagnosticErrorDelegate<HistogramUniquifier<3> >();
123}
124
125sql::ErrorDelegate* GetErrorHandlerForWebDb() {
126  return new sql::DiagnosticErrorDelegate<HistogramUniquifier<4> >();
127}
128
129DiagnosticTest* MakeSqliteWebDbTest() {
130  return new SqliteIntegrityTest(true, ASCIIToUTF16("Web DB"),
131                                 FilePath(chrome::kWebDataFilename));
132}
133
134DiagnosticTest* MakeSqliteCookiesDbTest() {
135  return new SqliteIntegrityTest(true, ASCIIToUTF16("Cookies DB"),
136                                 FilePath(chrome::kCookieFilename));
137}
138
139DiagnosticTest* MakeSqliteHistoryDbTest() {
140  return new SqliteIntegrityTest(true, ASCIIToUTF16("History DB"),
141                                 FilePath(chrome::kHistoryFilename));
142}
143
144DiagnosticTest* MakeSqliteArchivedHistoryDbTest() {
145  return new SqliteIntegrityTest(false, ASCIIToUTF16("Archived History DB"),
146                                 FilePath(chrome::kArchivedHistoryFilename));
147}
148
149DiagnosticTest* MakeSqliteThumbnailsDbTest() {
150  return new SqliteIntegrityTest(false, ASCIIToUTF16("Thumbnails DB"),
151                                 FilePath(chrome::kThumbnailsFilename));
152}
153
154DiagnosticTest* 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
161DiagnosticTest* 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