debug: CTF Tracing with POSIX backend
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>
This commit is contained in:
parent
43c41d94ea
commit
4fac841869
16 changed files with 1010 additions and 3 deletions
|
@ -79,4 +79,11 @@ config SDL_DISPLAY
|
|||
|
||||
endif # DISPLAY
|
||||
|
||||
if TRACING_CTF
|
||||
|
||||
config TRACING_CTF_BOTTOM_POSIX
|
||||
default y
|
||||
|
||||
endif # TRACING_CTF
|
||||
|
||||
endif # BOARD_NATIVE_POSIX
|
||||
|
|
236
doc/subsystems/tracing/ctf.rst
Normal file
236
doc/subsystems/tracing/ctf.rst
Normal file
|
@ -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);``
|
||||
|
|
@ -27,4 +27,13 @@ it to *y*. For example, this can be added to the
|
|||
|
||||
.. _SEGGER SystemView: https://www.segger.com/products/development-tools/systemview/
|
||||
|
||||
Common Trace Format (CTF) Support
|
||||
*********************************
|
||||
|
||||
Documentation on CTF support can be found in the following subsection:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
ctf.rst
|
||||
|
||||
|
|
|
@ -37,8 +37,8 @@
|
|||
* [3] https://en.wikipedia.org/wiki/Map_(higher-order_function)
|
||||
*/
|
||||
|
||||
#ifndef _CTF_MAP_H
|
||||
#define _CTF_MAP_H
|
||||
#ifndef EXT_DEBUG_CTF_CTF_MAP_H
|
||||
#define EXT_DEBUG_CTF_CTF_MAP_H
|
||||
|
||||
#define EVAL0(...) __VA_ARGS__
|
||||
#define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__)))
|
||||
|
@ -78,4 +78,4 @@
|
|||
*/
|
||||
#define MAP_LIST(f, ...) EVAL(MAP_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
|
||||
|
||||
#endif /* _CTF_MAP_H */
|
||||
#endif /* EXT_DEBUG_CTF_CTF_MAP_H */
|
||||
|
|
|
@ -33,6 +33,9 @@ void z_sys_trace_thread_switched_out(void);
|
|||
#elif defined CONFIG_TRACING_CPU_STATS
|
||||
#include "tracing_cpu_stats.h"
|
||||
|
||||
#elif defined CONFIG_TRACING_CTF
|
||||
#include "tracing_ctf.h"
|
||||
|
||||
#else
|
||||
|
||||
/**
|
||||
|
|
|
@ -257,6 +257,21 @@ config TRACING_CPU_STATS_INTERVAL
|
|||
|
||||
endmenu
|
||||
|
||||
config TRACING_CTF
|
||||
bool "Tracing via Common Trace Format support"
|
||||
select THREAD_MONITOR
|
||||
select TRACING
|
||||
help
|
||||
Enable tracing to a Common Trace Format stream. In order to use it a
|
||||
CTF bottom layer should be selected, such as TRACING_CTF_BOTTOM_POSIX.
|
||||
|
||||
config TRACING_CTF_BOTTOM_POSIX
|
||||
bool "CTF backend for the native_posix port, using a file in the host filesystem"
|
||||
depends on TRACING_CTF
|
||||
depends on ARCH_POSIX
|
||||
help
|
||||
Enable POSIX backend for CTF tracing. It will output the CTF stream to a
|
||||
file using fwrite.
|
||||
|
||||
|
||||
source "subsys/debug/Kconfig.segger"
|
||||
|
|
|
@ -10,3 +10,5 @@ zephyr_sources_ifdef(
|
|||
CONFIG_TRACING_CPU_STATS
|
||||
cpu_stats.c
|
||||
)
|
||||
|
||||
add_subdirectory_ifdef(CONFIG_TRACING_CTF ctf)
|
||||
|
|
4
subsys/debug/tracing/ctf/CMakeLists.txt
Normal file
4
subsys/debug/tracing/ctf/CMakeLists.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
zephyr_include_directories(.)
|
||||
zephyr_sources(ctf_top.c)
|
||||
|
||||
add_subdirectory_ifdef(CONFIG_TRACING_CTF_BOTTOM_POSIX bottoms/posix)
|
2
subsys/debug/tracing/ctf/bottoms/posix/CMakeLists.txt
Normal file
2
subsys/debug/tracing/ctf/bottoms/posix/CMakeLists.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
zephyr_include_directories(.)
|
||||
zephyr_sources(ctf_bottom.c)
|
59
subsys/debug/tracing/ctf/bottoms/posix/ctf_bottom.c
Normal file
59
subsys/debug/tracing/ctf/bottoms/posix/ctf_bottom.c
Normal file
|
@ -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);
|
||||
|
76
subsys/debug/tracing/ctf/bottoms/posix/ctf_bottom.h
Normal file
76
subsys/debug/tracing/ctf/bottoms/posix/ctf_bottom.h
Normal file
|
@ -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 */
|
217
subsys/debug/tracing/ctf/ctf_middle.h
Normal file
217
subsys/debug/tracing/ctf/ctf_middle.h
Normal file
|
@ -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 */
|
177
subsys/debug/tracing/ctf/ctf_top.c
Normal file
177
subsys/debug/tracing/ctf/ctf_top.c
Normal file
|
@ -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);
|
11
subsys/debug/tracing/ctf/ctf_top.h
Normal file
11
subsys/debug/tracing/ctf/ctf_top.h
Normal file
|
@ -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 */
|
150
subsys/debug/tracing/ctf/tsdl/metadata
Normal file
150
subsys/debug/tracing/ctf/tsdl/metadata
Normal file
|
@ -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;
|
||||
};
|
||||
};
|
39
subsys/debug/tracing/include/tracing_ctf.h
Normal file
39
subsys/debug/tracing/include/tracing_ctf.h
Normal file
|
@ -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…
Add table
Add a link
Reference in a new issue