forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Windows][Host][Logging] Adding EtwTraceConsumer class
This CL adds the class which consumes the ETW events from Windows. The class is fairly straight-forward but there is one interesting bit as the call to consumer events blocks us from running tasks on that thread. In order to receive events and not block any of the existing threads, we create a new AutoThread which we use to listen for and handle events on. When it is time to clean up, we unblock the listening thread by stopping the trace session. Future CLs will add the logic needed to parse the events and redirect the output to a new location (likely based on registry settings). I've kept that out of this CL to keep its size on the smaller side. Bug: 1144185 Change-Id: Ibf494251d0929a1741cb2796f26d69d258d96a64 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2515140 Reviewed-by: Jamie Walch <jamiewalch@chromium.org> Commit-Queue: Joe Downing <joedow@chromium.org> Cr-Commit-Position: refs/heads/master@{#823596}
- Loading branch information
Joe Downing
authored and
Commit Bot
committed
Nov 3, 2020
1 parent
fb80772
commit 341aef4
Showing
4 changed files
with
271 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
// Copyright 2020 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 "remoting/host/win/etw_trace_consumer.h" | ||
|
||
#include <stdint.h> | ||
|
||
#include <memory> | ||
|
||
#include "base/logging.h" | ||
#include "base/logging_win.h" | ||
#include "base/macros.h" | ||
#include "base/threading/thread_checker.h" | ||
#include "base/win/event_trace_consumer.h" | ||
#include "base/win/event_trace_controller.h" | ||
#include "remoting/base/auto_thread_task_runner.h" | ||
#include "remoting/host/logging.h" | ||
#include "remoting/host/win/etw_trace_controller.h" | ||
|
||
namespace remoting { | ||
|
||
namespace { | ||
|
||
class EtwTraceConsumerImpl : public EtwTraceConsumer { | ||
public: | ||
EtwTraceConsumerImpl(); | ||
EtwTraceConsumerImpl(const EtwTraceConsumerImpl&) = delete; | ||
EtwTraceConsumerImpl& operator=(const EtwTraceConsumerImpl&) = delete; | ||
~EtwTraceConsumerImpl() override; | ||
|
||
bool StartLogging(scoped_refptr<AutoThreadTaskRunner> task_runner); | ||
void StopLogging(); | ||
|
||
private: | ||
class Core : public base::win::EtwTraceConsumerBase<Core> { | ||
public: | ||
Core() = default; | ||
Core(const Core&) = delete; | ||
Core& operator=(const Core&) = delete; | ||
~Core() = default; | ||
|
||
static VOID WINAPI ProcessEvent(PEVENT_TRACE event); | ||
|
||
bool Start(); | ||
void Stop(); | ||
|
||
// Blocking call to begin receiving ETW events from Windows. Must be called | ||
// on an IO thread which allows blocking. Call Stop() to unblock the thread | ||
// and allow it to be cleaned up. | ||
void ConsumeEvents(); | ||
|
||
private: | ||
// Parses an event and passes it along to the delegate for processing. | ||
void DispatchEvent(PEVENT_TRACE event); | ||
|
||
// Handlers which parse and log an ETW event. | ||
void HandleFullMessage(PEVENT_TRACE event); | ||
void HandleMessage(PEVENT_TRACE event); | ||
|
||
static Core* instance_; | ||
|
||
std::unique_ptr<EtwTraceController> controller_; | ||
|
||
THREAD_CHECKER(thread_checker_); | ||
}; | ||
|
||
std::unique_ptr<Core> core_; | ||
scoped_refptr<AutoThreadTaskRunner> task_runner_; | ||
}; | ||
|
||
// static | ||
EtwTraceConsumerImpl::Core* EtwTraceConsumerImpl::Core::instance_ = nullptr; | ||
|
||
// static | ||
void EtwTraceConsumerImpl::Core::ProcessEvent(PEVENT_TRACE event) { | ||
// This method is called on the same thread as Consume(). | ||
EtwTraceConsumerImpl::Core* instance = instance_; | ||
if (instance) { | ||
instance->DispatchEvent(event); | ||
} | ||
} | ||
|
||
bool EtwTraceConsumerImpl::Core::Start() { | ||
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | ||
DCHECK(!instance_); | ||
instance_ = this; | ||
|
||
controller_ = std::make_unique<EtwTraceController>(); | ||
if (!controller_->Start()) { | ||
return false; | ||
} | ||
|
||
HRESULT hr = OpenRealtimeSession(controller_->GetActiveSessionName()); | ||
if (FAILED(hr)) { | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
void EtwTraceConsumerImpl::Core::Stop() { | ||
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | ||
if (!instance_) { | ||
return; | ||
} | ||
|
||
DCHECK_EQ(instance_, this); | ||
if (controller_) { | ||
controller_->Stop(); | ||
controller_.reset(); | ||
} | ||
instance_ = nullptr; | ||
} | ||
|
||
void EtwTraceConsumerImpl::Core::ConsumeEvents() { | ||
// Consume will block the thread until the provider is disabled so make sure | ||
// it is not run on the same thread that |core_| was created on. | ||
DCHECK(!thread_checker_.CalledOnValidThread()); | ||
Consume(); | ||
} | ||
|
||
void EtwTraceConsumerImpl::Core::DispatchEvent(PEVENT_TRACE event) { | ||
// This method is called on the same thread as Consume(). | ||
DCHECK(!thread_checker_.CalledOnValidThread()); | ||
if (!event) { | ||
return; | ||
} | ||
|
||
if (!IsEqualGUID(event->Header.Guid, logging::kLogEventId)) { | ||
// Event was not logged from our provider. | ||
return; | ||
} | ||
|
||
uint8_t event_type = event->Header.Class.Type; | ||
if (event_type == logging::LOG_MESSAGE_FULL) { | ||
HandleFullMessage(event); | ||
} else if (event_type != logging::LOG_MESSAGE) { | ||
HandleMessage(event); | ||
} else { | ||
NOTREACHED() << "Unknown event type."; | ||
} | ||
} | ||
|
||
void EtwTraceConsumerImpl::Core::HandleFullMessage(PEVENT_TRACE event) { | ||
// TODO(joedow): Implement parsing and logging for this event type. | ||
NOTIMPLEMENTED(); | ||
} | ||
|
||
void EtwTraceConsumerImpl::Core::HandleMessage(PEVENT_TRACE event) { | ||
// TODO(joedow): Implement parsing and logging for this event type. | ||
NOTIMPLEMENTED(); | ||
} | ||
|
||
EtwTraceConsumerImpl::EtwTraceConsumerImpl() = default; | ||
|
||
EtwTraceConsumerImpl::~EtwTraceConsumerImpl() { | ||
StopLogging(); | ||
} | ||
|
||
bool EtwTraceConsumerImpl::StartLogging( | ||
scoped_refptr<AutoThreadTaskRunner> task_runner) { | ||
DCHECK(!core_); | ||
|
||
core_ = std::make_unique<Core>(); | ||
if (!core_->Start()) { | ||
core_.reset(); | ||
return false; | ||
} | ||
|
||
task_runner_ = task_runner; | ||
// base::Unretained is safe because |core_| is destroyed on |task_runner_|. | ||
task_runner_->PostTask( | ||
FROM_HERE, base::BindOnce(&EtwTraceConsumerImpl::Core::ConsumeEvents, | ||
base::Unretained(core_.get()))); | ||
return true; | ||
} | ||
|
||
void EtwTraceConsumerImpl::StopLogging() { | ||
if (!core_) { | ||
return; | ||
} | ||
|
||
// |core_| is consuming trace events on |task_runner_| which is effectively | ||
// blocked (Windows is calling it back but we can't schedule work on it). | ||
// To unblock that thread, we first need to stop tracing, after that we | ||
// schedule a deletion on the tracing thread so it occurs after all of the | ||
// pending events have been handled. | ||
core_->Stop(); | ||
task_runner_->DeleteSoon(FROM_HERE, core_.release()); | ||
} | ||
|
||
} // namespace | ||
|
||
// static | ||
std::unique_ptr<EtwTraceConsumer> EtwTraceConsumer::Create( | ||
scoped_refptr<AutoThreadTaskRunner> task_runner) { | ||
// TODO(joedow): Configure logging destination before returning the instance. | ||
|
||
auto etw_trace_consumer = std::make_unique<EtwTraceConsumerImpl>(); | ||
if (!etw_trace_consumer->StartLogging(task_runner)) { | ||
return nullptr; | ||
} | ||
|
||
return etw_trace_consumer; | ||
} | ||
|
||
} // namespace remoting |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// Copyright 2020 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. | ||
|
||
#ifndef REMOTING_HOST_WIN_ETW_TRACE_CONSUMER_H_ | ||
#define REMOTING_HOST_WIN_ETW_TRACE_CONSUMER_H_ | ||
|
||
#include <memory> | ||
|
||
#include "base/memory/ref_counted.h" | ||
|
||
namespace remoting { | ||
|
||
class AutoThreadTaskRunner; | ||
|
||
class EtwTraceConsumer { | ||
public: | ||
virtual ~EtwTraceConsumer() = default; | ||
|
||
// Creates an ETW Trace Consumer which listens for Host ETW events. | ||
// TODO(joedow): Add output functionality (log file / etw file / event log). | ||
// Listening starts as soon as an instance is created and stops when the | ||
// instance is destroyed. Only one instance can be active at a time. | ||
static std::unique_ptr<EtwTraceConsumer> Create( | ||
scoped_refptr<AutoThreadTaskRunner> task_runner); | ||
}; | ||
|
||
} // namespace remoting | ||
|
||
#endif // REMOTING_HOST_WIN_ETW_TRACE_CONSUMER_H_ |