Sample of running integration test as a simple performance test.
Review URL: http://codereview.chromium.org/2934005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@52338 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome_frame/test/perf/chrome_frame_perftest.cc b/chrome_frame/test/perf/chrome_frame_perftest.cc
index 614cb0d..0b5d5d8 100644
--- a/chrome_frame/test/perf/chrome_frame_perftest.cc
+++ b/chrome_frame/test/perf/chrome_frame_perftest.cc
@@ -11,6 +11,8 @@
 
 #include "chrome_tab.h"  // Generated from chrome_tab.idl.
 
+#include "base/event_trace_controller_win.h"
+#include "base/event_trace_consumer_win.h"
 #include "base/file_path.h"
 #include "base/file_util.h"
 #include "base/path_service.h"
@@ -22,6 +24,7 @@
 #include "base/scoped_variant_win.h"
 #include "base/string_util.h"
 #include "base/time.h"
+#include "base/trace_event_win.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_paths_internal.h"
@@ -1120,3 +1123,227 @@
                  arraysize(binaries_to_evict), binaries_to_evict,
                  false /* important */, true);
 }
+
+namespace {
+
+// Derive from this class in order to receive custom events traced
+// via TRACE_EVENT_XXXX macros from ChromeFrame/Chrome.
+class TracedEvents {
+ public:
+  virtual void OnTraceEventBegin(EVENT_TRACE* event) {}
+  virtual void OnTraceEventEnd(EVENT_TRACE* event) {}
+  virtual void OnTraceEventInstant(EVENT_TRACE* event) {}
+};
+
+// For the time being we pass to delegate only base::kTraceEventClass32
+// events i.e. these generated by TRACE_EVENT_XXXX macros.
+// We may need to add kernel provider and pass Process Start/Exit events etc,
+// but for the time being we stick with base::kChromeTraceProviderName
+// provider only.
+class EtwConsumer : public EtwTraceConsumerBase<EtwConsumer> {
+ public:
+  EtwConsumer() {
+    set_delegate(NULL);
+  }
+
+  ~EtwConsumer() {
+    set_delegate(NULL);
+  }
+
+  void set_delegate(TracedEvents* delegate) {
+    delegate_ = delegate;
+  }
+
+  static void ProcessEvent(EVENT_TRACE* event) {
+    DCHECK(delegate_);
+    if (event->Header.Guid != base::kTraceEventClass32)
+      return;
+    if (event->Header.Class.Version != 0)
+      return;
+
+    switch (event->Header.Class.Type) {
+      case base::kTraceEventTypeBegin:
+        delegate_->OnTraceEventBegin(event);
+        break;
+      case base::kTraceEventTypeEnd:
+        delegate_->OnTraceEventEnd(event);
+        break;
+      case base::kTraceEventTypeInstant:
+        delegate_->OnTraceEventInstant(event);
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
+  }
+
+  static TracedEvents* delegate_;
+};
+
+TracedEvents* EtwConsumer::delegate_ = NULL;
+};  // namespace
+
+class EtwPerfSession {
+ public:
+  EtwPerfSession() {
+  }
+
+  ~EtwPerfSession() {
+    file_util::Delete(etl_log_file_, false);
+  }
+
+  void Start() {
+    // To ensure there is no session leftover from crashes, previous runs, etc.
+    // EtwTraceController::Stop(L"cf_perf", NULL);
+    ASSERT_TRUE(file_util::CreateTemporaryFile(&etl_log_file_));
+    ASSERT_HRESULT_SUCCEEDED(controller_.StartFileSession(L"cf_perf",
+        etl_log_file_.value().c_str(), false));
+    ASSERT_HRESULT_SUCCEEDED(controller_.EnableProvider(
+        base::kChromeTraceProviderName,
+        TRACE_LEVEL_INFORMATION,
+        ~(base::CAPTURE_STACK_TRACE)));
+  }
+
+  void AnalyzeOutput(TracedEvents* delegate) {
+    EXPECT_HRESULT_SUCCEEDED(controller_.Stop(NULL));
+    EtwConsumer consumer;
+    consumer.set_delegate(delegate);
+    consumer.OpenFileSession(etl_log_file_.value().c_str());
+    consumer.Consume();
+    consumer.Close();
+  }
+
+  FilePath etl_log_file_;
+  EtwTraceController controller_;
+};
+
+class MonitorTracePair : public TracedEvents {
+ public:
+  MonitorTracePair() : event_(NULL) {
+  }
+
+  void set_interesting_event(const char* event) {
+    event_ = event;
+  }
+
+  virtual void OnTraceEventBegin(EVENT_TRACE* event) {
+    if (IsMatchingEvent(event)) {
+      EXPECT_TRUE(start_.is_null());
+      start_ = base::Time::FromFileTime(
+        reinterpret_cast<FILETIME&>(event->Header.TimeStamp));
+    }
+  }
+
+  virtual void OnTraceEventEnd(EVENT_TRACE* event) {
+    if (IsMatchingEvent(event)) {
+      EXPECT_FALSE(start_.is_null());
+      EXPECT_TRUE(end_.is_null());
+      end_ = base::Time::FromFileTime(
+        reinterpret_cast<FILETIME&>(event->Header.TimeStamp));
+    }
+  }
+
+  bool IsMatchingEvent(EVENT_TRACE* event) {
+    return event->MofLength > event_.size() &&
+      (memcmp(event_.data(), event->MofData, event_.size() + 1) == 0);
+  }
+
+  bool is_valid() const {
+    return !start_.is_null() && !end_.is_null() && start_ <= end_;
+  }
+
+  base::TimeDelta duration() const {
+    return end_ - start_;
+  }
+
+  base::Time start_;
+  base::Time end_;
+  base::StringPiece event_;
+};
+
+
+// The very same as UITestBase::PrintResultXXXX without the need to
+// create an UITestBase instance.
+void PrintResultsImpl(const std::string& measurement,
+                      const std::string& modifier,
+                      const std::string& trace,
+                      const std::string& values,
+                      const std::string& prefix,
+                      const std::string& suffix,
+                      const std::string& units,
+                      bool important) {
+  // <*>RESULT <graph_name>: <trace_name>= <value> <units>
+  // <*>RESULT <graph_name>: <trace_name>= {<mean>, <std deviation>} <units>
+  // <*>RESULT <graph_name>: <trace_name>= [<value>,value,value,...,] <units>
+  printf("%sRESULT %s%s: %s= %s%s%s %s\n",
+         important ? "*" : "", measurement.c_str(), modifier.c_str(),
+         trace.c_str(), prefix.c_str(), values.c_str(), suffix.c_str(),
+         units.c_str());
+}
+
+void PrintResultList(const std::string& measurement,
+                     const std::string& modifier,
+                     const std::string& trace,
+                     const std::string& values,
+                     const std::string& units,
+                     bool important) {
+  PrintResultsImpl(measurement, modifier, trace, values,
+      "[", "]", units, important);
+}
+
+bool RunSingleTestOutOfProc(const std::string& test_name) {
+  FilePath path;
+  PathService::Get(base::DIR_EXE, &path);
+  path = path.Append(L"chrome_frame_tests.exe");
+
+  CommandLine cmd_line(path);
+  // Always enable disabled tests.  This method is not called with disabled
+  // tests unless this flag was specified to the browser test executable.
+  cmd_line.AppendSwitch("gtest_also_run_disabled_tests");
+  cmd_line.AppendSwitchWithValue("gtest_filter", test_name);
+
+  base::ProcessHandle process_handle;
+  if (!base::LaunchApp(cmd_line, false, false, &process_handle))
+    return false;
+
+  int test_terminate_timeout_ms = 30 * 1000;
+  int exit_code = 0;
+  if (!base::WaitForExitCodeWithTimeout(process_handle, &exit_code,
+    test_terminate_timeout_ms)) {
+      LOG(ERROR) << "Test timeout (" << test_terminate_timeout_ms
+        << " ms) exceeded for " << test_name;
+
+      exit_code = -1;  // Set a non-zero exit code to signal a failure.
+
+      // Ensure that the process terminates.
+      base::KillProcess(process_handle, -1, true);
+  }
+
+  return exit_code == 0;
+}
+
+TEST(TestAsPerfTest, MetaTag_createproxy) {
+  const int kNumCycles = 10;
+  MonitorTracePair monitor[kNumCycles];
+
+  for (int i = 0; i < kNumCycles; ++i) {
+    EtwPerfSession perf_session;
+    ASSERT_NO_FATAL_FAILURE(perf_session.Start());
+    ASSERT_TRUE(RunSingleTestOutOfProc(
+        "ChromeFrameTestWithWebServer.FullTabModeIE_MetaTag"));
+    // Since we cannot have array of objects with a non-default constructor,
+    // dedicated method is used to initialize watched event.
+    monitor[i].set_interesting_event("chromeframe.createproxy");
+    perf_session.AnalyzeOutput(&monitor[i]);
+  }
+
+  // Print results
+  std::string times;
+  for (int i = 0; i < kNumCycles; ++i) {
+    ASSERT_TRUE(monitor[i].is_valid());
+    StringAppendF(&times, "%.2f,", monitor[i].duration().InMillisecondsF());
+  }
+
+  bool important = false;
+  PrintResultList("createproxy", "", "t", times, "ms", important);
+}