tracing: add infrastructure for collection of tracing data

First, this commit adds user interface in tracing_format.h which
can trace both string format and data format packet.

Second, it adds method both for asynchronous and synchronous way.
For asynchronous method, tracing packet will be buffered in tracing
buffer first, tracing thread will output the stream data with the
help of tracing backend when tracing thread get scheduled.

Third, it adds UART and USB tracing backend for asynchronous
tracing method, and adds POSIX tracing backend for synchronous
tracing way.

Also it can receive command from host to dynamically enable and
disable tracing to have host capture tracing data conveniently.

Signed-off-by: Wentong Wu <wentong.wu@intel.com>
This commit is contained in:
Wentong Wu 2020-02-06 11:59:54 +08:00 committed by Anas Nashif
commit 8ccc04de6a
16 changed files with 1410 additions and 10 deletions

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2019 Intel corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_TRACE_FORMAT_H
#define ZEPHYR_INCLUDE_TRACE_FORMAT_H
#include <toolchain/common.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief A structure to represent tracing data format.
*/
typedef struct tracing_data {
u8_t *data;
u32_t length;
} __packed tracing_data_t;
/**
* @brief Macro to trace a message in string format.
*/
#define TRACING_STRING(fmt, ...) \
do { \
tracing_format_string(fmt, ##__VA_ARGS__); \
} while (false)
/**
* @brief Macro to format data to tracing data format.
*/
#define TRACING_FORMAT_DATA(x) \
((struct tracing_data){.data = (u8_t *)&(x), .length = sizeof((x))})
/**
* @brief Macro to trace a message in tracing data format.
*
* All the parameters should be struct tracing_data.
*/
#define TRACING_DATA(...) \
do { \
struct tracing_data arg[] = {__VA_ARGS__}; \
\
tracing_format_data(arg, sizeof(arg) / \
sizeof(struct tracing_data)); \
} while (false)
/**
* @brief Tracing a message in string format.
*
* @param str String to format.
* @param ... Variable length arguments.
*/
void tracing_format_string(const char *str, ...);
/**
* @brief Tracing a message in raw data format.
*
* @param data Raw data to be traced.
* @param length Raw data length.
*/
void tracing_format_raw_data(u8_t *data, u32_t length);
/**
* @brief Tracing a message in tracing data format.
*
* @param tracing_data_array Tracing_data format data array to be traced.
* @param count Tracing_data array data count.
*/
void tracing_format_data(tracing_data_t *tracing_data_array, u32_t count);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -160,3 +160,10 @@
KEEP(*(SORT_BY_NAME("._cfb_font.*")))
__font_entry_end = .;
} GROUP_LINK_IN(ROMABLE_REGION)
SECTION_DATA_PROLOGUE(tracing_backends_sections,,)
{
_tracing_backend_list_start = .;
KEEP(*("._tracing_backend.*"));
_tracing_backend_list_end = .;
} GROUP_LINK_IN(ROMABLE_REGION)

View file

