diff --git a/include/debug/tracing_format.h b/include/debug/tracing_format.h new file mode 100644 index 00000000000..48b0396e9e7 --- /dev/null +++ b/include/debug/tracing_format.h @@ -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 + +#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 diff --git a/include/linker/common-rom.ld b/include/linker/common-rom.ld index f1e4db54703..35f184cffd0 100644 --- a/include/linker/common-rom.ld +++ b/include/linker/common-rom.ld @@ -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) diff --git a/subsys/debug/Kconfig b/subsys/debug/Kconfig index 3015b8f475f..a6ce22e7e4b 100644 --- a/subsys/debug/Kconfig +++ b/subsys/debug/Kconfig @@ -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 diff --git a/subsys/debug/tracing/CMakeLists.txt b/subsys/debug/tracing/CMakeLists.txt index b283fbb1569..796986f6710 100644 --- a/subsys/debug/tracing/CMakeLists.txt +++ b/subsys/debug/tracing/CMakeLists.txt @@ -1,27 +1,57 @@ # 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 ) - - if(CONFIG_TRACING) - zephyr_library_include_directories( - ${ZEPHYR_BASE}/kernel/include - ${ZEPHYR_BASE}/arch/${ARCH}/include - ) - endif() endif() +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 +) + zephyr_include_directories_ifdef(CONFIG_TRACING include) add_subdirectory_ifdef(CONFIG_TRACING_CTF ctf) diff --git a/subsys/debug/tracing/include/tracing_backend.h b/subsys/debug/tracing/include/tracing_backend.h new file mode 100644 index 00000000000..75b74747fa9 --- /dev/null +++ b/subsys/debug/tracing/include/tracing_backend.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2019 Intel corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _TRACE_BACKEND_H +#define _TRACE_BACKEND_H + +#include +#include + +#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 diff --git a/subsys/debug/tracing/include/tracing_buffer.h b/subsys/debug/tracing/include/tracing_buffer.h new file mode 100644 index 00000000000..b1270c725c9 --- /dev/null +++ b/subsys/debug/tracing/include/tracing_buffer.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2019 Intel corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _TRACE_BUFFER_H +#define _TRACE_BUFFER_H + +#include +#include + +#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 diff --git a/subsys/debug/tracing/include/tracing_core.h b/subsys/debug/tracing/include/tracing_core.h new file mode 100644 index 00000000000..355b51e19d9 --- /dev/null +++ b/subsys/debug/tracing/include/tracing_core.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019 Intel corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _TRACE_CORE_H +#define _TRACE_CORE_H + +#include +#include + +#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 diff --git a/subsys/debug/tracing/include/tracing_format_common.h b/subsys/debug/tracing/include/tracing_format_common.h new file mode 100644 index 00000000000..e22b4a8d9d2 --- /dev/null +++ b/subsys/debug/tracing/include/tracing_format_common.h @@ -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 +#include +#include + +#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 diff --git a/subsys/debug/tracing/tracing_backend_posix.c b/subsys/debug/tracing/tracing_backend_posix.c new file mode 100644 index 00000000000..79b9bfb9f4a --- /dev/null +++ b/subsys/debug/tracing/tracing_backend_posix.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018 Oticon A/S + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +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); diff --git a/subsys/debug/tracing/tracing_backend_uart.c b/subsys/debug/tracing/tracing_backend_uart.c new file mode 100644 index 00000000000..80beb7d2fd2 --- /dev/null +++ b/subsys/debug/tracing/tracing_backend_uart.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2019 Intel corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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); diff --git a/subsys/debug/tracing/tracing_backend_usb.c b/subsys/debug/tracing/tracing_backend_usb.c new file mode 100644 index 00000000000..9be62d1041c --- /dev/null +++ b/subsys/debug/tracing/tracing_backend_usb.c @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2019 Intel corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); diff --git a/subsys/debug/tracing/tracing_buffer.c b/subsys/debug/tracing/tracing_buffer.c new file mode 100644 index 00000000000..01e88f2c255 --- /dev/null +++ b/subsys/debug/tracing/tracing_buffer.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2019 Intel corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +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); +} diff --git a/subsys/debug/tracing/tracing_core.c b/subsys/debug/tracing/tracing_core.c new file mode 100644 index 00000000000..63f473fc5f1 --- /dev/null +++ b/subsys/debug/tracing/tracing_core.c @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2019 Intel corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/subsys/debug/tracing/tracing_format_async.c b/subsys/debug/tracing/tracing_format_async.c new file mode 100644 index 00000000000..7ed40a4a48f --- /dev/null +++ b/subsys/debug/tracing/tracing_format_async.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2019 Intel corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +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(); + } +} diff --git a/subsys/debug/tracing/tracing_format_common.c b/subsys/debug/tracing/tracing_format_common.c new file mode 100644 index 00000000000..3277e405135 --- /dev/null +++ b/subsys/debug/tracing/tracing_format_common.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2019 Intel corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +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; +} diff --git a/subsys/debug/tracing/tracing_format_sync.c b/subsys/debug/tracing/tracing_format_sync.c new file mode 100644 index 00000000000..e8830ca0f7f --- /dev/null +++ b/subsys/debug/tracing/tracing_format_sync.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019 Intel corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +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(); +}