text: // 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_LEDGER_LIB_CALLBACK_TRACE_CALLBACK_H_
  #define SRC_LEDGER_LIB_CALLBACK_TRACE_CALLBACK_H_
  
  #include <lib/fit/defer.h>
  #include <lib/fit/function.h>
  #include <lib/trace/event.h>
  
  #include <cstring>
  #include <functional>
  #include <utility>
  
  #include "src/ledger/lib/logging/logging.h"
  
  namespace ledger {
  
  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_) {
      LEDGER_DCHECK(!other.did_run_or_moved_out_);
      other.did_run_or_moved_out_ = true;
    }
  
    TracingLambda(const TracingLambda&) = delete;
    TracingLambda& operator=(const TracingLambda&) = delete;
  
    ~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 {
      LEDGER_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_;
  };
  
  // 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 ledger
  
  // 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()                                                                           \
              ? ::ledger::internal::TraceCallback(                                                  \
                    std::move(trace_local_cb__),                                                    \
                    ::ledger::internal::CheckConstantCString<__builtin_strlen(category)>(category), \
                    ::ledger::internal::CheckConstantCString<__builtin_strlen(name)>(name),         \
                    name "_callback", ##args)                                                       \
              : ::ledger::internal::TraceCallback(std::move(trace_local_cb__)));                    \
    }())
  
  #endif  // SRC_LEDGER_LIB_CALLBACK_TRACE_CALLBACK_H_
  