1 /*
2  * Copyright 2018 Oticon A/S
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <string.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <stdarg.h>
11 #include <stdint.h>
12 #include <unistd.h>
13 
14 #include "bs_tracing.h"
15 #include "bs_types.h"
16 #include "bs_utils.h"
17 #include "bs_oswrap.h"
18 #include "bs_symbols.h"
19 
20 static int is_a_tty[2] = {-1,-1}; //-1 = we do not know yet ; Indexed 0:stdout, 1:stderr
21 
22 static int trace_level = 0;
23 static main_cleanup_f main_cleanup_fpr = NULL; //Function from the application which will be called on exit or error
24 static time_f time_fpr = NULL; //Function from the application to get the current time
25 
26 #define MAX_PREFIX_LEN 40
27 static char prefix_s[MAX_PREFIX_LEN]="?_??:"; //In case somebody throws messages before initializing the prefix we initialize it to this (will happen if verbosity is high, during command line parsing)
28 static char prefix_s_color[MAX_PREFIX_LEN]="?_??:"; //like prefix_s, but with colors
29 
bs_trace_disable_color(char * argv,int offset)30 void bs_trace_disable_color(char * argv, int offset){
31   is_a_tty[0] = 0;
32   is_a_tty[1] = 0;
33 }
34 
bs_trace_enable_color(char * argv,int offset)35 void bs_trace_enable_color(char * argv, int offset){
36   //In the next print it will be checked again if this is a tty or not
37   is_a_tty[0] = -1;
38   is_a_tty[1] = -1;
39 }
40 
bs_trace_force_color(char * argv,int offset)41 void bs_trace_force_color(char * argv, int offset){
42   is_a_tty[0] = 1;
43   is_a_tty[1] = 1;
44 }
45 
bs_trace_is_tty(int file_index)46 int bs_trace_is_tty(int file_index){
47   if (is_a_tty[file_index] == -1) {
48     FILE* fptrs[2] = {stdout, stderr};
49     is_a_tty[file_index] = isatty(fileno(fptrs[file_index]));
50   }
51   return is_a_tty[file_index];
52 }
53 
bs_trace_set_level(int new_trace_level)54 void bs_trace_set_level(int new_trace_level) {
55   trace_level = BS_MAX(new_trace_level,0);
56 }
57 
bs_trace_will_it_be_traced(int this_message_level)58 int bs_trace_will_it_be_traced(int this_message_level) {
59   return ( this_message_level <= trace_level );
60 }
61 
bs_trace_register_cleanup_function(main_cleanup_f cleanup_f)62 void bs_trace_register_cleanup_function(main_cleanup_f cleanup_f) {
63   main_cleanup_fpr = cleanup_f;
64 }
65 
bs_trace_register_time_function(time_f t_function)66 void bs_trace_register_time_function(time_f t_function) {
67   time_fpr = t_function;
68 }
69 
bs_trace_set_prefix(const char * prefix)70 void bs_trace_set_prefix(const char* prefix) {
71   strncpy(prefix_s, prefix, MAX_PREFIX_LEN-1);
72   int size = BS_MIN(strlen(prefix), MAX_PREFIX_LEN-1);
73   prefix_s[size] = 0;
74 
75   strncpy(prefix_s_color, prefix, MAX_PREFIX_LEN-1);
76   size = BS_MIN(strlen(prefix), MAX_PREFIX_LEN-1);
77   prefix_s_color[size] = 0;
78 }
79 
bs_trace_set_color_prefix(const char * prefix)80 void bs_trace_set_color_prefix(const char* prefix) {
81   strncpy(prefix_s_color, prefix, MAX_PREFIX_LEN-1);
82   int size = BS_MIN(strlen(prefix), MAX_PREFIX_LEN-1);
83   prefix_s_color[size] = 0;
84 }
85 
bs_trace_set_prefix_phy(const char * phy_id)86 void bs_trace_set_prefix_phy(const char *phy_id) {
87   char trace_prefix[strlen(phy_id) + 5];
88   sprintf(trace_prefix, "p_%s:", phy_id);
89   bs_trace_set_prefix(trace_prefix);
90 }
91 
bs_trace_set_prefix_dev(int dev_nbr)92 void bs_trace_set_prefix_dev(int dev_nbr) {
93   static const char * const device_colortable[8] = {
94       "\x1b[0;97;41m",
95       "\x1b[0;97;42m",
96       "\x1b[0;97;43m",
97       "\x1b[0;97;44m",
98       "\x1b[0;97;45m",
99       "\x1b[0;97;46m",
100       "\x1b[0;97;47m",
101       "\x1b[0;97;40m"
102   };
103 
104   char prefix[40]; //32 bit uint can have max 10 digits
105   snprintf(prefix, 14 , "d_%02i:", dev_nbr);
106   bs_trace_set_prefix(prefix);
107 
108   snprintf(prefix, 40, "%sd_%02i:\x1b[0;39m", device_colortable[dev_nbr & 0x7], dev_nbr);
109   bs_trace_set_color_prefix(prefix);
110 }
111 
get_time()112 static bs_time_t get_time(){
113   if( time_fpr != NULL ){
114     return time_fpr();
115   } else {
116     return TIME_NEVER;
117   }
118 }
119 
bs_trace_silent_exit(uint8_t code)120 void bs_trace_silent_exit(uint8_t code){
121   #if (_CS_TSYMBOLS_TRACE )
122     cs_clear_Tsymbols();
123   #endif
124   uint8_t main_code = 0;
125   if ( main_cleanup_fpr != NULL ){
126     main_code = main_cleanup_fpr();
127   }
128   exit(BS_MAX(code, main_code));
129 }
130 
131 static const char *base_trace_type_prefixes[] = {
132   "EXIT:", "ERROR:", "WARNING:", "INFO:", "DEBUG:", ""
133 };
134 
135 static const char *trace_type_esc_start[] = {
136  "\x1b[0;39m", //EXIT     //reset all styles
137  "\x1b[1;31m", //ERROR    //Foreground color = red, bold
138  "\x1b[95m",   //WARNING  //Foreground color = magenta
139  "\x1b[0;39m", //INFO     //reset all styles
140  "\x1b[0;39m", //DEBUG    //reset all styles
141  "\x1b[0;39m"  //RAW      //reset all styles
142 };
143 
144 static const char trace_esc_end[] = "\x1b[0;39m"; //reset all styles
145 
bs_trace_vprint(base_trace_type_t type,const char * caller_filename,unsigned int caller_line,int this_message_trace_level,base_trace_timed_type_t time_type,bs_time_t time,const char * format,va_list variable_args)146 void bs_trace_vprint(base_trace_type_t type,
147                      const char *caller_filename, unsigned int caller_line,
148                      int this_message_trace_level,
149                      base_trace_timed_type_t time_type, bs_time_t time,
150                      const char *format, va_list variable_args){
151 
152   uint file_index = 0; //by default thru stdout
153   if (( type == BS_TRACE_ERROR ) || ( type == BS_TRACE_WARNING )) {
154     this_message_trace_level = 0; //we promote the message, so it is always printed
155     file_index = 1; //errors and warnings thru stderr
156   }
157   if ( this_message_trace_level <= trace_level ) {
158     FILE* fptrs[2] = {stdout, stderr};
159 
160     char time_s[20] = {0};
161     if ( time_type > BS_TRACE_NOTIME ) {
162       time_s[0] = ' ';
163       time_s[1] = '@';
164       if ( time_type == BS_TRACE_AUTOTIME ){
165         time = get_time();
166       }
167       bs_time_to_str(&time_s[2], time);
168     }
169 
170     if ( is_a_tty[file_index] == -1 ){
171       is_a_tty[file_index] = isatty(fileno(fptrs[file_index]));
172     }
173     if ( is_a_tty[file_index] ){
174       fprintf(fptrs[file_index],"%s%s",prefix_s_color, trace_type_esc_start[type]);
175     } else {
176       fprintf(fptrs[file_index],"%s",prefix_s);
177     }
178     fprintf(fptrs[file_index], "%s %s ", time_s, base_trace_type_prefixes[type]);
179     if ( caller_filename != NULL ) {
180       fprintf(fptrs[file_index], "(%s:%u): ", caller_filename, caller_line);
181     }
182     vfprintf(fptrs[file_index], format, variable_args);
183 
184     if ( is_a_tty[file_index] ) {
185       fprintf(fptrs[file_index],"%s", trace_esc_end);
186     }
187   }
188 
189   if ( type == BS_TRACE_EXIT ) {
190     bs_trace_silent_exit(0);
191   } else if ( type == BS_TRACE_ERROR ) {
192     bs_trace_silent_exit(255);
193   }
194 }
195 
bs_trace_print(base_trace_type_t type,const char * caller_filename,unsigned int caller_line,int this_message_trace_level,base_trace_timed_type_t time_type,bs_time_t time,const char * format,...)196 void bs_trace_print(base_trace_type_t type,
197                         const char *caller_filename, unsigned int caller_line,
198                         int this_message_trace_level,
199                         base_trace_timed_type_t time_type, bs_time_t time,
200                         const char *format, ...){
201   if ((type == BS_TRACE_ERROR) || (type == BS_TRACE_WARNING)) {
202     this_message_trace_level = 0; //we promote the message, so it is always printed
203   }
204   if ((this_message_trace_level <= trace_level) || (type == BS_TRACE_EXIT)) {
205     va_list variable_args;
206     va_start(variable_args, format);
207     bs_trace_vprint(type,
208                         caller_filename, caller_line,
209                         this_message_trace_level,
210                         time_type, time,
211                         format, variable_args);
212     va_end(variable_args);
213   }
214 }
215