t/cli: Add trace piping support

This commit is contained in:
Jakob Bornecrantz 2021-02-22 20:25:30 +00:00
parent 049f6be351
commit e2104a4f31
5 changed files with 362 additions and 2 deletions

View file

@ -13,6 +13,13 @@ set(SOURCE_FILES
cli_main.c
)
if(NOT WIN32)
# Lots of linux assumptions in this file.
list(APPEND SOURCE_FILES
cli_cmd_trace.c
)
endif()
if(NOT WIN32)
# No getline on Windows, so until we have a portable impl
list(APPEND SOURCE_FILES

View file

@ -0,0 +1,334 @@
// Copyright 2021, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Trace marker parsing and coversion code.
* @author Jakob Bornecrantz <jakob@collabora.com>
*/
#include "xrt/xrt_compiler.h"
#include "util/u_misc.h"
#include "util/u_trace_marker.h"
#include "cli_common.h"
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <inttypes.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#define P(...) fprintf(stderr, __VA_ARGS__)
#define TRACE_PIPE_FILENAME "/sys/kernel/tracing/trace_pipe"
#define TRACE_MARKER_FILENAME "/sys/kernel/tracing/trace_marker"
#define BUF_SIZE (1024 * 8)
struct trace
{
int fd;
FILE *file;
bool running;
char buffer[BUF_SIZE];
};
static struct trace t = {0};
/*
*
* JSON writing.
*
*/
static void
json_w_header()
{
fprintf(t.file,
"{\n"
"\t\"displayTimeUnit\": \"ms\",\n"
"\t\"traceEvents\": [\n"
"\t\t{\n"
"\t\t\t\"#\": \"This is to avoid having to deal with ',' all over the code.\"\n"
"\t\t}");
}
static void
json_w_end()
{
fprintf(t.file,
"\n"
"\t]\n"
"}\n");
}
/*
*
* Functions.
*
*/
static int
open_file_fd(const char *filename, int flags)
{
int fd = open(filename, flags);
if (fd < 0) {
fprintf(stderr, " :: Failed to open the file: '%s'\n", filename);
fprintf(stderr, " See command help!\n");
}
return fd;
}
static int
open_fd()
{
int check_fd = open_file_fd(TRACE_MARKER_FILENAME, O_WRONLY);
if (check_fd < 0) {
return -1;
}
close(check_fd);
fprintf(stderr, " :: Checked '%s'\n", TRACE_MARKER_FILENAME);
t.fd = open_file_fd(TRACE_PIPE_FILENAME, O_RDONLY);
fprintf(stderr, " :: Opened '%s'\n", TRACE_PIPE_FILENAME);
return 0;
}
static void
handle_data(const char *rest_of_line, size_t len)
{
int type = 0;
int data_length = 0;
int consumed = 0;
int scan = sscanf(rest_of_line, "%i %i %n", &type, &data_length, &consumed);
char data[1024];
if ((int)data_length * 2 != (int)len - (int)consumed) {
return;
}
if ((size_t)data_length > sizeof(data)) {
return;
}
rest_of_line = rest_of_line + consumed;
len = len - (size_t)consumed;
for (int i = 0; i < data_length; i++) {
int tmp = 0;
scan = sscanf(rest_of_line + (i * 2), "%2x", &tmp);
if (scan < 1) {
return;
}
data[i] = tmp;
}
switch (type) {
case U_TRACE_DATA_TYPE_TIMING_FRAME: u_timing_frame_write_json(t.file, (void *)data); break;
default: fprintf(stderr, "%.*s\n", (int)len, rest_of_line); break;
}
}
static void
handle_line(const char *line, size_t len)
{
int pid = 0, tid = 0;
int secs = 0, usecs = 0;
char cmd = '\0';
int consumed = 0;
char function[256] = {0};
int scan = sscanf( //
line, //
" <...>-%i %*s %*s %d.%d: tracing_mark_write: %1s %i %n", //
&tid, //
&secs, //
&usecs, //
&cmd, //
&pid, //
&consumed); //
if (scan < 5) {
fprintf(stderr, "%.*s\n", (int)len, line);
return;
}
switch (cmd) {
case 'E':
case 'B':
scan = sscanf(line + consumed, "%256s", function);
if (scan < 1) {
fprintf(stderr, "%.*s\n", (int)len, line);
return;
}
fprintf(t.file,
",\n"
"\t\t{\n"
"\t\t\t\"ph\": \"%.1s\",\n"
"\t\t\t\"name\": \"%s\",\n"
"\t\t\t\"cat\": \"%s\",\n"
"\t\t\t\"ts\": %u%06u,\n"
"\t\t\t\"pid\": %u,\n"
"\t\t\t\"tid\": %u\n"
"\t\t}",
&cmd, function, "func", secs, usecs, pid, tid);
break;
case 'r': handle_data(line + consumed, len - (size_t)consumed); break;
default: fprintf(stderr, "%.*s\n", (int)len, line); break;
}
}
static int
loop()
{
while (t.running) {
ssize_t ret = read(t.fd, t.buffer, BUF_SIZE);
if (ret < 0) {
return 1;
}
if (ret == 0) {
continue;
}
size_t pos = 0;
size_t count = 0;
while (count < (size_t)ret) {
if (t.buffer[count] != '\n') {
count++;
continue;
}
assert(t.buffer[pos] != '\n');
size_t length = count - pos;
handle_line(t.buffer + pos, length);
// Point *after* the newline.
count++;
pos = count;
}
fflush(t.file);
}
return 0;
}
void
signal_handler(int signum, siginfo_t *info, void *ptr)
{
t.running = false;
// Since we are doing a clean shutdown ^C be on it's own line.
write(STDERR_FILENO, "\n", 1);
}
void
catch_sigterm()
{
static struct sigaction _sigact;
memset(&_sigact, 0, sizeof(_sigact));
_sigact.sa_sigaction = signal_handler;
_sigact.sa_flags = SA_SIGINFO;
sigaction(SIGTERM, &_sigact, NULL);
sigaction(SIGINT, &_sigact, NULL);
}
static int
trace_pipe(int argc, const char **argv)
{
catch_sigterm();
t.file = stdout;
t.running = true;
int ret = open_fd();
if (ret < 0) {
return 1;
}
json_w_header();
u_timing_frame_write_json_metadata(t.file);
P(" :: Looping\n");
ret = loop();
if (ret < 0) {
return 1;
}
json_w_end();
P(" :: Clean shutdown\n");
return 0;
}
static void
print_help(int argc, const char **argv)
{
if (argc >= 3) {
P("Unknown trace command '%s'\n\n", argv[2]);
}
P("Usage %s trace <cmd>\n", argv[0]);
P("\n");
P("Commands:\n");
P(" pipe - Read the trace_pipe stream and convert into json outputted to stdout.\n");
P("\n");
P("Example:\n");
P(" $ %s trace pipe 1> /tmp/chrome_tracing.json\n", argv[0]);
P("\n");
P("Make sure your user has access to the files:\n");
P(" '%s'.\n", TRACE_PIPE_FILENAME);
P(" '%s'.\n", TRACE_MARKER_FILENAME);
P("\n");
P("The reference clocks needs to be the same in Monado and the tracing framework.\n");
P(" $ echo mono | sudo dd of=/sys/kernel/tracing/trace_clock\n");
P("This command is very unsecure but will make things work.\n");
P(" $ sudo chown -R <user>:<user> /sys/kernel/tracing\n");
P("\n");
P("See https://lwn.net/Articles/366796/\n");
}
int
cli_cmd_trace(int argc, const char **argv)
{
if (argc <= 2) {
print_help(argc, argv);
return 1;
}
if (strcmp(argv[2], "help") == 0) {
print_help(2, argv);
return 0;
}
if (strcmp(argv[2], "pipe") == 0) {
return trace_pipe(argc, argv);
}
print_help(argc, argv);
return 1;
}

View file

@ -9,6 +9,11 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
int
cli_cmd_calibrate(int argc, const char **argv);
@ -20,3 +25,11 @@ cli_cmd_probe(int argc, const char **argv);
int
cli_cmd_test(int argc, const char **argv);
int
cli_cmd_trace(int argc, const char **argv);
#ifdef __cplusplus
}
#endif

View file

@ -30,8 +30,8 @@ cli_print_help(int argc, const char **argv)
P(" test - List found devices, for prober testing.\n");
P(" probe - Just probe and then exit.\n");
P(" lighthouse - Control the power of lighthouses [on|off].\n");
P(" calibrate - Calibrate a camera and save config (not implemented "
"yet).\n");
P(" calibrate - Calibrate a camera and save config (not implemented yet).\n");
P(" trace - Tracing functionality.\n");
return 1;
}
@ -57,5 +57,10 @@ main(int argc, const char **argv)
if (strcmp(argv[1], "lighthouse") == 0) {
return cli_cmd_lighthouse(argc, argv);
}
#ifndef XRT_OS_WINDOWS
if (strcmp(argv[1], "trace") == 0) {
return cli_cmd_trace(argc, argv);
}
#endif // !XRT_OS_WINDOWS
return cli_print_help(argc, argv);
}

View file

@ -8,6 +8,7 @@ cli = executable(
'cli_cmd_lighthouse.c',
'cli_cmd_probe.c',
'cli_cmd_test.c',
'cli_cmd_trace.c',
'cli_common.h',
'cli_main.c',
),