Browse Source
This commit implements a CTF-backend for Zephyr's tracing API. The CTF-backend itself is split in a middle-layer and a bottom-layer. - Middle-layer decides the payload in event transactions, - Bottom-layer implements the IO transport. A simple POSIX bottom-layer is provided so far. Signed-off-by: François Delawarde <fnde@oticon.com>pull/12974/head
16 changed files with 1010 additions and 3 deletions
@ -0,0 +1,236 @@
@@ -0,0 +1,236 @@
|
||||
.. _ctf: |
||||
|
||||
Common Trace Format for Zephyr |
||||
############################## |
||||
|
||||
Common Trace Format, CTF, is an open format and language to describe trace |
||||
formats. This enables tool reuse, of which line-textual (babeltrace) and |
||||
graphical (TraceCompass) variants already exist. |
||||
|
||||
CTF should look familiar to C programmers but adds stronger typing. |
||||
See `CTF - A Flexible, High-performance Binary Trace Format |
||||
<http://diamon.org/ctf/>`_. |
||||
|
||||
Every system has application-specific events to trace out. Historically, |
||||
that has implied: |
||||
|
||||
1. Determining the application-specific payload, |
||||
2. Choosing suitable serialization-format, |
||||
3. Writing the on-target serialization code, |
||||
4. Deciding on and writing the I/O transport mechanics, |
||||
5. Writing the PC-side deserializer/parser, |
||||
6. Writing custom ad-hoc tools for filtering and presentation. |
||||
|
||||
CTF allows us to formally describe #1 and #2, which enables common |
||||
infrastructure for #5 and #6. This leaves #3 serialization code and #4 |
||||
I/O mechanics up to a custom implementation. |
||||
|
||||
This CTF debug module aims at providing a common #1 and #2 for Zephyr |
||||
("middle"), while providing a lean & generic interface for I/O ("bottom"). |
||||
Currently, only one CTF bottom-layer exists, POSIX ``fwrite``, but many others |
||||
are possible: |
||||
|
||||
- Async UART |
||||
- Async DMA |
||||
- Sync GPIO |
||||
- ... and many more. |
||||
|
||||
In fact, I/O varies greatly from system to system. Therefore, it is |
||||
instructive to create a taxonomy for I/O types when we must ensure the |
||||
interface between CTF-middle and CTF-bottom is generic and efficient |
||||
enough to model these. See the *I/O taxonomy* section below. |
||||
|
||||
|
||||
A Generic Interface |
||||
------------------- |
||||
|
||||
In CTF, an event is serialized to a packet containing one or more fields. |
||||
As seen from *I/O taxonomy* section below, a bottom layer may: |
||||
|
||||
- perform actions at transaction-start (e.g. mutex-lock), |
||||
- process each field in some way (e.g. sync-push emit, concat, enqueue to |
||||
thread-bound FIFO), |
||||
- perform actions at transaction-stop (e.g. mutex-release, emit of concat |
||||
buffer). |
||||
|
||||
The bottom-layer then needs to implement the following macros: |
||||
|
||||
- ``CTF_BOTTOM_LOCK``: No-op or how to lock the I/O transaction |
||||
- ``CTF_BOTTOM_UNLOCK``: No-op or how to release the I/O transaction |
||||
- ``CTF_BOTTOM_FIELDS``: Var-args of fields. May process each field with ``MAP`` |
||||
- ``CTF_BOTTOM_TIMESTAMPED_INTERNALLY``: Tells where timestamping is done |
||||
|
||||
These macros along with inline functions of the middle-layer can yield a |
||||
very low-overhead tracing infrastructure. |
||||
|
||||
|
||||
CTF Middle-Layer Example |
||||
------------------------ |
||||
|
||||
The CTF_EVENT macro will serialize each argument to a field:: |
||||
|
||||
/* Example for illustration */ |
||||
static inline void ctf_middle_foo(u32_t thread_id, ctf_bounded_string_t name) |
||||
{ |
||||
CTF_EVENT( |
||||
CTF_LITERAL(u8_t, 42), |
||||
thread_id, |
||||
name, |
||||
"hello, I was emitted from function: ", |
||||
__func__ /* __func__ is standard since C99 */ |
||||
); |
||||
} |
||||
|
||||
How to serialize and emit fields as well as handling alignment, can be done |
||||
internally and statically at compile-time in the bottom-layer. |
||||
|
||||
|
||||
How to Activate? |
||||
---------------- |
||||
|
||||
Make sure ``CONFIG_TRACING_CTF=y`` is set (``CONFIG_TRACING_CTF_BOTTOM_POSIX=y`` |
||||
is selected by default when using ``BOARD_NATIVE_POSIX``). |
||||
|
||||
|
||||
How to Use? |
||||
----------- |
||||
|
||||
The resulting CTF output can be visualized using babeltrace or TraceCompass: |
||||
|
||||
- The CTF output file can be specified in native posix using the ``-ctf-path`` |
||||
command line option |
||||
|
||||
- Create a new empty directory and copy into it: |
||||
|
||||
- The TSDL file (``subsys/debug/tracing/ctf/tsdl/metadata``) |
||||
|
||||
- The CTF output file renaming it to ``channel0_0`` |
||||
|
||||
- The trace can be opened by pointing TraceCompass or babeltrace to this new |
||||
directory |
||||
|
||||
|
||||
What is TraceCompass? |
||||
--------------------- |
||||
|
||||
TraceCompass is an open source tool that visualizes CTF events such as thread |
||||
scheduling and interrupts, and is helpful to find unintended interactions and |
||||
resource conflicts on complex systems. |
||||
|
||||
See also the presentation by Ericsson, |
||||
`Advanced Trouble-shooting Of Real-time Systems |
||||
<https://wiki.eclipse.org/images/0/0e/TechTalkOnlineDemoFeb2017_v1.pdf>`_. |
||||
|
||||
|
||||
Future LTTng Inspiration |
||||
------------------------ |
||||
|
||||
Currently, the middle-layer provided here is quite simple and bare-bones, |
||||
and needlessly copied from Zephyr's Segger SystemView debug module. |
||||
|
||||
For an OS like Zephyr, it would make sense to draw inspiration from |
||||
Linux's LTTng and change the middle-layer to serialize to the same format. |
||||
Doing this would enable direct reuse of TraceCompass' canned analyses |
||||
for Linux. Alternatively, LTTng-analyses in TraceCompass could be |
||||
customized to Zephyr. It is ongoing work to enable TraceCompass |
||||
visibility of Zephyr in a target-agnostic and open source way. |
||||
|
||||
|
||||
I/O Taxonomy |
||||
------------ |
||||
|
||||
- Atomic Push/Produce/Write/Enqueue: |
||||
|
||||
- synchronous: |
||||
means data-transmission has completed with the return of the |
||||
call. |
||||
|
||||
- asynchronous: |
||||
means data-transmission is pending or ongoing with the return |
||||
of the call. Usually, interrupts/callbacks/signals or polling |
||||
is used to determine completion. |
||||
|
||||
- buffered: |
||||
means data-transmissions are copied and grouped together to |
||||
form a larger ones. Usually for amortizing overhead (burst |
||||
dequeue) or jitter-mitigation (steady dequeue). |
||||
|
||||
Examples: |
||||
- sync unbuffered |
||||
E.g. PIO via GPIOs having steady stream, no extra FIFO memory needed. |
||||
Low jitter but may be less efficient (cant amortize the overhead of |
||||
writing). |
||||
|
||||
- sync buffered |
||||
E.g. ``fwrite()`` or enqueuing into FIFO. |
||||
Blockingly burst the FIFO when its buffer-waterlevel exceeds threshold. |
||||
Jitter due to bursts may lead to missed deadlines. |
||||
|
||||
- async unbuffered |
||||
E.g. DMA, or zero-copying in shared memory. |
||||
Be careful of data hazards, race conditions, etc! |
||||
|
||||
- async buffered |
||||
E.g. enqueuing into FIFO. |
||||
|
||||
|
||||
|
||||
- Atomic Pull/Consume/Read/Dequeue: |
||||
|
||||
- synchronous: |
||||
means data-reception has completed with the return of the call. |
||||
|
||||
- asynchronous: |
||||
means data-reception is pending or ongoing with the return of |
||||
the call. Usually, interrupts/callbacks/signals or polling is |
||||
used to determine completion. |
||||
|
||||
- buffered: |
||||
means data is copied-in in larger chunks than request-size. |
||||
Usually for amortizing wait-time. |
||||
|
||||
Examples: |
||||
- sync unbuffered |
||||
E.g. Blocking read-call, ``fread()`` or SPI-read, zero-copying in shared |
||||
memory. |
||||
|
||||
- sync buffered |
||||
E.g. Blocking read-call with caching applied. |
||||
Makes sense if read pattern exhibits spatial locality. |
||||
|
||||
- async unbuffered |
||||
E.g. zero-copying in shared memory. |
||||
Be careful of data hazards, race conditions, etc! |
||||
|
||||
- async buffered |
||||
E.g. ``aio_read()`` or DMA. |
||||
|
||||
|
||||
|
||||
Unfortunately, I/O may not be atomic and may, therefore, require locking. |
||||
Locking may not be needed if multiple independent channels are available. |
||||
|
||||
- The system has non-atomic write and one shared channel |
||||
E.g. UART. Locking required. |
||||
|
||||
``lock(); emit(a); emit(b); emit(c); release();`` |
||||
|
||||
- The system has non-atomic write but many channels |
||||
E.g. Multi-UART. Lock-free if the bottom-layer maps each Zephyr |
||||
thread+ISR to its own channel, thus alleviating races as each |
||||
thread is sequentially consistent with itself. |
||||
|
||||
``emit(a,thread_id); emit(b,thread_id); emit(c,thread_id);`` |
||||
|
||||
- The system has atomic write but one shared channel |
||||
E.g. ``native_posix`` or board with DMA. May or may not need locking. |
||||
|
||||
``emit(a ## b ## c); /* Concat to buffer */`` |
||||
|
||||
``lock(); emit(a); emit(b); emit(c); release(); /* No extra mem */`` |
||||
|
||||
- The system has atomic write and many channels |
||||
E.g. native_posix or board with multi-channel DMA. Lock-free. |
||||
|
||||
``emit(a ## b ## c, thread_id);`` |
||||
|
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
zephyr_include_directories(.) |
||||
zephyr_sources(ctf_top.c) |
||||
|
||||
add_subdirectory_ifdef(CONFIG_TRACING_CTF_BOTTOM_POSIX bottoms/posix) |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
zephyr_include_directories(.) |
||||
zephyr_sources(ctf_bottom.c) |
@ -0,0 +1,59 @@
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Oticon A/S |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
#include "ctf_bottom.h" |
||||
#include "soc.h" |
||||
#include "cmdline.h" /* native_posix command line options header */ |
||||
#include "posix_trace.h" |
||||
|
||||
|
||||
ctf_bottom_ctx_t ctf_bottom; |
||||
|
||||
void ctf_bottom_configure(void) |
||||
{ |
||||
if (ctf_bottom.pathname == NULL) { |
||||
ctf_bottom.pathname = "channel0_0"; |
||||
} |
||||
|
||||
ctf_bottom.ostream = fopen(ctf_bottom.pathname, "wb"); |
||||
if (ctf_bottom.ostream == NULL) { |
||||
posix_print_error_and_exit("CTF trace: " |
||||
"Problem opening file %s.\n", |
||||
ctf_bottom.pathname); |
||||
} |
||||
} |
||||
|
||||
void ctf_bottom_start(void) |
||||
{ |
||||
} |
||||
|
||||
/* command line option to specify ctf output file */ |
||||
void add_ctf_option(void) |
||||
{ |
||||
static struct args_struct_t ctf_options[] = { |
||||
/*
|
||||
* Fields: |
||||
* manual, mandatory, switch, |
||||
* option_name, var_name ,type, |
||||
* destination, callback, |
||||
* description |
||||
*/ |
||||
{ .manual = false, |
||||
.is_mandatory = false, |
||||
.is_switch = false, |
||||
.option = "ctf-path", |
||||
.name = "file_name", |
||||
.type = 's', |
||||
.dest = (void *)&ctf_bottom.pathname, |
||||
.call_when_found = NULL, |
||||
.descript = "File name for CTF tracing output." }, |
||||
ARG_TABLE_ENDMARKER |
||||
}; |
||||
|
||||
native_add_command_line_opts(ctf_options); |
||||
} |
||||
NATIVE_TASK(add_ctf_option, PRE_BOOT_1, 1); |
||||
|
@ -0,0 +1,76 @@
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Oticon A/S |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
#ifndef SUBSYS_DEBUG_TRACING_BOTTOMS_POSIX_CTF_BOTTOM_H |
||||
#define SUBSYS_DEBUG_TRACING_BOTTOMS_POSIX_CTF_BOTTOM_H |
||||
|
||||
#include <stddef.h> |
||||
#include <string.h> |
||||
#include <stdio.h> |
||||
#include <zephyr/types.h> |
||||
#include <ctf_map.h> |
||||
|
||||
|
||||
/* Obtain a field's size at compile-time.
|
||||
* Internal to this bottom-layer. |
||||
*/ |
||||
#define CTF_BOTTOM_INTERNAL_FIELD_SIZE(x) + sizeof(x) |
||||
|
||||
/* Append a field to current event-packet.
|
||||
* Internal to this bottom-layer. |
||||
*/ |
||||
#define CTF_BOTTOM_INTERNAL_FIELD_APPEND(x) \ |
||||
{ \ |
||||
memcpy(epacket_cursor, &(x), sizeof(x)); \ |
||||
epacket_cursor += sizeof(x); \ |
||||
} |
||||
|
||||
/* Gather fields to a contiguous event-packet, then atomically emit.
|
||||
* Used by middle-layer. |
||||
*/ |
||||
#define CTF_BOTTOM_FIELDS(...) \ |
||||
{ \ |
||||
u8_t epacket[0 MAP(CTF_BOTTOM_INTERNAL_FIELD_SIZE, ##__VA_ARGS__)]; \ |
||||
u8_t *epacket_cursor = &epacket[0]; \ |
||||
\ |
||||
MAP(CTF_BOTTOM_INTERNAL_FIELD_APPEND, ##__VA_ARGS__) \ |
||||
ctf_bottom_emit(epacket, sizeof(epacket)); \ |
||||
} |
||||
|
||||
/* No need for locking when ctf_bottom_emit does POSIX fwrite(3) which is thread
|
||||
* safe. Used by middle-layer. |
||||
*/ |
||||
#define CTF_BOTTOM_LOCK() { /* empty */ } |
||||
#define CTF_BOTTOM_UNLOCK() { /* empty */ } |
||||
|
||||
/* On native_posix board, the code must sample time by itself.
|
||||
* Used by middle-layer. |
||||
*/ |
||||
#define CTF_BOTTOM_TIMESTAMPED_INTERNALLY |
||||
|
||||
|
||||
typedef struct { |
||||
const char *pathname; |
||||
FILE *ostream; |
||||
} ctf_bottom_ctx_t; |
||||
|
||||
extern ctf_bottom_ctx_t ctf_bottom; |
||||
|
||||
|
||||
/* Configure initializes ctf_bottom context and opens the IO channel */ |
||||
void ctf_bottom_configure(void); |
||||
|
||||
/* Start a new trace stream */ |
||||
void ctf_bottom_start(void); |
||||
|
||||
/* Emit IO in system-specific way */ |
||||
static inline void ctf_bottom_emit(const void *ptr, size_t size) |
||||
{ |
||||
/* Simplest possible example is atomic fwrite */ |
||||
fwrite(ptr, size, 1, ctf_bottom.ostream); |
||||
} |
||||
|
||||
#endif /* SUBSYS_DEBUG_TRACING_BOTTOMS_POSIX_CTF_BOTTOM_H */ |
@ -0,0 +1,217 @@
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Oticon A/S |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
#ifndef SUBSYS_DEBUG_TRACING_CTF_MIDDLE_H |
||||
#define SUBSYS_DEBUG_TRACING_CTF_MIDDLE_H |
||||
|
||||
#include <stddef.h> |
||||
#include <string.h> |
||||
#include <ctf_bottom.h> |
||||
|
||||
/* Limit strings to 20 bytes to optimize bandwidth */ |
||||
#define CTF_MAX_STRING_LEN 20 |
||||
|
||||
/* Optionally enter into a critical region, decided by bottom layer */ |
||||
#define CTF_CRITICAL_REGION(x) \ |
||||
{ \ |
||||
CTF_BOTTOM_LOCK(); \ |
||||
x; \ |
||||
CTF_BOTTOM_UNLOCK(); \ |
||||
} |
||||
|
||||
|
||||
#ifdef CTF_BOTTOM_TIMESTAMPED_EXTERNALLY |
||||
/* Emit CTF event using the bottom-level IO mechanics */ |
||||
#define CTF_EVENT(...) \ |
||||
{ \ |
||||
CTF_CRITICAL_REGION(CTF_BOTTOM_FIELDS(__VA_ARGS__)) \ |
||||
} |
||||
#endif /* CTF_BOTTOM_TIMESTAMPED_EXTERNALLY */ |
||||
|
||||
#ifdef CTF_BOTTOM_TIMESTAMPED_INTERNALLY |
||||
/* Emit CTF event using the bottom-level IO mechanics. Prefix by sample time */ |
||||
#define CTF_EVENT(...) \ |
||||
{ \ |
||||
const u32_t tstamp = k_cycle_get_32(); \ |
||||
CTF_CRITICAL_REGION(CTF_BOTTOM_FIELDS(tstamp, __VA_ARGS__)) \ |
||||
} |
||||
#endif /* CTF_BOTTOM_TIMESTAMPED_INTERNALLY */ |
||||
|
||||
|
||||
/* Anonymous compound literal with 1 member. Legal since C99.
|
||||
* This permits us to take the address of literals, like so: |
||||
* &CTF_LITERAL(int, 1234) |
||||
* |
||||
* This may be required if a ctf_bottom layer uses memcpy. |
||||
* |
||||
* NOTE string literals already support address-of and sizeof, |
||||
* so string literals should not be wrapped with CTF_LITERAL. |
||||
*/ |
||||
#define CTF_LITERAL(type, value) ((type) { (type)(value) }) |
||||
|
||||
|
||||
typedef enum { |
||||
CTF_EVENT_THREAD_SWITCHED_OUT = 0x10, |
||||
CTF_EVENT_THREAD_SWITCHED_IN = 0x11, |
||||
CTF_EVENT_THREAD_PRIORITY_SET = 0x12, |
||||
CTF_EVENT_THREAD_CREATE = 0x13, |
||||
CTF_EVENT_THREAD_ABORT = 0x14, |
||||
CTF_EVENT_THREAD_SUSPEND = 0x15, |
||||
CTF_EVENT_THREAD_RESUME = 0x16, |
||||
CTF_EVENT_THREAD_READY = 0x17, |
||||
CTF_EVENT_THREAD_PENDING = 0x18, |
||||
CTF_EVENT_THREAD_INFO = 0x19, |
||||
CTF_EVENT_ISR_ENTER = 0x20, |
||||
CTF_EVENT_ISR_EXIT = 0x21, |
||||
CTF_EVENT_ISR_EXIT_TO_SCHEDULER = 0x22, |
||||
CTF_EVENT_IDLE = 0x30, |
||||
CTF_EVENT_ID_START_CALL = 0x41, |
||||
CTF_EVENT_ID_END_CALL = 0x42 |
||||
} ctf_event_t; |
||||
|
||||
|
||||
typedef struct { |
||||
char buf[CTF_MAX_STRING_LEN]; |
||||
} ctf_bounded_string_t; |
||||
|
||||
|
||||
static inline void ctf_middle_thread_switched_out(u32_t thread_id) |
||||
{ |
||||
CTF_EVENT( |
||||
CTF_LITERAL(u8_t, CTF_EVENT_THREAD_SWITCHED_OUT), |
||||
thread_id |
||||
); |
||||
} |
||||
|
||||
static inline void ctf_middle_thread_switched_in(u32_t thread_id) |
||||
{ |
||||
CTF_EVENT( |
||||
CTF_LITERAL(u8_t, CTF_EVENT_THREAD_SWITCHED_IN), |
||||
thread_id |
||||
); |
||||
} |
||||
|
||||
static inline void ctf_middle_thread_priority_set(u32_t thread_id, s8_t prio) |
||||
{ |
||||
CTF_EVENT( |
||||
CTF_LITERAL(u8_t, CTF_EVENT_THREAD_PRIORITY_SET), |
||||
thread_id, |
||||
prio |
||||
); |
||||
} |
||||
|
||||
static inline void ctf_middle_thread_create( |
||||
u32_t thread_id, |
||||
s8_t prio, |
||||
ctf_bounded_string_t name |
||||
) |
||||
{ |
||||
CTF_EVENT( |
||||
CTF_LITERAL(u8_t, CTF_EVENT_THREAD_CREATE), |
||||
thread_id, |
||||
name |
||||
); |
||||
} |
||||
|
||||
static inline void ctf_middle_thread_abort(u32_t thread_id) |
||||
{ |
||||
CTF_EVENT( |
||||
CTF_LITERAL(u8_t, CTF_EVENT_THREAD_ABORT), |
||||
thread_id |
||||
); |
||||
} |
||||
|
||||
static inline void ctf_middle_thread_suspend(u32_t thread_id) |
||||
{ |
||||
CTF_EVENT( |
||||
CTF_LITERAL(u8_t, CTF_EVENT_THREAD_SUSPEND), |
||||
thread_id |
||||
); |
||||
} |
||||
|
||||
static inline void ctf_middle_thread_resume(u32_t thread_id) |
||||
{ |
||||
CTF_EVENT( |
||||
CTF_LITERAL(u8_t, CTF_EVENT_THREAD_RESUME), |
||||
thread_id |
||||
); |
||||
} |
||||
|
||||
static inline void ctf_middle_thread_ready(u32_t thread_id) |
||||
{ |
||||
CTF_EVENT( |
||||
CTF_LITERAL(u8_t, CTF_EVENT_THREAD_READY), |
||||
thread_id |
||||
); |
||||
} |
||||
|
||||
static inline void ctf_middle_thread_pend(u32_t thread_id) |
||||
{ |
||||
CTF_EVENT( |
||||
CTF_LITERAL(u8_t, CTF_EVENT_THREAD_PENDING), |
||||
thread_id |
||||
); |
||||
} |
||||
|
||||
static inline void ctf_middle_thread_info( |
||||
u32_t thread_id, |
||||
u32_t stack_base, |
||||
u32_t stack_size |
||||
) |
||||
{ |
||||
CTF_EVENT( |
||||
CTF_LITERAL(u8_t, CTF_EVENT_THREAD_INFO), |
||||
thread_id, |
||||
stack_base, |
||||
stack_size |
||||
); |
||||
} |
||||
|
||||
static inline void ctf_middle_isr_enter(void) |
||||
{ |
||||
CTF_EVENT( |
||||
CTF_LITERAL(u8_t, CTF_EVENT_ISR_ENTER) |
||||
); |
||||
} |
||||
|
||||
static inline void ctf_middle_isr_exit(void) |
||||
{ |
||||
CTF_EVENT( |
||||
CTF_LITERAL(u8_t, CTF_EVENT_ISR_EXIT) |
||||
); |
||||
} |
||||
|
||||
static inline void ctf_middle_isr_exit_to_scheduler(void) |
||||
{ |
||||
CTF_EVENT( |
||||
CTF_LITERAL(u8_t, CTF_EVENT_ISR_EXIT_TO_SCHEDULER) |
||||
); |
||||
} |
||||
|
||||
static inline void ctf_middle_idle(void) |
||||
{ |
||||
CTF_EVENT( |
||||
CTF_LITERAL(u8_t, CTF_EVENT_IDLE) |
||||
); |
||||
} |
||||
|
||||
static inline void ctf_middle_void(u32_t id) |
||||
{ |
||||
CTF_EVENT( |
||||
CTF_LITERAL(u8_t, CTF_EVENT_ID_START_CALL), |
||||
id |
||||
); |
||||
} |
||||
|
||||
static inline void ctf_middle_end_call(u32_t id) |
||||
{ |
||||
CTF_EVENT( |
||||
CTF_LITERAL(u8_t, CTF_EVENT_ID_END_CALL), |
||||
id |
||||
); |
||||
} |
||||
|
||||
#endif /* SUBSYS_DEBUG_TRACING_CTF_MIDDLE_H */ |
@ -0,0 +1,177 @@
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Oticon A/S |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
#include <zephyr.h> |
||||
#include <kernel_structs.h> |
||||
#include <init.h> |
||||
|
||||
#include <ctf_middle.h> |
||||
#include "ctf_top.h" |
||||
|
||||
|
||||
#ifndef CONFIG_SMP |
||||
extern k_tid_t const _idle_thread; |
||||
#endif |
||||
|
||||
static inline int is_idle_thread(struct k_thread *thread) |
||||
{ |
||||
#ifdef CONFIG_SMP |
||||
return thread->base.is_idle; |
||||
#else |
||||
return thread == _idle_thread; |
||||
#endif |
||||
} |
||||
|
||||
void sys_trace_thread_switched_out(void) |
||||
{ |
||||
struct k_thread *thread = k_current_get(); |
||||
|
||||
ctf_middle_thread_switched_out((u32_t)(uintptr_t)thread); |
||||
} |
||||
|
||||
void sys_trace_thread_switched_in(void) |
||||
{ |
||||
struct k_thread *thread = k_current_get(); |
||||
|
||||
ctf_middle_thread_switched_in((u32_t)(uintptr_t)thread); |
||||
} |
||||
|
||||
void sys_trace_thread_priority_set(struct k_thread *thread) |
||||
{ |
||||
ctf_middle_thread_priority_set((u32_t)(uintptr_t)thread, |
||||
thread->base.prio); |
||||
} |
||||
|
||||
void sys_trace_thread_create(struct k_thread *thread) |
||||
{ |
||||
ctf_bounded_string_t name = { "Unnamed thread" }; |
||||
|
||||
#if defined(CONFIG_THREAD_NAME) |
||||
if (thread->name != NULL) { |
||||
strncpy(name.buf, thread->name, sizeof(name.buf)); |
||||
/* strncpy may not always null-terminate */ |
||||
name.buf[sizeof(name.buf) - 1] = 0; |
||||
} |
||||
#endif |
||||
|
||||
ctf_middle_thread_create( |
||||
(u32_t)(uintptr_t)thread, |
||||
thread->base.prio, |
||||
name |
||||
); |
||||
|
||||
#if defined(CONFIG_THREAD_STACK_INFO) |
||||
ctf_middle_thread_info( |
||||
(u32_t)(uintptr_t)thread, |
||||
thread->stack_info.size, |
||||
thread->stack_info.start |
||||
); |
||||
#endif |
||||
} |
||||
|
||||
void sys_trace_thread_abort(struct k_thread *thread) |
||||
{ |
||||
ctf_middle_thread_abort((u32_t)(uintptr_t)thread); |
||||
} |
||||
|
||||
void sys_trace_thread_suspend(struct k_thread *thread) |
||||
{ |
||||
ctf_middle_thread_suspend((u32_t)(uintptr_t)thread); |
||||
} |
||||
|
||||
void sys_trace_thread_resume(struct k_thread *thread) |
||||
{ |
||||
ctf_middle_thread_resume((u32_t)(uintptr_t)thread); |
||||
} |
||||
|
||||
void sys_trace_thread_ready(struct k_thread *thread) |
||||
{ |
||||
ctf_middle_thread_ready((u32_t)(uintptr_t)thread); |
||||
} |
||||
|
||||
void sys_trace_thread_pend(struct k_thread *thread) |
||||
{ |
||||
ctf_middle_thread_pend((u32_t)(uintptr_t)thread); |
||||
} |
||||
|
||||
void sys_trace_thread_info(struct k_thread *thread) |
||||
{ |
||||
#if defined(CONFIG_THREAD_STACK_INFO) |
||||
ctf_middle_thread_info( |
||||
(u32_t)(uintptr_t)thread, |
||||
thread->stack_info.size, |
||||
thread->stack_info.start |
||||
); |
||||
#endif |
||||
} |
||||
|
||||
void sys_trace_isr_enter(void) |
||||
{ |
||||
ctf_middle_isr_enter(); |
||||
} |
||||
|
||||
void sys_trace_isr_exit(void) |
||||
{ |
||||
ctf_middle_isr_exit(); |
||||
} |
||||
|
||||
void sys_trace_isr_exit_to_scheduler(void) |
||||
{ |
||||
ctf_middle_isr_exit_to_scheduler(); |
||||
} |
||||
|
||||
void sys_trace_idle(void) |
||||
{ |
||||
ctf_middle_idle(); |
||||
} |
||||
|
||||
void sys_trace_void(unsigned int id) |
||||
{ |
||||
ctf_middle_void(id); |
||||
} |
||||
|
||||
void sys_trace_end_call(unsigned int id) |
||||
{ |
||||
ctf_middle_end_call(id); |
||||
} |
||||
|
||||
|
||||
void z_sys_trace_thread_switched_out(void) |
||||
{ |
||||
sys_trace_thread_switched_out(); |
||||
} |
||||
void z_sys_trace_thread_switched_in(void) |
||||
{ |
||||
sys_trace_thread_switched_in(); |
||||
} |
||||
void z_sys_trace_isr_enter(void) |
||||
{ |
||||
sys_trace_isr_enter(); |
||||
} |
||||
void z_sys_trace_isr_exit(void) |
||||
{ |
||||
sys_trace_isr_exit(); |
||||
} |
||||
void z_sys_trace_isr_exit_to_scheduler(void) |
||||
{ |
||||
sys_trace_isr_exit_to_scheduler(); |
||||
} |
||||
void z_sys_trace_idle(void) |
||||
{ |
||||
sys_trace_idle(); |
||||
} |
||||
|
||||
|
||||
static int ctf_top_init(struct device *arg) |
||||
{ |
||||
ARG_UNUSED(arg); |
||||
|
||||
ctf_bottom_configure(); |
||||
ctf_bottom_start(); |
||||
return 0; |
||||
} |
||||
|
||||
SYS_INIT(ctf_top_init, PRE_KERNEL_1, 0); |
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Oticon A/S |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
#ifndef SUBSYS_DEBUG_TRACING_CTF_TOP_H |
||||
#define SUBSYS_DEBUG_TRACING_CTF_TOP_H |
||||
|
||||
|
||||
#endif /* SUBSYS_DEBUG_TRACING_CTF_TOP_H */ |
@ -0,0 +1,150 @@
@@ -0,0 +1,150 @@
|
||||
/* CTF 1.8 */ |
||||
typealias integer { size = 8; align = 8; signed = true; } := int8_t; |
||||
typealias integer { size = 8; align = 8; signed = false; } := uint8_t; |
||||
typealias integer { size = 16; align = 8; signed = false; } := uint16_t; |
||||
typealias integer { size = 32; align = 8; signed = false; } := uint32_t; |
||||
typealias integer { size = 64; align = 8; signed = false; } := uint64_t; |
||||
typealias integer { size = 8; align = 8; signed = false; encoding = ASCII; } := ctf_bounded_string_t; |
||||
typealias enum : uint32_t { |
||||
MUTEX_INIT = 33, |
||||
MUTEX_UNLOCK = 34, |
||||
MUTEX_LOCK = 35, |
||||
SEMA_INIT = 36, |
||||
SEMA_GIVE = 37, |
||||
SEMA_TAKE = 38 |
||||
} := call_id; |
||||
|
||||
struct event_header { |
||||
uint32_t timestamp; |
||||
uint8_t id; |
||||
}; |
||||
|
||||
trace { |
||||
major = 1; |
||||
minor = 8; |
||||
byte_order = le; |
||||
}; |
||||
|
||||
stream { |
||||
event.header := struct event_header; |
||||
}; |
||||
|
||||
event { |
||||
name = thread_switched_out; |
||||
id = 0x10; |
||||
fields := struct { |
||||
uint32_t thread_id; |
||||
}; |
||||
}; |
||||
|
||||
event { |
||||
name = thread_switched_in; |
||||
id = 0x11; |
||||
fields := struct { |
||||
uint32_t thread_id; |
||||
}; |
||||
}; |
||||
|
||||
event { |
||||
name = thread_priority_set; |
||||
id = 0x12; |
||||
fields := struct { |
||||
uint32_t thread_id; |
||||
int8_t prio; |
||||
}; |
||||
|
||||
}; |
||||
|
||||
event { |
||||
name = thread_create; |
||||
id = 0x13; |
||||
fields := struct { |
||||
uint32_t thread_id; |
||||
ctf_bounded_string_t name[20]; |
||||
}; |
||||
}; |
||||
|
||||
event { |
||||
name = thread_abort; |
||||
id = 0x14; |
||||
fields := struct { |
||||
uint32_t thread_id; |
||||
}; |
||||
}; |
||||
|
||||
event { |
||||
name = thread_suspend; |
||||
id = 0x15; |
||||
fields := struct { |
||||
uint32_t thread_id; |
||||
}; |
||||
}; |
||||
|
||||
event { |
||||
name = thread_resume; |
||||
id = 0x16; |
||||
fields := struct { |
||||
uint32_t thread_id; |
||||
}; |
||||
}; |
||||
event { |
||||
name = thread_ready; |
||||
id = 0x17; |
||||
fields := struct { |
||||
uint32_t thread_id; |
||||
}; |
||||
}; |
||||
|
||||
event { |
||||
name = thread_pending; |
||||
id = 0x18; |
||||
fields := struct { |
||||
uint32_t thread_id; |
||||
}; |
||||
}; |
||||
|
||||
event { |
||||
name = thread_info; |
||||
id = 0x19; |
||||
fields := struct { |
||||
uint32_t thread_id; |
||||
uint32_t stack_base; |
||||
uint32_t stack_size; |
||||
}; |
||||
}; |
||||
|
||||
event { |
||||
name = isr_enter; |
||||
id = 0x20; |
||||
}; |
||||
|
||||
event { |
||||
name = isr_exit; |
||||
id = 0x21; |
||||
}; |
||||
|
||||
event { |
||||
name = isr_exit_to_scheduler; |
||||
id = 0x22; |
||||
}; |
||||
|
||||
event { |
||||
name = idle; |
||||
id = 0x30; |
||||
}; |
||||
|
||||
event { |
||||
name = start_call; |
||||
id = 0x41; |
||||
fields := struct { |
||||
call_id id; |
||||
}; |
||||
}; |
||||
|
||||
event { |
||||
name = end_call; |
||||
id = 0x42; |
||||
fields := struct { |
||||
call_id id; |
||||
}; |
||||
}; |
@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Oticon A/S |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
#ifndef _TRACE_CTF_H |
||||
#define _TRACE_CTF_H |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
#include <kernel.h> |
||||
#include <kernel_structs.h> |
||||
#include <init.h> |
||||
|
||||
void sys_trace_thread_switched_out(void); |
||||
void sys_trace_thread_switched_in(void); |
||||
void sys_trace_thread_priority_set(struct k_thread *thread); |
||||
void sys_trace_thread_create(struct k_thread *thread); |
||||
void sys_trace_thread_abort(struct k_thread *thread); |
||||
void sys_trace_thread_suspend(struct k_thread *thread); |
||||
void sys_trace_thread_resume(struct k_thread *thread); |
||||
void sys_trace_thread_ready(struct k_thread *thread); |
||||
void sys_trace_thread_pend(struct k_thread *thread); |
||||
void sys_trace_thread_info(struct k_thread *thread); |
||||
void sys_trace_isr_enter(void); |
||||
void sys_trace_isr_exit(void); |
||||
void sys_trace_isr_exit_to_scheduler(void); |
||||
void sys_trace_idle(void); |
||||
void sys_trace_void(unsigned int id); |
||||
void sys_trace_end_call(unsigned int id); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif /* _TRACE_CTF_H */ |
Loading…
Reference in new issue