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"
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"
25 #include "base/win/windows_version.h"
26 #include "chrome/browser/enumerate_modules_model_win.h"
27 #include "chrome/installer/util/install_util.h"
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.
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
38 namespace diagnostics
{
42 const int64 kOneKilobyte
= 1024;
43 const int64 kOneMegabyte
= 1024 * kOneKilobyte
;
45 class InstallTypeTest
;
46 InstallTypeTest
* g_install_type
= 0;
48 // Check if any conflicting DLLs are loaded.
49 class ConflictingDllsTest
: public DiagnosticsTest
{
52 : DiagnosticsTest(DIAGNOSTICS_CONFLICTING_DLLS_TEST
) {}
54 bool ExecuteImpl(DiagnosticsModel::Observer
* observer
) override
{
56 EnumerateModulesModel
* model
= EnumerateModulesModel::GetInstance();
57 model
->set_limited_mode(true);
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");
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");
75 if (!dictionary
->GetInteger("status", &status
))
76 RecordFailure(DIAG_RECON_NO_STATUS_FIELD
, "No 'status' field found");
77 if (status
< ModuleEnumerator::SUSPECTED_BAD
)
80 if (!dictionary
->GetString("location", &location
)) {
81 RecordFailure(DIAG_RECON_NO_LOCATION_FIELD
,
82 "No 'location' field found");
85 if (!dictionary
->GetString("name", &name
)) {
86 RecordFailure(DIAG_RECON_NO_NAME_FIELD
, "No 'name' field found");
90 failures
+= "\n" + location
+ name
;
92 RecordFailure(DIAG_RECON_CONFLICTING_MODULES
, failures
);
95 RecordFailure(DIAG_RECON_NOT_IMPLEMENTED
, "Not implemented");
97 #endif // defined(OS_WIN)
101 DISALLOW_COPY_AND_ASSIGN(ConflictingDllsTest
);
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
{
108 DiskSpaceTest() : DiagnosticsTest(DIAGNOSTICS_DISK_SPACE_TEST
) {}
110 bool ExecuteImpl(DiagnosticsModel::Observer
* observer
) override
{
111 base::FilePath data_dir
;
112 if (!PathService::Get(chrome::DIR_USER_DATA
, &data_dir
))
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");
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
);
125 RecordSuccess("Free space: " + printable_size
);
130 DISALLOW_COPY_AND_ASSIGN(DiskSpaceTest
);
133 // Check if it is system install or per-user install.
134 class InstallTypeTest
: public DiagnosticsTest
{
137 : DiagnosticsTest(DIAGNOSTICS_INSTALL_TYPE_TEST
), user_level_(false) {}
139 bool ExecuteImpl(DiagnosticsModel::Observer
* observer
) override
{
141 base::FilePath chrome_exe
;
142 if (!PathService::Get(base::FILE_EXE
, &chrome_exe
)) {
143 RecordFailure(DIAG_RECON_INSTALL_PATH_PROVIDER
, "Path provider failure");
146 user_level_
= InstallUtil::IsPerUserInstall(chrome_exe
);
147 const char* type
= user_level_
? "User Level" : "System Level";
148 std::string
install_type(type
);
150 std::string
install_type("System Level");
151 #endif // defined(OS_WIN)
152 RecordSuccess(install_type
);
153 g_install_type
= this;
157 bool system_level() const { return !user_level_
; }
161 DISALLOW_COPY_AND_ASSIGN(InstallTypeTest
);
164 // Checks that a given JSON file can be correctly parsed.
165 class JSONTest
: public DiagnosticsTest
{
167 enum FileImportance
{
172 JSONTest(const base::FilePath
& path
,
173 DiagnosticsTestId id
,
175 FileImportance importance
)
176 : DiagnosticsTest(id
),
178 max_file_size_(max_file_size
),
179 importance_(importance
) {}
181 bool ExecuteImpl(DiagnosticsModel::Observer
* observer
) override
{
182 if (!base::PathExists(path_
)) {
183 if (importance_
== CRITICAL
) {
184 RecordOutcome(DIAG_RECON_FILE_NOT_FOUND
,
186 DiagnosticsModel::TEST_FAIL_CONTINUE
);
188 RecordOutcome(DIAG_RECON_FILE_NOT_FOUND_OK
,
189 "File not found (but that is OK)",
190 DiagnosticsModel::TEST_OK
);
195 if (!base::GetFileSize(path_
, &file_size
)) {
196 RecordFailure(DIAG_RECON_CANNOT_OBTAIN_FILE_SIZE
,
197 "Cannot obtain file size");
201 if (file_size
> max_file_size_
) {
202 RecordFailure(DIAG_RECON_FILE_TOO_BIG
, "File too big");
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");
213 JSONStringValueDeserializer
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
);
222 RecordFailure(DIAG_RECON_PARSE_ERROR
, error_message
);
226 RecordSuccess("File parsed OK");
231 base::FilePath path_
;
232 int64 max_file_size_
;
233 FileImportance importance_
;
234 DISALLOW_COPY_AND_ASSIGN(JSONTest
);
237 // Check that the flavor of the operating system is supported.
238 class OperatingSystemTest
: public DiagnosticsTest
{
240 OperatingSystemTest()
241 : DiagnosticsTest(DIAGNOSTICS_OPERATING_SYSTEM_TEST
) {}
243 bool ExecuteImpl(DiagnosticsModel::Observer
* observer
) override
{
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");
254 // TODO(port): define the OS criteria for Linux and Mac.
255 #endif // defined(OS_WIN)
257 base::StringPrintf("%s %s",
258 base::SysInfo::OperatingSystemName().c_str(),
259 base::SysInfo::OperatingSystemVersion().c_str()));
264 DISALLOW_COPY_AND_ASSIGN(OperatingSystemTest
);
267 struct TestPathInfo
{
268 DiagnosticsTestId test_id
;
276 const TestPathInfo kPathsToTest
[] = {
277 {DIAGNOSTICS_PATH_DICTIONARIES_TEST
, chrome::DIR_APP_DICTIONARIES
, true,
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,
283 {DIAGNOSTICS_PATH_USER_DATA_TEST
, chrome::DIR_USER_DATA
, true, false, true,
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
{
292 explicit PathTest(const TestPathInfo
& path_info
)
293 : DiagnosticsTest(path_info
.test_id
),
294 path_info_(path_info
) {}
296 bool ExecuteImpl(DiagnosticsModel::Observer
* observer
) override
{
297 if (!g_install_type
) {
298 RecordStopFailure(DIAG_RECON_DEPENDENCY
, "Install dependency failure");
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");
306 if (!base::PathExists(dir_or_file
)) {
308 DIAG_RECON_PATH_NOT_FOUND
,
310 base::UTF16ToUTF8(dir_or_file
.LossyDisplayName()));
314 int64 dir_or_file_size
= 0;
315 if (path_info_
.is_directory
) {
316 dir_or_file_size
= base::ComputeDirectorySize(dir_or_file
);
318 base::GetFileSize(dir_or_file
, &dir_or_file_size
);
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()));
326 std::string printable_size
= base::Int64ToString(dir_or_file_size
);
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()));
336 if (g_install_type
->system_level() && !path_info_
.test_writable
) {
337 RecordSuccess("Path exists");
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()));
346 RecordSuccess("Path exists and is writable: " + printable_size
);
351 TestPathInfo path_info_
;
352 DISALLOW_COPY_AND_ASSIGN(PathTest
);
355 // Check the version of Chrome.
356 class VersionTest
: public DiagnosticsTest
{
358 VersionTest() : DiagnosticsTest(DIAGNOSTICS_VERSION_TEST
) {}
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");
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
);
379 DISALLOW_COPY_AND_ASSIGN(VersionTest
);
384 DiagnosticsTest
* MakeConflictingDllsTest() { return new ConflictingDllsTest(); }
386 DiagnosticsTest
* MakeDiskSpaceTest() { return new DiskSpaceTest(); }
388 DiagnosticsTest
* MakeInstallTypeTest() { return new InstallTypeTest(); }
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
,
396 JSONTest::NON_CRITICAL
);
399 DiagnosticsTest
* MakeLocalStateTest() {
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
,
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
,
419 DiagnosticsTest
* MakeOperatingSystemTest() { return new OperatingSystemTest(); }
421 DiagnosticsTest
* MakeDictonaryDirTest() {
422 return new PathTest(kPathsToTest
[0]);
425 DiagnosticsTest
* MakeLocalStateFileTest() {
426 return new PathTest(kPathsToTest
[1]);
429 DiagnosticsTest
* MakeResourcesFileTest() {
430 return new PathTest(kPathsToTest
[2]);
433 DiagnosticsTest
* MakeUserDirTest() { return new PathTest(kPathsToTest
[3]); }
435 DiagnosticsTest
* MakeVersionTest() { return new VersionTest(); }
437 } // namespace diagnostics