1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202

base / win / event_trace_provider.h [blame]

// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
#pragma allow_unsafe_buffers
#endif

// Declaration of a Windows event trace provider class, to allow using
// Windows Event Tracing for logging transport and control.
#ifndef BASE_WIN_EVENT_TRACE_PROVIDER_H_
#define BASE_WIN_EVENT_TRACE_PROVIDER_H_

#include <windows.h>

#include <cguid.h>
#include <evntrace.h>
#include <stddef.h>
#include <stdint.h>
#include <wmistr.h>

#include <limits>

#include "base/base_export.h"
#include "base/compiler_specific.h"

namespace base {
namespace win {

using EtwEventClass = GUID;
using EtwEventType = UCHAR;
using EtwEventLevel = UCHAR;
using EtwEventVersion = USHORT;
using EtwEventFlags = ULONG;

// Base class is a POD for correctness.
template <size_t N>
struct EtwMofEventBase {
  EVENT_TRACE_HEADER header;
  MOF_FIELD fields[N];
};

// Utility class to auto-initialize event trace header structures.
template <size_t N>
class EtwMofEvent : public EtwMofEventBase<N> {
 public:
  using Super = EtwMofEventBase<N>;

  // Clang and the C++ standard don't allow unqualified lookup into dependent
  // bases, hence these using decls to explicitly pull the names out.
  using EtwMofEventBase<N>::header;
  using EtwMofEventBase<N>::fields;

  EtwMofEvent() { memset(static_cast<Super*>(this), 0, sizeof(Super)); }

  EtwMofEvent(const EtwEventClass& event_class,
              EtwEventType type,
              EtwEventLevel level) {
    memset(static_cast<Super*>(this), 0, sizeof(Super));
    header.Size = sizeof(Super);
    header.Guid = event_class;
    header.Class.Type = type;
    header.Class.Level = level;
    header.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
  }

  EtwMofEvent(const EtwEventClass& event_class,
              EtwEventType type,
              EtwEventVersion version,
              EtwEventLevel level) {
    memset(static_cast<Super*>(this), 0, sizeof(Super));
    header.Size = sizeof(Super);
    header.Guid = event_class;
    header.Class.Type = type;
    header.Class.Version = version;
    header.Class.Level = level;
    header.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
  }

  EtwMofEvent(const EtwMofEvent&) = delete;
  EtwMofEvent& operator=(const EtwMofEvent&) = delete;

  void SetField(size_t field, size_t size, const void* data) {
    // DCHECK(field < N);
    if ((field < N) && (size <= std::numeric_limits<uint32_t>::max())) {
      fields[field].DataPtr = reinterpret_cast<ULONG64>(data);
      fields[field].Length = static_cast<ULONG>(size);
    }
  }

  EVENT_TRACE_HEADER* get() { return &header; }
};

// Trace provider with Event Tracing for Windows. The trace provider
// registers with ETW by its name which is a GUID. ETW calls back to
// the object whenever the trace level or enable flags for this provider
// name changes.
// Users of this class can test whether logging is currently enabled at
// a particular trace level, and whether particular enable flags are set,
// before other resources are consumed to generate and issue the log
// messages themselves.
class BASE_EXPORT EtwTraceProvider {
 public:
  // Creates an event trace provider identified by provider_name, which
  // will be the name registered with Event Tracing for Windows (ETW).
  explicit EtwTraceProvider(const GUID& provider_name);

  // Creates an unnamed event trace provider, the provider must be given
  // a name before registration.
  EtwTraceProvider();

  EtwTraceProvider(const EtwTraceProvider&) = delete;
  EtwTraceProvider& operator=(const EtwTraceProvider&) = delete;

  virtual ~EtwTraceProvider();

  // Registers the trace provider with Event Tracing for Windows.
  // Note: from this point forward ETW may call the provider's control
  //    callback. If the provider's name is enabled in some trace session
  //    already, the callback may occur recursively from this call, so
  //    call this only when you're ready to handle callbacks.
  ULONG Register();
  // Unregisters the trace provider with ETW.
  ULONG Unregister();

  // Accessors.
  void set_provider_name(const GUID& provider_name) {
    provider_name_ = provider_name;
  }
  const GUID& provider_name() const LIFETIME_BOUND { return provider_name_; }
  TRACEHANDLE registration_handle() const { return registration_handle_; }
  TRACEHANDLE session_handle() const { return session_handle_; }
  EtwEventFlags enable_flags() const { return enable_flags_; }
  EtwEventLevel enable_level() const { return enable_level_; }

  // Returns true iff logging should be performed for "level" and "flags".
  // Note: flags is treated as a bitmask, and should normally have a single
  //      bit set, to test whether to log for a particular sub "facility".
  bool ShouldLog(EtwEventLevel level, EtwEventFlags flags) {
    return NULL != session_handle_ && level >= enable_level_ &&
           (0 != (flags & enable_flags_));
  }

  // Simple wrappers to log Unicode and ANSI strings.
  // Do nothing if !ShouldLog(level, 0xFFFFFFFF).
  ULONG Log(const EtwEventClass& event_class,
            EtwEventType type,
            EtwEventLevel level,
            const char* message);
  ULONG Log(const EtwEventClass& event_class,
            EtwEventType type,
            EtwEventLevel level,
            const wchar_t* message);

  // Log the provided event.
  ULONG Log(EVENT_TRACE_HEADER* event);

 protected:
  // Called after events have been enabled, override in subclasses
  // to set up state or log at the start of a session.
  // Note: This function may be called ETW's thread and may be racy,
  //    bring your own locking if needed.
  virtual void OnEventsEnabled() {}

  // Called just before events are disabled, override in subclasses
  // to tear down state or log at the end of a session.
  // Note: This function may be called ETW's thread and may be racy,
  //    bring your own locking if needed.
  virtual void OnEventsDisabled() {}

  // Called just after events have been disabled, override in subclasses
  // to tear down state at the end of a session. At this point it's
  // to late to log anything to the session.
  // Note: This function may be called ETW's thread and may be racy,
  //    bring your own locking if needed.
  virtual void PostEventsDisabled() {}

 private:
  ULONG EnableEvents(PVOID buffer);
  ULONG DisableEvents();
  ULONG Callback(WMIDPREQUESTCODE request, PVOID buffer);
  static ULONG WINAPI ControlCallback(WMIDPREQUESTCODE request,
                                      PVOID context,
                                      ULONG* reserved,
                                      PVOID buffer);

  GUID provider_name_ = GUID_NULL;
  TRACEHANDLE registration_handle_ = NULL;
  TRACEHANDLE session_handle_ = NULL;
  EtwEventFlags enable_flags_ = 0;
  EtwEventLevel enable_level_ = 0;

  // We don't use this, but on XP we're obliged to pass one in to
  // RegisterTraceGuids. Non-const, because that's how the API needs it.
  static TRACE_GUID_REGISTRATION obligatory_guid_registration_;
};

}  // namespace win
}  // namespace base

#endif  // BASE_WIN_EVENT_TRACE_PROVIDER_H_