Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion psh/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@ PSH_COMMANDS ?= $(PSH_ALLCOMMANDS)
PSH_INTERNAL_APPLETS := pshapp help $(filter $(PSH_ALLCOMMANDS), $(PSH_COMMANDS))

SRCS := $(foreach app, $(PSH_INTERNAL_APPLETS), $(wildcard $(LOCAL_PATH)$(app)/*.c))
LIBS := libtrace

# Header for project-specific applets
HEADERS:= $(LOCAL_PATH)/psh.h

# Project-specific applets .a files and their dependencies
LIBS := $(PSH_PROJECT_DEPS)
LIBS += $(PSH_PROJECT_DEPS)

# Applets to create links:
PSH_LINK_APPLETS := $(PSH_COMMANDS) pshlogin
Expand Down
244 changes: 177 additions & 67 deletions psh/perf/perf.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
*
* perf - track kernel performance events
*
* Copyright 2017, 2018, 2020, 2021 Phoenix Systems
* Author: Pawel Pisarczyk, Jan Sikorski, Maciej Purski, Mateusz Niewiadomski
* Copyright 2017, 2018, 2020, 2021, 2025 Phoenix Systems
* Author: Pawel Pisarczyk, Jan Sikorski, Maciej Purski, Mateusz Niewiadomski, Adam Greloch
*
* This file is part of Phoenix-RTOS.
*
Expand All @@ -13,105 +13,215 @@

#include <errno.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>

#include <sys/threads.h>
#include <sys/perf.h>

Check failure on line 22 in psh/perf/perf.c

View workflow job for this annotation

GitHub Actions / call-ci / build (armv8r52-mps3an536-qemu)

sys/perf.h: No such file or directory

Check failure on line 22 in psh/perf/perf.c

View workflow job for this annotation

GitHub Actions / call-ci / build (armv7m4-stm32l4x6-nucleo)

sys/perf.h: No such file or directory

Check failure on line 22 in psh/perf/perf.c

View workflow job for this annotation

GitHub Actions / call-ci / build (armv7a9-zynq7000-zedboard)

sys/perf.h: No such file or directory

Check failure on line 22 in psh/perf/perf.c

View workflow job for this annotation

GitHub Actions / call-ci / build (riscv64-generic-qemu)

sys/perf.h: No such file or directory

Check failure on line 22 in psh/perf/perf.c

View workflow job for this annotation

GitHub Actions / call-ci / build (sparcv8leon-generic-qemu)

sys/perf.h: No such file or directory

Check failure on line 22 in psh/perf/perf.c

View workflow job for this annotation

GitHub Actions / call-ci / build (sparcv8leon-gr712rc-board)

sys/perf.h: No such file or directory

Check failure on line 22 in psh/perf/perf.c

View workflow job for this annotation

GitHub Actions / call-ci / build (sparcv8leon-gr716-mimas)

sys/perf.h: No such file or directory

Check failure on line 22 in psh/perf/perf.c

View workflow job for this annotation

GitHub Actions / call-ci / build (armv7m7-imxrt117x-evk)

sys/perf.h: No such file or directory
#include <sys/stat.h>

#include <trace.h>

#include "../psh.h"

#include <board_config.h>


#ifndef PSH_PERF_BUFSZ
#define PSH_PERF_BUFSZ (512 << 10)
#endif

#ifndef PERF_RTT_ENABLED
#define PERF_RTT_ENABLED 0
#endif

#define PSH_PERF_DEFAULT_TIMEOUT_MS (3 * 1000)
#define PSH_PERF_DEFAULT_SLEEPTIME_MS (100)
#define PSH_PERF_BUFSZ_EXP (18)

#define LOG_TAG "perf: "

/* clang-format off */
#define log_info(fmt, ...) do { fprintf(stderr, LOG_TAG fmt "\n", ##__VA_ARGS__); } while (0)
#define log_warning(fmt, ...) do { log_info("warning: " fmt, ##__VA_ARGS__); } while (0)
#define log_error(fmt, ...) do { log_info("error: " fmt, ##__VA_ARGS__); } while (0)
/* clang-format on */

void psh_perfinfo(void)

static char *modeStrs[] = {
[perf_mode_trace] = "trace",
};

_Static_assert(sizeof(modeStrs) / sizeof(modeStrs[0]) == perf_mode_count, "modeStrs must handle all perf modes");


static void psh_perfinfo(void)
{
printf("track kernel performance events");
return;
}


int psh_perf(int argc, char **argv)
static void perfHelp(void)
{
time_t timeout = 0, elapsed = 0, sleeptime = 200 * 1000;
threadinfo_t *info, *rinfo;
const size_t bufsz = 4 << 20;
int bcount, tcnt, n = 32;
perf_event_t *buffer;
char *end;

if (argc > 1) {
timeout = strtoul(argv[1], &end, 10);

if (*end != '\0' || !timeout) {
fprintf(stderr, "perf: timeout argument must be integer greater than 0\n");
return -EINVAL;
}
timeout *= 1000 * 1000;
}
printf("Usage: perf -m MODE"
#if !PERF_RTT_ENABLED
" -o [stream output dir]"
#endif
" [options]\n"
"Modes:\n"
" trace - kernel tracing\n"
"Options:\n"
" -t [timeout] (default: %d ms)\n"
" -b [bufsize exp] (default: %d -> (2 << 18) B)\n"
" -s [sleeptime] (default: %d ms)]\n"
" -j [start | stop] - just start/stop perf and exit\n"
" -p [prio]\n",
PSH_PERF_DEFAULT_TIMEOUT_MS,
PSH_PERF_BUFSZ_EXP,
PSH_PERF_DEFAULT_SLEEPTIME_MS);
}

if ((info = malloc(n * sizeof(threadinfo_t))) == NULL) {
fprintf(stderr, "perf: out of memory\n");
return -ENOMEM;
}

while ((tcnt = threadsinfo(n, info)) >= n) {
n *= 2;
if ((rinfo = realloc(info, n * sizeof(threadinfo_t))) == NULL) {
fprintf(stderr, "perf: out of memory\n");
free(info);
return -ENOMEM;
}
info = rinfo;
}
static int psh_perf(int argc, char **argv)
{
char *end, *modeStr = NULL, *destDir = NULL;

if (fwrite(&tcnt, sizeof(tcnt), 1, stdout) != 1) {
fprintf(stderr, "perf: failed or partial write\n");
free(info);
return -EIO;
}
time_t timeoutMs = PSH_PERF_DEFAULT_TIMEOUT_MS;
time_t sleeptimeMs = PSH_PERF_DEFAULT_SLEEPTIME_MS;

if (fwrite(info, sizeof(threadinfo_t), tcnt, stdout) != tcnt) {
fprintf(stderr, "perf: failed or partial write\n");
free(info);
return -EIO;
}
perf_mode_t mode = perf_mode_trace;

free(info);
size_t bufSize = 2 << PSH_PERF_BUFSZ_EXP;

if (argc == 1)
return EOK;
/* clang-format off */
enum { perf_none, perf_just_start, perf_just_stop } restrictTo = perf_none;
/* clang-format on */

if ((buffer = malloc(bufsz)) == NULL) {
fprintf(stderr, "perf: out of memory\n");
return -ENOMEM;
int opt;
for (;;) {
opt = getopt(argc, argv, "o:m:t:b:s:p:j:h");
if (opt == -1) {
break;
}
switch (opt) {
case 'o':
destDir = optarg;
break;
case 'm':
modeStr = optarg;
mode = -1;
for (size_t i = 0; i < sizeof(modeStrs) / sizeof(modeStrs[0]); i++) {
if (strcmp(modeStr, modeStrs[i]) == 0) {
mode = i;
break;
}
}
if (mode == -1) {
log_error("invalid mode: %s", modeStr);
return -EINVAL;
}
break;
case 't':
timeoutMs = strtoul(optarg, &end, 10);
if (*end != '\0' || timeoutMs == 0) {
log_error("timeout argument must be integer greater than 0");
return -EINVAL;
}
break;
case 'b': {
size_t exp = strtoul(optarg, &end, 10);
if (*end != '\0' || exp == 0) {
log_error("bufsize argument must be integer greater than 0");
return -EINVAL;
}
bufSize = 2 << exp;
} break;
case 's':
sleeptimeMs = strtoul(optarg, &end, 10);
if (*end != '\0' || sleeptimeMs == 0) {
log_error("sleeptime argument must be integer greater than 0");
return -EINVAL;
}
break;
case 'p': {
int prio = strtoul(optarg, &end, 10);
if (*end != '\0') {
log_error("bad prio: %s", optarg);
return -EINVAL;
}
priority(prio);
} break;
case 'j':
if (strcmp(optarg, "start") == 0) {
restrictTo = perf_just_start;
}
else if (strcmp(optarg, "stop") == 0) {
restrictTo = perf_just_stop;
}
else {
log_error("bad arg: -n %s", optarg);
return -EINVAL;
}
break;
default:
case 'h':
perfHelp();
return EOK;
}
}

if (perf_start(-1) < 0) {
fprintf(stderr, "perf: could not start\n");
free(buffer);
return -1;
if (modeStr == NULL || (PERF_RTT_ENABLED == 0 && destDir == NULL)) {
perfHelp();
return -EINVAL;
}

while (elapsed < timeout) {
bcount = perf_read(buffer, bufsz);

if (fwrite(buffer, 1, bcount, stdout) < bcount) {
fprintf(stderr, "perf: failed or partial write\n");
switch (mode) {
case perf_mode_trace: {
trace_ctx_t ctx;

int res = trace_init(&ctx, false);
if (res < 0) {
log_error("trace_init failed: %d", res);
return -EIO;
}

if (restrictTo == perf_just_start) {
res = trace_start(&ctx);
if (res < 0) {
log_error("trace_start failed: %d", res);
return -EIO;
}
}
else if (restrictTo == perf_just_stop) {
res = trace_stopAndGather(&ctx, bufSize, destDir);
if (res < 0) {
log_error("trace_stopAndGather failed: %d", res);
return -EIO;
}
}
else {
res = trace_record(&ctx, sleeptimeMs, timeoutMs, bufSize, destDir);
if (res < 0) {
log_error("trace_record failed: %d", res);
return -EIO;
}
}
break;
}

fprintf(stderr, "perf: wrote %d/%zd bytes\n", bcount, bufsz);

usleep(sleeptime);
elapsed += sleeptime;
default:
log_error("unsupported mode");
break;
}

perf_finish();
free(buffer);

return EOK;
}


void __attribute__((constructor)) perf_registerapp(void)
{
static psh_appentry_t app = {.name = "perf", .run = psh_perf, .info = psh_perfinfo};
static psh_appentry_t app = { .name = "perf", .run = psh_perf, .info = psh_perfinfo };
psh_registerapp(&app);
}
Loading