• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
ConflictingDllsTest()51    ConflictingDllsTest()
52        : DiagnosticsTest(DIAGNOSTICS_CONFLICTING_DLLS_TEST) {}
53  
ExecuteImpl(DiagnosticsModel::Observer * observer)54    virtual 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:
DiskSpaceTest()108    DiskSpaceTest() : DiagnosticsTest(DIAGNOSTICS_DISK_SPACE_TEST) {}
109  
ExecuteImpl(DiagnosticsModel::Observer * observer)110    virtual 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:
InstallTypeTest()136    InstallTypeTest()
137        : DiagnosticsTest(DIAGNOSTICS_INSTALL_TYPE_TEST), user_level_(false) {}
138  
ExecuteImpl(DiagnosticsModel::Observer * observer)139    virtual 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  
system_level() const157    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  
JSONTest(const base::FilePath & path,DiagnosticsTestId id,int64 max_file_size,FileImportance importance)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  
ExecuteImpl(DiagnosticsModel::Observer * observer)181    virtual 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:
OperatingSystemTest()240    OperatingSystemTest()
241        : DiagnosticsTest(DIAGNOSTICS_OPERATING_SYSTEM_TEST) {}
242  
ExecuteImpl(DiagnosticsModel::Observer * observer)243    virtual 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:
PathTest(const TestPathInfo & path_info)292    explicit PathTest(const TestPathInfo& path_info)
293        : DiagnosticsTest(path_info.test_id),
294          path_info_(path_info) {}
295  
ExecuteImpl(DiagnosticsModel::Observer * observer)296    virtual 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:
VersionTest()358    VersionTest() : DiagnosticsTest(DIAGNOSTICS_VERSION_TEST) {}
359  
ExecuteImpl(DiagnosticsModel::Observer * observer)360    virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
361      chrome::VersionInfo version_info;
362      if (!version_info.is_valid()) {
363        RecordFailure(DIAG_RECON_NO_VERSION, "No Version");
364        return true;
365      }
366      std::string current_version = version_info.Version();
367      if (current_version.empty()) {
368        RecordFailure(DIAG_RECON_EMPTY_VERSION, "Empty Version");
369        return true;
370      }
371      std::string version_modifier =
372          chrome::VersionInfo::GetVersionStringModifier();
373      if (!version_modifier.empty())
374        current_version += " " + version_modifier;
375  #if defined(GOOGLE_CHROME_BUILD)
376      current_version += " GCB";
377  #endif  // defined(GOOGLE_CHROME_BUILD)
378      RecordSuccess(current_version);
379      return true;
380    }
381  
382   private:
383    DISALLOW_COPY_AND_ASSIGN(VersionTest);
384  };
385  
386  }  // namespace
387  
MakeConflictingDllsTest()388  DiagnosticsTest* MakeConflictingDllsTest() { return new ConflictingDllsTest(); }
389  
MakeDiskSpaceTest()390  DiagnosticsTest* MakeDiskSpaceTest() { return new DiskSpaceTest(); }
391  
MakeInstallTypeTest()392  DiagnosticsTest* MakeInstallTypeTest() { return new InstallTypeTest(); }
393  
MakeBookMarksTest()394  DiagnosticsTest* MakeBookMarksTest() {
395    base::FilePath path = DiagnosticsTest::GetUserDefaultProfileDir();
396    path = path.Append(bookmarks::kBookmarksFileName);
397    return new JSONTest(path,
398                        DIAGNOSTICS_JSON_BOOKMARKS_TEST,
399                        2 * kOneMegabyte,
400                        JSONTest::NON_CRITICAL);
401  }
402  
MakeLocalStateTest()403  DiagnosticsTest* MakeLocalStateTest() {
404    base::FilePath path;
405    PathService::Get(chrome::DIR_USER_DATA, &path);
406    path = path.Append(chrome::kLocalStateFilename);
407    return new JSONTest(path,
408                        DIAGNOSTICS_JSON_LOCAL_STATE_TEST,
409                        50 * kOneKilobyte,
410                        JSONTest::CRITICAL);
411  }
412  
MakePreferencesTest()413  DiagnosticsTest* MakePreferencesTest() {
414    base::FilePath path = DiagnosticsTest::GetUserDefaultProfileDir();
415    path = path.Append(chrome::kPreferencesFilename);
416    return new JSONTest(path,
417                        DIAGNOSTICS_JSON_PREFERENCES_TEST,
418                        100 * kOneKilobyte,
419                        JSONTest::CRITICAL);
420  }
421  
422  
MakeOperatingSystemTest()423  DiagnosticsTest* MakeOperatingSystemTest() { return new OperatingSystemTest(); }
424  
MakeDictonaryDirTest()425  DiagnosticsTest* MakeDictonaryDirTest() {
426    return new PathTest(kPathsToTest[0]);
427  }
428  
MakeLocalStateFileTest()429  DiagnosticsTest* MakeLocalStateFileTest() {
430    return new PathTest(kPathsToTest[1]);
431  }
432  
MakeResourcesFileTest()433  DiagnosticsTest* MakeResourcesFileTest() {
434    return new PathTest(kPathsToTest[2]);
435  }
436  
MakeUserDirTest()437  DiagnosticsTest* MakeUserDirTest() { return new PathTest(kPathsToTest[3]); }
438  
MakeVersionTest()439  DiagnosticsTest* MakeVersionTest() { return new VersionTest(); }
440  
441  }  // namespace diagnostics
442