| // Copyright 2018 The Fuchsia 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 SRC_LIB_CALLBACK_TRACE_CALLBACK_H_ | |
| #define SRC_LIB_CALLBACK_TRACE_CALLBACK_H_ | |
| #include <lib/fit/defer.h> | |
| #include <lib/fit/function.h> | |
| #include <lib/syslog/cpp/macros.h> | |
| #include <lib/trace/event.h> | |
| #include <cstring> | |
| #include <functional> | |
| #include <utility> | |
| namespace callback { | |
| namespace internal { | |
| template <typename C> | |
| class TracingLambda { | |
| public: | |
| TracingLambda(C callback, uint64_t id, const char* category, const char* name, | |
| const char* callback_name) | |
| : id_(id), | |
| category_(category), | |
| name_(name), | |
| callback_name_(callback_name), | |
| callback_(std::move(callback)), | |
| did_run_or_moved_out_(false), | |
| trace_enabled_(true) {} | |
| explicit TracingLambda(C callback) | |
| : id_(0u), | |
| category_(nullptr), | |
| name_(nullptr), | |
| callback_name_(nullptr), | |
| callback_(std::move(callback)), | |
| did_run_or_moved_out_(false), | |
| trace_enabled_(false) {} | |
| // Copy constructor so that the resulting callback can be used as an | |
| // fit::function, but acts as a move constructor. Only the last copy is valid. | |
| TracingLambda(TracingLambda&& other) noexcept | |
| : id_(other.id_), | |
| category_(other.category_), | |
| name_(other.name_), | |
| callback_name_(other.callback_name_), | |
| callback_(std::move(other.callback_)), | |
| did_run_or_moved_out_(other.did_run_or_moved_out_), | |
| trace_enabled_(other.trace_enabled_) { | |
| FX_DCHECK(!other.did_run_or_moved_out_); | |
| other.did_run_or_moved_out_ = true; | |
| } | |
| ~TracingLambda() { | |
| if (!did_run_or_moved_out_ && trace_enabled_) { | |
| TRACE_ASYNC_END(category_, name_, id_, "NotRun", true); | |
| } | |
| } | |
| template <typename... ArgType> | |
| auto operator()(ArgType&&... args) const { | |
| FX_DCHECK(!did_run_or_moved_out_); | |
| did_run_or_moved_out_ = true; | |
| if (trace_enabled_) { | |
| TRACE_ASYNC_END(category_, name_, id_); | |
| TRACE_ASYNC_BEGIN(category_, callback_name_, id_); | |
| } | |
| auto guard = fit::defer([trace_enabled = trace_enabled_, id = id_, category = category_, | |
| callback_name = callback_name_] { | |
| if (trace_enabled) { | |
| TRACE_ASYNC_END(category, callback_name, id); | |
| } | |
| }); | |
| return callback_(std::forward<ArgType>(args)...); | |
| } | |
| private: | |
| const uint64_t id_; | |
| const char* const category_; | |
| const char* const name_; | |
| const char* const callback_name_; | |
| mutable C callback_; | |
| mutable bool did_run_or_moved_out_; | |
| bool trace_enabled_; | |
| FXL_DISALLOW_COPY_AND_ASSIGN(TracingLambda); | |
| }; | |
| // NOLINT suppresses check about 'args' being unused. It might be used in the | |
| // future if TRACE_CALLBACK (see below) is used providing additional arguments. | |
| template <typename C, typename... ArgType> | |
| auto TraceCallback(C callback, const char* category, const char* name, const char* callback_name, | |
| ArgType... args) { // NOLINT | |
| uint64_t id = TRACE_NONCE(); | |
| TRACE_ASYNC_BEGIN(category, name, id, std::forward<ArgType>(args)...); | |
| return TracingLambda<C>(std::move(callback), id, category, name, callback_name); | |
| } | |
| template <typename C> | |
| auto TraceCallback(C callback) { | |
| return TracingLambda<C>(std::move(callback)); | |
| } | |
| // Identity functions. This is used to ensure that a C string is a compile time | |
| // constant in conjunction with __builtin_strlen. | |
| template <size_t S> | |
| constexpr const char* CheckConstantCString(const char* value) { | |
| return value; | |
| } | |
| } // namespace internal | |
| } // namespace callback | |
| // Wraps the given callback so that it's traced using async tracing from the | |
| // time it's wrapped to the time it completes. Can be used only for callbacks | |
| // that will be called at most once. | |
| #define TRACE_CALLBACK(cb, category, name, args...) \ | |
| ([&]() mutable { \ | |
| auto trace_local_cb__ = cb; \ | |
| return (TRACE_ENABLED() \ | |
| ? ::callback::internal::TraceCallback( \ | |
| std::move(trace_local_cb__), \ | |
| ::callback::internal::CheckConstantCString<__builtin_strlen(category)>( \ | |
| category), \ | |
| ::callback::internal::CheckConstantCString<__builtin_strlen(name)>(name), \ | |
| name "_callback", ##args) \ | |
| : ::callback::internal::TraceCallback(std::move(trace_local_cb__))); \ | |
| }()) | |
| #endif // SRC_LIB_CALLBACK_TRACE_CALLBACK_H_ |