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