Update To 11.40.268.0
[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/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"
23
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
29
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.
33
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.
37
38 namespace diagnostics {
39
40 namespace {
41
42 const int64 kOneKilobyte = 1024;
43 const int64 kOneMegabyte = 1024 * kOneKilobyte;
44
45 class InstallTypeTest;
46 InstallTypeTest* g_install_type = 0;
47
48 // Check if any conflicting DLLs are loaded.
49 class ConflictingDllsTest : public DiagnosticsTest {
50  public:
51   ConflictingDllsTest()
52       : DiagnosticsTest(DIAGNOSTICS_CONFLICTING_DLLS_TEST) {}
53
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;
64     }
65
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;
79
80       if (!dictionary->GetString("location", &location)) {
81         RecordFailure(DIAG_RECON_NO_LOCATION_FIELD,
82                       "No 'location' field found");
83         return true;
84       }
85       if (!dictionary->GetString("name", &name)) {
86         RecordFailure(DIAG_RECON_NO_NAME_FIELD, "No 'name' field found");
87         return true;
88       }
89
90       failures += "\n" + location + name;
91     }
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)
98   }
99
100  private:
101   DISALLOW_COPY_AND_ASSIGN(ConflictingDllsTest);
102 };
103
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) {}
109
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;
118     }
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;
124     }
125     RecordSuccess("Free space: " + printable_size);
126     return true;
127   }
128
129  private:
130   DISALLOW_COPY_AND_ASSIGN(DiskSpaceTest);
131 };
132
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) {}
138
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;
145     }
146     user_level_ = InstallUtil::IsPerUserInstall(chrome_exe.value().c_str());
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;
155   }
156
157   bool system_level() const { return !user_level_; }
158
159  private:
160   bool user_level_;
161   DISALLOW_COPY_AND_ASSIGN(InstallTypeTest);
162 };
163
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
170   };
171
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) {}
180
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);
191       }
192       return true;
193     }
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;
199     }
200
201     if (file_size > max_file_size_) {
202       RecordFailure(DIAG_RECON_FILE_TOO_BIG, "File too big");
203       return true;
204     }
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;
211     }
212
213     JSONStringValueSerializer 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);
221       }
222       RecordFailure(DIAG_RECON_PARSE_ERROR, error_message);
223       return true;
224     }
225
226     RecordSuccess("File parsed OK");
227     return true;
228   }
229
230  private:
231   base::FilePath path_;
232   int64 max_file_size_;
233   FileImportance importance_;
234   DISALLOW_COPY_AND_ASSIGN(JSONTest);
235 };
236
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) {}
242
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;
252     }
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;
261   }
262
263  private:
264   DISALLOW_COPY_AND_ASSIGN(OperatingSystemTest);
265 };
266
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;
274 };
275
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},
285 };
286
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) {}
295
296   bool ExecuteImpl(DiagnosticsModel::Observer* observer) override {
297     if (!g_install_type) {
298       RecordStopFailure(DIAG_RECON_DEPENDENCY, "Install dependency failure");
299       return false;
300     }
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;
305     }
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;
312     }
313
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);
319     }
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;
325     }
326     std::string printable_size = base::Int64ToString(dir_or_file_size);
327
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;
334       }
335     }
336     if (g_install_type->system_level() && !path_info_.test_writable) {
337       RecordSuccess("Path exists");
338       return true;
339     }
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;
345     }
346     RecordSuccess("Path exists and is writable: " + printable_size);
347     return true;
348   }
349
350  private:
351   TestPathInfo path_info_;
352   DISALLOW_COPY_AND_ASSIGN(PathTest);
353 };
354
355 // Check the version of Chrome.
356 class VersionTest : public DiagnosticsTest {
357  public:
358   VersionTest() : DiagnosticsTest(DIAGNOSTICS_VERSION_TEST) {}
359
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;
366     }
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;
376   }
377
378  private:
379   DISALLOW_COPY_AND_ASSIGN(VersionTest);
380 };
381
382 }  // namespace
383
384 DiagnosticsTest* MakeConflictingDllsTest() { return new ConflictingDllsTest(); }
385
386 DiagnosticsTest* MakeDiskSpaceTest() { return new DiskSpaceTest(); }
387
388 DiagnosticsTest* MakeInstallTypeTest() { return new InstallTypeTest(); }
389
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);
397 }
398
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);
407 }
408
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);
416 }
417
418
419 DiagnosticsTest* MakeOperatingSystemTest() { return new OperatingSystemTest(); }
420
421 DiagnosticsTest* MakeDictonaryDirTest() {
422   return new PathTest(kPathsToTest[0]);
423 }
424
425 DiagnosticsTest* MakeLocalStateFileTest() {
426   return new PathTest(kPathsToTest[1]);
427 }
428
429 DiagnosticsTest* MakeResourcesFileTest() {
430   return new PathTest(kPathsToTest[2]);
431 }
432
433 DiagnosticsTest* MakeUserDirTest() { return new PathTest(kPathsToTest[3]); }
434
435 DiagnosticsTest* MakeVersionTest() { return new VersionTest(); }
436
437 }  // namespace diagnostics