xref: /aosp_15_r20/external/openscreen/util/trace_logging/scoped_trace_operations.h (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef UTIL_TRACE_LOGGING_SCOPED_TRACE_OPERATIONS_H_
6 #define UTIL_TRACE_LOGGING_SCOPED_TRACE_OPERATIONS_H_
7 
8 #include <atomic>
9 #include <cstring>
10 #include <memory>
11 #include <stack>
12 #include <utility>
13 #include <vector>
14 
15 #include "platform/api/time.h"
16 #include "platform/base/error.h"
17 #include "platform/base/trace_logging_types.h"
18 #include "util/osp_logging.h"
19 
20 #if defined(ENABLE_TRACE_LOGGING)
21 
22 namespace openscreen {
23 namespace internal {
24 
25 // A base class for all trace logging objects which will create new entries in
26 // the Trace Hierarchy.
27 // 1) The sharing of all static and thread_local variables across template
28 // specializations.
29 // 2) Including all children in the same traces vector.
30 class ScopedTraceOperation {
31  public:
32   // Define the destructor to remove this item from the stack when it's
33   // destroyed.
34   virtual ~ScopedTraceOperation();
35 
36   // Getters the current Trace Hierarchy. If the traces_ stack hasn't been
37   // created yet, return as if the empty root node is there.
current_id()38   static TraceId current_id() {
39     return traces_ == nullptr ? kEmptyTraceId : traces_->top()->trace_id_;
40   }
41 
root_id()42   static TraceId root_id() {
43     return traces_ == nullptr ? kEmptyTraceId : traces_->top()->root_id_;
44   }
45 
hierarchy()46   static TraceIdHierarchy hierarchy() {
47     if (traces_ == nullptr) {
48       return TraceIdHierarchy::Empty();
49     }
50 
51     return traces_->top()->to_hierarchy();
52   }
53 
54   // Static method to set the result of the most recent trace.
set_result(const Error & error)55   static void set_result(const Error& error) { set_result(error.code()); }
set_result(Error::Code error)56   static void set_result(Error::Code error) {
57     if (traces_ == nullptr) {
58       return;
59     }
60     traces_->top()->SetTraceResult(error);
61   }
62 
63   // Traces the end of an asynchronous call.
64   // NOTE: This returns a bool rather than a void because it keeps the syntax of
65   // the ternary operator in the macros simpler.
66   static bool TraceAsyncEnd(const uint32_t line,
67                             const char* file,
68                             TraceId id,
69                             Error::Code e);
70 
71  protected:
72   // Sets the result of this trace log.
73   // NOTE: this must be define in this class rather than TraceLogger so that it
74   // can be called on traces.back() without a potentially unsafe cast or type
75   // checking at runtime.
76   virtual void SetTraceResult(Error::Code error) = 0;
77 
78   // Constructor to set all trace id information.
79   ScopedTraceOperation(TraceId current_id = kUnsetTraceId,
80                        TraceId parent_id = kUnsetTraceId,
81                        TraceId root_id = kUnsetTraceId);
82 
83   // Current TraceId information.
84   TraceId trace_id_;
85   TraceId parent_id_;
86   TraceId root_id_;
87 
to_hierarchy()88   TraceIdHierarchy to_hierarchy() { return {trace_id_, parent_id_, root_id_}; }
89 
90  private:
91   // NOTE: A std::vector is used for backing the stack because it provides the
92   // best perf. Further perf improvement could be achieved later by swapping
93   // this out for a circular buffer once OSP supports that. Additional details
94   // can be found here:
95   // https://www.codeproject.com/Articles/1185449/Performance-of-a-Circular-Buffer-vs-Vector-Deque-a
96   using TraceStack =
97       std::stack<ScopedTraceOperation*, std::vector<ScopedTraceOperation*>>;
98 
99   // Counter to pick IDs when it is not provided.
100   static std::atomic<std::uint64_t> trace_id_counter_;
101 
102   // The LIFO stack of TraceLoggers currently being watched by this
103   // thread.
104   static thread_local TraceStack* traces_;
105   static thread_local ScopedTraceOperation* root_node_;
106 
107   OSP_DISALLOW_COPY_AND_ASSIGN(ScopedTraceOperation);
108 };
109 
110 // The class which does actual trace logging.
111 class TraceLoggerBase : public ScopedTraceOperation {
112  public:
113   TraceLoggerBase(TraceCategory::Value category,
114                   const char* name,
115                   const char* file,
116                   uint32_t line,
117                   TraceId current = kUnsetTraceId,
118                   TraceId parent = kUnsetTraceId,
119                   TraceId root = kUnsetTraceId);
120 
121   TraceLoggerBase(TraceCategory::Value category,
122                   const char* name,
123                   const char* file,
124                   uint32_t line,
125                   TraceIdHierarchy ids);
126 
127  protected:
128   // Set the result.
SetTraceResult(Error::Code error)129   void SetTraceResult(Error::Code error) override { result_ = error; }
130 
131   // Timestamp for when the object was created.
132   Clock::time_point start_time_;
133 
134   // Result of this operation.
135   Error::Code result_;
136 
137   // Name of this operation.
138   const char* name_;
139 
140   // Name of the file.
141   const char* file_name_;
142 
143   // Line number the log was generated from.
144   uint32_t line_number_;
145 
146   // Category of this trace log.
147   TraceCategory::Value category_;
148 
149  private:
150   OSP_DISALLOW_COPY_AND_ASSIGN(TraceLoggerBase);
151 };
152 
153 class SynchronousTraceLogger : public TraceLoggerBase {
154  public:
155   using TraceLoggerBase::TraceLoggerBase;
156 
157   ~SynchronousTraceLogger() override;
158 
159  private:
160   OSP_DISALLOW_COPY_AND_ASSIGN(SynchronousTraceLogger);
161 };
162 
163 class AsynchronousTraceLogger : public TraceLoggerBase {
164  public:
165   using TraceLoggerBase::TraceLoggerBase;
166 
167   ~AsynchronousTraceLogger() override;
168 
169  private:
170   OSP_DISALLOW_COPY_AND_ASSIGN(AsynchronousTraceLogger);
171 };
172 
173 // Inserts a fake element into the ScopedTraceOperation stack to set
174 // the current TraceId Hierarchy manually.
175 class TraceIdSetter final : public ScopedTraceOperation {
176  public:
TraceIdSetter(TraceIdHierarchy ids)177   explicit TraceIdSetter(TraceIdHierarchy ids)
178       : ScopedTraceOperation(ids.current, ids.parent, ids.root) {}
179   ~TraceIdSetter() final;
180 
181   // Creates a new TraceIdSetter to set the full TraceId Hierarchy to default
182   // values and does not push it to the traces stack.
183   static TraceIdSetter* CreateStackRootNode();
184 
185  private:
186   // Implement abstract method for use in Macros.
SetTraceResult(Error::Code error)187   void SetTraceResult(Error::Code error) {}
188 
189   OSP_DISALLOW_COPY_AND_ASSIGN(TraceIdSetter);
190 };
191 
192 // This helper object allows us to delete objects allocated on the stack in a
193 // unique_ptr.
194 template <class T>
195 class TraceInstanceHelper {
196  private:
197   class TraceOperationOnStackDeleter {
198    public:
operator()199     void operator()(T* ptr) { ptr->~T(); }
200   };
201 
202   using TraceInstanceWrapper = std::unique_ptr<T, TraceOperationOnStackDeleter>;
203 
204  public:
205   template <typename... Args>
Create(uint8_t storage[sizeof (T)],Args...args)206   static TraceInstanceWrapper Create(uint8_t storage[sizeof(T)], Args... args) {
207     return TraceInstanceWrapper(new (storage) T(std::forward<Args&&>(args)...));
208   }
209 
Empty()210   static TraceInstanceWrapper Empty() { return TraceInstanceWrapper(); }
211 };
212 
213 }  // namespace internal
214 }  // namespace openscreen
215 
216 #endif  // defined(ENABLE_TRACE_LOGGING)
217 
218 #endif  // UTIL_TRACE_LOGGING_SCOPED_TRACE_OPERATIONS_H_
219