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 #include "chrome_frame/test/perf/chrome_frame_perftest.h" 6 7 #include <atlhost.h> 8 #include <atlwin.h> 9 10 #include <map> 11 #include <string> 12 #include <vector> 13 14 #include "base/debug/trace_event_win.h" 15 #include "base/file_util.h" 16 #include "base/files/file_path.h" 17 #include "base/memory/scoped_ptr.h" 18 #include "base/path_service.h" 19 #include "base/process/kill.h" 20 #include "base/process/launch.h" 21 #include "base/process/process_iterator.h" 22 #include "base/strings/string_util.h" 23 #include "base/strings/stringprintf.h" 24 #include "base/strings/utf_string_conversions.h" 25 #include "base/test/perf_time_logger.h" 26 #include "base/test/test_file_util.h" 27 #include "base/threading/platform_thread.h" 28 #include "base/time/time.h" 29 #include "base/win/event_trace_consumer.h" 30 #include "base/win/event_trace_controller.h" 31 #include "base/win/registry.h" 32 #include "base/win/scoped_bstr.h" 33 #include "base/win/scoped_comptr.h" 34 #include "base/win/scoped_variant.h" 35 #include "chrome/app/image_pre_reader_win.h" 36 #include "chrome/common/chrome_constants.h" 37 #include "chrome/common/chrome_paths.h" 38 #include "chrome/common/chrome_paths_internal.h" 39 #include "chrome/test/base/chrome_process_util.h" 40 #include "chrome/test/ui/ui_perf_test.h" 41 #include "chrome_frame/chrome_tab.h" 42 #include "chrome_frame/test_utils.h" 43 #include "chrome_frame/utils.h" 44 #include "testing/perf/perf_test.h" 45 46 const wchar_t kSilverlightControlKey[] = 47 L"CLSID\\{DFEAF541-F3E1-4c24-ACAC-99C30715084A}\\InprocServer32"; 48 49 const wchar_t kFlashControlKey[] = 50 L"CLSID\\{D27CDB6E-AE6D-11cf-96B8-444553540000}\\InprocServer32"; 51 52 using base::TimeDelta; 53 using base::TimeTicks; 54 55 // This class implements an ActiveX container which hosts the ChromeFrame 56 // ActiveX control. It provides hooks which can be implemented by derived 57 // classes for implementing performance measurement, etc. 58 class ChromeFrameActiveXContainer 59 : public CWindowImpl<ChromeFrameActiveXContainer, CWindow, CWinTraits < 60 WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 61 WS_EX_APPWINDOW | WS_EX_WINDOWEDGE> >, 62 public CComObjectRootEx<CComSingleThreadModel>, 63 public IPropertyNotifySink { 64 public: 65 ~ChromeFrameActiveXContainer() { 66 if (m_hWnd) 67 DestroyWindow(); 68 } 69 70 DECLARE_WND_CLASS_EX(L"ChromeFrameActiveX_container", 0, 0) 71 72 BEGIN_COM_MAP(ChromeFrameActiveXContainer) 73 COM_INTERFACE_ENTRY(IPropertyNotifySink) 74 END_COM_MAP() 75 76 BEGIN_MSG_MAP(ChromeFrameActiveXContainer) 77 MESSAGE_HANDLER(WM_CREATE, OnCreate) 78 MESSAGE_HANDLER(WM_DESTROY, OnDestroy) 79 END_MSG_MAP() 80 81 HRESULT OnMessageCallback(const VARIANT* param) { 82 DVLOG(1) << __FUNCTION__; 83 OnMessageCallbackImpl(param); 84 return S_OK; 85 } 86 87 HRESULT OnLoadErrorCallback(const VARIANT* param) { 88 DVLOG(1) << __FUNCTION__ << " " << param->bstrVal; 89 OnLoadErrorCallbackImpl(param); 90 return S_OK; 91 } 92 93 HRESULT OnLoadCallback(const VARIANT* param) { 94 DVLOG(1) << __FUNCTION__ << " " << param->bstrVal; 95 OnLoadCallbackImpl(param); 96 return S_OK; 97 } 98 99 ChromeFrameActiveXContainer() : 100 prop_notify_cookie_(0), 101 onmsg_(this, &ChromeFrameActiveXContainer::OnMessageCallback), 102 onloaderror_(this, &ChromeFrameActiveXContainer::OnLoadErrorCallback), 103 onload_(this, &ChromeFrameActiveXContainer::OnLoadCallback) { 104 } 105 106 LRESULT OnCreate(UINT , WPARAM , LPARAM , BOOL& ) { 107 chromeview_.Attach(m_hWnd); 108 return 0; 109 } 110 111 // This will be called twice. 112 // Once from CAxHostWindow::OnDestroy (through DefWindowProc) 113 // and once more from the ATL since CAxHostWindow::OnDestroy claims the 114 // message is not handled. 115 LRESULT OnDestroy(UINT, WPARAM, LPARAM, BOOL& handled) { // NOLINT 116 if (prop_notify_cookie_) { 117 AtlUnadvise(tab_, IID_IPropertyNotifySink, prop_notify_cookie_); 118 prop_notify_cookie_ = 0; 119 } 120 121 tab_.Release(); 122 return 0; 123 } 124 125 virtual void OnFinalMessage(HWND /*hWnd*/) { 126 ::PostQuitMessage(6); 127 } 128 129 static const wchar_t* GetWndCaption() { 130 return L"ChromeFrame Container"; 131 } 132 133 // IPropertyNotifySink 134 STDMETHOD(OnRequestEdit)(DISPID disp_id) { 135 OnRequestEditImpl(disp_id); 136 return S_OK; 137 } 138 139 STDMETHOD(OnChanged)(DISPID disp_id) { 140 if (disp_id != DISPID_READYSTATE) 141 return S_OK; 142 143 long ready_state; 144 HRESULT hr = tab_->get_readyState(&ready_state); 145 DCHECK(hr == S_OK); 146 147 OnReadyStateChanged(ready_state); 148 149 if (ready_state == READYSTATE_COMPLETE) { 150 if (!starting_url_.empty()) { 151 Navigate(starting_url_.c_str()); 152 } else { 153 PostMessage(WM_CLOSE); 154 } 155 } else if (ready_state == READYSTATE_UNINITIALIZED) { 156 DLOG(ERROR) << __FUNCTION__ << " Chrome launch failed."; 157 } 158 159 return S_OK; 160 } 161 162 void CreateChromeFrameWindow(const std::string& starting_url) { 163 starting_url_ = starting_url; 164 RECT rc = { 0, 0, 800, 600 }; 165 Create(NULL, rc); 166 DCHECK(m_hWnd); 167 ShowWindow(SW_SHOWDEFAULT); 168 } 169 170 void CreateControl(bool setup_event_sinks) { 171 HRESULT hr = chromeview_.CreateControl(L"ChromeTab.ChromeFrame"); 172 EXPECT_HRESULT_SUCCEEDED(hr); 173 hr = chromeview_.QueryControl(tab_.Receive()); 174 EXPECT_HRESULT_SUCCEEDED(hr); 175 176 if (setup_event_sinks) 177 SetupEventSinks(); 178 } 179 180 void Navigate(const char* url) { 181 BeforeNavigateImpl(url); 182 183 HRESULT hr = tab_->put_src(base::win::ScopedBstr(UTF8ToWide(url).c_str())); 184 DCHECK(hr == S_OK) << "Chrome frame NavigateToURL(" << url 185 << base::StringPrintf(L") failed 0x%08X", hr); 186 } 187 188 void SetupEventSinks() { 189 HRESULT hr = AtlAdvise(tab_, this, IID_IPropertyNotifySink, 190 &prop_notify_cookie_); 191 DCHECK(hr == S_OK) << "AtlAdvice for IPropertyNotifySink failed " << hr; 192 193 base::win::ScopedVariant onmessage(onmsg_.ToDispatch()); 194 base::win::ScopedVariant onloaderror(onloaderror_.ToDispatch()); 195 base::win::ScopedVariant onload(onload_.ToDispatch()); 196 EXPECT_HRESULT_SUCCEEDED(tab_->put_onmessage(onmessage)); 197 EXPECT_HRESULT_SUCCEEDED(tab_->put_onloaderror(onloaderror)); 198 EXPECT_HRESULT_SUCCEEDED(tab_->put_onload(onload)); 199 } 200 201 protected: 202 // These functions are implemented by derived classes for special behavior 203 // like performance measurement, etc. 204 virtual void OnReadyStateChanged(long ready_state) {} 205 virtual void OnRequestEditImpl(DISPID disp_id) {} 206 207 virtual void OnMessageCallbackImpl(const VARIANT* param) {} 208 209 virtual void OnLoadCallbackImpl(const VARIANT* param) { 210 PostMessage(WM_CLOSE); 211 } 212 213 virtual void OnLoadErrorCallbackImpl(const VARIANT* param) { 214 PostMessage(WM_CLOSE); 215 } 216 virtual void BeforeNavigateImpl(const char* url) {} 217 218 CAxWindow chromeview_; 219 base::win::ScopedComPtr<IChromeFrame> tab_; 220 DWORD prop_notify_cookie_; 221 DispCallback<ChromeFrameActiveXContainer> onmsg_; 222 DispCallback<ChromeFrameActiveXContainer> onloaderror_; 223 DispCallback<ChromeFrameActiveXContainer> onload_; 224 std::string starting_url_; 225 }; 226 227 // This class overrides the hooks provided by the ChromeFrameActiveXContainer 228 // class and measures performance at various stages, like initialzation of 229 // the Chrome frame widget, navigation, etc. 230 class ChromeFrameActiveXContainerPerf : public ChromeFrameActiveXContainer { 231 public: 232 ChromeFrameActiveXContainerPerf() {} 233 234 void CreateControl(bool setup_event_sinks) { 235 perf_initialize_.reset(new base::PerfTimeLogger("Fully initialized")); 236 base::PerfTimeLogger perf_create("Create Control"); 237 238 HRESULT hr = chromeview_.CreateControl(L"ChromeTab.ChromeFrame"); 239 EXPECT_HRESULT_SUCCEEDED(hr); 240 hr = chromeview_.QueryControl(tab_.Receive()); 241 EXPECT_HRESULT_SUCCEEDED(hr); 242 243 perf_create.Done(); 244 if (setup_event_sinks) 245 SetupEventSinks(); 246 } 247 248 protected: 249 virtual void OnReadyStateChanged(long ready_state) { 250 // READYSTATE_COMPLETE is fired when the automation server is ready. 251 if (ready_state == READYSTATE_COMPLETE) { 252 perf_initialize_->Done(); 253 } else if (ready_state == READYSTATE_INTERACTIVE) { 254 // Window ready. Currently we never receive this notification because it 255 // is fired before we finish setting up our hosting environment. 256 // This is because of how ATL is written. Moving forward we might 257 // have our own hosting classes and then have more control over when we 258 // set up the prop notify sink. 259 } else { 260 DCHECK(ready_state != READYSTATE_UNINITIALIZED) << "failed to initialize"; 261 } 262 } 263 264 virtual void OnLoadCallbackImpl(const VARIANT* param) { 265 PostMessage(WM_CLOSE); 266 perf_navigate_->Done(); 267 } 268 269 virtual void OnLoadErrorCallbackImpl(const VARIANT* param) { 270 PostMessage(WM_CLOSE); 271 perf_navigate_->Done(); 272 } 273 274 virtual void BeforeNavigateImpl(const char* url ) { 275 std::string test_name = "Navigate "; 276 test_name += url; 277 perf_navigate_.reset(new base::PerfTimeLogger(test_name.c_str())); 278 } 279 280 scoped_ptr<base::PerfTimeLogger> perf_initialize_; 281 scoped_ptr<base::PerfTimeLogger> perf_navigate_; 282 }; 283 284 // This class provides common functionality which can be used for most of the 285 // ChromeFrame/Tab performance tests. 286 class ChromeFramePerfTestBase : public UIPerfTest { 287 public: 288 ChromeFramePerfTestBase() {} 289 protected: 290 scoped_ptr<ScopedChromeFrameRegistrar> chrome_frame_registrar_; 291 }; 292 293 class ChromeFrameStartupTest : public ChromeFramePerfTestBase { 294 public: 295 ChromeFrameStartupTest() {} 296 297 virtual void SetUp() { 298 ASSERT_TRUE(PathService::Get(chrome::DIR_APP, &dir_app_)); 299 300 chrome_dll_ = dir_app_.Append(L"chrome.dll"); 301 chrome_exe_ = dir_app_.Append(chrome::kBrowserProcessExecutableName); 302 chrome_frame_dll_ = dir_app_.Append(kChromeFrameDllName); 303 icu_dll_ = dir_app_.Append(L"icudt.dll"); 304 ffmpegsumo_dll_ = dir_app_.Append(L"ffmpegsumo.dll"); 305 } 306 307 // TODO(iyengar) 308 // This function is similar to the RunStartupTest function used in chrome 309 // startup tests. Refactor into a common implementation. 310 void RunStartupTest(const char* graph, const char* trace, 311 const char* startup_url, bool test_cold, 312 int total_binaries, 313 const base::FilePath binaries_to_evict[], 314 bool important, bool ignore_cache_error) { 315 const int kNumCycles = 20; 316 317 startup_url_ = startup_url; 318 319 TimeDelta timings[kNumCycles]; 320 321 for (int i = 0; i < kNumCycles; ++i) { 322 if (test_cold) { 323 for (int binary_index = 0; binary_index < total_binaries; 324 binary_index++) { 325 bool result = base::EvictFileFromSystemCacheWithRetry( 326 binaries_to_evict[binary_index]); 327 if (!ignore_cache_error) { 328 ASSERT_TRUE(result); 329 } else if (!result) { 330 LOG(ERROR) << GetLastError(); 331 printf("\nFailed to evict file %ls from cache. Not running test\n", 332 binaries_to_evict[binary_index].value().c_str()); 333 return; 334 } 335 } 336 } 337 338 TimeTicks start_time, end_time; 339 340 RunStartupTestImpl(&start_time, &end_time); 341 342 timings[i] = end_time - start_time; 343 344 CoFreeUnusedLibraries(); 345 346 // TODO(beng): Can't shut down so quickly. Figure out why, and fix. If we 347 // do, we crash. 348 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50)); 349 } 350 351 std::string times; 352 for (int i = 0; i < kNumCycles; ++i) 353 base::StringAppendF(×, "%.2f,", timings[i].InMillisecondsF()); 354 355 perf_test::PrintResultList(graph, "", trace, times, "ms", important); 356 } 357 358 base::FilePath dir_app_; 359 base::FilePath chrome_dll_; 360 base::FilePath chrome_exe_; 361 base::FilePath chrome_frame_dll_; 362 base::FilePath icu_dll_; 363 base::FilePath ffmpegsumo_dll_; 364 365 protected: 366 // Individual startup tests should implement this function. 367 virtual void RunStartupTestImpl(TimeTicks* start_time, 368 TimeTicks* end_time) {} 369 370 // The host is torn down by this function. It should not be used after 371 // this function returns. 372 static void ReleaseHostComReferences(CAxWindow& host) { 373 CComPtr<IAxWinHostWindow> spWinHost; 374 host.QueryHost(&spWinHost); 375 ASSERT_TRUE(spWinHost != NULL); 376 377 // Hack to get the host to release all interfaces and thus ensure that 378 // the COM server can be unloaded. 379 CAxHostWindow* host_window = static_cast<CAxHostWindow*>(spWinHost.p); 380 host_window->ReleaseAll(); 381 host.DestroyWindow(); 382 } 383 384 std::string startup_url_; 385 }; 386 387 class ChromeFrameStartupTestActiveX : public ChromeFrameStartupTest { 388 public: 389 virtual void SetUp() { 390 // Register the Chrome Frame DLL in the build directory. 391 chrome_frame_registrar_.reset(new ScopedChromeFrameRegistrar( 392 ScopedChromeFrameRegistrar::SYSTEM_LEVEL)); 393 394 ChromeFrameStartupTest::SetUp(); 395 } 396 397 protected: 398 virtual void RunStartupTestImpl(TimeTicks* start_time, 399 TimeTicks* end_time) { 400 *start_time = TimeTicks::Now(); 401 SimpleModule module; 402 AtlAxWinInit(); 403 CComObjectStackEx<ChromeFrameActiveXContainer> wnd; 404 wnd.CreateChromeFrameWindow(startup_url_); 405 wnd.CreateControl(true); 406 module.RunMessageLoop(); 407 *end_time = TimeTicks::Now(); 408 } 409 }; 410 411 // This class measures the load time of chrome and chrome frame binaries 412 class ChromeFrameBinariesLoadTest : public ChromeFrameStartupTestActiveX { 413 static const size_t kStepSize = 4 * 1024; 414 public: 415 enum PreReadType { 416 kPreReadNone, 417 kPreReadPartial, 418 kPreReadFull 419 }; 420 421 ChromeFrameBinariesLoadTest() 422 : pre_read_type_(kPreReadNone), 423 step_size_(kStepSize), 424 bytes_to_read_(0), 425 percentage_to_preread_(25) {} 426 427 protected: 428 virtual void RunStartupTestImpl(TimeTicks* start_time, 429 TimeTicks* end_time) { 430 *start_time = TimeTicks::Now(); 431 432 if (pre_read_type_ == kPreReadFull) { 433 EXPECT_TRUE(ImagePreReader::PreReadImage(chrome_exe_.value().c_str(), 434 bytes_to_read_, 435 step_size_)); 436 EXPECT_TRUE(ImagePreReader::PreReadImage(chrome_dll_.value().c_str(), 437 bytes_to_read_, 438 step_size_)); 439 } else if (pre_read_type_ == kPreReadPartial) { 440 EXPECT_TRUE( 441 ImagePreReader::PartialPreReadImage(chrome_exe_.value().c_str(), 442 percentage_to_preread_, 443 step_size_)); 444 EXPECT_TRUE( 445 ImagePreReader::PartialPreReadImage(chrome_dll_.value().c_str(), 446 percentage_to_preread_, 447 step_size_)); 448 } 449 450 HMODULE chrome_exe = LoadLibrary(chrome_exe_.value().c_str()); 451 EXPECT_TRUE(chrome_exe != NULL); 452 453 HMODULE chrome_dll = LoadLibrary(chrome_dll_.value().c_str()); 454 EXPECT_TRUE(chrome_dll != NULL); 455 456 *end_time = TimeTicks::Now(); 457 458 FreeLibrary(chrome_exe); 459 FreeLibrary(chrome_dll); 460 } 461 462 PreReadType pre_read_type_; 463 size_t bytes_to_read_; 464 size_t step_size_; 465 uint8 percentage_to_preread_; 466 }; 467 468 // This class provides functionality to run the startup performance test for 469 // the ChromeFrame ActiveX against a reference build. At this point we only run 470 // this test in warm mode. 471 class ChromeFrameStartupTestActiveXReference 472 : public ChromeFrameStartupTestActiveX { 473 public: 474 // override the browser directory to use the reference build instead. 475 virtual void SetUp() { 476 // Register the reference build Chrome Frame DLL. 477 chrome_frame_registrar_.reset(new ScopedChromeFrameRegistrar( 478 ScopedChromeFrameRegistrar::SYSTEM_LEVEL)); 479 chrome_frame_registrar_->RegisterReferenceChromeFrameBuild(); 480 481 ChromeFrameStartupTest::SetUp(); 482 483 chrome_frame_dll_ = base::FilePath( 484 chrome_frame_registrar_->GetReferenceChromeFrameDllPath()); 485 DVLOG(1) << __FUNCTION__ << ": " << chrome_frame_dll_.value(); 486 } 487 488 virtual void TearDown() { 489 // Reregister the Chrome Frame DLL in the build directory. 490 chrome_frame_registrar_.reset(NULL); 491 } 492 }; 493 494 // This class provides base functionality to measure ChromeFrame memory 495 // usage. 496 // TODO(iyengar) 497 // Some of the functionality in this class like printing the results, etc 498 // is based on the chrome\test\memory_test.cc. We need to factor out 499 // the common code. 500 class ChromeFrameMemoryTest : public ChromeFramePerfTestBase { 501 // Contains information about the memory consumption of a process. 502 class ProcessMemoryInfo { 503 public: 504 // Default constructor 505 // Added to enable us to add ProcessMemoryInfo instances to a map. 506 ProcessMemoryInfo() 507 : process_id_(0), 508 virtual_size_(0), 509 working_set_size_(0), 510 chrome_browser_process_(false), 511 chrome_frame_memory_test_instance_(NULL) {} 512 513 ProcessMemoryInfo(base::ProcessId process_id, bool chrome_browser_process, 514 ChromeFrameMemoryTest* memory_test_instance) 515 : process_id_(process_id), 516 virtual_size_(0), 517 working_set_size_(0), 518 chrome_browser_process_(chrome_browser_process), 519 chrome_frame_memory_test_instance_(memory_test_instance) {} 520 521 bool GetMemoryConsumptionDetails() { 522 base::ProcessHandle process_handle; 523 if (!base::OpenPrivilegedProcessHandle(process_id_, &process_handle)) { 524 NOTREACHED(); 525 } 526 527 // TODO(sgk): if/when base::ProcessMetrics can return real memory 528 // stats on mac, convert to: 529 // 530 // scoped_ptr<base::ProcessMetrics> process_metrics; 531 // process_metrics.reset( 532 // base::ProcessMetrics::CreateProcessMetrics(process_handle)); 533 scoped_ptr<ChromeTestProcessMetrics> process_metrics; 534 process_metrics.reset( 535 ChromeTestProcessMetrics::CreateProcessMetrics(process_handle)); 536 537 virtual_size_ = process_metrics->GetPagefileUsage(); 538 working_set_size_ = process_metrics->GetWorkingSetSize(); 539 540 base::CloseProcessHandle(process_handle); 541 return true; 542 } 543 544 void Print(const char* test_name) { 545 std::string trace_name(test_name); 546 547 ASSERT_TRUE(chrome_frame_memory_test_instance_ != NULL); 548 549 if (chrome_browser_process_) { 550 perf_test::PrintResult("vm_final_browser", "", trace_name + "_vm_b", 551 virtual_size_ / 1024, "KB", false /* not important */); 552 perf_test::PrintResult("ws_final_browser", "", trace_name + "_ws_b", 553 working_set_size_ / 1024, "KB", false /* not important */); 554 } else if (process_id_ == base::GetCurrentProcId()) { 555 perf_test::PrintResult("vm_current_process", "", trace_name + "_vm_c", 556 virtual_size_ / 1024, "KB", false /* not important */); 557 perf_test::PrintResult("ws_current_process", "", trace_name + "_ws_c", 558 working_set_size_ / 1024, "KB", false /* not important */); 559 } 560 561 printf("\n"); 562 } 563 564 base::ProcessId process_id_; 565 size_t virtual_size_; 566 size_t working_set_size_; 567 // Set to true if this is the chrome browser process. 568 bool chrome_browser_process_; 569 570 // A reference to the ChromeFrameMemoryTest instance. Used to print memory 571 // consumption information. 572 ChromeFrameMemoryTest* chrome_frame_memory_test_instance_; 573 }; 574 575 // This map tracks memory usage for a process. It is keyed on the process 576 // id. 577 typedef std::map<DWORD, ProcessMemoryInfo> ProcessMemoryConsumptionMap; 578 579 public: 580 ChromeFrameMemoryTest() : current_url_index_(0) { 581 } 582 583 virtual void SetUp() { 584 // Register the Chrome Frame DLL in the build directory. 585 chrome_frame_registrar_.reset(new ScopedChromeFrameRegistrar( 586 ScopedChromeFrameRegistrar::SYSTEM_LEVEL)); 587 } 588 589 void RunTest(const char* test_name, char* urls[], int total_urls) { 590 ASSERT_TRUE(urls != NULL); 591 ASSERT_GT(total_urls, 0); 592 593 // Record the initial CommitCharge. This is a system-wide measurement, 594 // so if other applications are running, they can create variance in this 595 // test. 596 start_commit_charge_ = base::GetSystemCommitCharge(); 597 598 for (int i = 0; i < total_urls; i++) 599 urls_.push_back(urls[i]); 600 601 std::string url; 602 GetNextUrl(&url); 603 ASSERT_TRUE(!url.empty()); 604 605 StartTest(url, test_name); 606 } 607 608 void OnNavigationSuccess(const VARIANT* param) { 609 ASSERT_TRUE(param != NULL); 610 ASSERT_EQ(VT_BSTR, param->vt); 611 612 DVLOG(1) << __FUNCTION__ << " " << param->bstrVal; 613 InitiateNextNavigation(); 614 } 615 616 void OnNavigationFailure(const VARIANT* param) { 617 ASSERT_TRUE(param != NULL); 618 ASSERT_EQ(VT_BSTR, param->vt); 619 620 DVLOG(1) << __FUNCTION__ << " " << param->bstrVal; 621 InitiateNextNavigation(); 622 } 623 624 protected: 625 bool GetNextUrl(std::string* url) { 626 if (current_url_index_ >= urls_.size()) 627 return false; 628 629 *url = urls_[current_url_index_++]; 630 return true; 631 } 632 633 void InitiateNextNavigation() { 634 // Get the memory consumption information for the child processes 635 // of the chrome browser. 636 ChromeProcessList child_processes = GetBrowserChildren(); 637 ChromeProcessList::iterator index; 638 for (index = child_processes.begin(); index != child_processes.end(); 639 ++index) { 640 AccountProcessMemoryUsage(*index); 641 } 642 643 // TODO(iyengar): Bug 2953 644 // Need to verify if this is still true. 645 // The automation crashes periodically if we cycle too quickly. 646 // To make these tests more reliable, slowing them down a bit. 647 Sleep(200); 648 649 std::string url; 650 bool next_url = GetNextUrl(&url); 651 if (!url.empty()) { 652 NavigateImpl(url); 653 } else { 654 TestCompleted(); 655 } 656 } 657 658 void PrintResults(const char* test_name) { 659 PrintMemoryUsageInfo(test_name); 660 memory_consumption_map_.clear(); 661 662 // Added to give the OS some time to flush the used pages for the 663 // chrome processes which would have exited by now. 664 Sleep(200); 665 666 size_t end_commit_charge = base::GetSystemCommitCharge(); 667 size_t commit_size = (end_commit_charge - start_commit_charge_) * 1024; 668 669 std::string trace_name(test_name); 670 trace_name.append("_cc"); 671 672 perf_test::PrintResult("commit_charge", "", trace_name, 673 commit_size / 1024, "KB", true /* important */); 674 printf("\n"); 675 } 676 677 base::ProcessId chrome_browser_process_id() { 678 base::NamedProcessIterator iter(L"chrome.exe", NULL); 679 const base::ProcessEntry* entry = iter.NextProcessEntry(); 680 if (entry) { 681 return entry->pid(); 682 } 683 return -1; 684 } 685 686 ChromeProcessList GetBrowserChildren() { 687 ChromeProcessList list = GetRunningChromeProcesses( 688 chrome_browser_process_id()); 689 ChromeProcessList::iterator browser = 690 std::find(list.begin(), list.end(), chrome_browser_process_id()); 691 if (browser != list.end()) { 692 list.erase(browser); 693 } 694 return list; 695 } 696 697 void AccountProcessMemoryUsage(DWORD process_id) { 698 ProcessMemoryInfo process_memory_info( 699 process_id, process_id == chrome_browser_process_id(), this); 700 701 ASSERT_TRUE(process_memory_info.GetMemoryConsumptionDetails()); 702 703 memory_consumption_map_[process_id] = process_memory_info; 704 } 705 706 void PrintMemoryUsageInfo(const char* test_name) { 707 printf("\n"); 708 709 std::string trace_name(test_name); 710 711 ProcessMemoryConsumptionMap::iterator index; 712 size_t total_virtual_size = 0; 713 size_t total_working_set_size = 0; 714 715 for (index = memory_consumption_map_.begin(); 716 index != memory_consumption_map_.end(); 717 ++index) { 718 ProcessMemoryInfo& memory_info = (*index).second; 719 memory_info.Print(test_name); 720 721 total_virtual_size += memory_info.virtual_size_; 722 total_working_set_size += memory_info.working_set_size_; 723 } 724 725 printf("\n"); 726 727 perf_test::PrintResult("vm_final_total", "", trace_name + "_vm", 728 total_virtual_size / 1024, "KB", 729 false /* not important */); 730 perf_test::PrintResult("ws_final_total", "", trace_name + "_ws", 731 total_working_set_size / 1024, "KB", 732 true /* important */); 733 } 734 735 // Should never get called. 736 virtual void StartTest(const std::string& url, 737 const std::string& test_name) = 0 { 738 ASSERT_FALSE(false); 739 } 740 741 // Should never get called. 742 virtual void NavigateImpl(const std::string& url) = 0 { 743 ASSERT_FALSE(false); 744 } 745 746 virtual void TestCompleted() = 0 { 747 ASSERT_FALSE(false); 748 } 749 750 // Holds the commit charge in KBytes at the start of the memory test run. 751 size_t start_commit_charge_; 752 753 // The index of the URL being tested. 754 size_t current_url_index_; 755 756 // Contains the list of urls against which the tests are run. 757 std::vector<std::string> urls_; 758 759 ProcessMemoryConsumptionMap memory_consumption_map_; 760 }; 761 762 // This class provides functionality to run the memory test against a reference 763 // chrome frame build. 764 class ChromeFrameMemoryTestReference : public ChromeFrameMemoryTest { 765 public: 766 virtual void SetUp() { 767 chrome_frame_registrar_.reset(new ScopedChromeFrameRegistrar( 768 ScopedChromeFrameRegistrar::SYSTEM_LEVEL)); 769 chrome_frame_registrar_->RegisterReferenceChromeFrameBuild(); 770 } 771 772 virtual void TearDown() { 773 // Reregisters the chrome frame DLL in the build directory. 774 chrome_frame_registrar_.reset(NULL); 775 } 776 }; 777 778 // This class overrides the hooks provided by the ChromeFrameActiveXContainer 779 // class and calls back into the ChromeFrameMemoryTest object instance, 780 // which measures ChromeFrame memory usage. 781 class ChromeFrameActiveXContainerMemory : public ChromeFrameActiveXContainer { 782 public: 783 ChromeFrameActiveXContainerMemory() 784 : delegate_(NULL) {} 785 786 ~ChromeFrameActiveXContainerMemory() {} 787 788 void Initialize(ChromeFrameMemoryTest* delegate) { 789 ASSERT_TRUE(delegate != NULL); 790 delegate_ = delegate; 791 } 792 793 protected: 794 virtual void OnLoadCallbackImpl(const VARIANT* param) { 795 delegate_->OnNavigationSuccess(param); 796 } 797 798 virtual void OnLoadErrorCallbackImpl(const VARIANT* param) { 799 delegate_->OnNavigationFailure(param); 800 } 801 802 ChromeFrameMemoryTest* delegate_; 803 }; 804 805 // This class runs memory tests against the ChromeFrame ActiveX. 806 template<class MemoryTestBase> 807 class ChromeFrameActiveXMemoryTest : public MemoryTestBase { 808 public: 809 ChromeFrameActiveXMemoryTest() 810 : chrome_frame_container_(NULL), 811 test_completed_(false) {} 812 813 ~ChromeFrameActiveXMemoryTest() { 814 } 815 816 void StartTest(const std::string& url, const std::string& test_name) { 817 ASSERT_TRUE(chrome_frame_container_ == NULL); 818 819 test_name_ = test_name; 820 821 SimpleModule module; 822 AtlAxWinInit(); 823 824 CComObject<ChromeFrameActiveXContainerMemory>::CreateInstance( 825 &chrome_frame_container_); 826 chrome_frame_container_->AddRef(); 827 828 chrome_frame_container_->Initialize(this); 829 830 chrome_frame_container_->CreateChromeFrameWindow(url.c_str()); 831 chrome_frame_container_->CreateControl(true); 832 833 module.RunMessageLoop(); 834 835 chrome_frame_container_->Release(); 836 837 PrintResults(test_name_.c_str()); 838 839 CoFreeUnusedLibraries(); 840 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100)); 841 } 842 843 void NavigateImpl(const std::string& url) { 844 ASSERT_TRUE(chrome_frame_container_ != NULL); 845 ASSERT_TRUE(!url.empty()); 846 chrome_frame_container_->Navigate(url.c_str()); 847 } 848 849 void TestCompleted() { 850 // This can get called multiple times if the last url results in a 851 // redirect. 852 if (!test_completed_) { 853 // Measure memory usage for the browser process. 854 AccountProcessMemoryUsage(chrome_browser_process_id()); 855 // Measure memory usage for the current process. 856 AccountProcessMemoryUsage(GetCurrentProcessId()); 857 858 test_completed_ = true; 859 EXPECT_TRUE(PostMessage(static_cast<HWND>(*chrome_frame_container_), 860 WM_CLOSE, 0, 0)); 861 } 862 } 863 864 protected: 865 CComObject<ChromeFrameActiveXContainerMemory>* chrome_frame_container_; 866 std::string test_name_; 867 bool test_completed_; 868 }; 869 870 // This class runs tests to measure chrome frame creation only. This will help 871 // track overall page load performance with chrome frame instances. 872 class ChromeFrameCreationTest : public ChromeFrameStartupTest { 873 protected: 874 virtual void RunStartupTestImpl(TimeTicks* start_time, 875 TimeTicks* end_time) { 876 SimpleModule module; 877 AtlAxWinInit(); 878 CComObjectStackEx<ChromeFrameActiveXContainer> wnd; 879 wnd.CreateChromeFrameWindow(startup_url_); 880 *start_time = TimeTicks::Now(); 881 wnd.CreateControl(false); 882 *end_time = TimeTicks::Now(); 883 } 884 }; 885 886 // This class provides functionality to run the chrome frame 887 // performance test against a reference build. 888 class ChromeFrameCreationTestReference : public ChromeFrameCreationTest { 889 public: 890 // override the browser directory to use the reference build instead. 891 virtual void SetUp() { 892 chrome_frame_registrar_.reset(new ScopedChromeFrameRegistrar( 893 ScopedChromeFrameRegistrar::SYSTEM_LEVEL)); 894 chrome_frame_registrar_->RegisterReferenceChromeFrameBuild(); 895 ChromeFrameStartupTest::SetUp(); 896 } 897 898 virtual void TearDown() { 899 chrome_frame_registrar_.reset(NULL); 900 } 901 }; 902 903 // This class measures the creation time for Flash, which would be used 904 // as a baseline to measure chrome frame creation performance. 905 class FlashCreationTest : public ChromeFrameStartupTest { 906 protected: 907 virtual void RunStartupTestImpl(TimeTicks* start_time, 908 TimeTicks* end_time) { 909 SimpleModule module; 910 AtlAxWinInit(); 911 CAxWindow host; 912 RECT rc = {0, 0, 800, 600}; 913 host.Create(NULL, rc, NULL, 914 WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 915 WS_EX_APPWINDOW | WS_EX_WINDOWEDGE); 916 EXPECT_TRUE(host.m_hWnd != NULL); 917 918 *start_time = TimeTicks::Now(); 919 HRESULT hr = host.CreateControl(L"ShockwaveFlash.ShockwaveFlash"); 920 EXPECT_HRESULT_SUCCEEDED(hr); 921 *end_time = TimeTicks::Now(); 922 923 ReleaseHostComReferences(host); 924 } 925 }; 926 927 // This class measures the creation time for Silverlight, which would be used 928 // as a baseline to measure chrome frame creation performance. 929 class SilverlightCreationTest : public ChromeFrameStartupTest { 930 protected: 931 virtual void RunStartupTestImpl(TimeTicks* start_time, 932 TimeTicks* end_time) { 933 SimpleModule module; 934 AtlAxWinInit(); 935 CAxWindow host; 936 RECT rc = {0, 0, 800, 600}; 937 host.Create(NULL, rc, NULL, 938 WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 939 WS_EX_APPWINDOW | WS_EX_WINDOWEDGE); 940 EXPECT_TRUE(host.m_hWnd != NULL); 941 942 *start_time = TimeTicks::Now(); 943 HRESULT hr = host.CreateControl(L"AgControl.AgControl"); 944 EXPECT_HRESULT_SUCCEEDED(hr); 945 *end_time = TimeTicks::Now(); 946 947 ReleaseHostComReferences(host); 948 } 949 }; 950 951 // TODO(rogerm): Flesh out the *PreReadImage* tests to validate an observed 952 // change in paging behaviour between raw loading and pre-reading. 953 954 // TODO(rogerm): Add checks to the *PreReadImage* tests to validate the 955 // handling of invalid pe files and paths as input. 956 957 TEST(ImagePreReader, PreReadImage) { 958 base::FilePath current_exe; 959 ASSERT_TRUE(PathService::Get(base::FILE_EXE, ¤t_exe)); 960 961 int64 file_size_64 = 0; 962 ASSERT_TRUE(base::GetFileSize(current_exe, &file_size_64)); 963 ASSERT_TRUE(file_size_64 < std::numeric_limits<std::size_t>::max()); 964 size_t file_size = static_cast<size_t>(file_size_64); 965 966 const wchar_t* module_path = current_exe.value().c_str(); 967 const size_t kStepSize = 2 * 1024 * 1024; 968 969 ASSERT_TRUE( 970 ImagePreReader::PreReadImage(module_path, 0, kStepSize)); 971 ASSERT_TRUE( 972 ImagePreReader::PreReadImage(module_path, file_size / 4, kStepSize)); 973 ASSERT_TRUE( 974 ImagePreReader::PreReadImage(module_path, file_size / 2, kStepSize)); 975 ASSERT_TRUE( 976 ImagePreReader::PreReadImage(module_path, file_size, kStepSize)); 977 ASSERT_TRUE( 978 ImagePreReader::PreReadImage(module_path, file_size * 2, kStepSize)); 979 } 980 981 TEST(ImagePreReader, PartialPreReadImage) { 982 base::FilePath current_exe; 983 ASSERT_TRUE(PathService::Get(base::FILE_EXE, ¤t_exe)); 984 985 const wchar_t* module_path = current_exe.value().c_str(); 986 const size_t kStepSize = 2 * 1024 * 1024; 987 988 ASSERT_TRUE( 989 ImagePreReader::PartialPreReadImage(module_path, 0, kStepSize)); 990 ASSERT_TRUE( 991 ImagePreReader::PartialPreReadImage(module_path, 25, kStepSize)); 992 ASSERT_TRUE( 993 ImagePreReader::PartialPreReadImage(module_path, 50, kStepSize)); 994 ASSERT_TRUE( 995 ImagePreReader::PartialPreReadImage(module_path, 100, kStepSize)); 996 ASSERT_TRUE( 997 ImagePreReader::PartialPreReadImage(module_path, 150, kStepSize)); 998 } 999 1000 TEST(ImagePreReader, PartialPreReadImageOnDisk) { 1001 base::FilePath current_exe; 1002 ASSERT_TRUE(PathService::Get(base::FILE_EXE, ¤t_exe)); 1003 1004 const wchar_t* module_path = current_exe.value().c_str(); 1005 const size_t kChunkSize = 2 * 1024 * 1024; 1006 1007 ASSERT_TRUE( 1008 ImagePreReader::PartialPreReadImageOnDisk(module_path, 0, kChunkSize)); 1009 ASSERT_TRUE( 1010 ImagePreReader::PartialPreReadImageOnDisk(module_path, 25, kChunkSize)); 1011 ASSERT_TRUE( 1012 ImagePreReader::PartialPreReadImageOnDisk(module_path, 50, kChunkSize)); 1013 ASSERT_TRUE( 1014 ImagePreReader::PartialPreReadImageOnDisk(module_path, 100, kChunkSize)); 1015 ASSERT_TRUE( 1016 ImagePreReader::PartialPreReadImageOnDisk(module_path, 150, kChunkSize)); 1017 } 1018 1019 TEST(ImagePreReader, PartialPreReadImageInMemory) { 1020 base::FilePath current_exe; 1021 ASSERT_TRUE(PathService::Get(base::FILE_EXE, ¤t_exe)); 1022 const wchar_t* module_path = current_exe.value().c_str(); 1023 1024 ASSERT_TRUE( 1025 ImagePreReader::PartialPreReadImageInMemory(module_path, 0)); 1026 ASSERT_TRUE( 1027 ImagePreReader::PartialPreReadImageInMemory(module_path, 25)); 1028 ASSERT_TRUE( 1029 ImagePreReader::PartialPreReadImageInMemory(module_path, 50)); 1030 ASSERT_TRUE( 1031 ImagePreReader::PartialPreReadImageInMemory(module_path, 100)); 1032 ASSERT_TRUE( 1033 ImagePreReader::PartialPreReadImageInMemory(module_path, 150)); 1034 } 1035 1036 TEST(ChromeFramePerf, DISABLED_HostActiveX) { 1037 // TODO(stoyan): Create a low integrity level thread && perform the test there 1038 SimpleModule module; 1039 AtlAxWinInit(); 1040 CComObjectStackEx<ChromeFrameActiveXContainerPerf> wnd; 1041 wnd.CreateChromeFrameWindow("http://www.google.com"); 1042 wnd.CreateControl(true); 1043 module.RunMessageLoop(); 1044 } 1045 1046 TEST(ChromeFramePerf, DISABLED_HostActiveXInvalidURL) { 1047 // TODO(stoyan): Create a low integrity level thread && perform the test there 1048 SimpleModule module; 1049 AtlAxWinInit(); 1050 CComObjectStackEx<ChromeFrameActiveXContainerPerf> wnd; 1051 wnd.CreateChromeFrameWindow("http://non-existent-domain.org/"); 1052 wnd.CreateControl(true); 1053 module.RunMessageLoop(); 1054 } 1055 1056 TEST_F(ChromeFrameStartupTestActiveX, PerfWarm) { 1057 RunStartupTest("warm", "t", "about:blank", false /* cold */, 0, NULL, 1058 true /* important */, false); 1059 } 1060 1061 TEST_F(ChromeFrameBinariesLoadTest, PerfWarm) { 1062 RunStartupTest("binary_load_warm", "t", "", false /* cold */, 0, NULL, 1063 true /* important */, false); 1064 } 1065 1066 TEST_F(ChromeFrameStartupTestActiveX, PerfCold) { 1067 SetConfigInt(L"PreRead", 0); 1068 base::FilePath binaries_to_evict[] = { 1069 ffmpegsumo_dll_, chrome_exe_, chrome_dll_, chrome_frame_dll_ 1070 }; 1071 RunStartupTest("cold", "t", "about:blank", true /* cold */, 1072 arraysize(binaries_to_evict), binaries_to_evict, 1073 false /* not important */, false); 1074 DeleteConfigValue(L"PreRead"); 1075 } 1076 1077 TEST_F(ChromeFrameStartupTestActiveX, PerfColdPreRead) { 1078 SetConfigInt(L"PreRead", 1); 1079 base::FilePath binaries_to_evict[] = { 1080 ffmpegsumo_dll_, chrome_exe_, chrome_dll_, chrome_frame_dll_ 1081 }; 1082 RunStartupTest("cold_preread", "t", "about:blank", true /* cold */, 1083 arraysize(binaries_to_evict), binaries_to_evict, 1084 false /* not important */, false); 1085 DeleteConfigValue(L"PreRead"); 1086 } 1087 1088 TEST_F(ChromeFrameBinariesLoadTest, PerfCold) { 1089 base::FilePath binaries_to_evict[] = {chrome_exe_, chrome_dll_}; 1090 RunStartupTest("binary_load_cold", "t", "", true /* cold */, 1091 arraysize(binaries_to_evict), binaries_to_evict, 1092 false /* not important */, false); 1093 } 1094 1095 TEST_F(ChromeFrameBinariesLoadTest, PerfColdPreRead) { 1096 base::FilePath binaries_to_evict[] = {chrome_exe_, chrome_dll_}; 1097 pre_read_type_ = kPreReadFull; 1098 RunStartupTest("binary_load_cold_preread", "t", "", true /* cold */, 1099 arraysize(binaries_to_evict), binaries_to_evict, 1100 false /* not important */, false); 1101 } 1102 1103 TEST_F(ChromeFrameBinariesLoadTest, PerfColdPartialPreRead15) { 1104 base::FilePath binaries_to_evict[] = {chrome_exe_, chrome_dll_}; 1105 pre_read_type_ = kPreReadPartial; 1106 percentage_to_preread_ = 15; 1107 RunStartupTest("binary_load_cold_partial_preread", "t", "", true /* cold */, 1108 arraysize(binaries_to_evict), binaries_to_evict, 1109 false /* not important */, false); 1110 } 1111 1112 1113 TEST_F(ChromeFrameBinariesLoadTest, PerfColdPartialPreRead25) { 1114 base::FilePath binaries_to_evict[] = {chrome_exe_, chrome_dll_}; 1115 pre_read_type_ = kPreReadPartial; 1116 percentage_to_preread_ = 25; 1117 RunStartupTest("binary_load_cold_partial_preread", "t", "", true /* cold */, 1118 arraysize(binaries_to_evict), binaries_to_evict, 1119 false /* not important */, false); 1120 } 1121 1122 TEST_F(ChromeFrameBinariesLoadTest, PerfColdPartialPreRead40) { 1123 base::FilePath binaries_to_evict[] = {chrome_exe_, chrome_dll_}; 1124 pre_read_type_ = kPreReadPartial; 1125 percentage_to_preread_ = 40; 1126 RunStartupTest("binary_load_cold_partial_preread", "t", "", true /* cold */, 1127 arraysize(binaries_to_evict), binaries_to_evict, 1128 false /* not important */, false); 1129 } 1130 1131 TEST_F(ChromeFrameStartupTestActiveXReference, PerfWarm) { 1132 RunStartupTest("warm", "t_ref", "about:blank", false /* cold */, 0, NULL, 1133 true /* important */, false); 1134 } 1135 1136 TEST_F(ChromeFrameStartupTestActiveX, PerfChromeFrameInitializationWarm) { 1137 RunStartupTest("ChromeFrame_init_warm", "t", "", false /* cold */, 0, 1138 NULL, true /* important */, false); 1139 } 1140 1141 TEST_F(ChromeFrameStartupTestActiveX, PerfChromeFrameInitializationCold) { 1142 base::FilePath binaries_to_evict[] = {chrome_frame_dll_}; 1143 RunStartupTest("ChromeFrame_init_cold", "t", "", true /* cold */, 1144 arraysize(binaries_to_evict), binaries_to_evict, 1145 false /* not important */, false); 1146 } 1147 1148 TEST_F(ChromeFrameStartupTestActiveXReference, 1149 PerfChromeFrameInitializationWarm) { 1150 RunStartupTest("ChromeFrame_init_warm", "t_ref", "", false /* cold */, 0, 1151 NULL, true /* important */, false); 1152 } 1153 1154 typedef ChromeFrameActiveXMemoryTest<ChromeFrameMemoryTest> 1155 RegularChromeFrameActiveXMemoryTest; 1156 1157 TEST_F(RegularChromeFrameActiveXMemoryTest, MemoryTestAboutBlank) { 1158 char *urls[] = {"about:blank"}; 1159 RunTest("memory_about_blank", urls, arraysize(urls)); 1160 } 1161 1162 // TODO(iyengar) 1163 // Revisit why the chrome frame dll does not unload correctly when this test is 1164 // run. 1165 // http://code.google.com/p/chromium/issues/detail?id=47812 1166 TEST_F(RegularChromeFrameActiveXMemoryTest, DISABLED_MemoryTestUrls) { 1167 // TODO(iyengar) 1168 // We should use static pages to measure memory usage. 1169 char *urls[] = { 1170 "http://www.youtube.com/watch?v=PN2HAroA12w", 1171 "http://www.youtube.com/watch?v=KmLJDrsaJmk&feature=channel" 1172 }; 1173 1174 RunTest("memory", urls, arraysize(urls)); 1175 } 1176 1177 typedef ChromeFrameActiveXMemoryTest<ChromeFrameMemoryTestReference> 1178 ReferenceBuildChromeFrameActiveXMemoryTest; 1179 1180 // Disabled to investigate why the chrome frame dll does not unload while 1181 // running this test. 1182 // http://code.google.com/p/chromium/issues/detail?id=47812 1183 TEST_F(ReferenceBuildChromeFrameActiveXMemoryTest, 1184 DISABLED_MemoryTestAboutBlank) { 1185 char *urls[] = {"about:blank"}; 1186 RunTest("memory_about_blank_reference", urls, arraysize(urls)); 1187 } 1188 1189 // TODO(iyengar) 1190 // Revisit why the chrome frame dll does not unload correctly when this test is 1191 // run. 1192 TEST_F(ReferenceBuildChromeFrameActiveXMemoryTest, DISABLED_MemoryTestUrls) { 1193 // TODO(iyengar) 1194 // We should use static pages to measure memory usage. 1195 char *urls[] = { 1196 "http://www.youtube.com/watch?v=PN2HAroA12w", 1197 "http://www.youtube.com/watch?v=KmLJDrsaJmk&feature=channel" 1198 }; 1199 1200 RunTest("memory_reference", urls, arraysize(urls)); 1201 } 1202 1203 TEST_F(ChromeFrameCreationTest, PerfWarm) { 1204 RunStartupTest("creation_warm", "t", "", false /* cold */, 0, 1205 NULL, true /* important */, false); 1206 } 1207 1208 TEST_F(ChromeFrameCreationTestReference, PerfWarm) { 1209 RunStartupTest("creation_warm", "t_ref", "about:blank", false /* cold */, 0, 1210 NULL, true /* not important */, false); 1211 } 1212 1213 TEST_F(FlashCreationTest, DISABLED_PerfWarm) { 1214 RunStartupTest("creation_warm", "t_flash", "", false /* cold */, 0, NULL, 1215 true /* not important */, false); 1216 } 1217 1218 TEST_F(SilverlightCreationTest, DISABLED_PerfWarm) { 1219 RunStartupTest("creation_warm", "t_silverlight", "", false /* cold */, 0, 1220 NULL, false /* not important */, false); 1221 } 1222 1223 TEST_F(ChromeFrameCreationTest, PerfCold) { 1224 base::FilePath binaries_to_evict[] = {chrome_frame_dll_}; 1225 1226 RunStartupTest("creation_cold", "t", "", true /* cold */, 1227 arraysize(binaries_to_evict), binaries_to_evict, 1228 true /* important */, false); 1229 } 1230 1231 // Attempt to evict the Flash control can fail on the buildbot as the dll 1232 // is marked read only. The test run is aborted if we fail to evict the file 1233 // from the cache. This could also fail if the Flash control is in use. 1234 // On Vista this could fail because of UAC 1235 TEST_F(FlashCreationTest, PerfCold) { 1236 base::win::RegKey flash_key(HKEY_CLASSES_ROOT, kFlashControlKey, KEY_READ); 1237 1238 std::wstring plugin_path; 1239 ASSERT_EQ(ERROR_SUCCESS, flash_key.ReadValue(L"", &plugin_path)); 1240 ASSERT_FALSE(plugin_path.empty()); 1241 1242 base::FilePath flash_path = base::FilePath(plugin_path); 1243 base::FilePath binaries_to_evict[] = {flash_path}; 1244 1245 RunStartupTest("creation_cold", "t_flash", "", true /* cold */, 1246 arraysize(binaries_to_evict), binaries_to_evict, 1247 false/* important */, true); 1248 } 1249 1250 // This test would fail on Vista due to UAC or if the Silverlight control is 1251 // in use. The test run is aborted if we fail to evict the file from the cache. 1252 // Disabling this test as the Silverlight dll does not seem to get unloaded 1253 // correctly causing the attempt to evict the dll from the system cache to 1254 // fail. 1255 TEST_F(SilverlightCreationTest, DISABLED_PerfCold) { 1256 base::win::RegKey silverlight_key(HKEY_CLASSES_ROOT, kSilverlightControlKey, 1257 KEY_READ); 1258 1259 std::wstring plugin_path; 1260 ASSERT_EQ(ERROR_SUCCESS, silverlight_key.ReadValue(L"", &plugin_path)); 1261 ASSERT_FALSE(plugin_path.empty()); 1262 1263 base::FilePath silverlight_path = base::FilePath(plugin_path); 1264 base::FilePath binaries_to_evict[] = {silverlight_path}; 1265 1266 RunStartupTest("creation_cold", "t_silverlight", "", true /* cold */, 1267 arraysize(binaries_to_evict), binaries_to_evict, 1268 false /* important */, true); 1269 } 1270 1271 namespace { 1272 1273 // Derive from this class in order to receive custom events traced 1274 // via TRACE_EVENT_XXXX macros from ChromeFrame/Chrome. 1275 class TracedEvents { 1276 public: 1277 virtual void OnTraceEventBegin(EVENT_TRACE* event) {} 1278 virtual void OnTraceEventEnd(EVENT_TRACE* event) {} 1279 virtual void OnTraceEventInstant(EVENT_TRACE* event) {} 1280 }; 1281 1282 // For the time being we pass to delegate only base::kTraceEventClass32 1283 // events i.e. these generated by TRACE_EVENT_XXXX macros. 1284 // We may need to add kernel provider and pass Process Start/Exit events etc, 1285 // but for the time being we stick with base::kChromeTraceProviderName 1286 // provider only. 1287 class EtwConsumer : public base::win::EtwTraceConsumerBase<EtwConsumer> { 1288 public: 1289 EtwConsumer() { 1290 set_delegate(NULL); 1291 } 1292 1293 ~EtwConsumer() { 1294 set_delegate(NULL); 1295 } 1296 1297 void set_delegate(TracedEvents* delegate) { 1298 delegate_ = delegate; 1299 } 1300 1301 static void ProcessEvent(EVENT_TRACE* event) { 1302 DCHECK(delegate_); 1303 if (event->Header.Guid != base::debug::kTraceEventClass32) 1304 return; 1305 if (event->Header.Class.Version != 0) 1306 return; 1307 1308 switch (event->Header.Class.Type) { 1309 case base::debug::kTraceEventTypeBegin: 1310 delegate_->OnTraceEventBegin(event); 1311 break; 1312 case base::debug::kTraceEventTypeEnd: 1313 delegate_->OnTraceEventEnd(event); 1314 break; 1315 case base::debug::kTraceEventTypeInstant: 1316 delegate_->OnTraceEventInstant(event); 1317 break; 1318 default: 1319 NOTREACHED(); 1320 break; 1321 } 1322 } 1323 1324 static TracedEvents* delegate_; 1325 }; 1326 1327 TracedEvents* EtwConsumer::delegate_ = NULL; 1328 }; // namespace 1329 1330 class EtwPerfSession { 1331 public: 1332 EtwPerfSession() { 1333 } 1334 1335 ~EtwPerfSession() { 1336 base::DeleteFile(etl_log_file_, false); 1337 } 1338 1339 void Start() { 1340 // To ensure there is no session leftover from crashes, previous runs, etc. 1341 base::win::EtwTraceProperties ignore; 1342 base::win::EtwTraceController::Stop(L"cf_perf", &ignore); 1343 ASSERT_TRUE(base::CreateTemporaryFile(&etl_log_file_)); 1344 ASSERT_HRESULT_SUCCEEDED(controller_.StartFileSession(L"cf_perf", 1345 etl_log_file_.value().c_str(), false)); 1346 ASSERT_HRESULT_SUCCEEDED(controller_.EnableProvider( 1347 base::debug::kChromeTraceProviderName, 1348 TRACE_LEVEL_INFORMATION, 1349 ~(base::debug::CAPTURE_STACK_TRACE))); 1350 } 1351 1352 HRESULT Stop() { 1353 return controller_.Stop(NULL); 1354 } 1355 1356 void AnalyzeOutput(TracedEvents* delegate) { 1357 EtwConsumer consumer; 1358 consumer.set_delegate(delegate); 1359 consumer.OpenFileSession(etl_log_file_.value().c_str()); 1360 consumer.Consume(); 1361 consumer.Close(); 1362 } 1363 1364 base::FilePath etl_log_file_; 1365 base::win::EtwTraceController controller_; 1366 }; 1367 1368 // Base class for the tracing event helper classes. 1369 class MonitorTraceBase { 1370 public: 1371 static bool IsMatchingEvent(EVENT_TRACE* event, 1372 const base::StringPiece& event_to_compare) { 1373 return event->MofLength > event_to_compare.size() && 1374 (memcmp(event_to_compare.data(), event->MofData, 1375 event_to_compare.size() + 1) == 0); 1376 } 1377 1378 bool is_valid() const { 1379 return !start_.is_null() && !end_.is_null() && start_ <= end_; 1380 } 1381 1382 base::TimeDelta duration() const { 1383 return end_ - start_; 1384 } 1385 1386 base::Time start_; 1387 base::Time end_; 1388 }; 1389 1390 // This class measures the time between begin and end events of a particular 1391 // type. 1392 class MonitorTracePair : public MonitorTraceBase, 1393 public TracedEvents { 1394 public: 1395 MonitorTracePair() : event_(NULL) { 1396 } 1397 1398 void set_interesting_event(const char* event) { 1399 event_ = event; 1400 } 1401 1402 virtual void OnTraceEventBegin(EVENT_TRACE* event) { 1403 if (IsMatchingEvent(event, event_)) { 1404 EXPECT_TRUE(start_.is_null()); 1405 start_ = base::Time::FromFileTime( 1406 reinterpret_cast<FILETIME&>(event->Header.TimeStamp)); 1407 } 1408 } 1409 1410 virtual void OnTraceEventEnd(EVENT_TRACE* event) { 1411 if (IsMatchingEvent(event, event_)) { 1412 EXPECT_FALSE(start_.is_null()); 1413 EXPECT_TRUE(end_.is_null()); 1414 end_ = base::Time::FromFileTime( 1415 reinterpret_cast<FILETIME&>(event->Header.TimeStamp)); 1416 } 1417 } 1418 1419 base::StringPiece event_; 1420 }; 1421 1422 // This class measures the time between two events. 1423 class MonitorTraceBetweenEventPair : public MonitorTraceBase, 1424 public TracedEvents { 1425 public: 1426 MonitorTraceBetweenEventPair() : event_end_(NULL), 1427 event_start_(NULL) { 1428 } 1429 1430 void set_start_event(const char* event) { 1431 event_start_ = event; 1432 } 1433 1434 void set_end_event(const char* event) { 1435 event_end_ = event; 1436 } 1437 1438 virtual void OnTraceEventBegin(EVENT_TRACE* event) { 1439 if (IsMatchingEvent(event, event_start_)) { 1440 EXPECT_TRUE(start_.is_null()); 1441 EXPECT_TRUE(end_.is_null()); 1442 start_ = base::Time::FromFileTime( 1443 reinterpret_cast<FILETIME&>(event->Header.TimeStamp)); 1444 } else if (IsMatchingEvent(event, event_end_)) { 1445 EXPECT_FALSE(start_.is_null()); 1446 EXPECT_TRUE(end_.is_null()); 1447 end_ = base::Time::FromFileTime( 1448 reinterpret_cast<FILETIME&>(event->Header.TimeStamp)); 1449 } 1450 } 1451 1452 virtual void OnTraceEventEnd(EVENT_TRACE* event) {} 1453 1454 base::StringPiece event_start_; 1455 base::StringPiece event_end_; 1456 }; 1457 1458 // The very same as UIPerfTest::PrintResultXXXX without the need to 1459 // create an UIPerfTest instance. 1460 void PrintResultsImpl(const std::string& measurement, 1461 const std::string& modifier, 1462 const std::string& trace, 1463 const std::string& values, 1464 const std::string& prefix, 1465 const std::string& suffix, 1466 const std::string& units, 1467 bool important) { 1468 // <*>RESULT <graph_name>: <trace_name>= <value> <units> 1469 // <*>RESULT <graph_name>: <trace_name>= {<mean>, <std deviation>} <units> 1470 // <*>RESULT <graph_name>: <trace_name>= [<value>,value,value,...,] <units> 1471 printf("%sRESULT %s%s: %s= %s%s%s %s\n", 1472 important ? "*" : "", measurement.c_str(), modifier.c_str(), 1473 trace.c_str(), prefix.c_str(), values.c_str(), suffix.c_str(), 1474 units.c_str()); 1475 } 1476 1477 void PrintResultList(const std::string& measurement, 1478 const std::string& modifier, 1479 const std::string& trace, 1480 const std::string& values, 1481 const std::string& units, 1482 bool important) { 1483 PrintResultsImpl(measurement, modifier, trace, values, 1484 "[", "]", units, important); 1485 } 1486 1487 bool RunSingleTestOutOfProc(const std::string& test_name) { 1488 base::FilePath path; 1489 if (!PathService::Get(base::DIR_EXE, &path)) 1490 return false; 1491 path = path.Append(L"chrome_frame_tests.exe"); 1492 1493 CommandLine cmd_line(path); 1494 // Always enable disabled tests. This method is not called with disabled 1495 // tests unless this flag was specified to the browser test executable. 1496 cmd_line.AppendSwitch("gtest_also_run_disabled_tests"); 1497 cmd_line.AppendSwitchASCII("gtest_filter", test_name); 1498 1499 base::ProcessHandle process_handle; 1500 if (!base::LaunchProcess(cmd_line, base::LaunchOptions(), &process_handle)) 1501 return false; 1502 1503 base::TimeDelta test_terminate_timeout = base::TimeDelta::FromMinutes(1); 1504 int exit_code = 0; 1505 if (!base::WaitForExitCodeWithTimeout(process_handle, &exit_code, 1506 test_terminate_timeout)) { 1507 LOG(ERROR) << "Test timeout (" << test_terminate_timeout.InMilliseconds() 1508 << " ms) exceeded for " << test_name; 1509 1510 exit_code = -1; // Set a non-zero exit code to signal a failure. 1511 1512 // Ensure that the process terminates. 1513 base::KillProcess(process_handle, -1, true); 1514 } 1515 1516 base::CloseProcessHandle(process_handle); 1517 1518 return exit_code == 0; 1519 } 1520 1521 template <class Monitor> 1522 void PrintPerfTestResults(const Monitor* monitor, 1523 int num_cycles, 1524 const char* result_name) { 1525 std::string times; 1526 1527 for (int i = 0; i < num_cycles; ++i) { 1528 ASSERT_TRUE(monitor[i].is_valid()); 1529 base::StringAppendF(×, 1530 "%.2f,", 1531 monitor[i].duration().InMillisecondsF()); 1532 } 1533 1534 PrintResultList(result_name, "", "t", times, "ms", false); 1535 } 1536 1537 TEST(TestAsPerfTest, MetaTag_createproxy) { 1538 const int kNumCycles = 10; 1539 1540 MonitorTracePair create_proxy_monitor[kNumCycles]; 1541 MonitorTraceBetweenEventPair browser_main_start_monitor[kNumCycles]; 1542 MonitorTraceBetweenEventPair browser_main_loop_monitor[kNumCycles]; 1543 MonitorTraceBetweenEventPair automation_provider_start_monitor[kNumCycles]; 1544 MonitorTraceBetweenEventPair automation_provider_connect_monitor[kNumCycles]; 1545 MonitorTracePair external_tab_navigate_monitor[kNumCycles]; 1546 MonitorTracePair pre_read_chrome_monitor[kNumCycles]; 1547 MonitorTraceBetweenEventPair renderer_main_monitor[kNumCycles]; 1548 1549 for (int i = 0; i < kNumCycles; ++i) { 1550 EtwPerfSession perf_session; 1551 ASSERT_NO_FATAL_FAILURE(perf_session.Start()); 1552 ASSERT_TRUE(RunSingleTestOutOfProc( 1553 "ChromeFrameTestWithWebServer.FullTabModeIE_MetaTag")); 1554 // Since we cannot have array of objects with a non-default constructor, 1555 // dedicated method is used to initialize watched event. 1556 create_proxy_monitor[i].set_interesting_event("chromeframe.createproxy"); 1557 1558 browser_main_start_monitor[i].set_start_event("chromeframe.createproxy"); 1559 browser_main_start_monitor[i].set_end_event("BrowserMain"); 1560 1561 browser_main_loop_monitor[i].set_start_event("BrowserMain"); 1562 browser_main_loop_monitor[i].set_end_event("BrowserMain:MESSAGE_LOOP"); 1563 1564 automation_provider_start_monitor[i].set_start_event("BrowserMain"); 1565 automation_provider_start_monitor[i].set_end_event( 1566 "AutomationProvider::AutomationProvider"); 1567 1568 automation_provider_connect_monitor[i].set_start_event( 1569 "AutomationProvider::AutomationProvider"); 1570 automation_provider_connect_monitor[i].set_end_event( 1571 "AutomationProvider::InitializeChannel"); 1572 1573 external_tab_navigate_monitor[i].set_interesting_event( 1574 "ExternalTabContainerWin::Navigate"); 1575 1576 renderer_main_monitor[i].set_start_event( 1577 "ExternalTabContainerWin::Navigate"); 1578 renderer_main_monitor[i].set_end_event("RendererMain"); 1579 1580 pre_read_chrome_monitor[i].set_interesting_event("PreReadImage"); 1581 1582 ASSERT_HRESULT_SUCCEEDED(perf_session.Stop()); 1583 1584 perf_session.AnalyzeOutput(&create_proxy_monitor[i]); 1585 perf_session.AnalyzeOutput(&browser_main_start_monitor[i]); 1586 perf_session.AnalyzeOutput(&browser_main_loop_monitor[i]); 1587 perf_session.AnalyzeOutput(&automation_provider_start_monitor[i]); 1588 perf_session.AnalyzeOutput(&automation_provider_connect_monitor[i]); 1589 perf_session.AnalyzeOutput(&external_tab_navigate_monitor[i]); 1590 perf_session.AnalyzeOutput(&pre_read_chrome_monitor[i]); 1591 perf_session.AnalyzeOutput(&renderer_main_monitor[i]); 1592 } 1593 1594 // Print results 1595 PrintPerfTestResults(create_proxy_monitor, kNumCycles, "createproxy"); 1596 PrintPerfTestResults(browser_main_start_monitor, kNumCycles, 1597 "browserstart"); 1598 PrintPerfTestResults(browser_main_loop_monitor, kNumCycles, 1599 "browserloop"); 1600 PrintPerfTestResults(automation_provider_start_monitor, kNumCycles, 1601 "automationproviderstart"); 1602 PrintPerfTestResults(automation_provider_connect_monitor, kNumCycles, 1603 "automationproviderconnect"); 1604 PrintPerfTestResults(external_tab_navigate_monitor, kNumCycles, 1605 "externaltabnavigate"); 1606 PrintPerfTestResults(renderer_main_monitor, kNumCycles, 1607 "beginrenderermain"); 1608 #ifdef NDEBUG 1609 PrintPerfTestResults(pre_read_chrome_monitor, kNumCycles, "PreReadImage"); 1610 #endif // NDEBUG 1611 } 1612