| // 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_ |