Skip to content

Commit

Permalink
samples: instrumentation: Add a sample for instrumentation subsys
Browse files Browse the repository at this point in the history
Adds a sample to show the tracing and profiling features when the
instrumentation subsystem is enabled (CONFIG_INSTRUMENTATION=y).

The sample consists in two threads in ping-pong mode, taking turns to
execute loops that spend some CPU cycles. This allows a couple of
context switches and so to demonstrate tracing of thread scheduling
events.

The sample also has an example on how to capture and show traces and
profile info via the CLI tool zaru and also how to export the traces
so they can be loaded in the Perfetto Trace Viewer tool.

Signed-off-by: Gustavo Romero <[email protected]>
Signed-off-by: Kevin Townsend <[email protected]>
  • Loading branch information
gromero committed Dec 6, 2023
1 parent 943d896 commit 93767f7
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 0 deletions.
8 changes: 8 additions & 0 deletions samples/subsys/instrumentation/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(instrumentation)

target_sources(app PRIVATE src/main.c)
36 changes: 36 additions & 0 deletions samples/subsys/instrumentation/boards/b_u585i_iot02a.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2023 Linaro
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <common/mem.h>

/ {
sram@200BF000 {
compatible = "zephyr,memory-region", "mmio-sram";
reg = <0x200BF000 0x20>;
zephyr,memory-region = "RetainedMem";
status = "okay";

retainedmem {
compatible = "zephyr,retained-ram";
status = "okay";
#address-cells = <1>;
#size-cells = <1>;

retention0: retention@0 {
compatible = "zephyr,retention";
status = "okay";

reg = <0x0 0x20>;

prefix = [BE EF];
};
};
};
};

&sram0 {
reg = <0x20000000 DT_SIZE_K(764)>;
};
2 changes: 2 additions & 0 deletions samples/subsys/instrumentation/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CONFIG_INSTRUMENTATION=y
CONFIG_MAIN_STACK_SIZE=4096
127 changes: 127 additions & 0 deletions samples/subsys/instrumentation/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Copyright 2023 Linaro
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/kernel.h>
#include <zephyr/instrumentation/instrumentation.h>

/*
* This sample shows the instrumentation subsystem tracing and profiling
* features. It basically consists in two threads in a ping-pong mode, taking
* turns to execute loops that spend some CPU cycles.
*
* After one flashes this sample to the target, it must be possible to
* collect and visualize traces and profiling info using the instrumentation CLI
* tool, zaru. Please _note_ that this subsystem uses the retained_mem driver,
* hence it's necessary to add the proper devicetree overlay for the target
* board (see './boards/b_u585i_iot02a.overlay' in this dir. as a reference).
*
* Example:
*
* $ export ZEPHYR_BASE=~/zephyrproject/zephyr
*
* $ west build -p always -b b_u585i_iot02a samples/subsys/instrumentation -t flash
* (wait ~2 seconds so the sample finishes 2 rounds of ping-pong between 'main'
* and 'thread_A')
*
* (check instrumentation status)
* $ scripts/zaru.py status
*
* (set the tracing / profiling location; in this sample the function
* 'get_sem_and_exec_function' is the one interesting to allow the observation
* of context switches)
* $ scripts/zaru.py trace -v -c get_sem_and_exec_function
*
* (reboot target so tracing/profiling at the location is effective)
* $ scripts/zaru.py reboot
* (wait ~2 seconds so the sample finishes 2 rounds of ping-pong between 'main'
* and 'thread_A')
*
* (get traces)
* $ scripts/zaru.py trace -v
*
* (.. and get profile)
* $ scripts/zaru.py profile -v -n 10
*
* Or alternatively, export the traces to Perfetto:
*
* (it's necessary to reboot because 'zaru.py trace' dumped the buffer and it's
* now empty)
* $ scripts/zaru.py reboot
* (wait ~2 seconds so the sample finishes 2 rounds of ping-pong between 'main'
* and 'thread_A')
*
* $ scripts/zaru.py trace -v --perfetto --output perfetto_zephyr.json
* (then go to http://perfetto.dev, Trace Viewer, and load perfetto_zephyr.json)
*
*/

#define SLEEPTIME 10
#define STACKSIZE 1024
#define PRIORITY 7

void __no_optimization loop_0(void)
{
/* Just loop to spend some cycles */
for (int i = 0; i < 1024; i++) {
/* Empty */
};
}

void __no_optimization loop_1(void)
{
/* Just loop to spend some cycles */
for (int i = 0; i < 1024*512; i++) {
/* Empty */
};
}

/*
* 'main' thread can take its mutex promptly and run. 'thread_A' needs to wait 'main' give its
* (thread_A) mutex to run.
*/
K_SEM_DEFINE(main_sem, 1, 1); /* Initialized as ready to be taken */
K_SEM_DEFINE(thread_a_sem, 0, 1); /* Initialized as already taken (blocked) */

K_THREAD_STACK_DEFINE(thread_a_stack, STACKSIZE);
static struct k_thread thread_a_data;

static int counter;

void get_sem_and_exec_function(struct k_sem *my_sem, struct k_sem *other_sem, void (*func)(void))
{
while (counter < 4) {
k_sem_take(my_sem, K_FOREVER);

func();
k_msleep(SLEEPTIME);

counter++;
k_sem_give(other_sem);
}
}

void thread_A(void *notused0, void *notused1, void *notused2)
{
ARG_UNUSED(notused0);
ARG_UNUSED(notused1);
ARG_UNUSED(notused2);

get_sem_and_exec_function(&thread_a_sem, &main_sem, loop_0);
}

void main(void)
{
k_tid_t thread_a;

/* Create Thread A */
thread_a = k_thread_create(&thread_a_data, thread_a_stack, STACKSIZE, thread_A, NULL, NULL,
NULL, PRIORITY, 0, K_NO_WAIT);

k_thread_name_set(thread_a, "thread_A");

/* Start ping-pong between 'main' and 'thread_A' */
get_sem_and_exec_function(&main_sem, &thread_a_sem, loop_1);
}

0 comments on commit 93767f7

Please sign in to comment.