blob: 34d945675ff310bccf1f0e3a6b65caa03e0447c1 [file] [log] [blame]
// Copyright 2020 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
//==============================================================================
//
// This file provides the interface for working with the tokenized trace
// backend.
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#ifndef PW_TRACE_GET_TIME_DELTA
#ifdef __cplusplus
#include <type_traits>
#endif // __cplusplus
#endif // PW_TRACE_GET_TIME_DELTA
#include "pw_status/status.h"
#include "pw_tokenizer/tokenize.h"
#include "pw_trace_tokenized/config.h"
#include "pw_trace_tokenized/internal/trace_tokenized_internal.h"
#ifdef __cplusplus
namespace pw {
namespace trace {
using EventType = pw_trace_EventType;
namespace internal {
// Simple ring buffer which is suitable for use in a critical section.
template <size_t kSize>
class TraceQueue {
public:
struct QueueEventBlock {
uint32_t trace_token;
EventType event_type;
const char* module;
uint32_t trace_id;
uint8_t flags;
size_t data_size;
std::byte data_buffer[PW_TRACE_BUFFER_MAX_DATA_SIZE_BYTES];
};
pw::Status TryPushBack(uint32_t trace_token,
EventType event_type,
const char* module,
uint32_t trace_id,
uint8_t flags,
const void* data_buffer,
size_t data_size) {
if (IsFull()) {
return pw::Status::ResourceExhausted();
}
event_queue_[head_].trace_token = trace_token;
event_queue_[head_].event_type = event_type;
event_queue_[head_].module = module;
event_queue_[head_].trace_id = trace_id;
event_queue_[head_].flags = flags;
event_queue_[head_].data_size = data_size;
for (size_t i = 0; i < data_size; i++) {
event_queue_[head_].data_buffer[i] =
reinterpret_cast<const std::byte*>(data_buffer)[i];
}
head_ = (head_ + 1) % kSize;
is_empty_ = false;
return pw::OkStatus();
}
const volatile QueueEventBlock* PeekFront() const {
if (IsEmpty()) {
return nullptr;
}
return &event_queue_[tail_];
}
void PopFront() {
if (!IsEmpty()) {
tail_ = (tail_ + 1) % kSize;
is_empty_ = (tail_ == head_);
}
}
void Clear() {
head_ = 0;
tail_ = 0;
is_empty_ = true;
}
bool IsEmpty() const { return is_empty_; }
bool IsFull() const { return !is_empty_ && (head_ == tail_); }
private:
std::array<volatile QueueEventBlock, kSize> event_queue_;
volatile size_t head_ = 0; // Next write
volatile size_t tail_ = 0; // Next read
volatile bool is_empty_ =
true; // Used to distinquish if head==tail is empty or full
};
} // namespace internal
class TokenizedTraceImpl {
public:
void Enable(bool enable) {
if (enable != enabled_ && enable) {
event_queue_.Clear();
}
enabled_ = enable;
}
bool IsEnabled() const { return enabled_; }
void HandleTraceEvent(uint32_t trace_token,
EventType event_type,
const char* module,
uint32_t trace_id,
uint8_t flags,
const void* data_buffer,
size_t data_size);
private:
using TraceQueue = internal::TraceQueue<PW_TRACE_QUEUE_SIZE_EVENTS>;
PW_TRACE_TIME_TYPE last_trace_time_ = 0;
bool enabled_ = false;
TraceQueue event_queue_;
void HandleNextItemInQueue(
const volatile TraceQueue::QueueEventBlock* event_block);
};
// A singleton object of the TokenizedTraceImpl class which can be used to
// interface with trace using the C++ API.
// Example: pw::trace::TokenizedTrace::Instance().Enable(true);
class TokenizedTrace {
public:
static TokenizedTraceImpl& Instance() { return instance_; };
private:
static TokenizedTraceImpl instance_;
};
} // namespace trace
} // namespace pw
#endif // __cplusplus
// PW_TRACE_SET_ENABLED is used to enable or disable tracing.
#define PW_TRACE_SET_ENABLED(enabled) pw_trace_Enable(enabled)
// PW_TRACE_REF provides the uint32_t token value for a specific trace event.
// this can be used in the callback to perform specific actions for that trace.
// All the fields must match exactly to generate the correct trace reference.
// If the trace does not have a group, use PW_TRACE_GROUP_LABEL_DEFAULT.
//
// For example this can be used to skip a specific trace:
// pw_trace_TraceEventReturnFlags TraceEventCallback(
// uint32_t trace_ref,
// pw_trace_EventType event_type,
// const char* module,
// uint32_t trace_id,
// uint8_t flags) {
// auto skip_trace_ref = PW_TRACE_REF(PW_TRACE_TYPE_INSTANT,
// "test_module", // Module
// "test_label", // Label
// PW_TRACE_FLAGS_DEFAULT,
// PW_TRACE_GROUP_LABEL_DEFAULT);
// if (trace_ref == skip_trace_ref) {
// return PW_TRACE_EVENT_RETURN_FLAGS_SKIP_EVENT;
// }
// return 0;
// }
//
// The above trace ref would provide the tokenize value for the string:
// "1|0|test_module||test_label"
//
// Another example:
// #define PW_TRACE_MODULE test_module
// PW_TRACE_INSTANT_DATA_FLAG(2, "label", "group", id, "%d", 5, 1);
// Would internally generate a token value for the string:
// "1|2|test_module|group|label|%d"
// The trace_id, and data value are runtime values and not included in the
// token string.
#define PW_TRACE_REF(event_type, module, label, flags, group) \
PW_TOKENIZE_STRING_DOMAIN("trace", \
PW_STRINGIFY(event_type) "|" PW_STRINGIFY( \
flags) "|" module "|" group "|" label)
#define PW_TRACE_REF_DATA(event_type, module, label, flags, group, type) \
PW_TOKENIZE_STRING_DOMAIN( \
"trace", \
PW_STRINGIFY(event_type) "|" PW_STRINGIFY(flags) "|" module "|" group \
"|" label "|" type)