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