This patches includes - a profiler file/Makefile that can be added to an application to enable kernel event logger flush over UART - a nanokernel and a microkernel sample application permitting to exercise the profiler - scripts permitting to get kernel event profiler data over UART console and post-process this data Origin: Original Change-Id: Icacf4354c526c7f780b11371e9c28c87e6461eb8 Signed-off-by: Fabrice Olivero <fabrice.olivero@intel.com>
390 lines
8.6 KiB
C
390 lines
8.6 KiB
C
/*
|
|
* Copyright (c) 2016 Intel Corporation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <termios.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <pthread.h>
|
|
|
|
#define DLE 16
|
|
|
|
/* DEBUG mode allows to log all decoded profiler events in ASCII
|
|
* Replace #undef by #define to enable this
|
|
*/
|
|
#undef DEBUG
|
|
|
|
/* TESTMODE should be set to check UART link end-to-end is working
|
|
* In that case, target side profiler code should be build
|
|
* in testmode as well. In that case, bytes from 0x0 to 0xff
|
|
* are sent in sequence and this terminal will check no byte is
|
|
* missing
|
|
* Replace #undef by #define to enable this
|
|
*/
|
|
#undef TESTMODE
|
|
|
|
/* Redefined here since asm-generic/termbits.h is conflicting with termios.h */
|
|
struct termios2 {
|
|
tcflag_t c_iflag; /* input mode flags */
|
|
tcflag_t c_oflag; /* output mode flags */
|
|
tcflag_t c_cflag; /* control mode flags */
|
|
tcflag_t c_lflag; /* local mode flags */
|
|
cc_t c_line; /* line discipline */
|
|
cc_t c_cc[19]; /* control characters */
|
|
speed_t c_ispeed; /* input speed */
|
|
speed_t c_ospeed; /* output speed */
|
|
};
|
|
|
|
#define BOTHER 0010000
|
|
|
|
/* Global variables since used in SIGINT handler */
|
|
/* Profiler data hex output file */
|
|
FILE *prof_out_file;
|
|
/* Console ASCII output file */
|
|
FILE *console_out_file;
|
|
/* Device TTY descriptor (/dev/tty*) */
|
|
int dev_tty;
|
|
/* Device TTY settings save */
|
|
struct termios dev_tty_save1;
|
|
struct termios2 dev_tty_save2;
|
|
/* console settings save */
|
|
struct termios host_tty_save;
|
|
/* TX handler thread */
|
|
pthread_t tx_thread;
|
|
|
|
/*
|
|
* ____________ __________ __________
|
|
* logging ----->| | | | | |-----> stdout: logging (ASCII)
|
|
* | | | |<====>| host TTY |
|
|
* profiling ----->| device TTY |<====>| profterm | |__________|<----- sdtin: shell cmd
|
|
* | | | |
|
|
* shell cmd <-----| | | |-----> FILE: profiler data (HEX)
|
|
* |____________| |__________|-----> FILE: logging (ASCII)
|
|
*
|
|
*/
|
|
|
|
void exit_term(int dummy)
|
|
{
|
|
tcsetattr(STDIN_FILENO, TCSANOW, &host_tty_save);
|
|
tcsetattr(dev_tty, TCSANOW, &dev_tty_save1);
|
|
ioctl(dev_tty, TCSETS2, dev_tty_save2);
|
|
close(dev_tty);
|
|
fclose(prof_out_file);
|
|
if (console_out_file)
|
|
fclose(console_out_file);
|
|
printf("\n\n~~~ EXIT ~~~\n");
|
|
|
|
exit(0);
|
|
}
|
|
|
|
unsigned char read_char(int fd)
|
|
{
|
|
unsigned char c;
|
|
int n;
|
|
|
|
while (1) {
|
|
n = read(fd, &c, 1);
|
|
if (n > 0)
|
|
return c;
|
|
else if ((n < 0) && (errno != EAGAIN)) {
|
|
printf("Error reading from device: %s\n", strerror(errno));
|
|
exit_term(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void decode_profile(int fd, FILE *out)
|
|
{
|
|
unsigned int event_id;
|
|
unsigned int size = 0;
|
|
unsigned int data[10];
|
|
int i;
|
|
|
|
while (1) {
|
|
event_id = read_char(fd);
|
|
size = read_char(fd);
|
|
#ifdef DEBUG
|
|
printf("%08d Profiler event %d(%d):", count++, event_id, size);
|
|
#endif
|
|
for (i = 0; i < size; i++) {
|
|
data[i] = read_char(fd);
|
|
data[i] += read_char(fd) << 8;
|
|
data[i] += read_char(fd) << 16;
|
|
data[i] += read_char(fd) << 24;
|
|
#ifdef DEBUG
|
|
printf(" %08x", data[i]);
|
|
#endif
|
|
}
|
|
#ifdef DEBUG
|
|
printf("\n");
|
|
#endif
|
|
fwrite(&event_id, 1, 2, out);
|
|
fwrite(&size, 1, 2, out);
|
|
fwrite(data, size, 4, out);
|
|
return;
|
|
}
|
|
}
|
|
|
|
int uart_set(int tty, uint baud_rate)
|
|
{
|
|
struct termios ttyset1;
|
|
struct termios2 ttyset2;
|
|
int rc;
|
|
|
|
rc = tcgetattr(tty, &ttyset1);
|
|
if (rc < 0) {
|
|
printf("Failed to get attr: %d, %s\n", rc, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
dev_tty_save1 = ttyset1; /* preserve original settings for restoration */
|
|
|
|
cfmakeraw(&ttyset1);
|
|
|
|
ttyset1.c_cc[VMIN] = 1;
|
|
ttyset1.c_cc[VTIME] = 10;
|
|
|
|
ttyset1.c_cflag &= ~CSTOPB;
|
|
ttyset1.c_cflag &= ~CRTSCTS; /* no HW flow control? */
|
|
ttyset1.c_cflag |= CLOCAL | CREAD;
|
|
|
|
rc = tcsetattr(tty, TCSANOW, &ttyset1);
|
|
|
|
if (rc < 0) {
|
|
printf("Failed to set attr: %d, %s\n", rc, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
ioctl(tty, TCGETS2, &ttyset2);
|
|
dev_tty_save2 = ttyset2;
|
|
|
|
ttyset2.c_cflag &= ~CBAUD;
|
|
ttyset2.c_cflag |= BOTHER;
|
|
ttyset2.c_ispeed = baud_rate;
|
|
ttyset2.c_ospeed = baud_rate;
|
|
int r = ioctl(tty, TCSETS2, &ttyset2);
|
|
|
|
if (r < 0) {
|
|
printf("Failed to set speed: %s\n", strerror(errno));
|
|
tcsetattr(tty, TCSANOW, &dev_tty_save1);
|
|
return -1;
|
|
}
|
|
|
|
ioctl(tty, TCGETS2, &ttyset2);
|
|
printf("UART speed set to %d\n", ttyset2.c_ispeed);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int console_set(int tty)
|
|
{
|
|
struct termios ttyset;
|
|
int rc;
|
|
|
|
rc = tcgetattr(tty, &host_tty_save);
|
|
if (rc < 0) {
|
|
printf("Failed to get attr: %d, %s\n", rc, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
ttyset = host_tty_save;
|
|
|
|
ttyset.c_lflag &= ~(ICANON | ECHO);
|
|
|
|
rc = tcsetattr(tty, TCSANOW, &ttyset);
|
|
|
|
if (rc < 0) {
|
|
printf("Failed to set attr: %d, %s\n", rc, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void *tx_process(void *arg)
|
|
{
|
|
char c;
|
|
|
|
while (1) {
|
|
c = getchar();
|
|
if (c != EOF) {
|
|
if (write(dev_tty, &c, 1) < 0) {
|
|
printf("Failed writing to device: %s\n", strerror(errno));
|
|
exit_term(0);
|
|
}
|
|
if (c == '\n') {
|
|
char cr = '\r';
|
|
|
|
if (write(dev_tty, &cr, 1) < 0) {
|
|
printf("Failed writing to device: %s\n", strerror(errno));
|
|
exit_term(0);
|
|
}
|
|
}
|
|
}
|
|
fflush(stdout);
|
|
}
|
|
}
|
|
|
|
void rx_process(void)
|
|
{
|
|
int count = 0;
|
|
|
|
#ifdef TESTMODE
|
|
unsigned char prev_c;
|
|
int enable_check = 0;
|
|
#endif
|
|
|
|
while (1) {
|
|
unsigned char c;
|
|
|
|
c = read_char(dev_tty);
|
|
|
|
count++;
|
|
|
|
#ifdef TESTMODE
|
|
if (enable_check) {
|
|
printf("%02x ", c);
|
|
if (c != ++prev_c)
|
|
printf("*ERROR*\n");
|
|
prev_c = c;
|
|
} else {
|
|
if ((count > 10) && (c == 0)) {
|
|
enable_check = 1;
|
|
prev_c = 0;
|
|
} else {
|
|
printf("%c", c);
|
|
}
|
|
}
|
|
#else
|
|
if (c == DLE) {
|
|
decode_profile(dev_tty, prof_out_file);
|
|
} else {
|
|
putc(c, stdout);
|
|
if (console_out_file) {
|
|
fputc(c, console_out_file);
|
|
}
|
|
}
|
|
#endif /* ! TESTMODE */
|
|
|
|
fflush(stdout);
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
char *dev_tty_name = 0;
|
|
int baud_rate = 921600;
|
|
char *out_prof = 0;
|
|
char *out_console = 0;
|
|
int i = 0;
|
|
int flag = 0;
|
|
|
|
if (console_set(STDIN_FILENO) < 0) {
|
|
goto exit;
|
|
}
|
|
|
|
signal(SIGINT, exit_term);
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
if (strcmp("-s", argv[i]) == 0) {
|
|
i++;
|
|
if (i < argc) {
|
|
int baud = atoi(argv[i]);
|
|
|
|
if (baud > 0)
|
|
baud_rate = baud;
|
|
}
|
|
} else {
|
|
if (dev_tty_name == 0)
|
|
dev_tty_name = (char *)argv[i];
|
|
else if (out_prof == 0)
|
|
out_prof = (char *)argv[i];
|
|
else if (out_console == 0)
|
|
out_console = (char *)argv[i];
|
|
}
|
|
}
|
|
|
|
if ((dev_tty_name == 0) || (out_prof == 0)) {
|
|
printf("Usage: profterm [-s speed] <dev_tty> <prof> [out]\n");
|
|
printf(" <dev_tty> Device TTY\n");
|
|
printf(" <prof> Profiler output data\n");
|
|
printf(" [out] [OPTIONAL] Output file (profiler data filtered out)\n");
|
|
printf(" Options:\n");
|
|
printf(" -s [SPEED] UART baud rate (default:921600)\n");
|
|
goto err_set_console;
|
|
}
|
|
|
|
if (out_console != 0) {
|
|
console_out_file = fopen(out_console, "w");
|
|
if (console_out_file == 0) {
|
|
printf("Cannot open %s: error %s\n",
|
|
(char *)out_console,
|
|
strerror(errno));
|
|
goto err_set_console;
|
|
}
|
|
}
|
|
|
|
prof_out_file = fopen(out_prof, "w");
|
|
if (prof_out_file == 0) {
|
|
printf("Cannot open %s: error %s\n", (char *)out_prof, strerror(errno));
|
|
goto err_out_prof;
|
|
}
|
|
|
|
while (dev_tty <= 0) {
|
|
dev_tty = open(dev_tty_name, O_RDWR | O_NOCTTY | O_NONBLOCK);
|
|
if ((dev_tty <= 0) && (flag == 0)) {
|
|
printf("Waiting for connection [CTRL+C to abort]...\n");
|
|
flag = 1;
|
|
}
|
|
}
|
|
|
|
if (uart_set(dev_tty, baud_rate) < 0)
|
|
goto err_uart_set;
|
|
|
|
/* Start TX main loop */
|
|
if (pthread_create(&tx_thread, NULL, tx_process, NULL)) {
|
|
printf("Cannot create TX thread: %s\n", strerror(errno));
|
|
goto err_uart_set;
|
|
}
|
|
|
|
printf("~~~ READY [Press CTRL+C to stop] ~~~\n");
|
|
|
|
/* RX main loop */
|
|
rx_process();
|
|
|
|
/* Should never come here... */
|
|
return 0;
|
|
|
|
/* Error handling */
|
|
err_uart_set:
|
|
close(dev_tty);
|
|
fclose(prof_out_file);
|
|
err_out_prof:
|
|
if (console_out_file)
|
|
fclose(console_out_file);
|
|
err_set_console:
|
|
tcsetattr(STDIN_FILENO, TCSANOW, &host_tty_save);
|
|
exit:
|
|
return 1;
|
|
}
|