@ -3,6 +3,8 @@
# Copyright (c) 2015 Wind River Systems, Inc.
# SPDX-License-Identifier: Apache-2.0
DT_CHOSEN_Z_CONSOLE := zephyr,console
menu "System Monitoring Options"
config BOOT_TIME_MEASUREMENT
@ -43,6 +45,121 @@ config TRACING
Enable system tracing. This requires a backend such as SEGGER
Systemview to be enabled as well.
if TRACING
choice
prompt "Tracing Method"
default TRACING_ASYNC
config TRACING_SYNC
bool "Synchronous Tracing"
select RING_BUFFER
help
Enable synchronous tracing. This requires the backend to be
very low-latency.
config TRACING_ASYNC
bool "Asynchronous Tracing"
select RING_BUFFER
help
Enable asynchronous tracing. This will buffer all the tracing
packets to the ring buffer first, tracing thread will try to
output as much data in ring buffer as possible when tracing
thread get scheduled.
endchoice
config TRACING_THREAD_STACK_SIZE
int "Stack size of tracing thread"
default 1024
depends on TRACING_ASYNC
help
Stack size of tracing thread.
config TRACING_THREAD_WAIT_THRESHOLD
int "Tracing thread waiting threshold"
default 100
depends on TRACING_ASYNC
help
Tracing thread waiting period given in milliseconds after
every first packet put to tracing buffer.
config TRACING_BUFFER_SIZE
int "Size of tracing buffer"
default 2048 if TRACING_ASYNC
default TRACING_PACKET_MAX_SIZE if TRACING_SYNC
range 32 65536
help
Size of tracing buffer. If TRACING_ASYNC is enabled, tracing buffer
is used as ring_buffer to buffer data packet and string packet. If
TRACING_SYNC is enabled, it's used to hold the formated data.
config TRACING_PACKET_MAX_SIZE
int "Max size of one tracing packet"
default 32
help
Max size of one tracing packet.
choice
prompt "Tracing Backend"
default TRACING_BACKEND_UART
config TRACING_BACKEND_UART
bool "Enable UART backend"
depends on UART_CONSOLE
depends on TRACING_ASYNC
help
When enabled backend is using UART to output tracing data.
config TRACING_BACKEND_USB
bool "Enable USB backend"
depends on TRACING_ASYNC
help
When enabled backend is using USB to output tracing data.
config TRACING_BACKEND_POSIX
bool "Enable POSIX backend"
depends on TRACING_SYNC
depends on ARCH_POSIX
help
When enabled backend is using posix API to output tracing
data to file system.
endchoice
config TRACING_BACKEND_UART_NAME
string "Device Name of UART Device for UART backend"
default "$(dt_chosen_label,$(DT_CHOSEN_Z_CONSOLE))" if HAS_DTS
default "UART_0"
depends on TRACING_BACKEND_UART
help
This option specifies the name of UART device to be used for
tracing backend.
config TRACING_USB_MPS
int "USB backend max packet size"
default 64
depends on TRACING_BACKEND_USB
help
USB tracing backend max packet size(endpoint MPS).
config TRACING_HANDLE_HOST_CMD
bool "Enable host cmd handle"
select UART_INTERRUPT_DRIVEN if TRACING_BACKEND_UART
help
When enabled tracing will handle cmd from host to dynamically
enable and disable tracing to have host capture tracing stream
data conveniently.
config TRACING_CMD_BUFFER_SIZE
int "Size of tracing cmd buffer"
default 32
range 32 128
help
Size of tracing command buffer.
endif
config ASAN
bool "Build with address sanitizer"
depends on ARCH_POSIX

View file

