| // 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 <lib/ktrace/ktrace_internal.h> |
| #include <lib/ktrace/string_ref.h> |
| #include <lib/zircon-internal/ktrace.h> |
| #include <platform.h> |
| #include <zircon/compiler.h> |
| #include <zircon/types.h> |
| |
| #include <ktl/atomic.h> |
| |
| // Declaration of the global singleton KTraceState |
| extern internal::KTraceState KTRACE_STATE; |
| |
| // 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(); } |
| |
| // Indicate that the current time should be recorded when writing a trace record. |
| // |
| // Used for ktrace calls which accept a custom timestamp as a parameter. |
| inline constexpr uint64_t kRecordCurrentTimestamp = 0xffffffff'ffffffff; |
| |
| // 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) |
| |
| // Determine if ktrace is enabled for the given tag. |
| inline bool ktrace_enabled(uint32_t tag) { return KTRACE_STATE.tag_enabled(tag); } |
| |
| // Emits a tiny trace record. |
| inline void ktrace_tiny(uint32_t tag, uint32_t arg) { |
| if (unlikely(ktrace_enabled(tag))) { |
| KTRACE_STATE.WriteRecordTiny(tag, 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 = kRecordCurrentTimestamp) { |
| if constexpr (!enabled) { |
| return; |
| } |
| const uint32_t effective_tag = |
| KTRACE_TAG_FLAGS(tag, context == TraceContext::Thread ? 0 : KTRACE_FLAGS_CPU); |
| if (unlikely(ktrace_enabled(effective_tag))) { |
| KTRACE_STATE.WriteRecord(effective_tag, explicit_ts, a, b, c, 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 = kRecordCurrentTimestamp) { |
| 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) { |
| return; |
| } |
| 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); |
| if (unlikely(ktrace_enabled(effective_tag))) { |
| KTRACE_STATE.WriteRecord(effective_tag, kRecordCurrentTimestamp); |
| } |
| } |
| |
| template <bool enabled> |
| inline void ktrace_probe(TraceEnabled<enabled>, TraceContext context, StringRef* string_ref, |
| uint32_t a, uint32_t b) { |
| if constexpr (!enabled) { |
| return; |
| } |
| 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); |
| if (unlikely(ktrace_enabled(effective_tag))) { |
| KTRACE_STATE.WriteRecord(effective_tag, kRecordCurrentTimestamp, a, b); |
| } |
| } |
| |
| template <bool enabled> |
| inline void ktrace_probe(TraceEnabled<enabled>, TraceContext context, StringRef* string_ref, |
| uint64_t a) { |
| if constexpr (!enabled) { |
| return; |
| } |
| 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); |
| if (unlikely(ktrace_enabled(effective_tag))) { |
| KTRACE_STATE.WriteRecord(effective_tag, kRecordCurrentTimestamp, a); |
| } |
| } |
| |
| template <bool enabled> |
| inline void ktrace_probe(TraceEnabled<enabled>, TraceContext context, StringRef* string_ref, |
| uint64_t a, uint64_t b) { |
| if constexpr (!enabled) { |
| return; |
| } |
| 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); |
| if (unlikely(ktrace_enabled(effective_tag))) { |
| KTRACE_STATE.WriteRecord(effective_tag, kRecordCurrentTimestamp, a, b); |
| } |
| } |
| |
| template <bool enabled> |
| inline void ktrace_begin_duration(TraceEnabled<enabled>, TraceContext context, uint32_t group, |
| StringRef* string_ref) { |
| if constexpr (!enabled) { |
| return; |
| } |
| 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); |
| if (unlikely(ktrace_enabled(effective_tag))) { |
| KTRACE_STATE.WriteRecord(effective_tag, kRecordCurrentTimestamp); |
| } |
| } |
| |
| template <bool enabled> |
| inline void ktrace_end_duration(TraceEnabled<enabled>, TraceContext context, uint32_t group, |
| StringRef* string_ref) { |
| if constexpr (!enabled) { |
| return; |
| } |
| 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); |
| |
| if (unlikely(ktrace_enabled(effective_tag))) { |
| KTRACE_STATE.WriteRecord(effective_tag, kRecordCurrentTimestamp); |
| } |
| } |
| |
| 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) { |
| return; |
| } |
| 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); |
| if (unlikely(ktrace_enabled(effective_tag))) { |
| KTRACE_STATE.WriteRecord(effective_tag, kRecordCurrentTimestamp, a, 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) { |
| return; |
| } |
| 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); |
| if (unlikely(ktrace_enabled(effective_tag))) { |
| KTRACE_STATE.WriteRecord(effective_tag, kRecordCurrentTimestamp, a, 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) { |
| return; |
| } |
| 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); |
| if (unlikely(ktrace_enabled(effective_tag))) { |
| KTRACE_STATE.WriteRecord(effective_tag, kRecordCurrentTimestamp, flow_id, 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) { |
| return; |
| } |
| 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); |
| if (unlikely(ktrace_enabled(effective_tag))) { |
| KTRACE_STATE.WriteRecord(effective_tag, kRecordCurrentTimestamp, flow_id, a); |
| } |
| } |
| |
| template <bool enabled> |
| inline void ktrace_flow_step(TraceEnabled<enabled>, TraceContext context, uint32_t group, |
| StringRef* string_ref, uint64_t flow_id, uint64_t a = 0) { |
| if constexpr (!enabled) { |
| return; |
| } |
| const uint32_t tag = TAG_FLOW_STEP(string_ref->GetId(), group); |
| const uint32_t effective_tag = |
| KTRACE_TAG_FLAGS(tag, context == TraceContext::Thread ? 0 : KTRACE_FLAGS_CPU); |
| if (unlikely(ktrace_enabled(effective_tag))) { |
| KTRACE_STATE.WriteRecord(effective_tag, kRecordCurrentTimestamp, flow_id, a); |
| } |
| } |
| |
| template <bool enabled> |
| inline void ktrace_counter(TraceEnabled<enabled>, uint32_t group, StringRef* string_ref, |
| int64_t value, uint64_t counter_id = 0) { |
| if constexpr (!enabled) { |
| return; |
| } |
| const uint32_t tag = KTRACE_TAG_FLAGS(TAG_COUNTER(string_ref->GetId(), group), KTRACE_FLAGS_CPU); |
| if (unlikely(ktrace_enabled(tag))) { |
| KTRACE_STATE.WriteRecord(tag, kRecordCurrentTimestamp, counter_id, |
| static_cast<uint64_t>(value)); |
| } |
| } |
| |
| inline void ktrace_name_etc(uint32_t tag, uint32_t id, uint32_t arg, const char* name, |
| bool always) { |
| KTRACE_STATE.WriteNameEtc(tag, id, arg, name, always); |
| } |
| |
| inline void ktrace_name(uint32_t tag, uint32_t id, uint32_t arg, const char* name, |
| bool always = false) { |
| ktrace_name_etc(tag, id, arg, name, always); |
| } |
| |
| inline ssize_t ktrace_read_user(void* ptr, uint32_t off, size_t len) { |
| return KTRACE_STATE.ReadUser(ptr, off, len); |
| } |
| |
| zx_status_t ktrace_control(uint32_t action, uint32_t options, void* ptr); |
| |
| void ktrace_report_live_threads(); |
| void ktrace_report_live_processes(); |
| |
| // 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, uint16_t group, TraceContext = TraceContext::Thread> |
| class TraceDuration; |
| |
| template <bool enabled, uint16_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_ |