osmocom-analog/src/libdebug/debug.c

329 lines
7.5 KiB
C
Executable File

/* Simple debug functions for level and category filtering
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <math.h>
#include <time.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include "debug.h"
const char *debug_level[] = {
"debug ",
"info ",
"notice ",
"error ",
NULL,
};
struct debug_cat {
const char *name;
const char *color;
} debug_cat[] = {
{ "options", "\033[0;33m" },
{ "sender", "\033[1;33m" },
{ "sound", "\033[0;35m" },
{ "dsp", "\033[0;31m" },
{ "anetz", "\033[1;34m" },
{ "bnetz", "\033[1;34m" },
{ "cnetz", "\033[1;34m" },
{ "nmt", "\033[1;34m" },
{ "amps", "\033[1;34m" },
{ "r2000", "\033[1;34m" },
{ "imts", "\033[1;34m" },
{ "mpt1327", "\033[1;34m" },
{ "jollycom", "\033[1;34m" },
{ "eurosignal", "\033[1;34m" },
{ "pocsag", "\033[1;34m" },
{ "5-ton-folge", "\033[1;34m" },
{ "frame", "\033[0;36m" },
{ "call", "\033[0;37m" },
{ "cc", "\033[1;32m" },
{ "database", "\033[0;33m" },
{ "transaction", "\033[0;32m" },
{ "dms", "\033[0;33m" },
{ "sms", "\033[1;37m" },
{ "sdr", "\033[1;31m" },
{ "uhd", "\033[1;35m" },
{ "soapy", "\033[1;35m" },
{ "wave", "\033[1;33m" },
{ "radio", "\033[1;34m" },
{ "am791x", "\033[0;31m" },
{ "uart", "\033[0;32m" },
{ "device", "\033[0;33m" },
{ "datenklo", "\033[1;34m" },
{ "zeit", "\033[1;34m" },
{ "sim layer 1", "\033[0;31m" },
{ "sim layer 2", "\033[0;33m" },
{ "sim ICL layer", "\033[0;36m" },
{ "sim layer 7", "\033[0;37m" },
{ "mtp layer 2", "\033[1;33m" },
{ "mtp layer 3", "\033[1;36m" },
{ "MuP", "\033[1;37m" },
{ "router", "\033[1;35m" },
{ "stderr", "\033[1;37m" },
{ "ss5", "\033[1;34m" },
{ "isdn", "\033[1;35m" },
{ "misdn", "\033[0;34m" },
{ "dss1", "\033[1;34m" },
{ "sip", "\033[1;35m" },
{ "telephone", "\033[1;34m" },
{ "uk0", "\033[1;34m" },
{ "ph", "\033[0;33m" },
{ "dcf77", "\033[1;34m" },
{ NULL, NULL }
};
int debuglevel = DEBUG_INFO;
int debug_date = 0;
uint64_t debug_mask = ~0;
extern int num_kanal;
void (*clear_console_text)(void) = NULL;
void (*print_console_text)(void) = NULL;
int debug_limit_scroll = 0;
static int lock_initialized = 0;
static pthread_mutex_t debug_mutex;
void lock_debug(void)
{
int rc;
if (!lock_initialized) {
rc = pthread_mutex_init(&debug_mutex, NULL);
if (rc == 0)
lock_initialized = 1;
}
if (lock_initialized)
pthread_mutex_lock(&debug_mutex);
}
void unlock_debug(void)
{
if (lock_initialized)
pthread_mutex_unlock(&debug_mutex);
}
void get_win_size(int *w, int *h)
{
struct winsize win;
int rc;
rc = ioctl(0, TIOCGWINSZ, &win);
if (rc) {
*w = 80;
*h = 25;
return;
}
*h = win.ws_row;
*w = win.ws_col;
}
void _printdebug(const char *file, const char __attribute__((unused)) *function, int line, int cat, int level, const char *kanal, const char *fmt, ...)
{
char buffer[4096], *b = buffer;
int s = sizeof(buffer) - 1;
const char *p;
va_list args;
int w, h;
if (debuglevel > level)
return;
if (!(debug_mask & ((uint64_t)1 << cat)))
return;
lock_debug();
buffer[sizeof(buffer) - 1] = '\0';
/* if kanal is used, prefix the channel number */
if (num_kanal > 1 && kanal) {
sprintf(buffer, "(chan %s) ", kanal);
b = strchr(buffer, '\0');
s -= strlen(buffer);
}
va_start(args, fmt);
vsnprintf(b, s, fmt, args);
va_end(args);
while ((p = strchr(file, '/')))
file = p + 1;
if (clear_console_text)
clear_console_text();
if (debug_limit_scroll) {
get_win_size(&w, &h);
printf("\0337\033[%d;%dr\0338", debug_limit_scroll + 1, h);
}
if (debug_date) {
struct timeval tv;
struct tm *tm;
gettimeofday(&tv, NULL);
tm = localtime(&tv.tv_sec);
printf("%04d-%02d-%02d %02d:%02d:%02d.%03d ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)(tv.tv_usec / 10000.0));
}
printf("%s%s %4d %s-%s: %s\033[0;39m", debug_cat[cat].color, file, line, debug_cat[cat].name, debug_level[level], buffer);
if (debug_limit_scroll)
printf("\0337\033[%d;%dr\0338", 1, h);
if (print_console_text)
print_console_text();
fflush(stdout);
unlock_debug();
}
const char *debug_amplitude(double level)
{
static char text[42];
strcpy(text, " : ");
if (level > 1.0)
level = 1.0;
if (level < -1.0)
level = -1.0;
text[20 + (int)(level * 20)] = '*';
return text;
}
#define level2db(level) (20 * log10(level))
const char *debug_db(double level_db)
{
static char text[128];
int l;
strcpy(text, ": . : . : . : . : . : . : . : . | . : . : . : . : . : . : . : . :");
if (level_db <= 0.0)
return text;
l = (int)round(level2db(level_db));
if (l > 48)
return text;
if (l < -48)
return text;
text[l + 48] = '*';
return text;
}
void debug_print_help(void)
{
printf(" -v --verbose <level> | <level>,<category>[,<category>[,...]] | list\n");
printf(" Use 'list' to get a list of all levels and categories\n");
printf(" Verbose level: digit of debug level (default = '%d')\n", debuglevel);
printf(" Verbose level+category: level digit followed by one or more categories\n");
printf(" -> If no category is specified, all categories are selected\n");
printf(" -v --verbose date\n");
printf(" Show date with debug output\n");
}
void debug_list_cat(void)
{
int i;
printf("Give number of debug level:\n");
for (i = 0; debug_level[i]; i++)
printf(" %d = %s\n", i, debug_level[i]);
printf("\n");
printf("Give name(s) of debug category:\n");
for (i = 0; debug_cat[i].name; i++)
printf(" %s%s\033[0;39m\n", debug_cat[i].color, debug_cat[i].name);
printf("\n");
}
int parse_debug_opt(const char *optarg)
{
int i, max_level = 0;
char *dup, *dstring, *p;
if (!strcasecmp(optarg, "date")) {
debug_date = 1;
return 0;
}
for (i = 0; debug_level[i]; i++)
max_level = i;
dup = dstring = strdup(optarg);
p = strsep(&dstring, ",");
for (i = 0; i < p[i]; i++) {
if (p[i] < '0' || p[i] > '9') {
fprintf(stderr, "Only digits are allowed for debug level!\n");
free(dup);
return -EINVAL;
}
}
debuglevel = atoi(p);
if (debuglevel > max_level) {
fprintf(stderr, "Debug level too high, use 'list' to show available levels!\n");
free(dup);
return -EINVAL;
}
if (dstring)
debug_mask = 0;
while((p = strsep(&dstring, ","))) {
for (i = 0; debug_cat[i].name; i++) {
if (!strcasecmp(p, debug_cat[i].name))
break;
}
if (!debug_cat[i].name) {
fprintf(stderr, "Given debug category '%s' unknown, use 'list' to show available categories!\n", p);
free(dup);
return -EINVAL;
}
debug_mask |= ((uint64_t)1 << i);
}
free(dup);
return 0;
}
const char *debug_hex(const uint8_t *data, int len)
{
static char *text = NULL;
char *p;
int i;
if (text)
free(text);
p = text = calloc(1, len * 3 + 1);
for (i = 0; i < len; i++) {
sprintf(p, "%02x ", *data++);
p += 3;
}
if (text[0])
p[-1] = '\0';
return text;
}