Home | History | Annotate | Download | only in perf
      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(&times, "%.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, &current_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, &current_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, &current_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, &current_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(&times,
   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