aboutsummaryrefslogtreecommitdiff
path: root/util/trace_logging/scoped_trace_operations.h
blob: 592d45282254ab8fe352d60620979a7957601f9f (plain)
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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
// Copyright 2019 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 UTIL_TRACE_LOGGING_SCOPED_TRACE_OPERATIONS_H_
#define UTIL_TRACE_LOGGING_SCOPED_TRACE_OPERATIONS_H_

#include <atomic>
#include <cstring>
#include <stack>
#include <vector>

#include "build/config/features.h"
#include "platform/api/time.h"
#include "platform/base/error.h"
#include "platform/base/trace_logging_types.h"

#if defined(ENABLE_TRACE_LOGGING)

namespace openscreen {
namespace internal {

// A base class for all trace logging objects which will create new entries in
// the Trace Hierarchy.
// 1) The sharing of all static and thread_local variables across template
// specializations.
// 2) Including all children in the same traces vector.
class ScopedTraceOperation {
 public:
  // Define the destructor to remove this item from the stack when it's
  // destroyed.
  virtual ~ScopedTraceOperation();

  // Getters the current Trace Hierarchy. If the traces_ stack hasn't been
  // created yet, return as if the empty root node is there.
  static platform::TraceId current_id() {
    return traces_ == nullptr ? platform::kEmptyTraceId
                              : traces_->top()->trace_id_;
  }

  static platform::TraceId root_id() {
    return traces_ == nullptr ? platform::kEmptyTraceId
                              : traces_->top()->root_id_;
  }

  static platform::TraceIdHierarchy hierarchy() {
    if (traces_ == nullptr) {
      return platform::TraceIdHierarchy::Empty();
    }

    return traces_->top()->to_hierarchy();
  }

  // Static method to set the result of the most recent trace.
  static void set_result(const Error& error) { set_result(error.code()); }
  static void set_result(Error::Code error) {
    if (traces_ == nullptr) {
      return;
    }
    traces_->top()->SetTraceResult(error);
  }

  // Traces the end of an asynchronous call.
  // NOTE: This returns a bool rather than a void because it keeps the syntax of
  // the ternary operator in the macros simpler.
  static bool TraceAsyncEnd(const uint32_t line,
                            const char* file,
                            platform::TraceId id,
                            Error::Code e);

 protected:
  // Sets the result of this trace log.
  // NOTE: this must be define in this class rather than TraceLogger so that it
  // can be called on traces.back() without a potentially unsafe cast or type
  // checking at runtime.
  virtual void SetTraceResult(Error::Code error) = 0;

  // Constructor to set all trace id information.
  ScopedTraceOperation(platform::TraceId current_id = platform::kUnsetTraceId,
                       platform::TraceId parent_id = platform::kUnsetTraceId,
                       platform::TraceId root_id = platform::kUnsetTraceId);

  // Current TraceId information.
  platform::TraceId trace_id_;
  platform::TraceId parent_id_;
  platform::TraceId root_id_;

  platform::TraceIdHierarchy to_hierarchy() {
    return {trace_id_, parent_id_, root_id_};
  }

 private:
  // NOTE: A std::vector is used for backing the stack because it provides the
  // best perf. Further perf improvement could be achieved later by swapping
  // this out for a circular buffer once OSP supports that. Additional details
  // can be found here:
  // https://www.codeproject.com/Articles/1185449/Performance-of-a-Circular-Buffer-vs-Vector-Deque-a
  using TraceStack =
      std::stack<ScopedTraceOperation*, std::vector<ScopedTraceOperation*>>;

  // Counter to pick IDs when it is not provided.
  static std::atomic<std::uint64_t> trace_id_counter_;

  // The LIFO stack of TraceLoggers currently being watched by this
  // thread.
  static thread_local TraceStack* traces_;
  static thread_local ScopedTraceOperation* root_node_;

  OSP_DISALLOW_COPY_AND_ASSIGN(ScopedTraceOperation);
};

// The class which does actual trace logging.
class TraceLoggerBase : public ScopedTraceOperation {
 public:
  TraceLoggerBase(platform::TraceCategory::Value category,
                  const char* name,
                  const char* file,
                  uint32_t line,
                  platform::TraceId current = platform::kUnsetTraceId,
                  platform::TraceId parent = platform::kUnsetTraceId,
                  platform::TraceId root = platform::kUnsetTraceId);

  TraceLoggerBase(platform::TraceCategory::Value category,
                  const char* name,
                  const char* file,
                  uint32_t line,
                  platform::TraceIdHierarchy ids);

 protected:
  // Set the result.
  void SetTraceResult(Error::Code error) override { result_ = error; }

  // Timestamp for when the object was created.
  platform::Clock::time_point start_time_;

  // Result of this operation.
  Error::Code result_;

  // Name of this operation.
  const char* name_;

  // Name of the file.
  const char* file_name_;

  // Line number the log was generated from.
  uint32_t line_number_;

  // Category of this trace log.
  platform::TraceCategory::Value category_;

 private:
  OSP_DISALLOW_COPY_AND_ASSIGN(TraceLoggerBase);
};

class SynchronousTraceLogger : public TraceLoggerBase {
 public:
  using TraceLoggerBase::TraceLoggerBase;

  ~SynchronousTraceLogger() override;

 private:
  OSP_DISALLOW_COPY_AND_ASSIGN(SynchronousTraceLogger);
};

class AsynchronousTraceLogger : public TraceLoggerBase {
 public:
  using TraceLoggerBase::TraceLoggerBase;

  ~AsynchronousTraceLogger() override;

 private:
  OSP_DISALLOW_COPY_AND_ASSIGN(AsynchronousTraceLogger);
};

// Inserts a fake element into the ScopedTraceOperation stack to set
// the current TraceId Hierarchy manually.
class TraceIdSetter : public ScopedTraceOperation {
 public:
  explicit TraceIdSetter(platform::TraceIdHierarchy ids)
      : ScopedTraceOperation(ids.current, ids.parent, ids.root) {}
  ~TraceIdSetter() final;

  // Creates a new TraceIdSetter to set the full TraceId Hierarchy to default
  // values and does not push it to the traces stack.
  static TraceIdSetter* CreateStackRootNode();

 private:
  // Implement abstract method for use in Macros.
  void SetTraceResult(Error::Code error) {}

  OSP_DISALLOW_COPY_AND_ASSIGN(TraceIdSetter);
};

// This helper object allows us to delete objects allocated on the stack in a
// unique_ptr.
template <class T>
class TraceInstanceHelper {
 private:
  class TraceOperationOnStackDeleter {
   public:
    void operator()(T* ptr) { ptr->~T(); }
  };

  using TraceInstanceWrapper = std::unique_ptr<T, TraceOperationOnStackDeleter>;

 public:
  template <typename... Args>
  static TraceInstanceWrapper Create(uint8_t storage[sizeof(T)], Args... args) {
    return TraceInstanceWrapper(new (storage) T(std::forward<Args&&>(args)...));
  }

  static TraceInstanceWrapper Empty() { return TraceInstanceWrapper(); }
};

}  // namespace internal
}  // namespace openscreen

#endif  // defined(ENABLE_TRACE_LOGGING)

#endif  // UTIL_TRACE_LOGGING_SCOPED_TRACE_OPERATIONS_H_