Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / diagnostics / recon_diagnostics.cc
blob7e235ec65b10090ba7229a1305befb3c820ceecb
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/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"
23 #if defined(OS_WIN)
24 #include "base/win/windows_version.h"
25 #include "chrome/browser/enumerate_modules_model_win.h"
26 #include "chrome/installer/util/install_util.h"
27 #endif
29 // Reconnaissance diagnostics. These are the first and most critical
30 // diagnostic tests. Here we check for the existence of critical files.
31 // TODO(cpu): Define if it makes sense to localize strings.
33 // TODO(cpu): There are a few maximum file sizes hard-coded in this file
34 // that have little or no theoretical or experimental ground. Find a way
35 // to justify them.
37 namespace diagnostics {
39 namespace {
41 const int64 kOneKilobyte = 1024;
42 const int64 kOneMegabyte = 1024 * kOneKilobyte;
44 class InstallTypeTest;
45 InstallTypeTest* g_install_type = 0;
47 // Check if any conflicting DLLs are loaded.
48 class ConflictingDllsTest : public DiagnosticsTest {
49 public:
50 ConflictingDllsTest()
51 : DiagnosticsTest(DIAGNOSTICS_CONFLICTING_DLLS_TEST) {}
53 virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
54 #if defined(OS_WIN)
55 EnumerateModulesModel* model = EnumerateModulesModel::GetInstance();
56 model->set_limited_mode(true);
57 model->ScanNow();
58 scoped_ptr<base::ListValue> list(model->GetModuleList());
59 if (!model->confirmed_bad_modules_detected() &&
60 !model->suspected_bad_modules_detected()) {
61 RecordSuccess("No conflicting modules found");
62 return true;
65 std::string failures = "Possibly conflicting modules:";
66 base::DictionaryValue* dictionary;
67 for (size_t i = 0; i < list->GetSize(); ++i) {
68 if (!list->GetDictionary(i, &dictionary))
69 RecordFailure(DIAG_RECON_DICTIONARY_LOOKUP_FAILED,
70 "Dictionary lookup failed");
71 int status;
72 std::string location;
73 std::string name;
74 if (!dictionary->GetInteger("status", &status))
75 RecordFailure(DIAG_RECON_NO_STATUS_FIELD, "No 'status' field found");
76 if (status < ModuleEnumerator::SUSPECTED_BAD)
77 continue;
79 if (!dictionary->GetString("location", &location)) {
80 RecordFailure(DIAG_RECON_NO_LOCATION_FIELD,
81 "No 'location' field found");
82 return true;
84 if (!dictionary->GetString("name", &name)) {
85 RecordFailure(DIAG_RECON_NO_NAME_FIELD, "No 'name' field found");
86 return true;
89 failures += "\n" + location + name;
91 RecordFailure(DIAG_RECON_CONFLICTING_MODULES, failures);
92 return true;
93 #else
94 RecordFailure(DIAG_RECON_NOT_IMPLEMENTED, "Not implemented");
95 return true;
96 #endif // defined(OS_WIN)
99 private:
100 DISALLOW_COPY_AND_ASSIGN(ConflictingDllsTest);
103 // Check that the disk space in the volume where the user data directory
104 // normally lives is not dangerously low.
105 class DiskSpaceTest : public DiagnosticsTest {
106 public:
107 DiskSpaceTest() : DiagnosticsTest(DIAGNOSTICS_DISK_SPACE_TEST) {}
109 virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
110 base::FilePath data_dir;
111 if (!PathService::Get(chrome::DIR_USER_DATA, &data_dir))
112 return false;
113 int64 disk_space = base::SysInfo::AmountOfFreeDiskSpace(data_dir);
114 if (disk_space < 0) {
115 RecordFailure(DIAG_RECON_UNABLE_TO_QUERY, "Unable to query free space");
116 return true;
118 std::string printable_size = base::Int64ToString(disk_space);
119 if (disk_space < 80 * kOneMegabyte) {
120 RecordFailure(DIAG_RECON_LOW_DISK_SPACE,
121 "Low disk space: " + printable_size);
122 return true;
124 RecordSuccess("Free space: " + printable_size);
125 return true;
128 private:
129 DISALLOW_COPY_AND_ASSIGN(DiskSpaceTest);
132 // Check if it is system install or per-user install.
133 class InstallTypeTest : public DiagnosticsTest {
134 public:
135 InstallTypeTest()
136 : DiagnosticsTest(DIAGNOSTICS_INSTALL_TYPE_TEST), user_level_(false) {}
138 virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
139 #if defined(OS_WIN)
140 base::FilePath chrome_exe;
141 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
142 RecordFailure(DIAG_RECON_INSTALL_PATH_PROVIDER, "Path provider failure");
143 return false;
145 user_level_ = InstallUtil::IsPerUserInstall(chrome_exe.value().c_str());
146 const char* type = user_level_ ? "User Level" : "System Level";
147 std::string install_type(type);
148 #else
149 std::string install_type("System Level");
150 #endif // defined(OS_WIN)
151 RecordSuccess(install_type);
152 g_install_type = this;
153 return true;
156 bool system_level() const { return !user_level_; }
158 private:
159 bool user_level_;
160 DISALLOW_COPY_AND_ASSIGN(InstallTypeTest);
163 // Checks that a given JSON file can be correctly parsed.
164 class JSONTest : public DiagnosticsTest {
165 public:
166 enum FileImportance {
167 NON_CRITICAL,
168 CRITICAL
171 JSONTest(const base::FilePath& path,
172 DiagnosticsTestId id,
173 int64 max_file_size,
174 FileImportance importance)
175 : DiagnosticsTest(id),
176 path_(path),
177 max_file_size_(max_file_size),
178 importance_(importance) {}
180 virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
181 if (!base::PathExists(path_)) {
182 if (importance_ == CRITICAL) {
183 RecordOutcome(DIAG_RECON_FILE_NOT_FOUND,
184 "File not found",
185 DiagnosticsModel::TEST_FAIL_CONTINUE);
186 } else {
187 RecordOutcome(DIAG_RECON_FILE_NOT_FOUND_OK,
188 "File not found (but that is OK)",
189 DiagnosticsModel::TEST_OK);
191 return true;
193 int64 file_size;
194 if (!base::GetFileSize(path_, &file_size)) {
195 RecordFailure(DIAG_RECON_CANNOT_OBTAIN_FILE_SIZE,
196 "Cannot obtain file size");
197 return true;
200 if (file_size > max_file_size_) {
201 RecordFailure(DIAG_RECON_FILE_TOO_BIG, "File too big");
202 return true;
204 // Being small enough, we can process it in-memory.
205 std::string json_data;
206 if (!base::ReadFileToString(path_, &json_data)) {
207 RecordFailure(DIAG_RECON_UNABLE_TO_OPEN_FILE,
208 "Could not open file. Possibly locked by another process");
209 return true;
212 JSONStringValueSerializer json(json_data);
213 int error_code = base::JSONReader::JSON_NO_ERROR;
214 std::string error_message;
215 scoped_ptr<base::Value> json_root(
216 json.Deserialize(&error_code, &error_message));
217 if (base::JSONReader::JSON_NO_ERROR != error_code) {
218 if (error_message.empty()) {
219 error_message = "Parse error " + base::IntToString(error_code);
221 RecordFailure(DIAG_RECON_PARSE_ERROR, error_message);
222 return true;
225 RecordSuccess("File parsed OK");
226 return true;
229 private:
230 base::FilePath path_;
231 int64 max_file_size_;
232 FileImportance importance_;
233 DISALLOW_COPY_AND_ASSIGN(JSONTest);
236 // Check that the flavor of the operating system is supported.
237 class OperatingSystemTest : public DiagnosticsTest {
238 public:
239 OperatingSystemTest()
240 : DiagnosticsTest(DIAGNOSTICS_OPERATING_SYSTEM_TEST) {}
242 virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
243 #if defined(OS_WIN)
244 base::win::Version version = base::win::GetVersion();
245 if ((version < base::win::VERSION_XP) ||
246 ((version == base::win::VERSION_XP) &&
247 (base::win::OSInfo::GetInstance()->service_pack().major < 2))) {
248 RecordFailure(DIAG_RECON_PRE_WINDOW_XP_SP2,
249 "Must have Windows XP SP2 or later");
250 return false;
252 #else
253 // TODO(port): define the OS criteria for Linux and Mac.
254 #endif // defined(OS_WIN)
255 RecordSuccess(
256 base::StringPrintf("%s %s",
257 base::SysInfo::OperatingSystemName().c_str(),
258 base::SysInfo::OperatingSystemVersion().c_str()));
259 return true;
262 private:
263 DISALLOW_COPY_AND_ASSIGN(OperatingSystemTest);
266 struct TestPathInfo {
267 DiagnosticsTestId test_id;
268 int path_id;
269 bool is_directory;
270 bool is_optional;
271 bool test_writable;
272 int64 max_size;
275 const TestPathInfo kPathsToTest[] = {
276 {DIAGNOSTICS_PATH_DICTIONARIES_TEST, chrome::DIR_APP_DICTIONARIES, true,
277 true, false, 0},
278 {DIAGNOSTICS_PATH_LOCAL_STATE_TEST, chrome::FILE_LOCAL_STATE, false, false,
279 true, 500 * kOneKilobyte},
280 {DIAGNOSTICS_PATH_RESOURCES_TEST, chrome::FILE_RESOURCES_PACK, false, false,
281 false, 0},
282 {DIAGNOSTICS_PATH_USER_DATA_TEST, chrome::DIR_USER_DATA, true, false, true,
283 850 * kOneMegabyte},
286 // Check that the user's data directory exists and the paths are writable.
287 // If it is a system-wide install some paths are not expected to be writable.
288 // This test depends on |InstallTypeTest| having run successfully.
289 class PathTest : public DiagnosticsTest {
290 public:
291 explicit PathTest(const TestPathInfo& path_info)
292 : DiagnosticsTest(path_info.test_id),
293 path_info_(path_info) {}
295 virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
296 if (!g_install_type) {
297 RecordStopFailure(DIAG_RECON_DEPENDENCY, "Install dependency failure");
298 return false;
300 base::FilePath dir_or_file;
301 if (!PathService::Get(path_info_.path_id, &dir_or_file)) {
302 RecordStopFailure(DIAG_RECON_PATH_PROVIDER, "Path provider failure");
303 return false;
305 if (!base::PathExists(dir_or_file)) {
306 RecordFailure(
307 DIAG_RECON_PATH_NOT_FOUND,
308 "Path not found: " +
309 base::UTF16ToUTF8(dir_or_file.LossyDisplayName()));
310 return true;
313 int64 dir_or_file_size = 0;
314 if (path_info_.is_directory) {
315 dir_or_file_size = base::ComputeDirectorySize(dir_or_file);
316 } else {
317 base::GetFileSize(dir_or_file, &dir_or_file_size);
319 if (!dir_or_file_size && !path_info_.is_optional) {
320 RecordFailure(DIAG_RECON_CANNOT_OBTAIN_SIZE,
321 "Cannot obtain size for: " +
322 base::UTF16ToUTF8(dir_or_file.LossyDisplayName()));
323 return true;
325 std::string printable_size = base::Int64ToString(dir_or_file_size);
327 if (path_info_.max_size > 0) {
328 if (dir_or_file_size > path_info_.max_size) {
329 RecordFailure(DIAG_RECON_FILE_TOO_LARGE,
330 "Path contents too large (" + printable_size + ") for: " +
331 base::UTF16ToUTF8(dir_or_file.LossyDisplayName()));
332 return true;
335 if (g_install_type->system_level() && !path_info_.test_writable) {
336 RecordSuccess("Path exists");
337 return true;
339 if (!base::PathIsWritable(dir_or_file)) {
340 RecordFailure(DIAG_RECON_NOT_WRITABLE,
341 "Path is not writable: " +
342 base::UTF16ToUTF8(dir_or_file.LossyDisplayName()));
343 return true;
345 RecordSuccess("Path exists and is writable: " + printable_size);
346 return true;
349 private:
350 TestPathInfo path_info_;
351 DISALLOW_COPY_AND_ASSIGN(PathTest);
354 // Check the version of Chrome.
355 class VersionTest : public DiagnosticsTest {
356 public:
357 VersionTest() : DiagnosticsTest(DIAGNOSTICS_VERSION_TEST) {}
359 virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
360 chrome::VersionInfo version_info;
361 if (!version_info.is_valid()) {
362 RecordFailure(DIAG_RECON_NO_VERSION, "No Version");
363 return true;
365 std::string current_version = version_info.Version();
366 if (current_version.empty()) {
367 RecordFailure(DIAG_RECON_EMPTY_VERSION, "Empty Version");
368 return true;
370 std::string version_modifier =
371 chrome::VersionInfo::GetVersionStringModifier();
372 if (!version_modifier.empty())
373 current_version += " " + version_modifier;
374 #if defined(GOOGLE_CHROME_BUILD)
375 current_version += " GCB";
376 #endif // defined(GOOGLE_CHROME_BUILD)
377 RecordSuccess(current_version);
378 return true;
381 private:
382 DISALLOW_COPY_AND_ASSIGN(VersionTest);
385 } // namespace
387 DiagnosticsTest* MakeConflictingDllsTest() { return new ConflictingDllsTest(); }
389 DiagnosticsTest* MakeDiskSpaceTest() { return new DiskSpaceTest(); }
391 DiagnosticsTest* MakeInstallTypeTest() { return new InstallTypeTest(); }
393 DiagnosticsTest* MakeBookMarksTest() {
394 base::FilePath path = DiagnosticsTest::GetUserDefaultProfileDir();
395 path = path.Append(chrome::kBookmarksFileName);
396 return new JSONTest(path,
397 DIAGNOSTICS_JSON_BOOKMARKS_TEST,
398 2 * kOneMegabyte,
399 JSONTest::NON_CRITICAL);
402 DiagnosticsTest* MakeLocalStateTest() {
403 base::FilePath path;
404 PathService::Get(chrome::DIR_USER_DATA, &path);
405 path = path.Append(chrome::kLocalStateFilename);
406 return new JSONTest(path,
407 DIAGNOSTICS_JSON_LOCAL_STATE_TEST,
408 50 * kOneKilobyte,
409 JSONTest::CRITICAL);
412 DiagnosticsTest* MakePreferencesTest() {
413 base::FilePath path = DiagnosticsTest::GetUserDefaultProfileDir();
414 path = path.Append(chrome::kPreferencesFilename);
415 return new JSONTest(path,
416 DIAGNOSTICS_JSON_PREFERENCES_TEST,
417 100 * kOneKilobyte,
418 JSONTest::CRITICAL);
422 DiagnosticsTest* MakeOperatingSystemTest() { return new OperatingSystemTest(); }
424 DiagnosticsTest* MakeDictonaryDirTest() {
425 return new PathTest(kPathsToTest[0]);
428 DiagnosticsTest* MakeLocalStateFileTest() {
429 return new PathTest(kPathsToTest[1]);
432 DiagnosticsTest* MakeResourcesFileTest() {
433 return new PathTest(kPathsToTest[2]);
436 DiagnosticsTest* MakeUserDirTest() { return new PathTest(kPathsToTest[3]); }
438 DiagnosticsTest* MakeVersionTest() { return new VersionTest(); }
440 } // namespace diagnostics