Fire an error if a pref used in the UI is missing once all prefs are fetched.
[chromium-blink-merge.git] / chrome / browser / diagnostics / recon_diagnostics.cc
bloba0575db565540afe98eb5c1548d41f917988dfb7
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/recon_diagnostics.h"
7 #include <string>
9 #include "base/files/file_util.h"
10 #include "base/json/json_reader.h"
11 #include "base/json/json_string_value_serializer.h"
12 #include "base/path_service.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/sys_info.h"
18 #include "chrome/browser/diagnostics/diagnostics_test.h"
19 #include "chrome/common/chrome_constants.h"
20 #include "chrome/common/chrome_paths.h"
21 #include "chrome/common/chrome_version_info.h"
22 #include "components/bookmarks/common/bookmark_constants.h"
24 #if defined(OS_WIN)
25 #include "base/win/windows_version.h"
26 #include "chrome/browser/enumerate_modules_model_win.h"
27 #include "chrome/installer/util/install_util.h"
28 #endif
30 // Reconnaissance diagnostics. These are the first and most critical
31 // diagnostic tests. Here we check for the existence of critical files.
32 // TODO(cpu): Define if it makes sense to localize strings.
34 // TODO(cpu): There are a few maximum file sizes hard-coded in this file
35 // that have little or no theoretical or experimental ground. Find a way
36 // to justify them.
38 namespace diagnostics {
40 namespace {
42 const int64 kOneKilobyte = 1024;
43 const int64 kOneMegabyte = 1024 * kOneKilobyte;
45 class InstallTypeTest;
46 InstallTypeTest* g_install_type = 0;
48 // Check if any conflicting DLLs are loaded.
49 class ConflictingDllsTest : public DiagnosticsTest {
50 public:
51 ConflictingDllsTest()
52 : DiagnosticsTest(DIAGNOSTICS_CONFLICTING_DLLS_TEST) {}
54 bool ExecuteImpl(DiagnosticsModel::Observer* observer) override {
55 #if defined(OS_WIN)
56 EnumerateModulesModel* model = EnumerateModulesModel::GetInstance();
57 model->set_limited_mode(true);
58 model->ScanNow();
59 scoped_ptr<base::ListValue> list(model->GetModuleList());
60 if (!model->confirmed_bad_modules_detected() &&
61 !model->suspected_bad_modules_detected()) {
62 RecordSuccess("No conflicting modules found");
63 return true;
66 std::string failures = "Possibly conflicting modules:";
67 base::DictionaryValue* dictionary;
68 for (size_t i = 0; i < list->GetSize(); ++i) {
69 if (!list->GetDictionary(i, &dictionary))
70 RecordFailure(DIAG_RECON_DICTIONARY_LOOKUP_FAILED,
71 "Dictionary lookup failed");
72 int status;
73 std::string location;
74 std::string name;
75 if (!dictionary->GetInteger("status", &status))
76 RecordFailure(DIAG_RECON_NO_STATUS_FIELD, "No 'status' field found");
77 if (status < ModuleEnumerator::SUSPECTED_BAD)
78 continue;
80 if (!dictionary->GetString("location", &location)) {
81 RecordFailure(DIAG_RECON_NO_LOCATION_FIELD,
82 "No 'location' field found");
83 return true;
85 if (!dictionary->GetString("name", &name)) {
86 RecordFailure(DIAG_RECON_NO_NAME_FIELD, "No 'name' field found");
87 return true;
90 failures += "\n" + location + name;
92 RecordFailure(DIAG_RECON_CONFLICTING_MODULES, failures);
93 return true;
94 #else
95 RecordFailure(DIAG_RECON_NOT_IMPLEMENTED, "Not implemented");
96 return true;
97 #endif // defined(OS_WIN)
100 private:
101 DISALLOW_COPY_AND_ASSIGN(ConflictingDllsTest);
104 // Check that the disk space in the volume where the user data directory
105 // normally lives is not dangerously low.
106 class DiskSpaceTest : public DiagnosticsTest {
107 public:
108 DiskSpaceTest() : DiagnosticsTest(DIAGNOSTICS_DISK_SPACE_TEST) {}
110 bool ExecuteImpl(DiagnosticsModel::Observer* observer) override {
111 base::FilePath data_dir;
112 if (!PathService::Get(chrome::DIR_USER_DATA, &data_dir))
113 return false;
114 int64 disk_space = base::SysInfo::AmountOfFreeDiskSpace(data_dir);
115 if (disk_space < 0) {
116 RecordFailure(DIAG_RECON_UNABLE_TO_QUERY, "Unable to query free space");
117 return true;
119 std::string printable_size = base::Int64ToString(disk_space);
120 if (disk_space < 80 * kOneMegabyte) {
121 RecordFailure(DIAG_RECON_LOW_DISK_SPACE,
122 "Low disk space: " + printable_size);
123 return true;
125 RecordSuccess("Free space: " + printable_size);
126 return true;
129 private:
130 DISALLOW_COPY_AND_ASSIGN(DiskSpaceTest);
133 // Check if it is system install or per-user install.
134 class InstallTypeTest : public DiagnosticsTest {
135 public:
136 InstallTypeTest()
137 : DiagnosticsTest(DIAGNOSTICS_INSTALL_TYPE_TEST), user_level_(false) {}
139 bool ExecuteImpl(DiagnosticsModel::Observer* observer) override {
140 #if defined(OS_WIN)
141 base::FilePath chrome_exe;
142 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
143 RecordFailure(DIAG_RECON_INSTALL_PATH_PROVIDER, "Path provider failure");
144 return false;
146 user_level_ = InstallUtil::IsPerUserInstall(chrome_exe);
147 const char* type = user_level_ ? "User Level" : "System Level";
148 std::string install_type(type);
149 #else
150 std::string install_type("System Level");
151 #endif // defined(OS_WIN)
152 RecordSuccess(install_type);
153 g_install_type = this;
154 return true;
157 bool system_level() const { return !user_level_; }
159 private:
160 bool user_level_;
161 DISALLOW_COPY_AND_ASSIGN(InstallTypeTest);
164 // Checks that a given JSON file can be correctly parsed.
165 class JSONTest : public DiagnosticsTest {
166 public:
167 enum FileImportance {
168 NON_CRITICAL,
169 CRITICAL
172 JSONTest(const base::FilePath& path,
173 DiagnosticsTestId id,
174 int64 max_file_size,
175 FileImportance importance)
176 : DiagnosticsTest(id),
177 path_(path),
178 max_file_size_(max_file_size),
179 importance_(importance) {}
181 bool ExecuteImpl(DiagnosticsModel::Observer* observer) override {
182 if (!base::PathExists(path_)) {
183 if (importance_ == CRITICAL) {
184 RecordOutcome(DIAG_RECON_FILE_NOT_FOUND,
185 "File not found",
186 DiagnosticsModel::TEST_FAIL_CONTINUE);
187 } else {
188 RecordOutcome(DIAG_RECON_FILE_NOT_FOUND_OK,
189 "File not found (but that is OK)",
190 DiagnosticsModel::TEST_OK);
192 return true;
194 int64 file_size;
195 if (!base::GetFileSize(path_, &file_size)) {
196 RecordFailure(DIAG_RECON_CANNOT_OBTAIN_FILE_SIZE,
197 "Cannot obtain file size");
198 return true;
201 if (file_size > max_file_size_) {
202 RecordFailure(DIAG_RECON_FILE_TOO_BIG, "File too big");
203 return true;
205 // Being small enough, we can process it in-memory.
206 std::string json_data;
207 if (!base::ReadFileToString(path_, &json_data)) {
208 RecordFailure(DIAG_RECON_UNABLE_TO_OPEN_FILE,
209 "Could not open file. Possibly locked by another process");
210 return true;
213 JSONStringValueDeserializer json(json_data);
214 int error_code = base::JSONReader::JSON_NO_ERROR;
215 std::string error_message;
216 scoped_ptr<base::Value> json_root(
217 json.Deserialize(&error_code, &error_message));
218 if (base::JSONReader::JSON_NO_ERROR != error_code) {
219 if (error_message.empty()) {
220 error_message = "Parse error " + base::IntToString(error_code);
222 RecordFailure(DIAG_RECON_PARSE_ERROR, error_message);
223 return true;
226 RecordSuccess("File parsed OK");
227 return true;
230 private:
231 base::FilePath path_;
232 int64 max_file_size_;
233 FileImportance importance_;
234 DISALLOW_COPY_AND_ASSIGN(JSONTest);
237 // Check that the flavor of the operating system is supported.
238 class OperatingSystemTest : public DiagnosticsTest {
239 public:
240 OperatingSystemTest()
241 : DiagnosticsTest(DIAGNOSTICS_OPERATING_SYSTEM_TEST) {}
243 bool ExecuteImpl(DiagnosticsModel::Observer* observer) override {
244 #if defined(OS_WIN)
245 base::win::Version version = base::win::GetVersion();
246 if ((version < base::win::VERSION_XP) ||
247 ((version == base::win::VERSION_XP) &&
248 (base::win::OSInfo::GetInstance()->service_pack().major < 2))) {
249 RecordFailure(DIAG_RECON_PRE_WINDOW_XP_SP2,
250 "Must have Windows XP SP2 or later");
251 return false;
253 #else
254 // TODO(port): define the OS criteria for Linux and Mac.
255 #endif // defined(OS_WIN)
256 RecordSuccess(
257 base::StringPrintf("%s %s",
258 base::SysInfo::OperatingSystemName().c_str(),
259 base::SysInfo::OperatingSystemVersion().c_str()));
260 return true;
263 private:
264 DISALLOW_COPY_AND_ASSIGN(OperatingSystemTest);
267 struct TestPathInfo {
268 DiagnosticsTestId test_id;
269 int path_id;
270 bool is_directory;
271 bool is_optional;
272 bool test_writable;
273 int64 max_size;
276 const TestPathInfo kPathsToTest[] = {
277 {DIAGNOSTICS_PATH_DICTIONARIES_TEST, chrome::DIR_APP_DICTIONARIES, true,
278 true, false, 0},
279 {DIAGNOSTICS_PATH_LOCAL_STATE_TEST, chrome::FILE_LOCAL_STATE, false, false,
280 true, 500 * kOneKilobyte},
281 {DIAGNOSTICS_PATH_RESOURCES_TEST, chrome::FILE_RESOURCES_PACK, false, false,
282 false, 0},
283 {DIAGNOSTICS_PATH_USER_DATA_TEST, chrome::DIR_USER_DATA, true, false, true,
284 850 * kOneMegabyte},
287 // Check that the user's data directory exists and the paths are writable.
288 // If it is a system-wide install some paths are not expected to be writable.
289 // This test depends on |InstallTypeTest| having run successfully.
290 class PathTest : public DiagnosticsTest {
291 public:
292 explicit PathTest(const TestPathInfo& path_info)
293 : DiagnosticsTest(path_info.test_id),
294 path_info_(path_info) {}
296 bool ExecuteImpl(DiagnosticsModel::Observer* observer) override {
297 if (!g_install_type) {
298 RecordStopFailure(DIAG_RECON_DEPENDENCY, "Install dependency failure");
299 return false;
301 base::FilePath dir_or_file;
302 if (!PathService::Get(path_info_.path_id, &dir_or_file)) {
303 RecordStopFailure(DIAG_RECON_PATH_PROVIDER, "Path provider failure");
304 return false;
306 if (!base::PathExists(dir_or_file)) {
307 RecordFailure(
308 DIAG_RECON_PATH_NOT_FOUND,
309 "Path not found: " +
310 base::UTF16ToUTF8(dir_or_file.LossyDisplayName()));
311 return true;
314 int64 dir_or_file_size = 0;
315 if (path_info_.is_directory) {
316 dir_or_file_size = base::ComputeDirectorySize(dir_or_file);
317 } else {
318 base::GetFileSize(dir_or_file, &dir_or_file_size);
320 if (!dir_or_file_size && !path_info_.is_optional) {
321 RecordFailure(DIAG_RECON_CANNOT_OBTAIN_SIZE,
322 "Cannot obtain size for: " +
323 base::UTF16ToUTF8(dir_or_file.LossyDisplayName()));
324 return true;
326 std::string printable_size = base::Int64ToString(dir_or_file_size);
328 if (path_info_.max_size > 0) {
329 if (dir_or_file_size > path_info_.max_size) {
330 RecordFailure(DIAG_RECON_FILE_TOO_LARGE,
331 "Path contents too large (" + printable_size + ") for: " +
332 base::UTF16ToUTF8(dir_or_file.LossyDisplayName()));
333 return true;
336 if (g_install_type->system_level() && !path_info_.test_writable) {
337 RecordSuccess("Path exists");
338 return true;
340 if (!base::PathIsWritable(dir_or_file)) {
341 RecordFailure(DIAG_RECON_NOT_WRITABLE,
342 "Path is not writable: " +
343 base::UTF16ToUTF8(dir_or_file.LossyDisplayName()));
344 return true;
346 RecordSuccess("Path exists and is writable: " + printable_size);
347 return true;
350 private:
351 TestPathInfo path_info_;
352 DISALLOW_COPY_AND_ASSIGN(PathTest);
355 // Check the version of Chrome.
356 class VersionTest : public DiagnosticsTest {
357 public:
358 VersionTest() : DiagnosticsTest(DIAGNOSTICS_VERSION_TEST) {}
360 bool ExecuteImpl(DiagnosticsModel::Observer* observer) override {
361 chrome::VersionInfo version_info;
362 std::string current_version = version_info.Version();
363 if (current_version.empty()) {
364 RecordFailure(DIAG_RECON_EMPTY_VERSION, "Empty Version");
365 return true;
367 std::string version_modifier =
368 chrome::VersionInfo::GetVersionStringModifier();
369 if (!version_modifier.empty())
370 current_version += " " + version_modifier;
371 #if defined(GOOGLE_CHROME_BUILD)
372 current_version += " GCB";
373 #endif // defined(GOOGLE_CHROME_BUILD)
374 RecordSuccess(current_version);
375 return true;
378 private:
379 DISALLOW_COPY_AND_ASSIGN(VersionTest);
382 } // namespace
384 DiagnosticsTest* MakeConflictingDllsTest() { return new ConflictingDllsTest(); }
386 DiagnosticsTest* MakeDiskSpaceTest() { return new DiskSpaceTest(); }
388 DiagnosticsTest* MakeInstallTypeTest() { return new InstallTypeTest(); }
390 DiagnosticsTest* MakeBookMarksTest() {
391 base::FilePath path = DiagnosticsTest::GetUserDefaultProfileDir();
392 path = path.Append(bookmarks::kBookmarksFileName);
393 return new JSONTest(path,
394 DIAGNOSTICS_JSON_BOOKMARKS_TEST,
395 2 * kOneMegabyte,
396 JSONTest::NON_CRITICAL);
399 DiagnosticsTest* MakeLocalStateTest() {
400 base::FilePath path;
401 PathService::Get(chrome::DIR_USER_DATA, &path);
402 path = path.Append(chrome::kLocalStateFilename);
403 return new JSONTest(path,
404 DIAGNOSTICS_JSON_LOCAL_STATE_TEST,
405 50 * kOneKilobyte,
406 JSONTest::CRITICAL);
409 DiagnosticsTest* MakePreferencesTest() {
410 base::FilePath path = DiagnosticsTest::GetUserDefaultProfileDir();
411 path = path.Append(chrome::kPreferencesFilename);
412 return new JSONTest(path,
413 DIAGNOSTICS_JSON_PREFERENCES_TEST,
414 100 * kOneKilobyte,
415 JSONTest::CRITICAL);
419 DiagnosticsTest* MakeOperatingSystemTest() { return new OperatingSystemTest(); }
421 DiagnosticsTest* MakeDictonaryDirTest() {
422 return new PathTest(kPathsToTest[0]);
425 DiagnosticsTest* MakeLocalStateFileTest() {
426 return new PathTest(kPathsToTest[1]);
429 DiagnosticsTest* MakeResourcesFileTest() {
430 return new PathTest(kPathsToTest[2]);
433 DiagnosticsTest* MakeUserDirTest() { return new PathTest(kPathsToTest[3]); }
435 DiagnosticsTest* MakeVersionTest() { return new VersionTest(); }
437 } // namespace diagnostics