blob: acec8b52480af5105dea98f3c3d79be53745156c [file] [log] [blame]
// 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_