- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / diagnostics / recon_diagnostics.cc
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.
4
5 #include "chrome/browser/diagnostics/recon_diagnostics.h"
6
7 #include <string>
8
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"
22
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
28
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.
32
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.
36
37 namespace diagnostics {
38
39 namespace {
40
41 const int64 kOneKilobyte = 1024;
42 const int64 kOneMegabyte = 1024 * kOneKilobyte;
43
44 class InstallTypeTest;
45 InstallTypeTest* g_install_type = 0;
46
47 // Check if any conflicting DLLs are loaded.
48 class ConflictingDllsTest : public DiagnosticsTest {
49  public:
50   ConflictingDllsTest()
51       : DiagnosticsTest(DIAGNOSTICS_CONFLICTING_DLLS_TEST) {}
52
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<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;
63     }
64
65     std::string failures = "Possibly conflicting modules:";
66     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;
78
79       if (!dictionary->GetString("location", &location)) {
80         RecordFailure(DIAG_RECON_NO_LOCATION_FIELD,
81                       "No 'location' field found");
82         return true;
83       }
84       if (!dictionary->GetString("name", &name)) {
85         RecordFailure(DIAG_RECON_NO_NAME_FIELD, "No 'name' field found");
86         return true;
87       }
88
89       failures += "\n" + location + name;
90     }
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)
97   }
98
99  private:
100   DISALLOW_COPY_AND_ASSIGN(ConflictingDllsTest);
101 };
102
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) {}
108
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;
117     }
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;
123     }
124     RecordSuccess("Free space: " + printable_size);
125     return true;
126   }
127
128  private:
129   DISALLOW_COPY_AND_ASSIGN(DiskSpaceTest);
130 };
131
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) {}
137
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;
144     }
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;
154   }
155
156   bool system_level() const { return !user_level_; }
157
158  private:
159   bool user_level_;
160   DISALLOW_COPY_AND_ASSIGN(InstallTypeTest);
161 };
162
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
169   };
170
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) {}
179
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);
190       }
191       return true;
192     }
193     int64 file_size;
194     if (!file_util::GetFileSize(path_, &file_size)) {
195       RecordFailure(DIAG_RECON_CANNOT_OBTAIN_FILE_SIZE,
196                     "Cannot obtain file size");
197       return true;
198     }
199
200     if (file_size > max_file_size_) {
201       RecordFailure(DIAG_RECON_FILE_TOO_BIG, "File too big");
202       return true;
203     }
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;
210     }
211
212     JSONStringValueSerializer json(json_data);
213     int error_code = base::JSONReader::JSON_NO_ERROR;
214     std::string error_message;
215     scoped_ptr<Value> json_root(json.Deserialize(&error_code, &error_message));
216     if (base::JSONReader::JSON_NO_ERROR != error_code) {
217       if (error_message.empty()) {
218         error_message = "Parse error " + base::IntToString(error_code);
219       }
220       RecordFailure(DIAG_RECON_PARSE_ERROR, error_message);
221       return true;
222     }
223
224     RecordSuccess("File parsed OK");
225     return true;
226   }
227
228  private:
229   base::FilePath path_;
230   int64 max_file_size_;
231   FileImportance importance_;
232   DISALLOW_COPY_AND_ASSIGN(JSONTest);
233 };
234
235 // Check that the flavor of the operating system is supported.
236 class OperatingSystemTest : public DiagnosticsTest {
237  public:
238   OperatingSystemTest()
239       : DiagnosticsTest(DIAGNOSTICS_OPERATING_SYSTEM_TEST) {}
240
241   virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
242 #if defined(OS_WIN)
243     base::win::Version version = base::win::GetVersion();
244     if ((version < base::win::VERSION_XP) ||
245         ((version == base::win::VERSION_XP) &&
246          (base::win::OSInfo::GetInstance()->service_pack().major < 2))) {
247       RecordFailure(DIAG_RECON_PRE_WINDOW_XP_SP2,
248                     "Must have Windows XP SP2 or later");
249       return false;
250     }
251 #else
252 // TODO(port): define the OS criteria for Linux and Mac.
253 #endif  // defined(OS_WIN)
254     RecordSuccess(
255         base::StringPrintf("%s %s",
256                            base::SysInfo::OperatingSystemName().c_str(),
257                            base::SysInfo::OperatingSystemVersion().c_str()));
258     return true;
259   }
260
261  private:
262   DISALLOW_COPY_AND_ASSIGN(OperatingSystemTest);
263 };
264
265 struct TestPathInfo {
266   DiagnosticsTestId test_id;
267   int path_id;
268   bool is_directory;
269   bool is_optional;
270   bool test_writable;
271   int64 max_size;
272 };
273
274 const TestPathInfo kPathsToTest[] = {
275     {DIAGNOSTICS_PATH_DICTIONARIES_TEST, chrome::DIR_APP_DICTIONARIES, true,
276      true, false, 0},
277     {DIAGNOSTICS_PATH_LOCAL_STATE_TEST, chrome::FILE_LOCAL_STATE, false, false,
278      true, 500 * kOneKilobyte},
279     {DIAGNOSTICS_PATH_RESOURCES_TEST, chrome::FILE_RESOURCES_PACK, false, false,
280      false, 0},
281     {DIAGNOSTICS_PATH_USER_DATA_TEST, chrome::DIR_USER_DATA, true, false, true,
282      850 * kOneMegabyte},
283 };
284
285 // Check that the user's data directory exists and the paths are writable.
286 // If it is a system-wide install some paths are not expected to be writable.
287 // This test depends on |InstallTypeTest| having run successfully.
288 class PathTest : public DiagnosticsTest {
289  public:
290   explicit PathTest(const TestPathInfo& path_info)
291       : DiagnosticsTest(path_info.test_id),
292         path_info_(path_info) {}
293
294   virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
295     if (!g_install_type) {
296       RecordStopFailure(DIAG_RECON_DEPENDENCY, "Install dependency failure");
297       return false;
298     }
299     base::FilePath dir_or_file;
300     if (!PathService::Get(path_info_.path_id, &dir_or_file)) {
301       RecordStopFailure(DIAG_RECON_PATH_PROVIDER, "Path provider failure");
302       return false;
303     }
304     if (!base::PathExists(dir_or_file)) {
305       RecordFailure(
306           DIAG_RECON_PATH_NOT_FOUND,
307           "Path not found: " + UTF16ToUTF8(dir_or_file.LossyDisplayName()));
308       return true;
309     }
310
311     int64 dir_or_file_size = 0;
312     if (path_info_.is_directory) {
313       dir_or_file_size = base::ComputeDirectorySize(dir_or_file);
314     } else {
315       file_util::GetFileSize(dir_or_file, &dir_or_file_size);
316     }
317     if (!dir_or_file_size && !path_info_.is_optional) {
318       RecordFailure(DIAG_RECON_CANNOT_OBTAIN_SIZE,
319                     "Cannot obtain size for: " +
320                         UTF16ToUTF8(dir_or_file.LossyDisplayName()));
321       return true;
322     }
323     std::string printable_size = base::Int64ToString(dir_or_file_size);
324
325     if (path_info_.max_size > 0) {
326       if (dir_or_file_size > path_info_.max_size) {
327         RecordFailure(DIAG_RECON_FILE_TOO_LARGE,
328                       "Path contents too large (" + printable_size + ") for: " +
329                           UTF16ToUTF8(dir_or_file.LossyDisplayName()));
330         return true;
331       }
332     }
333     if (g_install_type->system_level() && !path_info_.test_writable) {
334       RecordSuccess("Path exists");
335       return true;
336     }
337     if (!base::PathIsWritable(dir_or_file)) {
338       RecordFailure(DIAG_RECON_NOT_WRITABLE,
339                     "Path is not writable: " +
340                         UTF16ToUTF8(dir_or_file.LossyDisplayName()));
341       return true;
342     }
343     RecordSuccess("Path exists and is writable: " + printable_size);
344     return true;
345   }
346
347  private:
348   TestPathInfo path_info_;
349   DISALLOW_COPY_AND_ASSIGN(PathTest);
350 };
351
352 // Check the version of Chrome.
353 class VersionTest : public DiagnosticsTest {
354  public:
355   VersionTest() : DiagnosticsTest(DIAGNOSTICS_VERSION_TEST) {}
356
357   virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
358     chrome::VersionInfo version_info;
359     if (!version_info.is_valid()) {
360       RecordFailure(DIAG_RECON_NO_VERSION, "No Version");
361       return true;
362     }
363     std::string current_version = version_info.Version();
364     if (current_version.empty()) {
365       RecordFailure(DIAG_RECON_EMPTY_VERSION, "Empty Version");
366       return true;
367     }
368     std::string version_modifier =
369         chrome::VersionInfo::GetVersionStringModifier();
370     if (!version_modifier.empty())
371       current_version += " " + version_modifier;
372 #if defined(GOOGLE_CHROME_BUILD)
373     current_version += " GCB";
374 #endif  // defined(GOOGLE_CHROME_BUILD)
375     RecordSuccess(current_version);
376     return true;
377   }
378
379  private:
380   DISALLOW_COPY_AND_ASSIGN(VersionTest);
381 };
382
383 }  // namespace
384
385 DiagnosticsTest* MakeConflictingDllsTest() { return new ConflictingDllsTest(); }
386
387 DiagnosticsTest* MakeDiskSpaceTest() { return new DiskSpaceTest(); }
388
389 DiagnosticsTest* MakeInstallTypeTest() { return new InstallTypeTest(); }
390
391 DiagnosticsTest* MakeBookMarksTest() {
392   base::FilePath path = DiagnosticsTest::GetUserDefaultProfileDir();
393   path = path.Append(chrome::kBookmarksFileName);
394   return new JSONTest(path,
395                       DIAGNOSTICS_JSON_BOOKMARKS_TEST,
396                       2 * kOneMegabyte,
397                       JSONTest::NON_CRITICAL);
398 }
399
400 DiagnosticsTest* MakeLocalStateTest() {
401   base::FilePath path;
402   PathService::Get(chrome::DIR_USER_DATA, &path);
403   path = path.Append(chrome::kLocalStateFilename);
404   return new JSONTest(path,
405                       DIAGNOSTICS_JSON_LOCAL_STATE_TEST,
406                       50 * kOneKilobyte,
407                       JSONTest::CRITICAL);
408 }
409
410 DiagnosticsTest* MakePreferencesTest() {
411   base::FilePath path = DiagnosticsTest::GetUserDefaultProfileDir();
412   path = path.Append(chrome::kPreferencesFilename);
413   return new JSONTest(path,
414                       DIAGNOSTICS_JSON_PREFERENCES_TEST,
415                       100 * kOneKilobyte,
416                       JSONTest::CRITICAL);
417 }
418
419
420 DiagnosticsTest* MakeOperatingSystemTest() { return new OperatingSystemTest(); }
421
422 DiagnosticsTest* MakeDictonaryDirTest() {
423   return new PathTest(kPathsToTest[0]);
424 }
425
426 DiagnosticsTest* MakeLocalStateFileTest() {
427   return new PathTest(kPathsToTest[1]);
428 }
429
430 DiagnosticsTest* MakeResourcesFileTest() {
431   return new PathTest(kPathsToTest[2]);
432 }
433
434 DiagnosticsTest* MakeUserDirTest() { return new PathTest(kPathsToTest[3]); }
435
436 DiagnosticsTest* MakeVersionTest() { return new VersionTest(); }
437
438 }  // namespace diagnostics