text: // Copyright (c) 2011 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "base/trace_event/trace_event.h"
  
  #include <strstream>
  
  #include "base/at_exit.h"
  #include "base/basictypes.h"
  #include "base/files/file_util.h"
  #include "base/trace_event/trace_event.h"
  #include "base/trace_event/trace_event_win.h"
  #include "base/win/event_trace_consumer.h"
  #include "base/win/event_trace_controller.h"
  #include "base/win/event_trace_provider.h"
  #include "base/win/windows_version.h"
  #include "testing/gmock/include/gmock/gmock.h"
  #include "testing/gtest/include/gtest/gtest.h"
  #include <initguid.h>  // NOLINT - must be last include.
  
  namespace base {
  namespace trace_event {
  
  namespace {
  
  using testing::_;
  using testing::AnyNumber;
  using testing::InSequence;
  using testing::Ge;
  using testing::Le;
  using testing::NotNull;
  
  using base::win::EtwEventType;
  using base::win::EtwTraceConsumerBase;
  using base::win::EtwTraceController;
  using base::win::EtwTraceProperties;
  
  // Data for unittests traces.
  const char kEmpty[] = "";
  const char kName[] = "unittest.trace_name";
  const char kExtra[] = "UnittestDummyExtraString";
  const void* kId = kName;
  
  const wchar_t kTestSessionName[] = L"TraceEvent unittest session";
  
  MATCHER_P(BufferStartsWith, str, "Buffer starts with") {
    return memcmp(arg, str.c_str(), str.length()) == 0;
  }
  
  // Duplicated from <evntrace.h> to fix link problems.
  DEFINE_GUID( /* 68fdd900-4a3e-11d1-84f4-0000f80464e3 */
      kEventTraceGuid,
      0x68fdd900,
      0x4a3e,
      0x11d1,
      0x84, 0xf4, 0x00, 0x00, 0xf8, 0x04, 0x64, 0xe3);
  
  class TestEventConsumer: public EtwTraceConsumerBase<TestEventConsumer> {
   public:
    TestEventConsumer() {
      EXPECT_TRUE(current_ == NULL);
      current_ = this;
    }
  
    ~TestEventConsumer() {
      EXPECT_TRUE(current_ == this);
      current_ = NULL;
    }
  
    MOCK_METHOD4(Event, void(REFGUID event_class,
                        EtwEventType event_type,
                        size_t buf_len,
                        const void* buf));
  
    static void ProcessEvent(EVENT_TRACE* event) {
      ASSERT_TRUE(current_ != NULL);
      current_->Event(event->Header.Guid,
                      event->Header.Class.Type,
                      event->MofLength,
                      event->MofData);
    }
  
   private:
    static TestEventConsumer* current_;
  };
  
  TestEventConsumer* TestEventConsumer::current_ = NULL;
  
  class TraceEventWinTest: public testing::Test {
   public:
    TraceEventWinTest() {
    }
  
    void SetUp() override {
      bool is_xp = win::GetVersion() < base::win::VERSION_VISTA;
  
      if (is_xp) {
        // Tear down any dangling session from an earlier failing test.
        EtwTraceProperties ignore;
        EtwTraceController::Stop(kTestSessionName, &ignore);
      }
  
      // Resurrect and initialize the TraceLog singleton instance.
      // On Vista and better, we need the provider registered before we
      // start the private, in-proc session, but on XP we need the global
      // session created and the provider enabled before we register our
      // provider.
      TraceEventETWProvider* tracelog = NULL;
      if (!is_xp) {
        TraceEventETWProvider::Resurrect();
        tracelog = TraceEventETWProvider::GetInstance();
        ASSERT_TRUE(tracelog != NULL);
        ASSERT_FALSE(tracelog->IsTracing());
      }
  
      // Create the log file.
      ASSERT_TRUE(base::CreateTemporaryFile(&log_file_));
  
      // Create a private log session on the file.
      EtwTraceProperties prop;
      ASSERT_HRESULT_SUCCEEDED(prop.SetLoggerFileName(log_file_.value().c_str()));
      EVENT_TRACE_PROPERTIES& p = *prop.get();
      p.Wnode.ClientContext = 1;  // QPC timer accuracy.
      p.LogFileMode = EVENT_TRACE_FILE_MODE_SEQUENTIAL;   // Sequential log.
  
      // On Vista and later, we create a private in-process log session, because
      // otherwise we'd need administrator privileges. Unfortunately we can't
      // do the same on XP and better, because the semantics of a private
      // logger session are different, and the IN_PROC flag is not supported.
      if (!is_xp) {
        p.LogFileMode |= EVENT_TRACE_PRIVATE_IN_PROC |  // In-proc for non-admin.
            EVENT_TRACE_PRIVATE_LOGGER_MODE;  // Process-private log.
      }
  
      p.MaximumFileSize = 100;  // 100M file size.
      p.FlushTimer = 1;  // 1 second flush lag.
      ASSERT_HRESULT_SUCCEEDED(controller_.Start(kTestSessionName, &prop));
  
      // Enable the TraceLog provider GUID.
      ASSERT_HRESULT_SUCCEEDED(
          controller_.EnableProvider(kChromeTraceProviderName,
                                     TRACE_LEVEL_INFORMATION,
                                     0));
  
      if (is_xp) {
        TraceEventETWProvider::Resurrect();
        tracelog = TraceEventETWProvider::GetInstance();
      }
      ASSERT_TRUE(tracelog != NULL);
      EXPECT_TRUE(tracelog->IsTracing());
    }
  
    void TearDown() override {
      EtwTraceProperties prop;
      if (controller_.session() != 0)
        EXPECT_HRESULT_SUCCEEDED(controller_.Stop(&prop));
  
      if (!log_file_.value().empty())
        base::DeleteFile(log_file_, false);
  
      // We want our singleton torn down after each test.
      TraceLog::DeleteForTesting();
    }
  
    void ExpectEvent(REFGUID guid,
                     EtwEventType type,
                     const char* name,
                     size_t name_len,
                     const void* id,
                     const char* extra,
                     size_t extra_len) {
      // Build the trace event buffer we expect will result from this.
      std::stringbuf str;
      str.sputn(name, name_len + 1);
      str.sputn(reinterpret_cast<const char*>(&id), sizeof(id));
      str.sputn(extra, extra_len + 1);
  
      // And set up the expectation for the event callback.
      EXPECT_CALL(consumer_, Event(guid,
                                   type,
                                   testing::Ge(str.str().length()),
                                   BufferStartsWith(str.str())));
    }
  
    void ExpectPlayLog() {
      // Ignore EventTraceGuid events.
      EXPECT_CALL(consumer_, Event(kEventTraceGuid, _, _, _))
          .Times(AnyNumber());
    }
  
    void PlayLog() {
      EtwTraceProperties prop;
      EXPECT_HRESULT_SUCCEEDED(controller_.Flush(&prop));
      EXPECT_HRESULT_SUCCEEDED(controller_.Stop(&prop));
      ASSERT_HRESULT_SUCCEEDED(
          consumer_.OpenFileSession(log_file_.value().c_str()));
  
      ASSERT_HRESULT_SUCCEEDED(consumer_.Consume());
    }
  
   private:
    // We want our singleton torn down after each test.
    ShadowingAtExitManager at_exit_manager_;
    EtwTraceController controller_;
    FilePath log_file_;
    TestEventConsumer consumer_;
  };
  
  }  // namespace
  
  
  TEST_F(TraceEventWinTest, TraceLog) {
    ExpectPlayLog();
  
    // The events should arrive in the same sequence as the expects.
    InSequence in_sequence;
  
    // Full argument version, passing lengths explicitly.
    TraceEventETWProvider::Trace(kName,
                          strlen(kName),
                          TRACE_EVENT_PHASE_BEGIN,
                          kId,
                          kExtra,
                          strlen(kExtra));
  
    ExpectEvent(kTraceEventClass32,
                kTraceEventTypeBegin,
                kName, strlen(kName),
                kId,
                kExtra, strlen(kExtra));
  
    // Const char* version.
    TraceEventETWProvider::Trace(static_cast<const char*>(kName),
                          TRACE_EVENT_PHASE_END,
                          kId,
                          static_cast<const char*>(kExtra));
  
    ExpectEvent(kTraceEventClass32,
                kTraceEventTypeEnd,
                kName, strlen(kName),
                kId,
                kExtra, strlen(kExtra));
  
    // std::string extra version.
    TraceEventETWProvider::Trace(static_cast<const char*>(kName),
                          TRACE_EVENT_PHASE_INSTANT,
                          kId,
                          std::string(kExtra));
  
    ExpectEvent(kTraceEventClass32,
                kTraceEventTypeInstant,
                kName, strlen(kName),
                kId,
                kExtra, strlen(kExtra));
  
  
    // Test for sanity on NULL inputs.
    TraceEventETWProvider::Trace(NULL,
                          0,
                          TRACE_EVENT_PHASE_BEGIN,
                          kId,
                          NULL,
                          0);
  
    ExpectEvent(kTraceEventClass32,
                kTraceEventTypeBegin,
                kEmpty, 0,
                kId,
                kEmpty, 0);
  
    TraceEventETWProvider::Trace(NULL,
                          TraceEventETWProvider::kUseStrlen,
                          TRACE_EVENT_PHASE_END,
                          kId,
                          NULL,
                          TraceEventETWProvider::kUseStrlen);
  
    ExpectEvent(kTraceEventClass32,
                kTraceEventTypeEnd,
                kEmpty, 0,
                kId,
                kEmpty, 0);
  
    PlayLog();
  }
  
  TEST_F(TraceEventWinTest, Macros) {
    ExpectPlayLog();
  
    // The events should arrive in the same sequence as the expects.
    InSequence in_sequence;
  
    TRACE_EVENT_BEGIN_ETW(kName, kId, kExtra);
    ExpectEvent(kTraceEventClass32,
                kTraceEventTypeBegin,
                kName, strlen(kName),
                kId,
                kExtra, strlen(kExtra));
  
    TRACE_EVENT_END_ETW(kName, kId, kExtra);
    ExpectEvent(kTraceEventClass32,
                kTraceEventTypeEnd,
                kName, strlen(kName),
                kId,
                kExtra, strlen(kExtra));
  
    TRACE_EVENT_INSTANT_ETW(kName, kId, kExtra);
    ExpectEvent(kTraceEventClass32,
                kTraceEventTypeInstant,
                kName, strlen(kName),
                kId,
                kExtra, strlen(kExtra));
  
    PlayLog();
  }
  
  }  // namespace trace_event
  }  // namespace base
  