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