123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 |
- // Copyright (c) 2012 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.
- //
- // Unit tests for event trace consumer base class.
- #include "base/win/event_trace_consumer.h"
- #include <objbase.h>
- #include <iterator>
- #include <list>
- #include "base/files/file_path.h"
- #include "base/files/file_util.h"
- #include "base/files/scoped_temp_dir.h"
- #include "base/logging.h"
- #include "base/process/process_handle.h"
- #include "base/strings/string_util.h"
- #include "base/strings/stringprintf.h"
- #include "base/win/event_trace_controller.h"
- #include "base/win/event_trace_provider.h"
- #include "base/win/scoped_handle.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #include <initguid.h> // NOLINT - has to be last
- namespace base {
- namespace win {
- namespace {
- using EventQueue = std::list<EVENT_TRACE>;
- class TestConsumer : public EtwTraceConsumerBase<TestConsumer> {
- public:
- TestConsumer() {
- sank_event_.Set(::CreateEvent(nullptr, TRUE, FALSE, nullptr));
- ClearQueue();
- }
- TestConsumer(const TestConsumer&) = delete;
- TestConsumer& operator=(const TestConsumer&) = delete;
- ~TestConsumer() {
- ClearQueue();
- sank_event_.Close();
- }
- void ClearQueue() {
- for (EventQueue::const_iterator it(events_.begin()), end(events_.end());
- it != end; ++it) {
- delete[] reinterpret_cast<char*>(it->MofData);
- }
- events_.clear();
- }
- static void EnqueueEvent(EVENT_TRACE* event) {
- events_.push_back(*event);
- EVENT_TRACE& back = events_.back();
- if (event->MofData != nullptr && event->MofLength != 0) {
- back.MofData = new char[event->MofLength];
- memcpy(back.MofData, event->MofData, event->MofLength);
- }
- }
- static void ProcessEvent(EVENT_TRACE* event) {
- EnqueueEvent(event);
- ::SetEvent(sank_event_.get());
- }
- static ScopedHandle sank_event_;
- static EventQueue events_;
- };
- ScopedHandle TestConsumer::sank_event_;
- EventQueue TestConsumer::events_;
- class EtwTraceConsumerBaseTest : public testing::Test {
- public:
- EtwTraceConsumerBaseTest()
- : session_name_(StringPrintf(L"TestSession-%d", GetCurrentProcId())) {}
- void SetUp() override {
- // Cleanup any potentially dangling sessions.
- EtwTraceProperties ignore;
- EtwTraceController::Stop(session_name_.c_str(), &ignore);
- // Allocate a new GUID for each provider test.
- ASSERT_HRESULT_SUCCEEDED(::CoCreateGuid(&test_provider_));
- }
- void TearDown() override {
- // Cleanup any potentially dangling sessions.
- EtwTraceProperties ignore;
- EtwTraceController::Stop(session_name_.c_str(), &ignore);
- }
- protected:
- GUID test_provider_;
- std::wstring session_name_;
- };
- } // namespace
- TEST_F(EtwTraceConsumerBaseTest, Initialize) {
- TestConsumer consumer_;
- }
- TEST_F(EtwTraceConsumerBaseTest, OpenRealtimeSucceedsWhenNoSession) {
- TestConsumer consumer_;
- ASSERT_HRESULT_SUCCEEDED(
- consumer_.OpenRealtimeSession(session_name_.c_str()));
- }
- TEST_F(EtwTraceConsumerBaseTest, ConsumerImmediateFailureWhenNoSession) {
- TestConsumer consumer_;
- ASSERT_HRESULT_SUCCEEDED(
- consumer_.OpenRealtimeSession(session_name_.c_str()));
- ASSERT_HRESULT_FAILED(consumer_.Consume());
- }
- namespace {
- class EtwTraceConsumerRealtimeTest : public EtwTraceConsumerBaseTest {
- public:
- void SetUp() override {
- EtwTraceConsumerBaseTest::SetUp();
- ASSERT_HRESULT_SUCCEEDED(
- consumer_.OpenRealtimeSession(session_name_.c_str()));
- }
- void TearDown() override {
- consumer_.Close();
- EtwTraceConsumerBaseTest::TearDown();
- }
- DWORD ConsumerThread() {
- ::SetEvent(consumer_ready_.get());
- return consumer_.Consume();
- }
- static DWORD WINAPI ConsumerThreadMainProc(void* arg) {
- return reinterpret_cast<EtwTraceConsumerRealtimeTest*>(arg)
- ->ConsumerThread();
- }
- HRESULT StartConsumerThread() {
- consumer_ready_.Set(::CreateEvent(nullptr, TRUE, FALSE, nullptr));
- EXPECT_TRUE(consumer_ready_.is_valid());
- consumer_thread_.Set(
- ::CreateThread(nullptr, 0, ConsumerThreadMainProc, this, 0, nullptr));
- if (consumer_thread_.get() == nullptr)
- return HRESULT_FROM_WIN32(::GetLastError());
- HANDLE events[] = {consumer_ready_.get(), consumer_thread_.get()};
- DWORD result =
- ::WaitForMultipleObjects(std::size(events), events, FALSE, INFINITE);
- switch (result) {
- case WAIT_OBJECT_0:
- // The event was set, the consumer_ is ready.
- return S_OK;
- case WAIT_OBJECT_0 + 1: {
- // The thread finished. This may race with the event, so check
- // explicitly for the event here, before concluding there's trouble.
- if (::WaitForSingleObject(consumer_ready_.get(), 0) == WAIT_OBJECT_0)
- return S_OK;
- DWORD exit_code = 0;
- if (::GetExitCodeThread(consumer_thread_.get(), &exit_code))
- return exit_code;
- return HRESULT_FROM_WIN32(::GetLastError());
- }
- default:
- return E_UNEXPECTED;
- }
- }
- // Waits for consumer_ thread to exit, and returns its exit code.
- HRESULT JoinConsumerThread() {
- if (::WaitForSingleObject(consumer_thread_.get(), INFINITE) !=
- WAIT_OBJECT_0) {
- return HRESULT_FROM_WIN32(::GetLastError());
- }
- DWORD exit_code = 0;
- if (::GetExitCodeThread(consumer_thread_.get(), &exit_code))
- return exit_code;
- return HRESULT_FROM_WIN32(::GetLastError());
- }
- TestConsumer consumer_;
- ScopedHandle consumer_ready_;
- ScopedHandle consumer_thread_;
- };
- } // namespace
- TEST_F(EtwTraceConsumerRealtimeTest, ConsumerReturnsWhenSessionClosed) {
- EtwTraceController controller;
- if (controller.StartRealtimeSession(session_name_.c_str(), 1024) ==
- E_ACCESSDENIED) {
- VLOG(1) << "You must be an administrator to run this test on Vista";
- return;
- }
- // Start the consumer_.
- ASSERT_HRESULT_SUCCEEDED(StartConsumerThread());
- // Wait around for the consumer_ thread a bit. This is inherently racy because
- // the consumer thread says that it is ready and then calls Consume() which
- // calls ::ProcessTrace. We need to call WaitForSingleObject after the call to
- // ::ProcessTrace but there is no way to know when that call has been made.
- // With a timeout of 50 ms this test was failing frequently when the system
- // was under load. It is hoped that 500 ms will be enough.
- ASSERT_EQ(static_cast<DWORD>(WAIT_TIMEOUT),
- ::WaitForSingleObject(consumer_thread_.get(), 500));
- ASSERT_HRESULT_SUCCEEDED(controller.Stop(nullptr));
- // The consumer_ returns success on session stop.
- ASSERT_HRESULT_SUCCEEDED(JoinConsumerThread());
- }
- namespace {
- // clang-format off
- // {57E47923-A549-476f-86CA-503D57F59E62}
- DEFINE_GUID(
- kTestEventType,
- 0x57e47923, 0xa549, 0x476f, 0x86, 0xca, 0x50, 0x3d, 0x57, 0xf5, 0x9e, 0x62);
- // clang-format on
- } // namespace
- TEST_F(EtwTraceConsumerRealtimeTest, ConsumeEvent) {
- EtwTraceController controller;
- if (controller.StartRealtimeSession(session_name_.c_str(), 1024) ==
- E_ACCESSDENIED) {
- VLOG(1) << "You must be an administrator to run this test on Vista";
- return;
- }
- ASSERT_HRESULT_SUCCEEDED(controller.EnableProvider(
- test_provider_, TRACE_LEVEL_VERBOSE, 0xFFFFFFFF));
- EtwTraceProvider provider(test_provider_);
- ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), provider.Register());
- // Start the consumer_.
- ASSERT_HRESULT_SUCCEEDED(StartConsumerThread());
- ASSERT_EQ(0u, TestConsumer::events_.size());
- EtwMofEvent<1> event(kTestEventType, 1, TRACE_LEVEL_ERROR);
- EXPECT_EQ(static_cast<DWORD>(ERROR_SUCCESS), provider.Log(&event.header));
- EXPECT_EQ(WAIT_OBJECT_0,
- ::WaitForSingleObject(TestConsumer::sank_event_.get(), INFINITE));
- ASSERT_HRESULT_SUCCEEDED(controller.Stop(nullptr));
- ASSERT_HRESULT_SUCCEEDED(JoinConsumerThread());
- ASSERT_NE(0u, TestConsumer::events_.size());
- }
- namespace {
- // We run events through a file session to assert that
- // the content comes through.
- class EtwTraceConsumerDataTest : public EtwTraceConsumerBaseTest {
- public:
- EtwTraceConsumerDataTest() = default;
- void SetUp() override {
- EtwTraceConsumerBaseTest::SetUp();
- EtwTraceProperties prop;
- EtwTraceController::Stop(session_name_.c_str(), &prop);
- // Create a temp dir for this test.
- ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
- // Construct a temp file name in our dir.
- temp_file_ = temp_dir_.GetPath().Append(FILE_PATH_LITERAL("test.etl"));
- }
- void TearDown() override {
- EXPECT_TRUE(DeleteFile(temp_file_));
- EtwTraceConsumerBaseTest::TearDown();
- }
- HRESULT LogEventToTempSession(PEVENT_TRACE_HEADER header) {
- EtwTraceController controller;
- // Set up a file session.
- HRESULT hr = controller.StartFileSession(session_name_.c_str(),
- temp_file_.value().c_str());
- if (FAILED(hr))
- return hr;
- // Enable our provider.
- EXPECT_HRESULT_SUCCEEDED(controller.EnableProvider(
- test_provider_, TRACE_LEVEL_VERBOSE, 0xFFFFFFFF));
- EtwTraceProvider provider(test_provider_);
- // Then register our provider, means we get a session handle immediately.
- EXPECT_EQ(static_cast<DWORD>(ERROR_SUCCESS), provider.Register());
- // Trace the event, it goes to the temp file.
- EXPECT_EQ(static_cast<DWORD>(ERROR_SUCCESS), provider.Log(header));
- EXPECT_HRESULT_SUCCEEDED(controller.DisableProvider(test_provider_));
- EXPECT_HRESULT_SUCCEEDED(provider.Unregister());
- EXPECT_HRESULT_SUCCEEDED(controller.Flush(nullptr));
- EXPECT_HRESULT_SUCCEEDED(controller.Stop(nullptr));
- return S_OK;
- }
- HRESULT ConsumeEventFromTempSession() {
- // Now consume the event(s).
- TestConsumer consumer_;
- HRESULT hr = consumer_.OpenFileSession(temp_file_.value().c_str());
- if (SUCCEEDED(hr))
- hr = consumer_.Consume();
- consumer_.Close();
- // And nab the result.
- events_.swap(TestConsumer::events_);
- return hr;
- }
- HRESULT RoundTripEvent(PEVENT_TRACE_HEADER header, PEVENT_TRACE* trace) {
- DeleteFile(temp_file_);
- HRESULT hr = LogEventToTempSession(header);
- if (SUCCEEDED(hr))
- hr = ConsumeEventFromTempSession();
- if (FAILED(hr))
- return hr;
- // We should now have the event in the queue.
- if (events_.empty())
- return E_FAIL;
- *trace = &events_.back();
- return S_OK;
- }
- EventQueue events_;
- ScopedTempDir temp_dir_;
- FilePath temp_file_;
- };
- } // namespace
- TEST_F(EtwTraceConsumerDataTest, RoundTrip) {
- EtwMofEvent<1> event(kTestEventType, 1, TRACE_LEVEL_ERROR);
- static const char kData[] = "This is but test data";
- event.fields[0].DataPtr = reinterpret_cast<ULONG64>(kData);
- event.fields[0].Length = sizeof(kData);
- PEVENT_TRACE trace = nullptr;
- HRESULT hr = RoundTripEvent(&event.header, &trace);
- if (hr == E_ACCESSDENIED) {
- VLOG(1) << "You must be an administrator to run this test on Vista";
- return;
- }
- ASSERT_HRESULT_SUCCEEDED(hr) << "RoundTripEvent failed";
- ASSERT_TRUE(trace != nullptr);
- ASSERT_EQ(sizeof(kData), trace->MofLength);
- ASSERT_STREQ(kData, reinterpret_cast<const char*>(trace->MofData));
- }
- } // namespace win
- } // namespace base
|