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/channel_info.h"
20 #include "chrome/common/chrome_constants.h"
21 #include "chrome/common/chrome_paths.h"
22 #include "components/bookmarks/common/bookmark_constants.h"
23 #include "components/version_info/version_info.h"
26 #include "base/win/windows_version.h"
27 #include "chrome/browser/enumerate_modules_model_win.h"
28 #include "chrome/installer/util/install_util.h"
31 // Reconnaissance diagnostics. These are the first and most critical
32 // diagnostic tests. Here we check for the existence of critical files.
33 // TODO(cpu): Define if it makes sense to localize strings.
35 // TODO(cpu): There are a few maximum file sizes hard-coded in this file
36 // that have little or no theoretical or experimental ground. Find a way
39 namespace diagnostics
{
43 const int64 kOneKilobyte
= 1024;
44 const int64 kOneMegabyte
= 1024 * kOneKilobyte
;
46 class InstallTypeTest
;
47 InstallTypeTest
* g_install_type
= 0;
49 // Check if any conflicting DLLs are loaded.
50 class ConflictingDllsTest
: public DiagnosticsTest
{
53 : DiagnosticsTest(DIAGNOSTICS_CONFLICTING_DLLS_TEST
) {}
55 bool ExecuteImpl(DiagnosticsModel::Observer
* observer
) override
{
57 EnumerateModulesModel
* model
= EnumerateModulesModel::GetInstance();
58 model
->set_limited_mode(true);
60 scoped_ptr
<base::ListValue
> list(model
->GetModuleList());
61 if (!model
->confirmed_bad_modules_detected() &&
62 !model
->suspected_bad_modules_detected()) {
63 RecordSuccess("No conflicting modules found");
67 std::string failures
= "Possibly conflicting modules:";
68 base::DictionaryValue
* dictionary
;
69 for (size_t i
= 0; i
< list
->GetSize(); ++i
) {
70 if (!list
->GetDictionary(i
, &dictionary
))
71 RecordFailure(DIAG_RECON_DICTIONARY_LOOKUP_FAILED
,
72 "Dictionary lookup failed");
76 if (!dictionary
->GetInteger("status", &status
))
77 RecordFailure(DIAG_RECON_NO_STATUS_FIELD
, "No 'status' field found");
78 if (status
< ModuleEnumerator::SUSPECTED_BAD
)
81 if (!dictionary
->GetString("location", &location
)) {
82 RecordFailure(DIAG_RECON_NO_LOCATION_FIELD
,
83 "No 'location' field found");
86 if (!dictionary
->GetString("name", &name
)) {
87 RecordFailure(DIAG_RECON_NO_NAME_FIELD
, "No 'name' field found");
91 failures
+= "\n" + location
+ name
;
93 RecordFailure(DIAG_RECON_CONFLICTING_MODULES
, failures
);
96 RecordFailure(DIAG_RECON_NOT_IMPLEMENTED
, "Not implemented");
98 #endif // defined(OS_WIN)
102 DISALLOW_COPY_AND_ASSIGN(ConflictingDllsTest
);
105 // Check that the disk space in the volume where the user data directory
106 // normally lives is not dangerously low.
107 class DiskSpaceTest
: public DiagnosticsTest
{
109 DiskSpaceTest() : DiagnosticsTest(DIAGNOSTICS_DISK_SPACE_TEST
) {}
111 bool ExecuteImpl(DiagnosticsModel::Observer
* observer
) override
{
112 base::FilePath data_dir
;
113 if (!PathService::Get(chrome::DIR_USER_DATA
, &data_dir
))
115 int64 disk_space
= base::SysInfo::AmountOfFreeDiskSpace(data_dir
);
116 if (disk_space
< 0) {
117 RecordFailure(DIAG_RECON_UNABLE_TO_QUERY
, "Unable to query free space");
120 std::string printable_size
= base::Int64ToString(disk_space
);
121 if (disk_space
< 80 * kOneMegabyte
) {
122 RecordFailure(DIAG_RECON_LOW_DISK_SPACE
,
123 "Low disk space: " + printable_size
);
126 RecordSuccess("Free space: " + printable_size
);
131 DISALLOW_COPY_AND_ASSIGN(DiskSpaceTest
);
134 // Check if it is system install or per-user install.
135 class InstallTypeTest
: public DiagnosticsTest
{
138 : DiagnosticsTest(DIAGNOSTICS_INSTALL_TYPE_TEST
), user_level_(false) {}
140 bool ExecuteImpl(DiagnosticsModel::Observer
* observer
) override
{
142 base::FilePath chrome_exe
;
143 if (!PathService::Get(base::FILE_EXE
, &chrome_exe
)) {
144 RecordFailure(DIAG_RECON_INSTALL_PATH_PROVIDER
, "Path provider failure");
147 user_level_
= InstallUtil::IsPerUserInstall(chrome_exe
);
148 const char* type
= user_level_
? "User Level" : "System Level";
149 std::string
install_type(type
);
151 std::string
install_type("System Level");
152 #endif // defined(OS_WIN)
153 RecordSuccess(install_type
);
154 g_install_type
= this;
158 bool system_level() const { return !user_level_
; }
162 DISALLOW_COPY_AND_ASSIGN(InstallTypeTest
);
165 // Checks that a given JSON file can be correctly parsed.
166 class JSONTest
: public DiagnosticsTest
{
168 enum FileImportance
{
173 JSONTest(const base::FilePath
& path
,
174 DiagnosticsTestId id
,
176 FileImportance importance
)
177 : DiagnosticsTest(id
),
179 max_file_size_(max_file_size
),
180 importance_(importance
) {}
182 bool ExecuteImpl(DiagnosticsModel::Observer
* observer
) override
{
183 if (!base::PathExists(path_
)) {
184 if (importance_
== CRITICAL
) {
185 RecordOutcome(DIAG_RECON_FILE_NOT_FOUND
,
187 DiagnosticsModel::TEST_FAIL_CONTINUE
);
189 RecordOutcome(DIAG_RECON_FILE_NOT_FOUND_OK
,
190 "File not found (but that is OK)",
191 DiagnosticsModel::TEST_OK
);
196 if (!base::GetFileSize(path_
, &file_size
)) {
197 RecordFailure(DIAG_RECON_CANNOT_OBTAIN_FILE_SIZE
,
198 "Cannot obtain file size");
202 if (file_size
> max_file_size_
) {
203 RecordFailure(DIAG_RECON_FILE_TOO_BIG
, "File too big");
206 // Being small enough, we can process it in-memory.
207 std::string json_data
;
208 if (!base::ReadFileToString(path_
, &json_data
)) {
209 RecordFailure(DIAG_RECON_UNABLE_TO_OPEN_FILE
,
210 "Could not open file. Possibly locked by another process");
214 JSONStringValueDeserializer
json(json_data
);
215 int error_code
= base::JSONReader::JSON_NO_ERROR
;
216 std::string error_message
;
217 scoped_ptr
<base::Value
> json_root(
218 json
.Deserialize(&error_code
, &error_message
));
219 if (base::JSONReader::JSON_NO_ERROR
!= error_code
) {
220 if (error_message
.empty()) {
221 error_message
= "Parse error " + base::IntToString(error_code
);
223 RecordFailure(DIAG_RECON_PARSE_ERROR
, error_message
);
227 RecordSuccess("File parsed OK");
232 base::FilePath path_
;
233 int64 max_file_size_
;
234 FileImportance importance_
;
235 DISALLOW_COPY_AND_ASSIGN(JSONTest
);
238 // Check that the flavor of the operating system is supported.
239 class OperatingSystemTest
: public DiagnosticsTest
{
241 OperatingSystemTest()
242 : DiagnosticsTest(DIAGNOSTICS_OPERATING_SYSTEM_TEST
) {}
244 bool ExecuteImpl(DiagnosticsModel::Observer
* observer
) override
{
246 base::win::Version version
= base::win::GetVersion();
247 if ((version
< base::win::VERSION_XP
) ||
248 ((version
== base::win::VERSION_XP
) &&
249 (base::win::OSInfo::GetInstance()->service_pack().major
< 2))) {
250 RecordFailure(DIAG_RECON_PRE_WINDOW_XP_SP2
,
251 "Must have Windows XP SP2 or later");
255 // TODO(port): define the OS criteria for Linux and Mac.
256 #endif // defined(OS_WIN)
258 base::StringPrintf("%s %s",
259 base::SysInfo::OperatingSystemName().c_str(),
260 base::SysInfo::OperatingSystemVersion().c_str()));
265 DISALLOW_COPY_AND_ASSIGN(OperatingSystemTest
);
268 struct TestPathInfo
{
269 DiagnosticsTestId test_id
;
277 const TestPathInfo kPathsToTest
[] = {
278 {DIAGNOSTICS_PATH_DICTIONARIES_TEST
, chrome::DIR_APP_DICTIONARIES
, true,
280 {DIAGNOSTICS_PATH_LOCAL_STATE_TEST
, chrome::FILE_LOCAL_STATE
, false, false,
281 true, 500 * kOneKilobyte
},
282 {DIAGNOSTICS_PATH_RESOURCES_TEST
, chrome::FILE_RESOURCES_PACK
, false, false,
284 {DIAGNOSTICS_PATH_USER_DATA_TEST
, chrome::DIR_USER_DATA
, true, false, true,
288 // Check that the user's data directory exists and the paths are writable.
289 // If it is a system-wide install some paths are not expected to be writable.
290 // This test depends on |InstallTypeTest| having run successfully.
291 class PathTest
: public DiagnosticsTest
{
293 explicit PathTest(const TestPathInfo
& path_info
)
294 : DiagnosticsTest(path_info
.test_id
),
295 path_info_(path_info
) {}
297 bool ExecuteImpl(DiagnosticsModel::Observer
* observer
) override
{
298 if (!g_install_type
) {
299 RecordStopFailure(DIAG_RECON_DEPENDENCY
, "Install dependency failure");
302 base::FilePath dir_or_file
;
303 if (!PathService::Get(path_info_
.path_id
, &dir_or_file
)) {
304 RecordStopFailure(DIAG_RECON_PATH_PROVIDER
, "Path provider failure");
307 if (!base::PathExists(dir_or_file
)) {
309 DIAG_RECON_PATH_NOT_FOUND
,
311 base::UTF16ToUTF8(dir_or_file
.LossyDisplayName()));
315 int64 dir_or_file_size
= 0;
316 if (path_info_
.is_directory
) {
317 dir_or_file_size
= base::ComputeDirectorySize(dir_or_file
);
319 base::GetFileSize(dir_or_file
, &dir_or_file_size
);
321 if (!dir_or_file_size
&& !path_info_
.is_optional
) {
322 RecordFailure(DIAG_RECON_CANNOT_OBTAIN_SIZE
,
323 "Cannot obtain size for: " +
324 base::UTF16ToUTF8(dir_or_file
.LossyDisplayName()));
327 std::string printable_size
= base::Int64ToString(dir_or_file_size
);
329 if (path_info_
.max_size
> 0) {
330 if (dir_or_file_size
> path_info_
.max_size
) {
331 RecordFailure(DIAG_RECON_FILE_TOO_LARGE
,
332 "Path contents too large (" + printable_size
+ ") for: " +
333 base::UTF16ToUTF8(dir_or_file
.LossyDisplayName()));
337 if (g_install_type
->system_level() && !path_info_
.test_writable
) {
338 RecordSuccess("Path exists");
341 if (!base::PathIsWritable(dir_or_file
)) {
342 RecordFailure(DIAG_RECON_NOT_WRITABLE
,
343 "Path is not writable: " +
344 base::UTF16ToUTF8(dir_or_file
.LossyDisplayName()));
347 RecordSuccess("Path exists and is writable: " + printable_size
);
352 TestPathInfo path_info_
;
353 DISALLOW_COPY_AND_ASSIGN(PathTest
);
356 // Check the version of Chrome.
357 class VersionTest
: public DiagnosticsTest
{
359 VersionTest() : DiagnosticsTest(DIAGNOSTICS_VERSION_TEST
) {}
361 bool ExecuteImpl(DiagnosticsModel::Observer
* observer
) override
{
362 std::string current_version
= version_info::GetVersionNumber();
363 if (current_version
.empty()) {
364 RecordFailure(DIAG_RECON_EMPTY_VERSION
, "Empty Version");
367 std::string version_modifier
= chrome::GetChannelString();
368 if (!version_modifier
.empty())
369 current_version
+= " " + version_modifier
;
370 #if defined(GOOGLE_CHROME_BUILD)
371 current_version
+= " GCB";
372 #endif // defined(GOOGLE_CHROME_BUILD)
373 RecordSuccess(current_version
);
378 DISALLOW_COPY_AND_ASSIGN(VersionTest
);
383 DiagnosticsTest
* MakeConflictingDllsTest() { return new ConflictingDllsTest(); }
385 DiagnosticsTest
* MakeDiskSpaceTest() { return new DiskSpaceTest(); }
387 DiagnosticsTest
* MakeInstallTypeTest() { return new InstallTypeTest(); }
389 DiagnosticsTest
* MakeBookMarksTest() {
390 base::FilePath path
= DiagnosticsTest::GetUserDefaultProfileDir();
391 path
= path
.Append(bookmarks::kBookmarksFileName
);
392 return new JSONTest(path
,
393 DIAGNOSTICS_JSON_BOOKMARKS_TEST
,
395 JSONTest::NON_CRITICAL
);
398 DiagnosticsTest
* MakeLocalStateTest() {
400 PathService::Get(chrome::DIR_USER_DATA
, &path
);
401 path
= path
.Append(chrome::kLocalStateFilename
);
402 return new JSONTest(path
,
403 DIAGNOSTICS_JSON_LOCAL_STATE_TEST
,
408 DiagnosticsTest
* MakePreferencesTest() {
409 base::FilePath path
= DiagnosticsTest::GetUserDefaultProfileDir();
410 path
= path
.Append(chrome::kPreferencesFilename
);
411 return new JSONTest(path
,
412 DIAGNOSTICS_JSON_PREFERENCES_TEST
,
418 DiagnosticsTest
* MakeOperatingSystemTest() { return new OperatingSystemTest(); }
420 DiagnosticsTest
* MakeDictonaryDirTest() {
421 return new PathTest(kPathsToTest
[0]);
424 DiagnosticsTest
* MakeLocalStateFileTest() {
425 return new PathTest(kPathsToTest
[1]);
428 DiagnosticsTest
* MakeResourcesFileTest() {
429 return new PathTest(kPathsToTest
[2]);
432 DiagnosticsTest
* MakeUserDirTest() { return new PathTest(kPathsToTest
[3]); }
434 DiagnosticsTest
* MakeVersionTest() { return new VersionTest(); }
436 } // namespace diagnostics