• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/string_util.h"
12 #include "base/string_number_conversions.h"
13 #include "base/utf_string_conversions.h"
14 #include "base/sys_info.h"
15 #include "base/path_service.h"
16 #include "chrome/browser/diagnostics/diagnostics_test.h"
17 #include "chrome/browser/platform_util.h"
18 #include "chrome/common/chrome_constants.h"
19 #include "chrome/common/chrome_paths.h"
20 #include "chrome/common/chrome_version_info.h"
21 #include "content/common/json_value_serializer.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 hardcoded in this file
34 // that have little or no theoretical or experimental ground. Find a way
35 // to justify them.
36 
37 namespace {
38 
39 class InstallTypeTest;
40 InstallTypeTest* g_install_type = 0;
41 
42 // Check that the flavor of the operating system is supported.
43 class OperatingSystemTest : public DiagnosticTest {
44  public:
OperatingSystemTest()45   OperatingSystemTest() : DiagnosticTest(ASCIIToUTF16("Operating System")) {}
46 
GetId()47   virtual int GetId() { return 0; }
48 
ExecuteImpl(DiagnosticsModel::Observer * observer)49   virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) {
50 #if defined(OS_WIN)
51     base::win::Version version = base::win::GetVersion();
52     if ((version < base::win::VERSION_XP) ||
53         ((version == base::win::VERSION_XP) &&
54          (base::win::OSInfo::GetInstance()->service_pack().major < 2))) {
55       RecordFailure(ASCIIToUTF16("Must have Windows XP SP2 or later"));
56       return false;
57     }
58 #else
59     // TODO(port): define the OS criteria for Linux and Mac.
60 #endif  // defined(OS_WIN)
61     RecordSuccess(ASCIIToUTF16(StringPrintf("%s %s",
62         base::SysInfo::OperatingSystemName().c_str(),
63         base::SysInfo::OperatingSystemVersion().c_str())));
64     return true;
65   }
66 
67  private:
68   DISALLOW_COPY_AND_ASSIGN(OperatingSystemTest);
69 };
70 
71 // Check if any conflicting DLLs are loaded.
72 class ConflictingDllsTest : public DiagnosticTest {
73  public:
ConflictingDllsTest()74   ConflictingDllsTest() : DiagnosticTest(ASCIIToUTF16("Conflicting modules")) {}
75 
GetId()76   virtual int GetId() { return 0; }
77 
ExecuteImpl(DiagnosticsModel::Observer * observer)78   virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) {
79 #if defined(OS_WIN)
80     EnumerateModulesModel* model = EnumerateModulesModel::GetInstance();
81     model->set_limited_mode(true);
82     model->ScanNow();
83     ListValue* list = model->GetModuleList();
84     if (!model->confirmed_bad_modules_detected() &&
85         !model->suspected_bad_modules_detected()) {
86       RecordSuccess(ASCIIToUTF16("No conflicting modules found"));
87       return true;
88     }
89 
90     string16 failures = ASCIIToUTF16("Possibly conflicting modules:");
91     DictionaryValue* dictionary;
92     for (size_t i = 0; i < list->GetSize(); ++i) {
93       if (!list->GetDictionary(i, &dictionary))
94         RecordFailure(ASCIIToUTF16("Dictionary lookup failed"));
95       int status;
96       string16 location;
97       string16 name;
98       if (!dictionary->GetInteger("status", &status))
99         RecordFailure(ASCIIToUTF16("No 'status' field found"));
100       if (status < ModuleEnumerator::SUSPECTED_BAD)
101         continue;
102 
103       if (!dictionary->GetString("location", &location)) {
104         RecordFailure(ASCIIToUTF16("No 'location' field found"));
105         return true;
106       }
107       if (!dictionary->GetString("name", &name)) {
108         RecordFailure(ASCIIToUTF16("No 'name' field found"));
109         return true;
110       }
111 
112       failures += ASCIIToUTF16("\n") + location + name;
113     }
114     RecordFailure(failures);
115     return true;
116 #else
117     RecordFailure(ASCIIToUTF16("Not implemented"));
118     return true;
119 #endif  // defined(OS_WIN)
120   }
121 
122  private:
123   DISALLOW_COPY_AND_ASSIGN(ConflictingDllsTest);
124 };
125 
126 // Check if it is system install or per-user install.
127 class InstallTypeTest : public DiagnosticTest {
128  public:
InstallTypeTest()129   InstallTypeTest() : DiagnosticTest(ASCIIToUTF16("Install Type")),
130                       user_level_(false) {}
131 
GetId()132   virtual int GetId() { return 0; }
133 
ExecuteImpl(DiagnosticsModel::Observer * observer)134   virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) {
135 #if defined(OS_WIN)
136     FilePath chrome_exe;
137     if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
138       RecordFailure(ASCIIToUTF16("Path provider failure"));
139       return false;
140     }
141     user_level_ = InstallUtil::IsPerUserInstall(chrome_exe.value().c_str());
142     const char* type = user_level_ ? "User Level" : "System Level";
143     string16 install_type(ASCIIToUTF16(type));
144 #else
145     string16 install_type(ASCIIToUTF16("System Level"));
146 #endif  // defined(OS_WIN)
147     RecordSuccess(install_type);
148     g_install_type = this;
149     return true;
150   }
151 
system_level() const152   bool system_level() const { return !user_level_; }
153 
154  private:
155   bool user_level_;
156   DISALLOW_COPY_AND_ASSIGN(InstallTypeTest);
157 };
158 
159 // Check the version of Chrome.
160 class VersionTest : public DiagnosticTest {
161  public:
VersionTest()162   VersionTest() : DiagnosticTest(ASCIIToUTF16("Browser Version")) {}
163 
GetId()164   virtual int GetId() { return 0; }
165 
ExecuteImpl(DiagnosticsModel::Observer * observer)166   virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) {
167     chrome::VersionInfo version_info;
168     if (!version_info.is_valid()) {
169       RecordFailure(ASCIIToUTF16("No Version"));
170       return true;
171     }
172     std::string current_version = version_info.Version();
173     if (current_version.empty()) {
174       RecordFailure(ASCIIToUTF16("Empty Version"));
175       return true;
176     }
177     std::string version_modifier = platform_util::GetVersionStringModifier();
178     if (!version_modifier.empty())
179       current_version += " " + version_modifier;
180 #if defined(GOOGLE_CHROME_BUILD)
181     current_version += " GCB";
182 #endif  // defined(GOOGLE_CHROME_BUILD)
183     RecordSuccess(ASCIIToUTF16(current_version));
184     return true;
185   }
186 
187  private:
188   DISALLOW_COPY_AND_ASSIGN(VersionTest);
189 };
190 
191 struct TestPathInfo {
192   const char* test_name;
193   int  path_id;
194   bool is_directory;
195   bool is_optional;
196   bool test_writable;
197   int64 max_size;
198 };
199 
200 const int64 kOneKilo = 1024;
201 const int64 kOneMeg = 1024 * kOneKilo;
202 
203 const TestPathInfo kPathsToTest[] = {
204   {"User data Directory", chrome::DIR_USER_DATA,
205       true, false, true, 850 * kOneMeg},
206   {"Local state file", chrome::FILE_LOCAL_STATE,
207       false, false, true, 500 * kOneKilo},
208   {"Dictionaries Directory", chrome::DIR_APP_DICTIONARIES,
209       true, true, false, 0},
210   {"Resources file", chrome::FILE_RESOURCES_PACK,
211       false, false, false, 0}
212 };
213 
214 // Check that the user's data directory exists and the paths are writable.
215 // If it is a systemwide install some paths are not expected to be writable.
216 // This test depends on |InstallTypeTest| having run successfully.
217 class PathTest : public DiagnosticTest {
218  public:
PathTest(const TestPathInfo & path_info)219   explicit PathTest(const TestPathInfo& path_info)
220       : DiagnosticTest(ASCIIToUTF16(path_info.test_name)),
221         path_info_(path_info) {}
222 
GetId()223   virtual int GetId() { return 0; }
224 
ExecuteImpl(DiagnosticsModel::Observer * observer)225   virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) {
226     if (!g_install_type) {
227       RecordStopFailure(ASCIIToUTF16("dependency failure"));
228       return false;
229     }
230     FilePath dir_or_file;
231     if (!PathService::Get(path_info_.path_id, &dir_or_file)) {
232       RecordStopFailure(ASCIIToUTF16("Path provider failure"));
233       return false;
234     }
235     if (!file_util::PathExists(dir_or_file)) {
236       RecordFailure(ASCIIToUTF16("Path not found: ") +
237                     dir_or_file.LossyDisplayName());
238       return true;
239     }
240 
241     int64 dir_or_file_size = 0;
242     if (path_info_.is_directory) {
243       dir_or_file_size = file_util::ComputeDirectorySize(dir_or_file);
244     } else {
245       file_util::GetFileSize(dir_or_file, &dir_or_file_size);
246     }
247     if (!dir_or_file_size && !path_info_.is_optional) {
248       RecordFailure(ASCIIToUTF16("Cannot obtain size for: ") +
249                     dir_or_file.LossyDisplayName());
250       return true;
251     }
252     DataUnits units = GetByteDisplayUnits(dir_or_file_size);
253     string16 printable_size = FormatBytes(dir_or_file_size, units, true);
254 
255     if (path_info_.max_size > 0) {
256       if (dir_or_file_size > path_info_.max_size) {
257         RecordFailure(ASCIIToUTF16("Path contents too large (") +
258                       printable_size +
259                       ASCIIToUTF16(") for: ") +
260                       dir_or_file.LossyDisplayName());
261         return true;
262       }
263     }
264     if (g_install_type->system_level() && !path_info_.test_writable) {
265       RecordSuccess(ASCIIToUTF16("Path exists"));
266       return true;
267     }
268     if (!file_util::PathIsWritable(dir_or_file)) {
269       RecordFailure(ASCIIToUTF16("Path is not writable: ") +
270                     dir_or_file.LossyDisplayName());
271       return true;
272     }
273     RecordSuccess(ASCIIToUTF16("Path exists and is writable: ")
274                   + printable_size);
275     return true;
276   }
277 
278  private:
279   TestPathInfo path_info_;
280   DISALLOW_COPY_AND_ASSIGN(PathTest);
281 };
282 
283 // Check that the disk space in the volume where the user data dir normally
284 // lives is not dangerously low.
285 class DiskSpaceTest : public DiagnosticTest {
286  public:
DiskSpaceTest()287   DiskSpaceTest() : DiagnosticTest(ASCIIToUTF16("Disk Space")) {}
288 
GetId()289   virtual int GetId() { return 0; }
290 
ExecuteImpl(DiagnosticsModel::Observer * observer)291   virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) {
292     FilePath data_dir;
293     if (!PathService::Get(chrome::DIR_USER_DATA, &data_dir))
294       return false;
295     int64 disk_space = base::SysInfo::AmountOfFreeDiskSpace(data_dir);
296     if (disk_space < 0) {
297       RecordFailure(ASCIIToUTF16("Unable to query free space"));
298       return true;
299     }
300     DataUnits units = GetByteDisplayUnits(disk_space);
301     string16 printable_size = FormatBytes(disk_space, units, true);
302     if (disk_space < 80 * kOneMeg) {
303       RecordFailure(ASCIIToUTF16("Low disk space : ") + printable_size);
304       return true;
305     }
306     RecordSuccess(ASCIIToUTF16("Free space : ") + printable_size);
307     return true;
308   }
309 
310  private:
311   DISALLOW_COPY_AND_ASSIGN(DiskSpaceTest);
312 };
313 
314 // Checks that a given json file can be correctly parsed.
315 class JSONTest : public DiagnosticTest {
316  public:
JSONTest(const FilePath & path,const string16 & name,int64 max_file_size)317   JSONTest(const FilePath& path, const string16& name, int64 max_file_size)
318       : DiagnosticTest(name), path_(path), max_file_size_(max_file_size) {
319   }
320 
GetId()321   virtual int GetId() { return 0; }
322 
ExecuteImpl(DiagnosticsModel::Observer * observer)323   virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) {
324     if (!file_util::PathExists(path_)) {
325       RecordFailure(ASCIIToUTF16("File not found"));
326       return true;
327     }
328     int64 file_size;
329     if (!file_util::GetFileSize(path_, &file_size)) {
330       RecordFailure(ASCIIToUTF16("Cannot obtain file size"));
331       return true;
332     }
333 
334     if (file_size > max_file_size_) {
335       RecordFailure(ASCIIToUTF16("File too big"));
336       return true;
337     }
338     // Being small enough, we can process it in-memory.
339     std::string json_data;
340     if (!file_util::ReadFileToString(path_, &json_data)) {
341       RecordFailure(ASCIIToUTF16(
342           "Could not open file. Possibly locked by other process"));
343       return true;
344     }
345 
346     JSONStringValueSerializer json(json_data);
347     int error_code = base::JSONReader::JSON_NO_ERROR;
348     std::string error_message;
349     scoped_ptr<Value> json_root(json.Deserialize(&error_code, &error_message));
350     if (base::JSONReader::JSON_NO_ERROR != error_code) {
351       if (error_message.empty()) {
352         error_message = "Parse error " + base::IntToString(error_code);
353       }
354       RecordFailure(UTF8ToUTF16(error_message));
355       return true;
356     }
357 
358     RecordSuccess(ASCIIToUTF16("File parsed OK"));
359     return true;
360   }
361 
362  private:
363   FilePath path_;
364   int64 max_file_size_;
365   DISALLOW_COPY_AND_ASSIGN(JSONTest);
366 };
367 
368 }  // namespace
369 
MakeUserDirTest()370 DiagnosticTest* MakeUserDirTest() {
371   return new PathTest(kPathsToTest[0]);
372 }
373 
MakeLocalStateFileTest()374 DiagnosticTest* MakeLocalStateFileTest() {
375   return new PathTest(kPathsToTest[1]);
376 }
377 
MakeDictonaryDirTest()378 DiagnosticTest* MakeDictonaryDirTest() {
379   return new PathTest(kPathsToTest[2]);
380 }
381 
MakeResourcesFileTest()382 DiagnosticTest* MakeResourcesFileTest() {
383   return new PathTest(kPathsToTest[3]);
384 }
385 
MakeVersionTest()386 DiagnosticTest* MakeVersionTest() {
387   return new VersionTest();
388 }
389 
MakeDiskSpaceTest()390 DiagnosticTest* MakeDiskSpaceTest() {
391   return new DiskSpaceTest();
392 }
393 
MakeOperatingSystemTest()394 DiagnosticTest* MakeOperatingSystemTest() {
395   return new OperatingSystemTest();
396 }
397 
MakeConflictingDllsTest()398 DiagnosticTest* MakeConflictingDllsTest() {
399   return new ConflictingDllsTest();
400 }
401 
MakeInstallTypeTest()402 DiagnosticTest* MakeInstallTypeTest() {
403   return new InstallTypeTest();
404 }
405 
MakePreferencesTest()406 DiagnosticTest* MakePreferencesTest() {
407   FilePath path = DiagnosticTest::GetUserDefaultProfileDir();
408   path = path.Append(chrome::kPreferencesFilename);
409   return new JSONTest(path, ASCIIToUTF16("Profile JSON"), 100 * kOneKilo);
410 }
411 
MakeBookMarksTest()412 DiagnosticTest* MakeBookMarksTest() {
413   FilePath path = DiagnosticTest::GetUserDefaultProfileDir();
414   path = path.Append(chrome::kBookmarksFileName);
415   return new JSONTest(path, ASCIIToUTF16("BookMarks JSON"), 2 * kOneMeg);
416 }
417 
MakeLocalStateTest()418 DiagnosticTest* MakeLocalStateTest() {
419   FilePath path;
420   PathService::Get(chrome::DIR_USER_DATA, &path);
421   path = path.Append(chrome::kLocalStateFilename);
422   return new JSONTest(path, ASCIIToUTF16("Local State JSON"), 50 * kOneKilo);
423 }
424