Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / diagnostics / recon_diagnostics.cc
blobfdce774cde89c8b5f339cc2593c4e8846b06d534
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/channel_info.h"
20 #include "chrome/common/chrome_constants.h"
21 #include "chrome/common/chrome_paths.h"
22 #include "components/bookmarks/common/bookmark_constants.h"
23 #include "components/version_info/version_info.h"
25 #if defined(OS_WIN)
26 #include "base/win/windows_version.h"
27 #include "chrome/browser/enumerate_modules_model_win.h"
28 #include "chrome/installer/util/install_util.h"
29 #endif
31 // Reconnaissance diagnostics. These are the first and most critical
32 // diagnostic tests. Here we check for the existence of critical files.
33 // TODO(cpu): Define if it makes sense to localize strings.
35 // TODO(cpu): There are a few maximum file sizes hard-coded in this file
36 // that have little or no theoretical or experimental ground. Find a way
37 // to justify them.
39 namespace diagnostics {
41 namespace {
43 const int64 kOneKilobyte = 1024;
44 const int64 kOneMegabyte = 1024 * kOneKilobyte;
46 class InstallTypeTest;
47 InstallTypeTest* g_install_type = 0;
49 // Check if any conflicting DLLs are loaded.
50 class ConflictingDllsTest : public DiagnosticsTest {
51 public:
52 ConflictingDllsTest()
53 : DiagnosticsTest(DIAGNOSTICS_CONFLICTING_DLLS_TEST) {}
55 bool ExecuteImpl(DiagnosticsModel::Observer* observer) override {
56 #if defined(OS_WIN)
57 EnumerateModulesModel* model = EnumerateModulesModel::GetInstance();
58 model->set_limited_mode(true);
59 model->ScanNow();
60 scoped_ptr<base::ListValue> list(model->GetModuleList());
61 if (!model->confirmed_bad_modules_detected() &&
62 !model->suspected_bad_modules_detected()) {
63 RecordSuccess("No conflicting modules found");
64 return true;
67 std::string failures = "Possibly conflicting modules:";
68 base::DictionaryValue* dictionary;
69 for (size_t i = 0; i < list->GetSize(); ++i) {
70 if (!list->GetDictionary(i, &dictionary))
71 RecordFailure(DIAG_RECON_DICTIONARY_LOOKUP_FAILED,
72 "Dictionary lookup failed");
73 int status;
74 std::string location;
75 std::string name;
76 if (!dictionary->GetInteger("status", &status))
77 RecordFailure(DIAG_RECON_NO_STATUS_FIELD, "No 'status' field found");
78 if (status < ModuleEnumerator::SUSPECTED_BAD)
79 continue;
81 if (!dictionary->GetString("location", &location)) {
82 RecordFailure(DIAG_RECON_NO_LOCATION_FIELD,
83 "No 'location' field found");
84 return true;
86 if (!dictionary->GetString("name", &name)) {
87 RecordFailure(DIAG_RECON_NO_NAME_FIELD, "No 'name' field found");
88 return true;
91 failures += "\n" + location + name;
93 RecordFailure(DIAG_RECON_CONFLICTING_MODULES, failures);
94 return true;
95 #else
96 RecordFailure(DIAG_RECON_NOT_IMPLEMENTED, "Not implemented");
97 return true;
98 #endif // defined(OS_WIN)
101 private:
102 DISALLOW_COPY_AND_ASSIGN(ConflictingDllsTest);
105 // Check that the disk space in the volume where the user data directory
106 // normally lives is not dangerously low.
107 class DiskSpaceTest : public DiagnosticsTest {
108 public:
109 DiskSpaceTest() : DiagnosticsTest(DIAGNOSTICS_DISK_SPACE_TEST) {}
111 bool ExecuteImpl(DiagnosticsModel::Observer* observer) override {
112 base::FilePath data_dir;
113 if (!PathService::Get(chrome::DIR_USER_DATA, &data_dir))
114 return false;
115 int64 disk_space = base::SysInfo::AmountOfFreeDiskSpace(data_dir);
116 if (disk_space < 0) {
117 RecordFailure(DIAG_RECON_UNABLE_TO_QUERY, "Unable to query free space");
118 return true;
120 std::string printable_size = base::Int64ToString(disk_space);
121 if (disk_space < 80 * kOneMegabyte) {
122 RecordFailure(DIAG_RECON_LOW_DISK_SPACE,
123 "Low disk space: " + printable_size);
124 return true;
126 RecordSuccess("Free space: " + printable_size);
127 return true;
130 private:
131 DISALLOW_COPY_AND_ASSIGN(DiskSpaceTest);
134 // Check if it is system install or per-user install.
135 class InstallTypeTest : public DiagnosticsTest {
136 public:
137 InstallTypeTest()
138 : DiagnosticsTest(DIAGNOSTICS_INSTALL_TYPE_TEST), user_level_(false) {}
140 bool ExecuteImpl(DiagnosticsModel::Observer* observer) override {
141 #if defined(OS_WIN)
142 base::FilePath chrome_exe;
143 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
144 RecordFailure(DIAG_RECON_INSTALL_PATH_PROVIDER, "Path provider failure");
145 return false;
147 user_level_ = InstallUtil::IsPerUserInstall(chrome_exe);
148 const char* type = user_level_ ? "User Level" : "System Level";
149 std::string install_type(type);
150 #else
151 std::string install_type("System Level");
152 #endif // defined(OS_WIN)
153 RecordSuccess(install_type);
154 g_install_type = this;
155 return true;
158 bool system_level() const { return !user_level_; }
160 private:
161 bool user_level_;
162 DISALLOW_COPY_AND_ASSIGN(InstallTypeTest);
165 // Checks that a given JSON file can be correctly parsed.
166 class JSONTest : public DiagnosticsTest {
167 public:
168 enum FileImportance {
169 NON_CRITICAL,
170 CRITICAL
173 JSONTest(const base::FilePath& path,
174 DiagnosticsTestId id,
175 int64 max_file_size,
176 FileImportance importance)
177 : DiagnosticsTest(id),
178 path_(path),
179 max_file_size_(max_file_size),
180 importance_(importance) {}
182 bool ExecuteImpl(DiagnosticsModel::Observer* observer) override {
183 if (!base::PathExists(path_)) {
184 if (importance_ == CRITICAL) {
185 RecordOutcome(DIAG_RECON_FILE_NOT_FOUND,
186 "File not found",
187 DiagnosticsModel::TEST_FAIL_CONTINUE);
188 } else {
189 RecordOutcome(DIAG_RECON_FILE_NOT_FOUND_OK,
190 "File not found (but that is OK)",
191 DiagnosticsModel::TEST_OK);
193 return true;
195 int64 file_size;
196 if (!base::GetFileSize(path_, &file_size)) {
197 RecordFailure(DIAG_RECON_CANNOT_OBTAIN_FILE_SIZE,
198 "Cannot obtain file size");
199 return true;
202 if (file_size > max_file_size_) {
203 RecordFailure(DIAG_RECON_FILE_TOO_BIG, "File too big");
204 return true;
206 // Being small enough, we can process it in-memory.
207 std::string json_data;
208 if (!base::ReadFileToString(path_, &json_data)) {
209 RecordFailure(DIAG_RECON_UNABLE_TO_OPEN_FILE,
210 "Could not open file. Possibly locked by another process");
211 return true;
214 JSONStringValueDeserializer json(json_data);
215 int error_code = base::JSONReader::JSON_NO_ERROR;
216 std::string error_message;
217 scoped_ptr<base::Value> json_root(
218 json.Deserialize(&error_code, &error_message));
219 if (base::JSONReader::JSON_NO_ERROR != error_code) {
220 if (error_message.empty()) {
221 error_message = "Parse error " + base::IntToString(error_code);
223 RecordFailure(DIAG_RECON_PARSE_ERROR, error_message);
224 return true;
227 RecordSuccess("File parsed OK");
228 return true;
231 private:
232 base::FilePath path_;
233 int64 max_file_size_;
234 FileImportance importance_;
235 DISALLOW_COPY_AND_ASSIGN(JSONTest);
238 // Check that the flavor of the operating system is supported.
239 class OperatingSystemTest : public DiagnosticsTest {
240 public:
241 OperatingSystemTest()
242 : DiagnosticsTest(DIAGNOSTICS_OPERATING_SYSTEM_TEST) {}
244 bool ExecuteImpl(DiagnosticsModel::Observer* observer) override {
245 #if defined(OS_WIN)
246 base::win::Version version = base::win::GetVersion();
247 if ((version < base::win::VERSION_XP) ||
248 ((version == base::win::VERSION_XP) &&
249 (base::win::OSInfo::GetInstance()->service_pack().major < 2))) {
250 RecordFailure(DIAG_RECON_PRE_WINDOW_XP_SP2,
251 "Must have Windows XP SP2 or later");
252 return false;
254 #else
255 // TODO(port): define the OS criteria for Linux and Mac.
256 #endif // defined(OS_WIN)
257 RecordSuccess(
258 base::StringPrintf("%s %s",
259 base::SysInfo::OperatingSystemName().c_str(),
260 base::SysInfo::OperatingSystemVersion().c_str()));
261 return true;
264 private:
265 DISALLOW_COPY_AND_ASSIGN(OperatingSystemTest);
268 struct TestPathInfo {
269 DiagnosticsTestId test_id;
270 int path_id;
271 bool is_directory;
272 bool is_optional;
273 bool test_writable;
274 int64 max_size;
277 const TestPathInfo kPathsToTest[] = {
278 {DIAGNOSTICS_PATH_DICTIONARIES_TEST, chrome::DIR_APP_DICTIONARIES, true,
279 true, false, 0},
280 {DIAGNOSTICS_PATH_LOCAL_STATE_TEST, chrome::FILE_LOCAL_STATE, false, false,
281 true, 500 * kOneKilobyte},
282 {DIAGNOSTICS_PATH_RESOURCES_TEST, chrome::FILE_RESOURCES_PACK, false, false,
283 false, 0},
284 {DIAGNOSTICS_PATH_USER_DATA_TEST, chrome::DIR_USER_DATA, true, false, true,
285 850 * kOneMegabyte},
288 // Check that the user's data directory exists and the paths are writable.
289 // If it is a system-wide install some paths are not expected to be writable.
290 // This test depends on |InstallTypeTest| having run successfully.
291 class PathTest : public DiagnosticsTest {
292 public:
293 explicit PathTest(const TestPathInfo& path_info)
294 : DiagnosticsTest(path_info.test_id),
295 path_info_(path_info) {}
297 bool ExecuteImpl(DiagnosticsModel::Observer* observer) override {
298 if (!g_install_type) {
299 RecordStopFailure(DIAG_RECON_DEPENDENCY, "Install dependency failure");
300 return false;
302 base::FilePath dir_or_file;
303 if (!PathService::Get(path_info_.path_id, &dir_or_file)) {
304 RecordStopFailure(DIAG_RECON_PATH_PROVIDER, "Path provider failure");
305 return false;
307 if (!base::PathExists(dir_or_file)) {
308 RecordFailure(
309 DIAG_RECON_PATH_NOT_FOUND,
310 "Path not found: " +
311 base::UTF16ToUTF8(dir_or_file.LossyDisplayName()));
312 return true;
315 int64 dir_or_file_size = 0;
316 if (path_info_.is_directory) {
317 dir_or_file_size = base::ComputeDirectorySize(dir_or_file);
318 } else {
319 base::GetFileSize(dir_or_file, &dir_or_file_size);
321 if (!dir_or_file_size && !path_info_.is_optional) {
322 RecordFailure(DIAG_RECON_CANNOT_OBTAIN_SIZE,
323 "Cannot obtain size for: " +
324 base::UTF16ToUTF8(dir_or_file.LossyDisplayName()));
325 return true;
327 std::string printable_size = base::Int64ToString(dir_or_file_size);
329 if (path_info_.max_size > 0) {
330 if (dir_or_file_size > path_info_.max_size) {
331 RecordFailure(DIAG_RECON_FILE_TOO_LARGE,
332 "Path contents too large (" + printable_size + ") for: " +
333 base::UTF16ToUTF8(dir_or_file.LossyDisplayName()));
334 return true;
337 if (g_install_type->system_level() && !path_info_.test_writable) {
338 RecordSuccess("Path exists");
339 return true;
341 if (!base::PathIsWritable(dir_or_file)) {
342 RecordFailure(DIAG_RECON_NOT_WRITABLE,
343 "Path is not writable: " +
344 base::UTF16ToUTF8(dir_or_file.LossyDisplayName()));
345 return true;
347 RecordSuccess("Path exists and is writable: " + printable_size);
348 return true;
351 private:
352 TestPathInfo path_info_;
353 DISALLOW_COPY_AND_ASSIGN(PathTest);
356 // Check the version of Chrome.
357 class VersionTest : public DiagnosticsTest {
358 public:
359 VersionTest() : DiagnosticsTest(DIAGNOSTICS_VERSION_TEST) {}
361 bool ExecuteImpl(DiagnosticsModel::Observer* observer) override {
362 std::string current_version = version_info::GetVersionNumber();
363 if (current_version.empty()) {
364 RecordFailure(DIAG_RECON_EMPTY_VERSION, "Empty Version");
365 return true;
367 std::string version_modifier = chrome::GetChannelString();
368 if (!version_modifier.empty())
369 current_version += " " + version_modifier;
370 #if defined(GOOGLE_CHROME_BUILD)
371 current_version += " GCB";
372 #endif // defined(GOOGLE_CHROME_BUILD)
373 RecordSuccess(current_version);
374 return true;
377 private:
378 DISALLOW_COPY_AND_ASSIGN(VersionTest);
381 } // namespace
383 DiagnosticsTest* MakeConflictingDllsTest() { return new ConflictingDllsTest(); }
385 DiagnosticsTest* MakeDiskSpaceTest() { return new DiskSpaceTest(); }
387 DiagnosticsTest* MakeInstallTypeTest() { return new InstallTypeTest(); }
389 DiagnosticsTest* MakeBookMarksTest() {
390 base::FilePath path = DiagnosticsTest::GetUserDefaultProfileDir();
391 path = path.Append(bookmarks::kBookmarksFileName);
392 return new JSONTest(path,
393 DIAGNOSTICS_JSON_BOOKMARKS_TEST,
394 2 * kOneMegabyte,
395 JSONTest::NON_CRITICAL);
398 DiagnosticsTest* MakeLocalStateTest() {
399 base::FilePath path;
400 PathService::Get(chrome::DIR_USER_DATA, &path);
401 path = path.Append(chrome::kLocalStateFilename);
402 return new JSONTest(path,
403 DIAGNOSTICS_JSON_LOCAL_STATE_TEST,
404 50 * kOneKilobyte,
405 JSONTest::CRITICAL);
408 DiagnosticsTest* MakePreferencesTest() {
409 base::FilePath path = DiagnosticsTest::GetUserDefaultProfileDir();
410 path = path.Append(chrome::kPreferencesFilename);
411 return new JSONTest(path,
412 DIAGNOSTICS_JSON_PREFERENCES_TEST,
413 100 * kOneKilobyte,
414 JSONTest::CRITICAL);
418 DiagnosticsTest* MakeOperatingSystemTest() { return new OperatingSystemTest(); }
420 DiagnosticsTest* MakeDictonaryDirTest() {
421 return new PathTest(kPathsToTest[0]);
424 DiagnosticsTest* MakeLocalStateFileTest() {
425 return new PathTest(kPathsToTest[1]);
428 DiagnosticsTest* MakeResourcesFileTest() {
429 return new PathTest(kPathsToTest[2]);
432 DiagnosticsTest* MakeUserDirTest() { return new PathTest(kPathsToTest[3]); }
434 DiagnosticsTest* MakeVersionTest() { return new VersionTest(); }
436 } // namespace diagnostics