// 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 #include #include #include #include #include #include "platform/api/time.h" #include "platform/base/error.h" #include "platform/base/trace_logging_types.h" #include "util/osp_logging.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 TraceId current_id() { return traces_ == nullptr ? kEmptyTraceId : traces_->top()->trace_id_; } static TraceId root_id() { return traces_ == nullptr ? kEmptyTraceId : traces_->top()->root_id_; } static TraceIdHierarchy hierarchy() { if (traces_ == nullptr) { return 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, 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(TraceId current_id = kUnsetTraceId, TraceId parent_id = kUnsetTraceId, TraceId root_id = kUnsetTraceId); // Current TraceId information. TraceId trace_id_; TraceId parent_id_; TraceId root_id_; 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>; // Counter to pick IDs when it is not provided. static std::atomic 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(TraceCategory::Value category, const char* name, const char* file, uint32_t line, TraceId current = kUnsetTraceId, TraceId parent = kUnsetTraceId, TraceId root = kUnsetTraceId); TraceLoggerBase(TraceCategory::Value category, const char* name, const char* file, uint32_t line, TraceIdHierarchy ids); protected: // Set the result. void SetTraceResult(Error::Code error) override { result_ = error; } // Timestamp for when the object was created. 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. 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 final : public ScopedTraceOperation { public: explicit TraceIdSetter(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 TraceInstanceHelper { private: class TraceOperationOnStackDeleter { public: void operator()(T* ptr) { ptr->~T(); } }; using TraceInstanceWrapper = std::unique_ptr; public: template static TraceInstanceWrapper Create(uint8_t storage[sizeof(T)], Args... args) { return TraceInstanceWrapper(new (storage) T(std::forward(args)...)); } static TraceInstanceWrapper Empty() { return TraceInstanceWrapper(); } }; } // namespace internal } // namespace openscreen #endif // defined(ENABLE_TRACE_LOGGING) #endif // UTIL_TRACE_LOGGING_SCOPED_TRACE_OPERATIONS_H_