@ -1,26 +1,56 @@
# SPDX-License-Identifier: Apache-2.0
if(CONFIG_SEGGER_SYSTEMVIEW OR CONFIG_TRACING_CPU_STATS)
zephyr_library()
zephyr_library_sources_ifdef(
zephyr_sources_ifdef(
CONFIG_SEGGER_SYSTEMVIEW
sysview_config.c
sysview.c
)
zephyr_library_sources_ifdef(
zephyr_sources_ifdef(
CONFIG_TRACING_CPU_STATS
cpu_stats.c
)
endif()
if(CONFIG_TRACING)
zephyr_library_include_directories(
zephyr_sources_ifdef(
CONFIG_TRACING
tracing_buffer.c
tracing_core.c
tracing_format_common.c
)
zephyr_sources_ifdef(
CONFIG_TRACING_SYNC
tracing_format_sync.c
)
zephyr_sources_ifdef(
CONFIG_TRACING_ASYNC
tracing_format_async.c
)
zephyr_sources_ifdef(
CONFIG_TRACING_BACKEND_USB
tracing_backend_usb.c
)
zephyr_sources_ifdef(
CONFIG_TRACING_BACKEND_UART
tracing_backend_uart.c
)
zephyr_sources_ifdef(
CONFIG_TRACING_BACKEND_POSIX
tracing_backend_posix.c
)
zephyr_include_directories_ifdef(
CONFIG_TRACING
${ZEPHYR_BASE}/kernel/include
${ZEPHYR_BASE}/arch/${ARCH}/include
)
endif()
endif()
)
zephyr_include_directories_ifdef(CONFIG_TRACING include)

View file

@ -0,0 +1,108 @@
/*
* Copyright (c) 2019 Intel corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _TRACE_BACKEND_H
#define _TRACE_BACKEND_H
#include <string.h>
#include <sys/util.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Tracing backend
* @defgroup Tracing_backend Tracing backend
* @{
* @}
*/
struct tracing_backend;
/**
* @brief Tracing backend API.
*/
struct tracing_backend_api {
void (*init)(void);
void (*output)(const struct tracing_backend *backend,
u8_t *data, u32_t length);
};
/**
* @brief Tracing backend structure.
*/
struct tracing_backend {
const char *name;
const struct tracing_backend_api *api;
};
/**
* @brief Create tracing_backend instance.
*
* @param _name Instance name.
* @param _api Tracing backend API.
*/
#define TRACING_BACKEND_DEFINE(_name, _api) \
static const Z_STRUCT_SECTION_ITERABLE(tracing_backend, _name) = \
{ \
.name = STRINGIFY(_name), \
.api = &_api \
}
/**
* @brief Initialize tracing backend.
*
* @param backend Pointer to tracing_backend instance.
*/
static inline void tracing_backend_init(
const struct tracing_backend *backend)
{
if (backend && backend->api) {
backend->api->init();
}
}
/**
* @brief Output tracing packet with tracing backend.
*
* @param backend Pointer to tracing_backend instance.
* @param data Address of outputing buffer.
* @param length Length of outputing buffer.
*/
static inline void tracing_backend_output(
const struct tracing_backend *backend,
u8_t *data, u32_t length)
{
if (backend && backend->api) {
backend->api->output(backend, data, length);
}
}
/**
* @brief Get tracing backend based on the name of
* tracing backend in tracing backend section.
*
* @param name Name of wanted tracing backend.
*
* @return Pointer of the wanted backend or NULL.
*/
static inline struct tracing_backend *tracing_backend_get(char *name)
{
Z_STRUCT_SECTION_FOREACH(tracing_backend, backend) {
if (strcmp(backend->name, name) == 0) {
return backend;
}
}
return NULL;
}
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,120 @@
/*
* Copyright (c) 2019 Intel corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _TRACE_BUFFER_H
#define _TRACE_BUFFER_H
#include <stdbool.h>
#include <zephyr/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Initialize tracing buffer.
*/
void tracing_buffer_init(void);
/**
* @brief Tracing buffer is empty or not.
*
* @return true if the ring buffer is empty, or false if not.
*/
bool tracing_buffer_is_empty(void);
/**
* @brief Get free space in the tracing buffer.
*
* @return Tracing buffer free space (in bytes).
*/
u32_t tracing_buffer_space_get(void);
/**
* @brief Get tracing buffer capacity (max size).
*
* @return Tracing buffer capacity (in bytes).
*/
u32_t tracing_buffer_capacity_get(void);
/**
* @brief Try to allocate buffer in the tracing buffer.
*
* @param data Pointer to the address. It's set to a location
* within the tracing buffer.
* @param size Requested buffer size (in bytes).
*
* @return Size of allocated buffer which can be smaller than
* requested if there isn't enough free space or buffer wraps.
*/
u32_t tracing_buffer_put_claim(u8_t **data, u32_t size);
/**
* @brief Indicate number of bytes written to the allocated buffer.
*
* @param size Number of bytes written to the allocated buffer.
*
* @retval 0 Successful operation.
* @retval -EINVAL Given @a size exceeds free space of tracing buffer.
*/
int tracing_buffer_put_finish(u32_t size);
/**
* @brief Write data to tracing buffer.
*
* @param data Address of data.
* @param size Data size (in bytes).
*
* @retval Number of bytes written to tracing buffer.
*/
u32_t tracing_buffer_put(u8_t *data, u32_t size);
/**
* @brief Get address of the first valid data in tracing buffer.
*
* @param data Pointer to the address. It's set to a location pointing to
* the first valid data within the tracing buffer.
* @param size Requested buffer size (in bytes).
*
* @return Size of valid buffer which can be smaller than requested
* if there isn't enough valid data or buffer wraps.
*/
u32_t tracing_buffer_get_claim(u8_t **data, u32_t size);
/**
* @brief Indicate number of bytes read from claimed buffer.
*
* @param size Number of bytes read from claimed buffer.
*
* @retval 0 Successful operation.
* @retval -EINVAL Given @a size exceeds available data of tracing buffer.
*/
int tracing_buffer_get_finish(u32_t size);
/**
* @brief Read data from tracing buffer to output buffer.
*
* @param data Address of the output buffer.
* @param size Data size (in bytes).
*
* @retval Number of bytes written to the output buffer.
*/
u32_t tracing_buffer_get(u8_t *data, u32_t size);
/**
* @brief Get buffer from tracing command buffer.
*
* @param data Pointer to tracing command buffer start address.
*
* @return Tracing command buffer size (in bytes).
*/
u32_t tracing_cmd_buffer_alloc(u8_t **data);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2019 Intel corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _TRACE_CORE_H
#define _TRACE_CORE_H
#include <irq.h>
#include <zephyr/types.h>
#ifdef __cplusplus
extern "C" {
#endif
#define TRACING_LOCK() { int key; key = irq_lock()
#define TRACING_UNLOCK() { irq_unlock(key); } }
/**
* @brief Check tracing enabled or not.
*
* @return True if tracing enabled; False if tracing disabled.
*/
bool is_tracing_enabled(void);
/**
* @brief Give tracing buffer to backend.
*
* @param data Tracing buffer address.
* @param length Tracing buffer length.
*/
void tracing_buffer_handle(u8_t *data, u32_t length);
/**
* @brief Handle tracing packet drop.
*/
void tracing_packet_drop_handle(void);
/**
* @brief Handle tracing command.
*
* @param buf Tracing command buffer address.
* @param length Tracing command buffer length.
*/
void tracing_cmd_handle(u8_t *buf, u32_t length);
/**
* @brief Trigger tracing thread to run after every first put.
*
* @param before_put_is_empty If tracing buffer is empty before this put.
*/
void tracing_trigger_output(bool before_put_is_empty);
/**
* @brief Check if we are in tracing thread context.
*
* @return True if in tracing thread context; False if not.
*/
bool is_tracing_thread(void);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2019 Intel corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _TRACE_FORMAT_COMMON_H
#define _TRACE_FORMAT_COMMON_H
#include <stdarg.h>
#include <sys/printk.h>
#include <debug/tracing_format.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief A structure to represent tracing format context.
*/
typedef struct {
int status;
u32_t length;
} tracing_ctx_t;
#ifdef CONFIG_NEWLIB_LIBC
typedef int (*str_put_func_t)(int c, void *ctx);
extern void z_vprintk(str_put_func_t out, void *ctx,
const char *fmt, va_list ap);
#else
extern int z_prf(int (*func)(), void *dest, char *format, va_list vargs);
#endif
/**
* @brief Put string format tracing message to tracing buffer.
*
* @param str String to format.
* @param args Variable parameters.
*
* @return true if put tracing message to tracing buffer successfully.
*/
bool tracing_format_string_put(const char *str, va_list args);
/**
* @brief Put raw data format tracing message to tracing buffer.
*
* @param data Raw data to be traced.
* @param length Raw data length.
*
* @return true if put tracing message to tracing buffer successfully.
*/
bool tracing_format_raw_data_put(u8_t *data, u32_t size);
/**
* @brief Put tracing_data format message to tracing buffer.
*
* @param tracing_data_array Tracing_data format data array to be traced.
* @param count Tracing_data array data count.
*
* @return true if put tracing message to tracing buffer successfully.
*/
bool tracing_format_data_put(tracing_data_t *tracing_data_array, u32_t count);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2018 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <soc.h>
#include <stdio.h>
#include <kernel.h>
#include <cmdline.h>
#include <sys/__assert.h>
#include <tracing_backend.h>
static void *out_stream;
static const char *file_name;
static void tracing_backend_posix_init(void)
{
if (file_name == NULL) {
file_name = "channel0_0";
}
out_stream = (void *)fopen(file_name, "wb");
__ASSERT(out_stream != NULL, "posix backend init failed");
}
static void tracing_backend_posix_output(
const struct tracing_backend *backend,
u8_t *data, u32_t length)
{
fwrite(data, length, 1, (FILE *)out_stream);
if (!k_is_in_isr()) {
fflush((FILE *)out_stream);
}
}
const struct tracing_backend_api tracing_backend_posix_api = {
.init = tracing_backend_posix_init,
.output = tracing_backend_posix_output
};
TRACING_BACKEND_DEFINE(tracing_backend_posix, tracing_backend_posix_api);
void tracing_backend_posix_option(void)
{
static struct args_struct_t tracing_backend_option[] = {
{
.manual = false,
.is_mandatory = false,
.is_switch = false,
.option = "trace-file",
.name = "file_name",
.type = 's',
.dest = (void *)&file_name,
.call_when_found = NULL,
.descript = "File name for tracing output.",
},
ARG_TABLE_ENDMARKER
};
native_add_command_line_opts(tracing_backend_option);
}
NATIVE_TASK(tracing_backend_posix_option, PRE_BOOT_1, 1);

View file

@ -0,0 +1,95 @@
/*
* Copyright (c) 2019 Intel corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <ctype.h>
#include <kernel.h>
#include <device.h>
#include <drivers/uart.h>
#include <sys/__assert.h>
#include <tracing_core.h>
#include <tracing_buffer.h>
#include <tracing_backend.h>
static struct device *dev;
#ifdef CONFIG_TRACING_HANDLE_HOST_CMD
static void uart_isr(struct device *dev)
{
int rx;
u8_t byte;
static u8_t *cmd;
static u32_t length, cur;
while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
if (!uart_irq_rx_ready(dev)) {
continue;
}
rx = uart_fifo_read(dev, &byte, 1);
if (rx < 0) {
uart_irq_rx_disable(dev);
return;
}
if (!cmd) {
length = tracing_cmd_buffer_alloc(&cmd);
}
if (!isprint(byte)) {
if (byte == '\r') {
cmd[cur] = '\0';
tracing_cmd_handle(cmd, cur);
cmd = NULL;
cur = 0U;
}
continue;
}
if (cur < length - 1) {
cmd[cur++] = byte;
}
}
}
#endif
static void tracing_backend_uart_output(
const struct tracing_backend *backend,
u8_t *data, u32_t length)
{
for (u32_t i = 0; i < length; i++) {
uart_poll_out(dev, data[i]);
}
}
static void tracing_backend_uart_init(void)
{
dev = device_get_binding(CONFIG_TRACING_BACKEND_UART_NAME);
__ASSERT(dev, "uart backend binding failed");
#ifdef CONFIG_TRACING_HANDLE_HOST_CMD
uart_irq_rx_disable(dev);
uart_irq_tx_disable(dev);
uart_irq_callback_set(dev, uart_isr);
while (uart_irq_rx_ready(dev)) {
u8_t c;
uart_fifo_read(dev, &c, 1);
}
uart_irq_rx_enable(dev);
#endif
}
const struct tracing_backend_api tracing_backend_uart_api = {
.init = tracing_backend_uart_init,
.output = tracing_backend_uart_output
};
TRACING_BACKEND_DEFINE(tracing_backend_uart, tracing_backend_uart_api);

View file

@ -0,0 +1,184 @@
/*
* Copyright (c) 2019 Intel corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <sys/util.h>
#include <sys/atomic.h>
#include <sys/__assert.h>
#include <sys/byteorder.h>
#include <usb/usb_device.h>
#include <usb/usb_common.h>
#include <usb_descriptor.h>
#include <tracing_core.h>
#include <tracing_buffer.h>
#include <tracing_backend.h>
#define USB_TRANSFER_ONGOING 1
#define USB_TRANSFER_FREE 0
#define TRACING_IF_IN_EP_ADDR 0x81
#define TRACING_IF_OUT_EP_ADDR 0x01
struct usb_device_desc {
struct usb_if_descriptor if0;
struct usb_ep_descriptor if0_in_ep;
struct usb_ep_descriptor if0_out_ep;
} __packed;
static volatile int transfer_state;
static enum usb_dc_status_code usb_device_status = USB_DC_UNKNOWN;
USBD_CLASS_DESCR_DEFINE(primary, 0) struct usb_device_desc dev_desc = {
/*
* Interface descriptor 0
*/
.if0 = {
.bLength = sizeof(struct usb_if_descriptor),
.bDescriptorType = USB_INTERFACE_DESC,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = CUSTOM_CLASS,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
.iInterface = 0,
},
/*
* Data Endpoint IN
*/
.if0_in_ep = {
.bLength = sizeof(struct usb_ep_descriptor),
.bDescriptorType = USB_ENDPOINT_DESC,
.bEndpointAddress = TRACING_IF_IN_EP_ADDR,
.bmAttributes = USB_DC_EP_BULK,
.wMaxPacketSize = sys_cpu_to_le16(CONFIG_TRACING_USB_MPS),
.bInterval = 0x00,
},
/*
* Data Endpoint OUT
*/
.if0_out_ep = {
.bLength = sizeof(struct usb_ep_descriptor),
.bDescriptorType = USB_ENDPOINT_DESC,
.bEndpointAddress = TRACING_IF_OUT_EP_ADDR,
.bmAttributes = USB_DC_EP_BULK,
.wMaxPacketSize = sys_cpu_to_le16(CONFIG_TRACING_USB_MPS),
.bInterval = 0x00,
},
};
static void dev_status_cb(struct usb_cfg_data *cfg,
enum usb_dc_status_code status,
const u8_t *param)
{
ARG_UNUSED(cfg);
ARG_UNUSED(param);
usb_device_status = status;
}
static void tracing_ep_out_cb(u8_t ep, enum usb_dc_ep_cb_status_code ep_status)
{
u8_t *cmd = NULL;
u32_t bytes_to_read, length;
usb_read(ep, NULL, 0, &bytes_to_read);
while (bytes_to_read) {
length = tracing_cmd_buffer_alloc(&cmd);
if (cmd) {
length = MIN(length, bytes_to_read);
usb_read(ep, cmd, length, NULL);
tracing_cmd_handle(cmd, length);
bytes_to_read -= length;
}
}
/*
* send ZLP to sync with host receive thread
*/
usb_write(TRACING_IF_IN_EP_ADDR, NULL, 0, NULL);
}
static void tracing_ep_in_cb(u8_t ep, enum usb_dc_ep_cb_status_code ep_status)
{
ARG_UNUSED(ep);
ARG_UNUSED(ep_status);
transfer_state = USB_TRANSFER_FREE;
}
static struct usb_ep_cfg_data ep_cfg[] = {
{
.ep_cb = tracing_ep_out_cb,
.ep_addr = TRACING_IF_OUT_EP_ADDR,
},
{
.ep_cb = tracing_ep_in_cb,
.ep_addr = TRACING_IF_IN_EP_ADDR,
},
};
USBD_CFG_DATA_DEFINE(primary, tracing_backend_usb)
struct usb_cfg_data tracing_backend_usb_config = {
.usb_device_description = NULL,
.interface_descriptor = &dev_desc.if0,
.cb_usb_status = dev_status_cb,
.interface = {
.class_handler = NULL,
.custom_handler = NULL,
.vendor_handler = NULL,
},
.num_endpoints = ARRAY_SIZE(ep_cfg),
.endpoint = ep_cfg,
};
static void tracing_backend_usb_output(const struct tracing_backend *backend,
u8_t *data, u32_t length)
{
int ret = 0;
u32_t bytes;
while (length > 0) {
transfer_state = USB_TRANSFER_ONGOING;
/*
* make sure every USB tansfer no need ZLP at all
* because we are in lowest priority thread content
* there are no deterministic time between real USB
* packet and ZLP
*/
ret = usb_write(TRACING_IF_IN_EP_ADDR, data,
length >= CONFIG_TRACING_USB_MPS ?
CONFIG_TRACING_USB_MPS - 1 : length, &bytes);
if (ret) {
continue;
}
data += bytes;
length -= bytes;
while (transfer_state == USB_TRANSFER_ONGOING) {
}
}
}
static void tracing_backend_usb_init(void)
{
int ret;
ret = usb_enable(NULL);
__ASSERT(ret == 0, "usb backend enable failed");
}
const struct tracing_backend_api tracing_backend_usb_api = {
.init = tracing_backend_usb_init,
.output = tracing_backend_usb_output
};
TRACING_BACKEND_DEFINE(tracing_backend_usb, tracing_backend_usb_api);

View file

@ -0,0 +1,69 @@
/*
* Copyright (c) 2019 Intel corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <sys/ring_buffer.h>
static struct ring_buf tracing_ring_buf;
static u8_t tracing_buffer[CONFIG_TRACING_BUFFER_SIZE + 1];
static u8_t tracing_cmd_buffer[CONFIG_TRACING_CMD_BUFFER_SIZE];
u32_t tracing_cmd_buffer_alloc(u8_t **data)
{
*data = &tracing_cmd_buffer[0];
return sizeof(tracing_cmd_buffer);
}
u32_t tracing_buffer_put_claim(u8_t **data, u32_t size)
{
return ring_buf_put_claim(&tracing_ring_buf, data, size);
}
int tracing_buffer_put_finish(u32_t size)
{
return ring_buf_put_finish(&tracing_ring_buf, size);
}
u32_t tracing_buffer_put(u8_t *data, u32_t size)
{
return ring_buf_put(&tracing_ring_buf, data, size);
}
u32_t tracing_buffer_get_claim(u8_t **data, u32_t size)
{
return ring_buf_get_claim(&tracing_ring_buf, data, size);
}
int tracing_buffer_get_finish(u32_t size)
{
return ring_buf_get_finish(&tracing_ring_buf, size);
}
u32_t tracing_buffer_get(u8_t *data, u32_t size)
{
return ring_buf_get(&tracing_ring_buf, data, size);
}
void tracing_buffer_init(void)
{
ring_buf_init(&tracing_ring_buf,
sizeof(tracing_buffer), tracing_buffer);
}
bool tracing_buffer_is_empty(void)
{
return ring_buf_is_empty(&tracing_ring_buf);
}
u32_t tracing_buffer_capacity_get(void)
{
return ring_buf_capacity_get(&tracing_ring_buf);
}
u32_t tracing_buffer_space_get(void)
{
return ring_buf_space_get(&tracing_ring_buf);
}

View file

@ -0,0 +1,153 @@
/*
* Copyright (c) 2019 Intel corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <init.h>
#include <string.h>
#include <kernel.h>
#include <sys/util.h>
#include <sys/atomic.h>
#include <tracing_core.h>
#include <tracing_buffer.h>
#include <tracing_backend.h>
#define TRACING_CMD_ENABLE "enable"
#define TRACING_CMD_DISABLE "disable"
#ifdef CONFIG_TRACING_BACKEND_UART
#define TRACING_BACKEND_NAME "tracing_backend_uart"
#elif defined CONFIG_TRACING_BACKEND_USB
#define TRACING_BACKEND_NAME "tracing_backend_usb"
#elif defined CONFIG_TRACING_BACKEND_POSIX
#define TRACING_BACKEND_NAME "tracing_backend_posix"
#else
#define TRACING_BACKEND_NAME ""
#endif
enum tracing_state {
TRACING_DISABLE = 0,
TRACING_ENABLE
};
static atomic_t tracing_state;
static atomic_t tracing_packet_drop_num;
static struct tracing_backend *working_backend;
#ifdef CONFIG_TRACING_ASYNC
#define TRACING_THREAD_NAME "tracing_thread"
static k_tid_t tracing_thread_tid;
static struct k_thread tracing_thread;
static struct k_timer tracing_thread_timer;
static K_SEM_DEFINE(tracing_thread_sem, 0, 1);
static K_THREAD_STACK_DEFINE(tracing_thread_stack,
CONFIG_TRACING_THREAD_STACK_SIZE);
static void tracing_thread_func(void *dummy1, void *dummy2, void *dummy3)
{
u8_t *transferring_buf;
u32_t transferring_length, tracing_buffer_max_length;
tracing_thread_tid = k_current_get();
tracing_buffer_max_length = tracing_buffer_capacity_get();
while (true) {
if (tracing_buffer_is_empty()) {
k_sem_take(&tracing_thread_sem, K_FOREVER);
} else {
transferring_length =
tracing_buffer_get_claim(
&transferring_buf,
tracing_buffer_max_length);
tracing_buffer_handle(transferring_buf,
transferring_length);
tracing_buffer_get_finish(transferring_length);
}
}
}
static void tracing_thread_timer_expiry_fn(struct k_timer *timer)
{
k_sem_give(&tracing_thread_sem);
}
#endif
static void tracing_set_state(enum tracing_state state)
{
atomic_set(&tracing_state, state);
}
static int tracing_init(struct device *arg)
{
ARG_UNUSED(arg);
tracing_buffer_init();
working_backend = tracing_backend_get(TRACING_BACKEND_NAME);
tracing_backend_init(working_backend);
atomic_set(&tracing_packet_drop_num, 0);
if (IS_ENABLED(CONFIG_TRACING_HANDLE_HOST_CMD)) {
tracing_set_state(TRACING_DISABLE);
} else {
tracing_set_state(TRACING_ENABLE);
}
#ifdef CONFIG_TRACING_ASYNC
k_timer_init(&tracing_thread_timer,
tracing_thread_timer_expiry_fn, NULL);
k_thread_create(&tracing_thread, tracing_thread_stack,
K_THREAD_STACK_SIZEOF(tracing_thread_stack),
tracing_thread_func, NULL, NULL, NULL,
K_LOWEST_APPLICATION_THREAD_PRIO, 0, K_NO_WAIT);
k_thread_name_set(&tracing_thread, TRACING_THREAD_NAME);
#endif
return 0;
}
SYS_INIT(tracing_init, APPLICATION, 0);
#ifdef CONFIG_TRACING_ASYNC
void tracing_trigger_output(bool before_put_is_empty)
{
if (before_put_is_empty) {
k_timer_start(&tracing_thread_timer,
CONFIG_TRACING_THREAD_WAIT_THRESHOLD, K_NO_WAIT);
}
}
bool is_tracing_thread(void)
{
return (!k_is_in_isr() && (k_current_get() == tracing_thread_tid));
}
#endif
bool is_tracing_enabled(void)
{
return atomic_get(&tracing_state) == TRACING_ENABLE;
}
void tracing_cmd_handle(u8_t *buf, u32_t length)
{
if (strncmp(buf, TRACING_CMD_ENABLE, length) == 0) {
tracing_set_state(TRACING_ENABLE);
} else if (strncmp(buf, TRACING_CMD_DISABLE, length) == 0) {
tracing_set_state(TRACING_DISABLE);
}
}
void tracing_buffer_handle(u8_t *data, u32_t length)
{
tracing_backend_output(working_backend, data, length);
}
void tracing_packet_drop_handle(void)
{
atomic_inc(&tracing_packet_drop_num);
}

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2019 Intel corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <tracing_core.h>
#include <tracing_buffer.h>
#include <tracing_format_common.h>
void tracing_format_string(const char *str, ...)
{
va_list args;
bool put_success, before_put_is_empty;
if (!is_tracing_enabled() || is_tracing_thread()) {
return;
}
va_start(args, str);
TRACING_LOCK();
before_put_is_empty = tracing_buffer_is_empty();
put_success = tracing_format_string_put(str, args);
TRACING_UNLOCK();
va_end(args);
if (put_success) {
tracing_trigger_output(before_put_is_empty);
} else {
tracing_packet_drop_handle();
}
}
void tracing_format_raw_data(u8_t *data, u32_t length)
{
bool put_success, before_put_is_empty;
if (!is_tracing_enabled() || is_tracing_thread()) {
return;
}
TRACING_LOCK();
before_put_is_empty = tracing_buffer_is_empty();
put_success = tracing_format_raw_data_put(data, length);
TRACING_UNLOCK();
if (put_success) {
tracing_trigger_output(before_put_is_empty);
} else {
tracing_packet_drop_handle();
}
}
void tracing_format_data(tracing_data_t *tracing_data_array, u32_t count)
{
bool put_success, before_put_is_empty;
if (!is_tracing_enabled() || is_tracing_thread()) {
return;
}
TRACING_LOCK();
before_put_is_empty = tracing_buffer_is_empty();
put_success = tracing_format_data_put(tracing_data_array, count);
TRACING_UNLOCK();
if (put_success) {
tracing_trigger_output(before_put_is_empty);
} else {
tracing_packet_drop_handle();
}
}

View file

@ -0,0 +1,88 @@
/*
* Copyright (c) 2019 Intel corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <tracing_buffer.h>
#include <tracing_format_common.h>
static int str_put(int c, void *ctx)
{
tracing_ctx_t *str_ctx = (tracing_ctx_t *)ctx;
if (str_ctx->status == 0) {
u8_t *buf;
u32_t claimed_size;
claimed_size = tracing_buffer_put_claim(&buf, 1);
if (claimed_size) {
*buf = (u8_t)c;
str_ctx->length++;
} else {
str_ctx->status = -1;
}
}
return 0;
}
bool tracing_format_string_put(const char *str, va_list args)
{
tracing_ctx_t str_ctx = {0};
#if !defined(CONFIG_NEWLIB_LIBC) && !defined(CONFIG_ARCH_POSIX)
(void)z_prf(str_put, (void *)&str_ctx, (char *)str, args);
#else
z_vprintk(str_put, (void *)&str_ctx, str, args);
#endif
if (str_ctx.status == 0) {
tracing_buffer_put_finish(str_ctx.length);
return true;
}
tracing_buffer_put_finish(0);
return false;
}
bool tracing_format_raw_data_put(u8_t *data, u32_t size)
{
u32_t space = tracing_buffer_space_get();
if (space >= size) {
tracing_buffer_put(data, size);
return true;
}
return false;
}
bool tracing_format_data_put(tracing_data_t *tracing_data_array, u32_t count)
{
u32_t total_size = 0U;
for (u32_t i = 0; i < count; i++) {
tracing_data_t *tracing_data =
tracing_data_array + i;
u8_t *data = tracing_data->data, *buf;
u32_t length = tracing_data->length, claimed_size;
do {
claimed_size = tracing_buffer_put_claim(&buf, length);
memcpy(buf, data, claimed_size);
total_size += claimed_size;
length -= claimed_size;
data += claimed_size;
} while (length && claimed_size);
if (length && claimed_size == 0) {
tracing_buffer_put_finish(0);
return false;
}
}
tracing_buffer_put_finish(total_size);
return true;
}

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2019 Intel corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <tracing_core.h>
#include <tracing_buffer.h>
#include <tracing_format_common.h>
void tracing_format_string(const char *str, ...)
{
u8_t *data;
va_list args;
bool put_success;
u32_t length, tracing_buffer_size;
if (!is_tracing_enabled()) {
return;
}
tracing_buffer_size = tracing_buffer_capacity_get();
va_start(args, str);
TRACING_LOCK();
put_success = tracing_format_string_put(str, args);
if (put_success) {
length = tracing_buffer_get_claim(&data, tracing_buffer_size);
tracing_buffer_handle(data, length);
tracing_buffer_get_finish(length);
} else {
tracing_packet_drop_handle();
}
TRACING_UNLOCK();
va_end(args);
}
void tracing_format_raw_data(u8_t *data, u32_t length)
{
if (!is_tracing_enabled()) {
return;
}
TRACING_LOCK();
tracing_buffer_handle(data, length);
TRACING_UNLOCK();
}
void tracing_format_data(tracing_data_t *tracing_data_array, u32_t count)
{
u8_t *data;
bool put_success;
u32_t length, tracing_buffer_size;
if (!is_tracing_enabled()) {
return;
}
tracing_buffer_size = tracing_buffer_capacity_get();
TRACING_LOCK();
put_success = tracing_format_data_put(tracing_data_array, count);
if (put_success) {
length = tracing_buffer_get_claim(&data, tracing_buffer_size);
tracing_buffer_handle(data, length);
tracing_buffer_get_finish(length);
} else {
tracing_packet_drop_handle();
}
TRACING_UNLOCK();
}