| OLD | NEW |
| (Empty) |
| 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 // This file provides reliablity test which runs under UI test framework. The | |
| 6 // test is intended to run within QEMU environment. | |
| 7 // | |
| 8 // Usage 1: reliability_test | |
| 9 // Upon invocation, it visits a hard coded list of sample URLs. This is mainly | |
| 10 // used by buildbot, to verify reliability_test itself runs ok. | |
| 11 // | |
| 12 // Usage 2: reliability_test --site=url --startpage=start --endpage=end [...] | |
| 13 // Upon invocation, it visits a list of URLs constructed as | |
| 14 // "http://url/page?id=k". (start <= k <= end). | |
| 15 // | |
| 16 // Usage 3: reliability_test --list=file --startline=start --endline=end [...] | |
| 17 // Upon invocation, it visits each of the URLs on line numbers between start | |
| 18 // and end, inclusive, stored in the input file. The line number starts from 1. | |
| 19 // | |
| 20 // If both "--site" and "--list" are provided, the "--site" set of arguments | |
| 21 // are ignored. | |
| 22 // | |
| 23 // Optional Switches: | |
| 24 // --iterations=num: goes through the list of URLs constructed in usage 2 or 3 | |
| 25 // num times. | |
| 26 // --continuousload: continuously visits the list of URLs without restarting | |
| 27 // browser for each page load. | |
| 28 // --memoryusage: prints out memory usage when visiting each page. | |
| 29 // --endurl=url: visits the specified url in the end. | |
| 30 // --logfile=filepath: saves the visit log to the specified path. | |
| 31 // --noclearprofile: do not clear profile dir before firing up each time. | |
| 32 // --savedebuglog: save Chrome, V8, and test debug log for each page loaded. | |
| 33 // --searchdumpsbypid: Look for crash dumps by browser process id. | |
| 34 // "crash_dir/pid/" | |
| 35 | |
| 36 #include <fstream> | |
| 37 #include <vector> | |
| 38 | |
| 39 #include "base/command_line.h" | |
| 40 #include "base/environment.h" | |
| 41 #include "base/file_util.h" | |
| 42 #include "base/file_version_info.h" | |
| 43 #include "base/files/file_enumerator.h" | |
| 44 #include "base/files/file_path.h" | |
| 45 #include "base/i18n/time_formatting.h" | |
| 46 #include "base/memory/scoped_ptr.h" | |
| 47 #include "base/path_service.h" | |
| 48 #include "base/prefs/json_pref_store.h" | |
| 49 #include "base/prefs/pref_registry_simple.h" | |
| 50 #include "base/prefs/pref_service.h" | |
| 51 #include "base/process/kill.h" | |
| 52 #include "base/strings/string_number_conversions.h" | |
| 53 #include "base/strings/string_util.h" | |
| 54 #include "base/test/test_file_util.h" | |
| 55 #include "base/threading/platform_thread.h" | |
| 56 #include "base/time/time.h" | |
| 57 #include "chrome/browser/prefs/pref_service_mock_builder.h" | |
| 58 #include "chrome/common/automation_messages.h" | |
| 59 #include "chrome/common/chrome_constants.h" | |
| 60 #include "chrome/common/chrome_paths.h" | |
| 61 #include "chrome/common/chrome_switches.h" | |
| 62 #include "chrome/common/chrome_version_info.h" | |
| 63 #include "chrome/common/logging_chrome.h" | |
| 64 #include "chrome/common/net/url_fixer_upper.h" | |
| 65 #include "chrome/common/pref_names.h" | |
| 66 #include "chrome/common/render_messages.h" | |
| 67 #include "chrome/common/url_constants.h" | |
| 68 #include "chrome/test/automation/automation_proxy.h" | |
| 69 #include "chrome/test/automation/browser_proxy.h" | |
| 70 #include "chrome/test/automation/tab_proxy.h" | |
| 71 #include "chrome/test/automation/window_proxy.h" | |
| 72 #include "chrome/test/ui/ui_test.h" | |
| 73 #include "net/base/net_util.h" | |
| 74 #include "v8/include/v8-testing.h" | |
| 75 | |
| 76 namespace { | |
| 77 | |
| 78 // See comments at the beginning of the file for the definition of switches. | |
| 79 const char kSiteSwitch[] = "site"; | |
| 80 const char kStartPageSwitch[] = "startpage"; | |
| 81 const char kEndPageSwitch[] = "endpage"; | |
| 82 const char kListSwitch[] = "list"; | |
| 83 const char kStartIndexSwitch[] = "startline"; | |
| 84 const char kEndIndexSwitch[] = "endline"; | |
| 85 const char kIterationSwitch[] = "iterations"; | |
| 86 const char kContinuousLoadSwitch[] = "continuousload"; | |
| 87 const char kMemoryUsageSwitch[] = "memoryusage"; | |
| 88 const char kEndURLSwitch[] = "endurl"; | |
| 89 const char kLogFileSwitch[] = "logfile"; | |
| 90 const char kNoPageDownSwitch[] = "nopagedown"; | |
| 91 const char kNoClearProfileSwitch[] = "noclearprofile"; | |
| 92 const char kSaveDebugLogSwitch[] = "savedebuglog"; | |
| 93 const char kStressOptSwitch[] = "stress-opt"; | |
| 94 const char kStressDeoptSwitch[] = "stress-deopt"; | |
| 95 | |
| 96 const char kDefaultServerUrl[] = "http://urllist.com"; | |
| 97 std::string g_server_url; | |
| 98 const char kTestPage1[] = "page1.html"; | |
| 99 const char kTestPage2[] = "page2.html"; | |
| 100 | |
| 101 // These are copied from v8 definitions as we cannot include them. | |
| 102 const char kV8LogFileSwitch[] = "logfile"; | |
| 103 const char kV8LogFileDefaultName[] = "v8.log"; | |
| 104 | |
| 105 // String name of local chrome dll for looking up file information. | |
| 106 const wchar_t kChromeDll[] = L"chrome.dll"; | |
| 107 | |
| 108 bool g_append_page_id = false; | |
| 109 int32 g_start_page; | |
| 110 int32 g_end_page; | |
| 111 base::FilePath g_url_file_path; | |
| 112 int32 g_start_index = 1; | |
| 113 int32 g_end_index = kint32max; | |
| 114 int32 g_iterations = 1; | |
| 115 bool g_memory_usage = false; | |
| 116 bool g_continuous_load = false; | |
| 117 bool g_browser_existing = false; | |
| 118 bool g_clear_profile = true; | |
| 119 std::string g_end_url; | |
| 120 base::FilePath g_log_file_path; | |
| 121 bool g_save_debug_log = false; | |
| 122 base::FilePath g_chrome_log_path; | |
| 123 base::FilePath g_v8_log_path; | |
| 124 base::FilePath g_test_log_path; | |
| 125 bool g_stand_alone = false; | |
| 126 bool g_stress_opt = false; | |
| 127 bool g_stress_deopt = false; | |
| 128 | |
| 129 void ReportHandler(const std::string& str) { | |
| 130 // Ignore report events. | |
| 131 } | |
| 132 | |
| 133 void SetPageRange(const CommandLine& parsed_command_line) { | |
| 134 // If calling into this function, we are running as a standalone program. | |
| 135 g_stand_alone = true; | |
| 136 | |
| 137 // Since we use --enable-dcheck for reliability tests, suppress the error | |
| 138 // dialog in the test process. | |
| 139 logging::SetLogReportHandler(ReportHandler); | |
| 140 | |
| 141 if (parsed_command_line.HasSwitch(kStartPageSwitch)) { | |
| 142 ASSERT_TRUE(parsed_command_line.HasSwitch(kEndPageSwitch)); | |
| 143 ASSERT_TRUE( | |
| 144 base::StringToInt(parsed_command_line.GetSwitchValueASCII( | |
| 145 kStartPageSwitch), | |
| 146 &g_start_page)); | |
| 147 ASSERT_TRUE( | |
| 148 base::StringToInt(parsed_command_line.GetSwitchValueASCII( | |
| 149 kEndPageSwitch), | |
| 150 &g_end_page)); | |
| 151 ASSERT_TRUE(g_start_page > 0 && g_end_page > 0); | |
| 152 ASSERT_TRUE(g_start_page < g_end_page); | |
| 153 g_append_page_id = true; | |
| 154 } else { | |
| 155 ASSERT_FALSE(parsed_command_line.HasSwitch(kEndPageSwitch)); | |
| 156 } | |
| 157 | |
| 158 if (parsed_command_line.HasSwitch(kSiteSwitch)) { | |
| 159 g_server_url = parsed_command_line.GetSwitchValueASCII(kSiteSwitch); | |
| 160 } | |
| 161 | |
| 162 if (parsed_command_line.HasSwitch(kStartIndexSwitch)) { | |
| 163 ASSERT_TRUE( | |
| 164 base::StringToInt(parsed_command_line.GetSwitchValueASCII( | |
| 165 kStartIndexSwitch), | |
| 166 &g_start_index)); | |
| 167 ASSERT_GT(g_start_index, 0); | |
| 168 } | |
| 169 | |
| 170 if (parsed_command_line.HasSwitch(kEndIndexSwitch)) { | |
| 171 ASSERT_TRUE( | |
| 172 base::StringToInt(parsed_command_line.GetSwitchValueASCII( | |
| 173 kEndIndexSwitch), | |
| 174 &g_end_index)); | |
| 175 ASSERT_GT(g_end_index, 0); | |
| 176 } | |
| 177 | |
| 178 ASSERT_TRUE(g_end_index >= g_start_index); | |
| 179 | |
| 180 if (parsed_command_line.HasSwitch(kListSwitch)) | |
| 181 g_url_file_path = parsed_command_line.GetSwitchValuePath(kListSwitch); | |
| 182 | |
| 183 if (parsed_command_line.HasSwitch(kIterationSwitch)) { | |
| 184 ASSERT_TRUE( | |
| 185 base::StringToInt(parsed_command_line.GetSwitchValueASCII( | |
| 186 kIterationSwitch), | |
| 187 &g_iterations)); | |
| 188 ASSERT_GT(g_iterations, 0); | |
| 189 } | |
| 190 | |
| 191 if (parsed_command_line.HasSwitch(kMemoryUsageSwitch)) | |
| 192 g_memory_usage = true; | |
| 193 | |
| 194 if (parsed_command_line.HasSwitch(kContinuousLoadSwitch)) | |
| 195 g_continuous_load = true; | |
| 196 | |
| 197 if (parsed_command_line.HasSwitch(kEndURLSwitch)) | |
| 198 g_end_url = parsed_command_line.GetSwitchValueASCII(kEndURLSwitch); | |
| 199 | |
| 200 if (parsed_command_line.HasSwitch(kLogFileSwitch)) | |
| 201 g_log_file_path = parsed_command_line.GetSwitchValuePath(kLogFileSwitch); | |
| 202 | |
| 203 if (parsed_command_line.HasSwitch(kNoClearProfileSwitch)) | |
| 204 g_clear_profile = false; | |
| 205 | |
| 206 if (parsed_command_line.HasSwitch(kSaveDebugLogSwitch)) { | |
| 207 g_save_debug_log = true; | |
| 208 g_chrome_log_path = logging::GetLogFileName(); | |
| 209 // We won't get v8 log unless --no-sandbox is specified. | |
| 210 if (parsed_command_line.HasSwitch(switches::kNoSandbox)) { | |
| 211 PathService::Get(base::DIR_CURRENT, &g_v8_log_path); | |
| 212 g_v8_log_path = g_v8_log_path.AppendASCII(kV8LogFileDefaultName); | |
| 213 // The command line switch may override the default v8 log path. | |
| 214 if (parsed_command_line.HasSwitch(switches::kJavaScriptFlags)) { | |
| 215 CommandLine v8_command_line( | |
| 216 parsed_command_line.GetSwitchValuePath(switches::kJavaScriptFlags)); | |
| 217 if (v8_command_line.HasSwitch(kV8LogFileSwitch)) { | |
| 218 g_v8_log_path = base::MakeAbsoluteFilePath( | |
| 219 v8_command_line.GetSwitchValuePath(kV8LogFileSwitch)); | |
| 220 } | |
| 221 } | |
| 222 } | |
| 223 } | |
| 224 | |
| 225 if (parsed_command_line.HasSwitch(kStressOptSwitch)) { | |
| 226 g_stress_opt = true; | |
| 227 } | |
| 228 if (parsed_command_line.HasSwitch(kStressDeoptSwitch)) { | |
| 229 g_stress_deopt = true; | |
| 230 } | |
| 231 } | |
| 232 | |
| 233 class PageLoadTest : public UITest { | |
| 234 public: | |
| 235 enum NavigationResult { | |
| 236 NAVIGATION_ERROR = 0, | |
| 237 NAVIGATION_SUCCESS, | |
| 238 NAVIGATION_AUTH_NEEDED, | |
| 239 NAVIGATION_TIME_OUT, | |
| 240 }; | |
| 241 | |
| 242 typedef struct { | |
| 243 // These are results from the test automation that drives Chrome | |
| 244 NavigationResult result; | |
| 245 int crash_dump_count; | |
| 246 // These are stability metrics recorded by Chrome itself | |
| 247 bool browser_clean_exit; | |
| 248 int browser_launch_count; | |
| 249 int page_load_count; | |
| 250 int browser_crash_count; | |
| 251 int renderer_crash_count; | |
| 252 int plugin_crash_count; | |
| 253 } NavigationMetrics; | |
| 254 | |
| 255 PageLoadTest() { | |
| 256 show_window_ = true; | |
| 257 SetPageRange(*CommandLine::ForCurrentProcess()); | |
| 258 } | |
| 259 | |
| 260 void EnsureBrowserAndServer() { | |
| 261 if (!g_browser_existing) { | |
| 262 LaunchBrowserAndServer(); | |
| 263 g_browser_existing = true; | |
| 264 } | |
| 265 } | |
| 266 | |
| 267 // Load a URL in a browser tab and perform a couple of page down events. | |
| 268 // url_string: The URL to navigate to. Accept URL as std::string here | |
| 269 // because the url may also act as a test id and needs to | |
| 270 // be logged in its original format even if invalid. | |
| 271 // log_file: Log file for test results and possible crash dump | |
| 272 // files. This file does not need to be opened in which | |
| 273 // case nothing is logged. | |
| 274 // metrics_output: Return metrics for the page load. | |
| 275 // keep_browser: Set to true if the browser should be kept open after | |
| 276 // loading the page. | |
| 277 // log_only_errors: Set to true if only errors should be logged otherwise | |
| 278 // successful navigations will also be logged. | |
| 279 bool NavigateToURLLogResult(const std::string& url_string, | |
| 280 std::ofstream& log_file, | |
| 281 NavigationMetrics* metrics_output, | |
| 282 bool keep_browser, | |
| 283 bool log_only_error) { | |
| 284 GURL url(url_string); | |
| 285 NavigationMetrics metrics = {NAVIGATION_ERROR}; | |
| 286 std::ofstream test_log; | |
| 287 | |
| 288 // Create a test log. | |
| 289 g_test_log_path = base::FilePath(FILE_PATH_LITERAL("test_log.log")); | |
| 290 test_log.open(g_test_log_path.value().c_str()); | |
| 291 | |
| 292 // Get the version of Chrome we're running. | |
| 293 std::string last_change; | |
| 294 #if defined(OS_WIN) | |
| 295 // Check file version info for chrome dll. | |
| 296 scoped_ptr<FileVersionInfo> file_info; | |
| 297 file_info.reset( | |
| 298 FileVersionInfo::CreateFileVersionInfo(base::FilePath(kChromeDll))); | |
| 299 last_change = WideToASCII(file_info->last_change()); | |
| 300 #elif defined(OS_POSIX) | |
| 301 // TODO(fmeawad): On Mac, the version retrieved here belongs to the test | |
| 302 // module and not the chrome binary, need to be changed to chrome binary | |
| 303 // instead. | |
| 304 chrome::VersionInfo version_info; | |
| 305 last_change = version_info.LastChange(); | |
| 306 #endif // !defined(OS_WIN) | |
| 307 test_log << "Last Change: "; | |
| 308 test_log << last_change << std::endl; | |
| 309 | |
| 310 // Log timestamp for test start. | |
| 311 base::Time time_now = base::Time::Now(); | |
| 312 double time_start = time_now.ToDoubleT(); | |
| 313 test_log << "Test Start: "; | |
| 314 test_log << base::TimeFormatFriendlyDateAndTime(time_now) << std::endl; | |
| 315 | |
| 316 // Make sure the browser is running. | |
| 317 EnsureBrowserAndServer(); | |
| 318 | |
| 319 // Log Browser Launched time. | |
| 320 time_now = base::Time::Now(); | |
| 321 test_log << "browser_launched_seconds="; | |
| 322 test_log << (time_now.ToDoubleT() - time_start) << std::endl; | |
| 323 | |
| 324 int result = AUTOMATION_MSG_NAVIGATION_ERROR; | |
| 325 // This is essentially what NavigateToURL does except we don't fire | |
| 326 // assertion when page loading fails. We log the result instead. | |
| 327 { | |
| 328 // TabProxy should be released before Browser is closed. | |
| 329 scoped_refptr<TabProxy> tab_proxy(GetActiveTab()); | |
| 330 if (tab_proxy.get()) | |
| 331 result = tab_proxy->NavigateToURL(url); | |
| 332 } | |
| 333 | |
| 334 // Log navigate complete time. | |
| 335 time_now = base::Time::Now(); | |
| 336 test_log << "navigate_complete_seconds="; | |
| 337 test_log << (time_now.ToDoubleT() - time_start) << std::endl; | |
| 338 | |
| 339 if (!keep_browser) { | |
| 340 CloseBrowserAndServer(); | |
| 341 g_browser_existing = false; | |
| 342 } | |
| 343 | |
| 344 // Log end of test time. | |
| 345 time_now = base::Time::Now(); | |
| 346 test_log << "total_duration_seconds="; | |
| 347 test_log << (time_now.ToDoubleT() - time_start) << std::endl; | |
| 348 | |
| 349 // Get navigation result and metrics, and optionally write to the log file | |
| 350 // provided. The log format is: | |
| 351 // <url> <navigation_result> <browser_crash_count> <renderer_crash_count> | |
| 352 // <plugin_crash_count> <crash_dump_count> [chrome_log=<path> | |
| 353 // v8_log=<path>] crash_dump=<path> | |
| 354 switch (result) { | |
| 355 case AUTOMATION_MSG_NAVIGATION_ERROR: | |
| 356 metrics.result = NAVIGATION_ERROR; | |
| 357 break; | |
| 358 case AUTOMATION_MSG_NAVIGATION_SUCCESS: | |
| 359 metrics.result = NAVIGATION_SUCCESS; | |
| 360 break; | |
| 361 case AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED: | |
| 362 metrics.result = NAVIGATION_AUTH_NEEDED; | |
| 363 break; | |
| 364 default: | |
| 365 metrics.result = NAVIGATION_ERROR; | |
| 366 break; | |
| 367 } | |
| 368 | |
| 369 // Get crash dumps - don't delete them if logging. | |
| 370 std::vector<base::FilePath> new_crash_dumps; | |
| 371 CollectNewCrashDumps(new_crash_dumps, &metrics, !log_file.is_open()); | |
| 372 | |
| 373 bool do_log = log_file.is_open() && | |
| 374 (!log_only_error || | |
| 375 metrics.result != NAVIGATION_SUCCESS || | |
| 376 !new_crash_dumps.empty()); | |
| 377 if (do_log) { | |
| 378 log_file << url_string; | |
| 379 switch (metrics.result) { | |
| 380 case NAVIGATION_ERROR: | |
| 381 log_file << " error"; | |
| 382 break; | |
| 383 case NAVIGATION_SUCCESS: | |
| 384 log_file << " success"; | |
| 385 break; | |
| 386 case NAVIGATION_AUTH_NEEDED: | |
| 387 log_file << " auth_needed"; | |
| 388 break; | |
| 389 case NAVIGATION_TIME_OUT: | |
| 390 log_file << " timeout"; | |
| 391 break; | |
| 392 default: | |
| 393 break; | |
| 394 } | |
| 395 } | |
| 396 | |
| 397 #if !defined(OS_MACOSX) // Not used by mac chromebot. | |
| 398 // Get stability metrics recorded by Chrome itself. | |
| 399 GetStabilityMetrics(&metrics); | |
| 400 #endif | |
| 401 | |
| 402 if (do_log) { | |
| 403 log_file << " " << metrics.browser_crash_count \ | |
| 404 // The renderer crash count is flaky due to 1183283. | |
| 405 // Ignore the count since we also catch crash by | |
| 406 // crash_dump_count. | |
| 407 << " " << 0 \ | |
| 408 << " " << metrics.plugin_crash_count \ | |
| 409 << " " << metrics.crash_dump_count; | |
| 410 } | |
| 411 | |
| 412 // Close test log. | |
| 413 test_log.close(); | |
| 414 | |
| 415 if (do_log && g_save_debug_log && !g_continuous_load) | |
| 416 SaveDebugLogs(log_file); | |
| 417 | |
| 418 // Log revision information for Chrome build under test. | |
| 419 if (do_log) | |
| 420 log_file << " " << "revision=" << last_change; | |
| 421 | |
| 422 if (do_log) { | |
| 423 for (size_t i = 0; i < new_crash_dumps.size(); i++) | |
| 424 log_file << " crash_dump=" << new_crash_dumps[i].value().c_str(); | |
| 425 } | |
| 426 | |
| 427 if (do_log) | |
| 428 log_file << std::endl; | |
| 429 | |
| 430 if (metrics_output) | |
| 431 *metrics_output = metrics; | |
| 432 | |
| 433 return do_log; | |
| 434 } | |
| 435 | |
| 436 void NavigateThroughPageID(std::ofstream& log_file) { | |
| 437 if (g_append_page_id) { | |
| 438 // For usage 2 | |
| 439 for (int i = g_start_page; i <= g_end_page; ++i) { | |
| 440 const char* server = g_server_url.empty() ? kDefaultServerUrl : | |
| 441 g_server_url.c_str(); | |
| 442 std::string test_page_url( | |
| 443 base::StringPrintf("%s/page?id=%d", server, i)); | |
| 444 NavigateToURLLogResult( | |
| 445 test_page_url, log_file, NULL, g_continuous_load, false); | |
| 446 } | |
| 447 } else { | |
| 448 // Don't run if running as a standalone program which is for distributed | |
| 449 // testing, to avoid mistakenly hitting web sites with many instances. | |
| 450 if (g_stand_alone) | |
| 451 return; | |
| 452 // For usage 1 | |
| 453 NavigationMetrics metrics; | |
| 454 | |
| 455 base::FilePath sample_data_dir = GetSampleDataDir(); | |
| 456 base::FilePath test_page_1 = sample_data_dir.AppendASCII(kTestPage1); | |
| 457 base::FilePath test_page_2 = sample_data_dir.AppendASCII(kTestPage2); | |
| 458 | |
| 459 GURL test_url_1 = net::FilePathToFileURL(test_page_1); | |
| 460 GURL test_url_2 = net::FilePathToFileURL(test_page_2); | |
| 461 | |
| 462 // Convert back to string so that all calls to navigate are the same. | |
| 463 const std::string test_url_1_string = test_url_1.spec(); | |
| 464 const std::string test_url_2_string = test_url_2.spec(); | |
| 465 | |
| 466 NavigateToURLLogResult( | |
| 467 test_url_1_string, log_file, &metrics, g_continuous_load, false); | |
| 468 // Verify everything is fine | |
| 469 EXPECT_EQ(NAVIGATION_SUCCESS, metrics.result); | |
| 470 EXPECT_EQ(0, metrics.crash_dump_count); | |
| 471 EXPECT_TRUE(metrics.browser_clean_exit); | |
| 472 EXPECT_EQ(1, metrics.browser_launch_count); | |
| 473 // Both starting page and test_url_1 are loaded. | |
| 474 EXPECT_EQ(2, metrics.page_load_count); | |
| 475 EXPECT_EQ(0, metrics.browser_crash_count); | |
| 476 EXPECT_EQ(0, metrics.renderer_crash_count); | |
| 477 EXPECT_EQ(0, metrics.plugin_crash_count); | |
| 478 | |
| 479 // Go to "about:crash" | |
| 480 NavigateToURLLogResult(content::kChromeUICrashURL, | |
| 481 log_file, | |
| 482 &metrics, | |
| 483 g_continuous_load, | |
| 484 false); | |
| 485 // Found a crash dump | |
| 486 EXPECT_EQ(1, metrics.crash_dump_count) << kFailedNoCrashService; | |
| 487 // Browser did not crash, and exited cleanly. | |
| 488 EXPECT_TRUE(metrics.browser_clean_exit); | |
| 489 EXPECT_EQ(1, metrics.browser_launch_count); | |
| 490 // Only the renderer should have crashed. | |
| 491 EXPECT_EQ(0, metrics.browser_crash_count); | |
| 492 EXPECT_EQ(1, metrics.renderer_crash_count); | |
| 493 EXPECT_EQ(0, metrics.plugin_crash_count); | |
| 494 | |
| 495 NavigateToURLLogResult( | |
| 496 test_url_2_string, log_file, &metrics, g_continuous_load, false); | |
| 497 // The data on previous crash should be cleared and we should get | |
| 498 // metrics for a successful page load. | |
| 499 EXPECT_EQ(NAVIGATION_SUCCESS, metrics.result); | |
| 500 EXPECT_EQ(0, metrics.crash_dump_count); | |
| 501 EXPECT_TRUE(metrics.browser_clean_exit); | |
| 502 EXPECT_EQ(1, metrics.browser_launch_count); | |
| 503 EXPECT_EQ(0, metrics.browser_crash_count); | |
| 504 EXPECT_EQ(0, metrics.renderer_crash_count); | |
| 505 EXPECT_EQ(0, metrics.plugin_crash_count); | |
| 506 | |
| 507 // Verify metrics service does what we need when browser process crashes. | |
| 508 LaunchBrowserAndServer(); | |
| 509 { | |
| 510 // TabProxy should be released before Browser is closed. | |
| 511 scoped_refptr<TabProxy> tab_proxy(GetActiveTab()); | |
| 512 EXPECT_TRUE(tab_proxy.get()); | |
| 513 if (tab_proxy.get()) { | |
| 514 EXPECT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, | |
| 515 tab_proxy->NavigateToURL(GURL(test_url_1))); | |
| 516 } | |
| 517 } | |
| 518 // Kill browser process. | |
| 519 base::ProcessHandle browser_process = process(); | |
| 520 base::KillProcess(browser_process, 0, false); | |
| 521 | |
| 522 GetStabilityMetrics(&metrics); | |
| 523 // This is not a clean shutdown. | |
| 524 EXPECT_FALSE(metrics.browser_clean_exit); | |
| 525 EXPECT_EQ(1, metrics.browser_crash_count); | |
| 526 EXPECT_EQ(0, metrics.renderer_crash_count); | |
| 527 EXPECT_EQ(0, metrics.plugin_crash_count); | |
| 528 // Relaunch browser so UITest does not fire assertion during TearDown. | |
| 529 LaunchBrowserAndServer(); | |
| 530 } | |
| 531 } | |
| 532 | |
| 533 // For usage 3 | |
| 534 void NavigateThroughURLList(std::ofstream& log_file) { | |
| 535 std::ifstream file(g_url_file_path.value().c_str()); | |
| 536 ASSERT_TRUE(file.is_open()); | |
| 537 | |
| 538 for (int line_index = 1; | |
| 539 line_index <= g_end_index && !file.eof(); | |
| 540 ++line_index) { | |
| 541 std::string url_str; | |
| 542 std::getline(file, url_str); | |
| 543 | |
| 544 if (file.fail()) | |
| 545 break; | |
| 546 | |
| 547 if (g_start_index <= line_index) { | |
| 548 if (g_stress_opt || g_stress_deopt) { | |
| 549 // Make sure the browser is running to communicate the stress | |
| 550 // setting. | |
| 551 EnsureBrowserAndServer(); | |
| 552 v8::Testing::StressType stress_type = | |
| 553 g_stress_opt | |
| 554 ? v8::Testing::kStressTypeOpt | |
| 555 : v8::Testing::kStressTypeDeopt; | |
| 556 scoped_refptr<TabProxy> tab_proxy(GetActiveTab()); | |
| 557 if (tab_proxy.get()) { | |
| 558 tab_proxy->JavaScriptStressTestControl( | |
| 559 kJavaScriptStressTestSetStressRunType, stress_type); | |
| 560 } | |
| 561 | |
| 562 bool success = true; | |
| 563 // Load each page a number of times and keep the same browser open | |
| 564 // for these loads. This loop will end if an error is encountered | |
| 565 // during one of the loads and the error logged. | |
| 566 for (int i = 0; | |
| 567 i < v8::Testing::GetStressRuns() && success; | |
| 568 i++) { | |
| 569 bool last_load = (i == (v8::Testing::GetStressRuns() - 1)); | |
| 570 bool keep_browser = !last_load || g_continuous_load; | |
| 571 bool log_only_error = !last_load; | |
| 572 NavigationMetrics metrics; | |
| 573 | |
| 574 scoped_refptr<TabProxy> tab_proxy(GetActiveTab()); | |
| 575 if (tab_proxy.get()) { | |
| 576 tab_proxy->JavaScriptStressTestControl( | |
| 577 kJavaScriptStressTestPrepareStressRun, i); | |
| 578 } | |
| 579 bool did_log_error; | |
| 580 did_log_error = NavigateToURLLogResult(url_str, | |
| 581 log_file, | |
| 582 &metrics, | |
| 583 keep_browser, | |
| 584 log_only_error); | |
| 585 success = metrics.result == NAVIGATION_SUCCESS && !did_log_error; | |
| 586 } | |
| 587 } else { | |
| 588 NavigateToURLLogResult( | |
| 589 url_str, log_file, NULL, g_continuous_load, false); | |
| 590 } | |
| 591 } | |
| 592 } | |
| 593 | |
| 594 file.close(); | |
| 595 } | |
| 596 | |
| 597 protected: | |
| 598 // Call the base class's SetUp method and initialize our own class members. | |
| 599 virtual void SetUp() { | |
| 600 // Set UI Test members before setting up browser. | |
| 601 clear_profile_ = g_clear_profile; | |
| 602 | |
| 603 UITest::SetUp(); | |
| 604 g_browser_existing = true; | |
| 605 | |
| 606 // If 'BREAKPAD_DUMP_LOCATION' environment variable is set, use it instead. | |
| 607 scoped_ptr<base::Environment> env(base::Environment::Create()); | |
| 608 std::string alternate_minidump_location; | |
| 609 if (env->GetVar("BREAKPAD_DUMP_LOCATION", &alternate_minidump_location)) { | |
| 610 crash_dumps_dir_path_ = base::FilePath::FromUTF8Unsafe( | |
| 611 alternate_minidump_location); | |
| 612 } else { | |
| 613 PathService::Get(chrome::DIR_CRASH_DUMPS, &crash_dumps_dir_path_); | |
| 614 } | |
| 615 | |
| 616 base::FileEnumerator enumerator(crash_dumps_dir_path_, | |
| 617 false, // not recursive | |
| 618 base::FileEnumerator::FILES); | |
| 619 for (base::FilePath path = enumerator.Next(); !path.value().empty(); | |
| 620 path = enumerator.Next()) { | |
| 621 if (path.MatchesExtension(FILE_PATH_LITERAL(".dmp"))) | |
| 622 crash_dumps_[path.BaseName()] = true; | |
| 623 } | |
| 624 } | |
| 625 | |
| 626 base::FilePath ConstructSavedDebugLogPath( | |
| 627 const base::FilePath& debug_log_path, | |
| 628 int index) { | |
| 629 std::string suffix("_"); | |
| 630 suffix.append(base::IntToString(index)); | |
| 631 return debug_log_path.InsertBeforeExtensionASCII(suffix); | |
| 632 } | |
| 633 | |
| 634 void SaveDebugLog(const base::FilePath& log_path, const std::wstring& log_id, | |
| 635 std::ofstream& log_file, int index) { | |
| 636 if (!log_path.empty()) { | |
| 637 base::FilePath saved_log_file_path = | |
| 638 ConstructSavedDebugLogPath(log_path, index); | |
| 639 if (base::Move(log_path, saved_log_file_path)) { | |
| 640 log_file << " " << log_id << "=" << saved_log_file_path.value(); | |
| 641 } | |
| 642 } | |
| 643 } | |
| 644 | |
| 645 // Rename the chrome and v8 debug log files if existing, and save the file | |
| 646 // paths in the log_file provided. | |
| 647 void SaveDebugLogs(std::ofstream& log_file) { | |
| 648 static int url_count = 1; | |
| 649 SaveDebugLog(g_chrome_log_path, L"chrome_log", log_file, url_count); | |
| 650 SaveDebugLog(g_v8_log_path, L"v8_log", log_file, url_count); | |
| 651 SaveDebugLog(g_test_log_path, L"test_log", log_file, url_count); | |
| 652 url_count++; | |
| 653 } | |
| 654 | |
| 655 // Delete a crash dump file. | |
| 656 void DeleteCrashDump(base::FilePath crash_dump_file_name) { | |
| 657 base::FilePath crash_dump_file_path(crash_dumps_dir_path_); | |
| 658 crash_dump_file_path = crash_dump_file_path.Append(crash_dump_file_name); | |
| 659 base::FilePath crash_text_file_path = | |
| 660 crash_dump_file_path.ReplaceExtension(FILE_PATH_LITERAL("txt")); | |
| 661 | |
| 662 ASSERT_TRUE(file_util::DieFileDie(crash_dump_file_path, false)); | |
| 663 ASSERT_TRUE(file_util::DieFileDie(crash_text_file_path, false)); | |
| 664 } | |
| 665 | |
| 666 bool HasNewCrashDumps() { | |
| 667 base::FileEnumerator enumerator(crash_dumps_dir_path_, | |
| 668 false, // not recursive | |
| 669 base::FileEnumerator::FILES); | |
| 670 for (base::FilePath path = enumerator.Next(); !path.value().empty(); | |
| 671 path = enumerator.Next()) { | |
| 672 if (path.MatchesExtension(FILE_PATH_LITERAL(".dmp")) && | |
| 673 !crash_dumps_[path.BaseName()]) { | |
| 674 return true; | |
| 675 } | |
| 676 } | |
| 677 | |
| 678 return false; | |
| 679 } | |
| 680 | |
| 681 // Check whether there are new .dmp files. Return the list and optionally | |
| 682 // delete them afterwards. | |
| 683 void CollectNewCrashDumps(std::vector<base::FilePath>& new_crash_dumps, | |
| 684 NavigationMetrics* metrics, | |
| 685 bool delete_dumps) { | |
| 686 int num_dumps = 0; | |
| 687 base::FileEnumerator enumerator(crash_dumps_dir_path_, | |
| 688 false, // not recursive | |
| 689 base::FileEnumerator::FILES); | |
| 690 for (base::FilePath path = enumerator.Next(); !path.value().empty(); | |
| 691 path = enumerator.Next()) { | |
| 692 if (path.MatchesExtension(FILE_PATH_LITERAL(".dmp")) && | |
| 693 !crash_dumps_[path.BaseName()]) { | |
| 694 crash_dumps_[path.BaseName()] = true; | |
| 695 base::FilePath crash_dump_file_path(crash_dumps_dir_path_); | |
| 696 crash_dump_file_path = crash_dump_file_path.Append(path.BaseName()); | |
| 697 new_crash_dumps.push_back(crash_dump_file_path); | |
| 698 if (delete_dumps) | |
| 699 DeleteCrashDump(path.BaseName()); | |
| 700 num_dumps++; | |
| 701 } | |
| 702 } | |
| 703 if (metrics) | |
| 704 metrics->crash_dump_count = num_dumps; | |
| 705 } | |
| 706 | |
| 707 // Get a PrefService whose contents correspond to the Local State file | |
| 708 // that was saved by the app as it closed. The caller takes ownership of the | |
| 709 // returned PrefService object. | |
| 710 PrefService* GetLocalState(PrefRegistry* registry) { | |
| 711 base::FilePath path = user_data_dir().Append(chrome::kLocalStateFilename); | |
| 712 PrefServiceMockBuilder builder; | |
| 713 builder.WithUserFilePrefs( | |
| 714 path, base::MessageLoop::current()->message_loop_proxy().get()); | |
| 715 return builder.Create(registry); | |
| 716 } | |
| 717 | |
| 718 void GetStabilityMetrics(NavigationMetrics* metrics) { | |
| 719 if (!metrics) | |
| 720 return; | |
| 721 scoped_refptr<PrefRegistrySimple> registry = new PrefRegistrySimple(); | |
| 722 registry->RegisterBooleanPref(prefs::kStabilityExitedCleanly, false); | |
| 723 registry->RegisterIntegerPref(prefs::kStabilityLaunchCount, -1); | |
| 724 registry->RegisterIntegerPref(prefs::kStabilityPageLoadCount, -1); | |
| 725 registry->RegisterIntegerPref(prefs::kStabilityCrashCount, 0); | |
| 726 registry->RegisterIntegerPref(prefs::kStabilityRendererCrashCount, 0); | |
| 727 | |
| 728 scoped_ptr<PrefService> local_state(GetLocalState(registry.get())); | |
| 729 if (!local_state.get()) | |
| 730 return; | |
| 731 | |
| 732 metrics->browser_clean_exit = | |
| 733 local_state->GetBoolean(prefs::kStabilityExitedCleanly); | |
| 734 metrics->browser_launch_count = | |
| 735 local_state->GetInteger(prefs::kStabilityLaunchCount); | |
| 736 metrics->page_load_count = | |
| 737 local_state->GetInteger(prefs::kStabilityPageLoadCount); | |
| 738 metrics->browser_crash_count = | |
| 739 local_state->GetInteger(prefs::kStabilityCrashCount); | |
| 740 metrics->renderer_crash_count = | |
| 741 local_state->GetInteger(prefs::kStabilityRendererCrashCount); | |
| 742 // TODO(huanr) | |
| 743 metrics->plugin_crash_count = 0; | |
| 744 | |
| 745 if (!metrics->browser_clean_exit) | |
| 746 metrics->browser_crash_count++; | |
| 747 } | |
| 748 | |
| 749 base::FilePath GetSampleDataDir() { | |
| 750 base::FilePath test_dir; | |
| 751 PathService::Get(chrome::DIR_TEST_DATA, &test_dir); | |
| 752 test_dir = test_dir.AppendASCII("reliability"); | |
| 753 test_dir = test_dir.AppendASCII("sample_pages"); | |
| 754 return test_dir; | |
| 755 } | |
| 756 | |
| 757 // The pathname of Chrome's crash dumps directory. | |
| 758 base::FilePath crash_dumps_dir_path_; | |
| 759 | |
| 760 // The set of all the crash dumps we have seen. Each crash generates a | |
| 761 // .dmp and a .txt file in the crash dumps directory. We only store the | |
| 762 // .dmp files in this set. | |
| 763 // | |
| 764 // The set is implemented as a std::map. The key is the file name, and | |
| 765 // the value is false (the file is not in the set) or true (the file is | |
| 766 // in the set). The initial value for any key in std::map is 0 (false), | |
| 767 // which in this case means a new file is not in the set initially, | |
| 768 // exactly the semantics we want. | |
| 769 std::map<base::FilePath, bool> crash_dumps_; | |
| 770 }; | |
| 771 | |
| 772 TEST_F(PageLoadTest, Reliability) { | |
| 773 std::ofstream log_file; | |
| 774 | |
| 775 if (!g_log_file_path.empty()) { | |
| 776 log_file.open(g_log_file_path.value().c_str()); | |
| 777 } | |
| 778 | |
| 779 for (int k = 0; k < g_iterations; ++k) { | |
| 780 if (g_url_file_path.empty()) { | |
| 781 NavigateThroughPageID(log_file); | |
| 782 } else { | |
| 783 NavigateThroughURLList(log_file); | |
| 784 } | |
| 785 } | |
| 786 | |
| 787 if (!g_end_url.empty()) { | |
| 788 NavigateToURLLogResult( | |
| 789 g_end_url, log_file, NULL, g_continuous_load, false); | |
| 790 } | |
| 791 | |
| 792 log_file.close(); | |
| 793 } | |
| 794 | |
| 795 } // namespace | |
| 796 | |
| OLD | NEW |