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 76namespace { 77 78// See comments at the beginning of the file for the definition of switches. 79const char kSiteSwitch[] = "site"; 80const char kStartPageSwitch[] = "startpage"; 81const char kEndPageSwitch[] = "endpage"; 82const char kListSwitch[] = "list"; 83const char kStartIndexSwitch[] = "startline"; 84const char kEndIndexSwitch[] = "endline"; 85const char kIterationSwitch[] = "iterations"; 86const char kContinuousLoadSwitch[] = "continuousload"; 87const char kMemoryUsageSwitch[] = "memoryusage"; 88const char kEndURLSwitch[] = "endurl"; 89const char kLogFileSwitch[] = "logfile"; 90const char kNoPageDownSwitch[] = "nopagedown"; 91const char kNoClearProfileSwitch[] = "noclearprofile"; 92const char kSaveDebugLogSwitch[] = "savedebuglog"; 93const char kStressOptSwitch[] = "stress-opt"; 94const char kStressDeoptSwitch[] = "stress-deopt"; 95 96const char kDefaultServerUrl[] = "http://urllist.com"; 97std::string g_server_url; 98const char kTestPage1[] = "page1.html"; 99const char kTestPage2[] = "page2.html"; 100 101// These are copied from v8 definitions as we cannot include them. 102const char kV8LogFileSwitch[] = "logfile"; 103const char kV8LogFileDefaultName[] = "v8.log"; 104 105// String name of local chrome dll for looking up file information. 106const wchar_t kChromeDll[] = L"chrome.dll"; 107 108bool g_append_page_id = false; 109int32 g_start_page; 110int32 g_end_page; 111base::FilePath g_url_file_path; 112int32 g_start_index = 1; 113int32 g_end_index = kint32max; 114int32 g_iterations = 1; 115bool g_memory_usage = false; 116bool g_continuous_load = false; 117bool g_browser_existing = false; 118bool g_clear_profile = true; 119std::string g_end_url; 120base::FilePath g_log_file_path; 121bool g_save_debug_log = false; 122base::FilePath g_chrome_log_path; 123base::FilePath g_v8_log_path; 124base::FilePath g_test_log_path; 125bool g_stand_alone = false; 126bool g_stress_opt = false; 127bool g_stress_deopt = false; 128 129void ReportHandler(const std::string& str) { 130 // Ignore report events. 131} 132 133void 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 233class 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 772TEST_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 797