recon_diagnostics.cc revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
1// Copyright (c) 2010 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 "chrome/common/json_value_serializer.h" 22 23#if defined(OS_WIN) 24#include "base/win/windows_version.h" 25#include "chrome/installer/util/install_util.h" 26#endif 27 28// Reconnaissance diagnostics. These are the first and most critical 29// diagnostic tests. Here we check for the existence of critical files. 30// TODO(cpu): Define if it makes sense to localize strings. 31 32// TODO(cpu): There are a few maxium file sizes hardcoded in this file 33// that have little or no theoretical or experimental ground. Find a way 34// to justify them. 35 36namespace { 37 38class InstallTypeTest; 39InstallTypeTest* g_install_type = 0; 40 41// Check that the flavor of the operating system is supported. 42class OperatingSystemTest : public DiagnosticTest { 43 public: 44 OperatingSystemTest() : DiagnosticTest(ASCIIToUTF16("Operating System")) {} 45 46 virtual int GetId() { return 0; } 47 48 virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) { 49 int version = 0; 50 int major = 0; 51 int minor = 0; 52#if defined(OS_WIN) 53 version = base::win::GetVersion(); 54 if (version < base::win::VERSION_XP) { 55 RecordFailure(ASCIIToUTF16("Windows 2000 or earlier")); 56 return false; 57 } 58 base::win::GetServicePackLevel(&major, &minor); 59 if ((version == base::win::VERSION_XP) && (major < 2)) { 60 RecordFailure(ASCIIToUTF16("XP Service Pack 1 or earlier")); 61 return false; 62 } 63#else 64 // TODO(port): define the OS criteria for linux and mac. 65#endif // defined(OS_WIN) 66 RecordSuccess(ASCIIToUTF16(StringPrintf("%s %s (%d [%d:%d])", 67 base::SysInfo::OperatingSystemName().c_str(), 68 base::SysInfo::OperatingSystemVersion().c_str(), 69 version, major, minor))); 70 return true; 71 } 72 73 private: 74 DISALLOW_COPY_AND_ASSIGN(OperatingSystemTest); 75}; 76 77// Check if it is system install or per-user install. 78class InstallTypeTest : public DiagnosticTest { 79 public: 80 InstallTypeTest() : DiagnosticTest(ASCIIToUTF16("Install Type")), 81 user_level_(false) {} 82 83 virtual int GetId() { return 0; } 84 85 virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) { 86#if defined(OS_WIN) 87 FilePath chrome_exe; 88 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { 89 RecordFailure(ASCIIToUTF16("Path provider failure")); 90 return false; 91 } 92 user_level_ = InstallUtil::IsPerUserInstall( 93 chrome_exe.ToWStringHack().c_str()); 94 const char* type = user_level_ ? "User Level" : "System Level"; 95 string16 install_type(ASCIIToUTF16(type)); 96#else 97 string16 install_type(ASCIIToUTF16("System Level")); 98#endif // defined(OS_WIN) 99 RecordSuccess(install_type); 100 g_install_type = this; 101 return true; 102 } 103 104 bool system_level() const { return !user_level_; } 105 106 private: 107 bool user_level_; 108 DISALLOW_COPY_AND_ASSIGN(InstallTypeTest); 109}; 110 111// Check the version of Chrome. 112class VersionTest : public DiagnosticTest { 113 public: 114 VersionTest() : DiagnosticTest(ASCIIToUTF16("Browser Version")) {} 115 116 virtual int GetId() { return 0; } 117 118 virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) { 119 chrome::VersionInfo version_info; 120 if (!version_info.is_valid()) { 121 RecordFailure(ASCIIToUTF16("No Version")); 122 return true; 123 } 124 std::string current_version = version_info.Version(); 125 if (current_version.empty()) { 126 RecordFailure(ASCIIToUTF16("Empty Version")); 127 return true; 128 } 129 std::string version_modifier = platform_util::GetVersionStringModifier(); 130 if (!version_modifier.empty()) 131 current_version += " " + version_modifier; 132#if defined(GOOGLE_CHROME_BUILD) 133 current_version += " GCB"; 134#endif // defined(GOOGLE_CHROME_BUILD) 135 RecordSuccess(ASCIIToUTF16(current_version)); 136 return true; 137 } 138 139 private: 140 DISALLOW_COPY_AND_ASSIGN(VersionTest); 141}; 142 143struct TestPathInfo { 144 const char* test_name; 145 int path_id; 146 bool is_directory; 147 bool is_optional; 148 bool test_writable; 149 int64 max_size; 150}; 151 152const int64 kOneKilo = 1024; 153const int64 kOneMeg = 1024 * kOneKilo; 154 155const TestPathInfo kPathsToTest[] = { 156 {"User data Directory", chrome::DIR_USER_DATA, 157 true, false, true, 850 * kOneMeg}, 158 {"Local state file", chrome::FILE_LOCAL_STATE, 159 false, false, true, 500 * kOneKilo}, 160 {"Dictionaries Directory", chrome::DIR_APP_DICTIONARIES, 161 true, true, false, 0}, 162 {"Inspector Directory", chrome::DIR_INSPECTOR, 163 true, false, false, 0} 164}; 165 166// Check that the user's data directory exists and the paths are writeable. 167// If it is a systemwide install some paths are not expected to be writeable. 168// This test depends on |InstallTypeTest| having run succesfuly. 169class PathTest : public DiagnosticTest { 170 public: 171 explicit PathTest(const TestPathInfo& path_info) 172 : DiagnosticTest(ASCIIToUTF16(path_info.test_name)), 173 path_info_(path_info) {} 174 175 virtual int GetId() { return 0; } 176 177 virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) { 178 if (!g_install_type) { 179 RecordStopFailure(ASCIIToUTF16("dependency failure")); 180 return false; 181 } 182 FilePath dir_or_file; 183 if (!PathService::Get(path_info_.path_id, &dir_or_file)) { 184 RecordStopFailure(ASCIIToUTF16("Path provider failure")); 185 return false; 186 } 187 if (!file_util::PathExists(dir_or_file)) { 188 RecordFailure(ASCIIToUTF16("Path not found")); 189 return true; 190 } 191 192 int64 dir_or_file_size = 0; 193 if (path_info_.is_directory) { 194 dir_or_file_size = file_util::ComputeDirectorySize(dir_or_file); 195 } else { 196 file_util::GetFileSize(dir_or_file, &dir_or_file_size); 197 } 198 if (!dir_or_file_size && !path_info_.is_optional) { 199 RecordFailure(ASCIIToUTF16("Cannot obtain size")); 200 return true; 201 } 202 DataUnits units = GetByteDisplayUnits(dir_or_file_size); 203 string16 printable_size = FormatBytes(dir_or_file_size, units, true); 204 205 if (path_info_.max_size > 0) { 206 if (dir_or_file_size > path_info_.max_size) { 207 RecordFailure(ASCIIToUTF16("Path is too big: ") + printable_size); 208 return true; 209 } 210 } 211 if (g_install_type->system_level() && !path_info_.test_writable) { 212 RecordSuccess(ASCIIToUTF16("Path exists")); 213 return true; 214 } 215 if (!file_util::PathIsWritable(dir_or_file)) { 216 RecordFailure(ASCIIToUTF16("Path is not writable")); 217 return true; 218 } 219 RecordSuccess(ASCIIToUTF16("Path exists and is writable: ") 220 + printable_size); 221 return true; 222 } 223 224 private: 225 TestPathInfo path_info_; 226 DISALLOW_COPY_AND_ASSIGN(PathTest); 227}; 228 229// Check that the disk space in the volume where the user data dir normally 230// lives is not dangerosly low. 231class DiskSpaceTest : public DiagnosticTest { 232 public: 233 DiskSpaceTest() : DiagnosticTest(ASCIIToUTF16("Disk Space")) {} 234 235 virtual int GetId() { return 0; } 236 237 virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) { 238 FilePath data_dir; 239 if (!PathService::Get(chrome::DIR_USER_DATA, &data_dir)) 240 return false; 241 int64 disk_space = base::SysInfo::AmountOfFreeDiskSpace(data_dir); 242 if (disk_space < 0) { 243 RecordFailure(ASCIIToUTF16("Unable to query free space")); 244 return true; 245 } 246 DataUnits units = GetByteDisplayUnits(disk_space); 247 string16 printable_size = FormatBytes(disk_space, units, true); 248 if (disk_space < 80 * kOneMeg) { 249 RecordFailure(ASCIIToUTF16("Low disk space : ") + printable_size); 250 return true; 251 } 252 RecordSuccess(ASCIIToUTF16("Free space : ") + printable_size); 253 return true; 254 } 255 256 private: 257 DISALLOW_COPY_AND_ASSIGN(DiskSpaceTest); 258}; 259 260// Checks that a given json file can be correctly parsed. 261class JSONTest : public DiagnosticTest { 262 public: 263 JSONTest(const FilePath& path, const string16 name, int64 max_file_size) 264 : DiagnosticTest(name), path_(path), max_file_size_(max_file_size) { 265 } 266 267 virtual int GetId() { return 0; } 268 269 virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) { 270 if (!file_util::PathExists(path_)) { 271 RecordFailure(ASCIIToUTF16("File not found")); 272 return true; 273 } 274 int64 file_size; 275 file_util::GetFileSize(path_, &file_size); 276 if (file_size > max_file_size_) { 277 RecordFailure(ASCIIToUTF16("File too big")); 278 return true; 279 } 280 // Being small enough, we can process it in-memory. 281 std::string json_data; 282 if (!file_util::ReadFileToString(path_, &json_data)) { 283 RecordFailure(ASCIIToUTF16( 284 "Could not open file. Possibly locked by other process")); 285 return true; 286 } 287 288 JSONStringValueSerializer json(json_data); 289 int error_code = base::JSONReader::JSON_NO_ERROR; 290 std::string error_message; 291 scoped_ptr<Value> json_root(json.Deserialize(&error_code, &error_message)); 292 if (base::JSONReader::JSON_NO_ERROR != error_code) { 293 if (error_message.empty()) { 294 error_message = "Parse error " + base::IntToString(error_code); 295 } 296 RecordFailure(UTF8ToUTF16(error_message)); 297 return true; 298 } 299 300 RecordSuccess(ASCIIToUTF16("File parsed OK")); 301 return true; 302 } 303 304 private: 305 FilePath path_; 306 int64 max_file_size_; 307 DISALLOW_COPY_AND_ASSIGN(JSONTest); 308}; 309 310} // namespace 311 312DiagnosticTest* MakeUserDirTest() { 313 return new PathTest(kPathsToTest[0]); 314} 315 316DiagnosticTest* MakeLocalStateFileTest() { 317 return new PathTest(kPathsToTest[1]); 318} 319 320DiagnosticTest* MakeDictonaryDirTest() { 321 return new PathTest(kPathsToTest[2]); 322} 323 324DiagnosticTest* MakeInspectorDirTest() { 325 return new PathTest(kPathsToTest[3]); 326} 327 328DiagnosticTest* MakeVersionTest() { 329 return new VersionTest(); 330} 331 332DiagnosticTest* MakeDiskSpaceTest() { 333 return new DiskSpaceTest(); 334} 335 336DiagnosticTest* MakeOperatingSystemTest() { 337 return new OperatingSystemTest(); 338} 339 340DiagnosticTest* MakeInstallTypeTest() { 341 return new InstallTypeTest(); 342} 343 344DiagnosticTest* MakePreferencesTest() { 345 FilePath path = DiagnosticTest::GetUserDefaultProfileDir(); 346 path = path.Append(chrome::kPreferencesFilename); 347 return new JSONTest(path, ASCIIToUTF16("Profile JSON"), 100 * kOneKilo); 348} 349 350DiagnosticTest* MakeBookMarksTest() { 351 FilePath path = DiagnosticTest::GetUserDefaultProfileDir(); 352 path = path.Append(chrome::kBookmarksFileName); 353 return new JSONTest(path, ASCIIToUTF16("BookMarks JSON"), 2 * kOneMeg); 354} 355 356DiagnosticTest* MakeLocalStateTest() { 357 FilePath path; 358 PathService::Get(chrome::DIR_USER_DATA, &path); 359 path = path.Append(chrome::kLocalStateFilename); 360 return new JSONTest(path, ASCIIToUTF16("Local State JSON"), 50 * kOneKilo); 361} 362