blob: 46fbe9900fe0e43cc82fe3178f339068b11883cf [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#ifndef ZIRCON_KERNEL_INCLUDE_LIB_KTRACE_H_
#define ZIRCON_KERNEL_INCLUDE_LIB_KTRACE_H_
#include <err.h>
#include <lib/ktrace/string_ref.h>
#include <lib/zircon-internal/ktrace.h>
#include <platform.h>
#include <zircon/compiler.h>
#include <zircon/types.h>
// Specifies whether the trace applies to the current thread or cpu.
enum class TraceContext {
Thread,
Cpu,
// TODO(eieio): Support process?
};
// Argument type that specifies whether a trace function is enabled or disabled.
template <bool enabled>
struct TraceEnabled {};
// Type that specifies whether tracing is enabled or disabled for the local
// compilation unit.
template <bool enabled>
constexpr auto LocalTrace = TraceEnabled<enabled>{};
// Constants that specify unconditional enabled or disabled tracing.
constexpr auto TraceAlways = TraceEnabled<true>{};
constexpr auto TraceNever = TraceEnabled<false>{};
static inline uint64_t ktrace_timestamp() { return current_ticks(); }
// Utility macro to convert string literals passed to local tracing macros into
// StringRef literals.
//
// Example:
//
// #define LOCAL_KTRACE_ENABLE 0
//
// #define LOCAL_KTRACE(string, args...)
// ktrace_probe(LocalTrace<LOCAL_KTRACE_ENABLE>, TraceContext::Cpu,
// KTRACE_STRING_REF(string), ##args)
//
#define KTRACE_STRING_REF_CAT(a, b) a##b
#define KTRACE_STRING_REF(string) KTRACE_STRING_REF_CAT(string, _stringref)
// Allocates a new trace record in the trace buffer. Returns a pointer to the
// start of the record or nullptr if tracing is disabled or the end of the
// buffer is reached.
void* ktrace_open(uint32_t tag, uint64_t ts = ktrace_timestamp());
// Emits a tiny trace record.
void ktrace_tiny(uint32_t tag, uint32_t arg);
// Emits a new trace record in the given context. Compiles to no-op if |enabled|
// is false.
template <bool enabled>
inline void ktrace(TraceEnabled<enabled>, TraceContext context, uint32_t tag, uint32_t a,
uint32_t b, uint32_t c, uint32_t d, uint64_t explicit_ts = ktrace_timestamp()) {
if constexpr (enabled) {
const uint32_t effective_tag =
KTRACE_TAG_FLAGS(tag, context == TraceContext::Thread ? 0 : KTRACE_FLAGS_CPU);
if (uint32_t* data = static_cast<uint32_t*>(ktrace_open(effective_tag, explicit_ts))) {
data[0] = a;
data[1] = b;
data[2] = c;
data[3] = d;
}
} else {
(void)context;
(void)tag;
(void)a;
(void)b;
(void)c;
(void)d;
}
}
// Backwards-compatible API for existing users of unconditional thread-context
// traces.
static inline void ktrace(uint32_t tag, uint32_t a, uint32_t b, uint32_t c, uint32_t d,
uint64_t explicit_ts = ktrace_timestamp()) {
ktrace(TraceAlways, TraceContext::Thread, tag, a, b, c, d, explicit_ts);
}
// Backwards-compatible API for existing users of unconditional thread-context
// pointer traces.
static inline void ktrace_ptr(uint32_t tag, const void* ptr, uint32_t c, uint32_t d) {
const uintptr_t raw_value = reinterpret_cast<uintptr_t>(ptr);
const uint32_t ptr_high = static_cast<uint32_t>(raw_value >> 32);
const uint32_t ptr_low = static_cast<uint32_t>(raw_value);
ktrace(tag, ptr_high, ptr_low, c, d);
}
template <bool enabled>
inline void ktrace_probe(TraceEnabled<enabled>, TraceContext context, StringRef* string_ref) {
if constexpr (enabled) {
const uint32_t tag = TAG_PROBE_16(string_ref->GetId());
const uint32_t effective_tag =
KTRACE_TAG_FLAGS(tag, context == TraceContext::Thread ? 0 : KTRACE_FLAGS_CPU);
ktrace_open(effective_tag);
} else {
(void)context;
(void)string_ref;
}
}
template <bool enabled>
inline void ktrace_probe(TraceEnabled<enabled>, TraceContext context, StringRef* string_ref,
uint32_t a, uint32_t b) {
if constexpr (enabled) {
const uint32_t tag = TAG_PROBE_24(string_ref->GetId());
const uint32_t effective_tag =
KTRACE_TAG_FLAGS(tag, context == TraceContext::Thread ? 0 : KTRACE_FLAGS_CPU);
void* const payload = ktrace_open(effective_tag);
uint32_t* const args = static_cast<uint32_t*>(payload);
if (args) {
args[0] = a;
args[1] = b;
}
} else {
(void)context;
(void)string_ref;
(void)a;
(void)b;
}
}
template <bool enabled>
inline void ktrace_probe(TraceEnabled<enabled>, TraceContext context, StringRef* string_ref,
uint64_t a) {
if constexpr (enabled) {
const uint32_t tag = TAG_PROBE_24(string_ref->GetId());
const uint32_t effective_tag =
KTRACE_TAG_FLAGS(tag, context == TraceContext::Thread ? 0 : KTRACE_FLAGS_CPU);
void* const payload = ktrace_open(effective_tag);
uint64_t* const args = static_cast<uint64_t*>(payload);
if (args) {
args[0] = a;
}
} else {
(void)context;
(void)string_ref;
(void)a;
}
}
template <bool enabled>
inline void ktrace_probe(TraceEnabled<enabled>, TraceContext context, StringRef* string_ref,
uint64_t a, uint64_t b) {
if constexpr (enabled) {
const uint32_t tag = TAG_PROBE_32(string_ref->GetId());
const uint32_t effective_tag =
KTRACE_TAG_FLAGS(tag, context == TraceContext::Thread ? 0 : KTRACE_FLAGS_CPU);
void* const payload = ktrace_open(effective_tag);
uint64_t* const args = static_cast<uint64_t*>(payload);
if (args) {
args[0] = a;
args[1] = b;
}
} else {
(void)context;
(void)string_ref;
(void)a;
(void)b;
}
}
template <bool enabled>
inline void ktrace_begin_duration(TraceEnabled<enabled>, TraceContext context, uint32_t group,
StringRef* string_ref) {
if constexpr (enabled) {
const uint32_t tag = TAG_BEGIN_DURATION_16(string_ref->GetId(), group);
const uint32_t effective_tag =
KTRACE_TAG_FLAGS(tag, context == TraceContext::Thread ? 0 : KTRACE_FLAGS_CPU);
ktrace_open(effective_tag);
} else {
(void)context;
(void)group;
(void)string_ref;
}
}
template <bool enabled>
inline void ktrace_end_duration(TraceEnabled<enabled>, TraceContext context, uint32_t group,
StringRef* string_ref) {
if constexpr (enabled) {
const uint32_t tag = TAG_END_DURATION_16(string_ref->GetId(), group);
const uint32_t effective_tag =
KTRACE_TAG_FLAGS(tag, context == TraceContext::Thread ? 0 : KTRACE_FLAGS_CPU);
ktrace_open(effective_tag);
} else {
(void)context;
(void)group;
(void)string_ref;
}
}
template <bool enabled>
inline void ktrace_begin_duration(TraceEnabled<enabled>, TraceContext context, uint32_t group,
StringRef* string_ref, uint64_t a, uint64_t b) {
if constexpr (enabled) {
const uint32_t tag = TAG_BEGIN_DURATION_32(string_ref->GetId(), group);
const uint32_t effective_tag =
KTRACE_TAG_FLAGS(tag, context == TraceContext::Thread ? 0 : KTRACE_FLAGS_CPU);
void* const payload = ktrace_open(effective_tag);
uint64_t* const args = static_cast<uint64_t*>(payload);
if (args) {
args[0] = a;
args[1] = b;
}
} else {
(void)context;
(void)group;
(void)string_ref;
(void)a;
(void)b;
}
}
template <bool enabled>
inline void ktrace_end_duration(TraceEnabled<enabled>, TraceContext context, uint32_t group,
StringRef* string_ref, uint64_t a, uint64_t b) {
if constexpr (enabled) {
const uint32_t tag = TAG_END_DURATION_32(string_ref->GetId(), group);
const uint32_t effective_tag =
KTRACE_TAG_FLAGS(tag, context == TraceContext::Thread ? 0 : KTRACE_FLAGS_CPU);
void* const payload = ktrace_open(effective_tag);
uint64_t* const args = static_cast<uint64_t*>(payload);
if (args) {
args[0] = a;
args[1] = b;
}
} else {
(void)context;
(void)group;
(void)string_ref;
(void)a;
(void)b;
}
}
template <bool enabled>
inline void ktrace_flow_begin(TraceEnabled<enabled>, TraceContext context, uint32_t group,
StringRef* string_ref, uint64_t flow_id, uint64_t a = 0) {
if constexpr (enabled) {
const uint32_t tag = TAG_FLOW_BEGIN(string_ref->GetId(), group);
const uint32_t effective_tag =
KTRACE_TAG_FLAGS(tag, context == TraceContext::Thread ? 0 : KTRACE_FLAGS_CPU);
void* const payload = ktrace_open(effective_tag);
uint64_t* const args = static_cast<uint64_t*>(payload);
if (args) {
args[0] = flow_id;
args[1] = a;
}
} else {
(void)context;
(void)group;
(void)string_ref;
(void)flow_id;
(void)a;
}
}
template <bool enabled>
inline void ktrace_flow_end(TraceEnabled<enabled>, TraceContext context, uint32_t group,
StringRef* string_ref, uint64_t flow_id, uint64_t a = 0) {
if constexpr (enabled) {
const uint32_t tag = TAG_FLOW_END(string_ref->GetId(), group);
const uint32_t effective_tag =
KTRACE_TAG_FLAGS(tag, context == TraceContext::Thread ? 0 : KTRACE_FLAGS_CPU);
void* const payload = ktrace_open(effective_tag);
uint64_t* const args = static_cast<uint64_t*>(payload);
if (args) {
args[0] = flow_id;
args[1] = a;
}
} else {
(void)context;
(void)group;
(void)string_ref;
(void)flow_id;
(void)a;
}
}
void ktrace_name_etc(uint32_t tag, uint32_t id, uint32_t arg, const char* name, bool always);
static inline void ktrace_name(uint32_t tag, uint32_t id, uint32_t arg, const char* name) {
ktrace_name_etc(tag, id, arg, name, false);
}
ssize_t ktrace_read_user(void* ptr, uint32_t off, size_t len);
zx_status_t ktrace_control(uint32_t action, uint32_t options, void* ptr);
#define KTRACE_DEFAULT_BUFSIZE 32 // MB
#define KTRACE_DEFAULT_GRPMASK 0xFFF
void ktrace_report_live_threads(void);
void ktrace_report_live_processes(void);
// RAII type that emits begin/end duration events covering the lifetime of the
// instance for use in tracing scopes.
// TODO(eieio): Add option to combine begin/end traces as a single complete
// event for better trace buffer efficiency.
template <typename Enabled, uint8_t group, TraceContext = TraceContext::Thread>
class TraceDuration;
template <bool enabled, uint8_t group, TraceContext context>
class TraceDuration<TraceEnabled<enabled>, group, context> {
public:
explicit TraceDuration(StringRef* string_ref) : string_ref_{string_ref} {
ktrace_begin_duration(TraceEnabled<enabled>{}, context, group, string_ref_);
}
TraceDuration(StringRef* string_ref, uint64_t a, uint64_t b) : string_ref_{string_ref} {
ktrace_begin_duration(TraceEnabled<enabled>{}, context, group, string_ref_, a, b);
}
~TraceDuration() { End(); }
TraceDuration(const TraceDuration&) = delete;
TraceDuration& operator=(const TraceDuration&) = delete;
TraceDuration(TraceDuration&&) = delete;
TraceDuration& operator=(TraceDuration&&) = delete;
// Emits the end trace early, before this instance destructs.
void End() {
if (string_ref_) {
ktrace_end_duration(TraceEnabled<enabled>{}, context, group, string_ref_);
string_ref_ = nullptr;
}
}
// Similar to the overload above, taking the given arguments for the end
// event.
void End(uint64_t a, uint64_t b) {
if (string_ref_) {
ktrace_end_duration(TraceEnabled<enabled>{}, context, group, string_ref_, a, b);
string_ref_ = nullptr;
}
}
// Returns a callable to complete this duration trace. This is useful to
// delegate closing the duration to a callee. The lifetime of the
// TraceDuration instance must not end before the completer is invoked.
auto Completer() {
return [this]() { End(); };
}
private:
StringRef* string_ref_;
};
#endif // ZIRCON_KERNEL_INCLUDE_LIB_KTRACE_H_