Line data Source code
1 : /* This file (Trace_test.c) was created by Ron Rechenmacher <ron@fnal.gov> on
2 : Jan 15, 2014. "TERMS AND CONDITIONS" governing this file are in the README
3 : or COPYING file. If you do not have such a file, one can be obtained by
4 : contacting Ron or Fermi Lab in Batavia IL, 60510, phone: 630-840-3000.
5 : $RCSfile: trace_cntl.c,v $
6 : */
7 : #define TRACE_CNTL_REV "$Revision: 1778 $$Date: 2026-09-27 23:42:09 -0500 (Sun, 27 Sep 2026) $"
8 : /*
9 : NOTE: This is a .c file instead of c++ mainly because C is friendlier when it
10 : comes to extended initializer lists.
11 : gxx_standards.sh trace_cntl.c
12 : for std in $standards;do\
13 : for ll in 0 1 2 3;do echo $std $ll:;TRACE_LVL=$ll ./Trace_test-$std;done;\
14 : done
15 :
16 : */
17 : #include <stdio.h> /* printf */
18 : #ifndef __USE_MISC
19 : # define __USE_MISC
20 : #endif
21 : #include <sched.h>
22 : #ifndef __USE_BSD
23 : # define __USE_BSD
24 : #endif
25 : #include <stdlib.h> /* setenv */
26 : #include <stdint.h> /* uint64_t */
27 : #include <libgen.h> /* basename */
28 : #include <unistd.h> /* getopt */
29 : #include <getopt.h> /* getopt */
30 : #include <sys/time.h> /* gettimeofday, struct timeval */
31 : #include <pthread.h> /* pthread_self */
32 : #include <locale.h> /* setlocale */
33 : #include <string.h> /* strnlen */
34 :
35 : #include "TRACE/trace.h"
36 : //#define TRACE_NAME basename(__FILE__) // don't define TRACE_NAME, so ton* (w/o -n or -N) works on default TRACE_NAME "TRACE"
37 :
38 : struct {
39 : const char *cmd;
40 : const char *hlp;
41 : } subcmdhlp[]= {
42 : {"show [-HqF|-c<count>|-s<startSlotIdx>",
43 : "[opts] [file[:off_usec]]... # Notes: -s invalid with multiple files; LC_NUMERIC=en_US.UTF-8 for %%'[df]"},
44 : {"info", ""},
45 : {"tids", ""},
46 : {"memreport [--sys] [--max-reverse-us <us>]", "per-TID counts of messages currently in memory by TRACE level (default max-reverse-us=10000)"},
47 : {"cntl", ""},
48 : {"mode[M|S]", ""},
49 : {"getcpu <1|0>", "enable/disable system call to get cpu on ARM architecture (fast path)"},
50 : {"lvlstrs", ""},
51 : {"lvl{msk,set,clr}<M|S|T>[g|G] <lvlmsk>",
52 : "M=memory, S=slow/console, T=trig; G=only active (not empth), g=all (current and futur)"},
53 : {"lvl{msk,set,clr}[g|G] <mskM> <mskS> <mskT>", "provide all 3 masks; G=only active (not empth), g=all (current and futur)"},
54 : {"trig", ""},
55 : {"limit_ms", ""},
56 : {"unlock", ""},
57 : {"sleep", ""},
58 : {"test1", ""},
59 : {"test-ro", ""},
60 : {"test-compare",
61 : "[-lloops] [test [modes [outfile]]] compare TRACE fmt+args vs. format+args converted (via sprintf). dflt loops=%d\n\
62 : examples: tcntl test-compar\n\
63 : for ff in '' ' %%F:';do TRACE_PRINT=\"%%T %%n %%*L$ff %%M\" tcntl test-compare 1 2;done\n\
64 : tcntl test-compare 8 0xa t.t; tshow|tdelta -stats|tail -5; cat t.t|tdelta -d 1 -i -stats|tail -5"},
65 : {"test-threads", ""},
66 : {"test", "tests various things (see output)."},
67 : };
68 : #define USAGE \
69 : "\
70 : %s [opts] <cmd> [command opt/args]\n\
71 : commands:\n\
72 : show [opts] [file[:off_usec]]... # Note: files... feature: -s invalid; mixed 32/64 environs not supported.\n\
73 : info\n\
74 : tids # show raw level bit masks for \n\
75 : memreport [--sys] [--max-reverse-us <us>] # show per-TID TRACE level counts of messages currently in memory (dflt 10000)\n\
76 : # max-reverse-us should be set to a allow reporting on a buffer that is actively\n\
77 : # being written to. One of the factors to consider is the size of the buffer.\n\
78 : cntl <int> # __func__ prepended to memory msg - 1=always, 0=TRACE_PRINT %%F, -1=never\n\
79 : mode <mode>\n\
80 : modeM <mode>\n\
81 : modeS <mode>\n\
82 : getcpu <0|1> # for ARM architecture only\n\
83 : lvlstrs # print the lvlstrs arrary\n\
84 : lvlmsk[M|S|T][g] <lvlmsk> # M=memory, S=slow/console, T=trig\n\
85 : lvlmsk[g|G] <mskM> <mskS> <mskT> # g=current and future name slots; G=only active (not empty)\n\
86 : lvlset[g|G] <mskM> <mskS> <mskT> # g=current and future name slots; G=only active (not empty)\n\
87 : lvlclr[g|G] <mskM> <mskS> <mskT> # g=current and future name slots; G=only active (not empty)\n\
88 : trig <postEntries> [lvlmskTM+] [quiet] # opt 2nd arg is for specified/dflt TID\n\
89 : reset\n\
90 : limit_ms <cnt> <on_ms> <off_ms>\n\
91 : unlock\n\
92 : sleep [seconds] # dlft:10 - allows time to look at /proc/fd/ and /proc/maps\n\
93 : opts:\n\
94 : -?, -h show this help\n\
95 : -f<file>\n\
96 : -n<name> name. For all commands - not wildcard, created if non-existent\n\
97 : -N<name> name. For lvl*, could be wildcard match spec.\n\
98 : -V print version and exit\n\
99 : -a for tids, show all\n\
100 : show opts:\n\
101 : -H no header\n\
102 : -q silence format errors\n\
103 : -F show forward, poll for new entries. \"count\" (each files) avoids older entries\n\
104 : -c<count> \n\
105 : -s<startSlotIndex> \n\
106 : other options encoded in TRACE_SHOW env.var.; export LC_NUMERIC=en_US or en_US.UTF-8 of %%'[df]\n\
107 : tests: (use %s show after test)\n\
108 : -x<thread_options_mask> b0=TRACE_CNTL\"file\", b1=TRACE_CNTL\"name\", b2=count mappings b3=more TRACEs\n\
109 : -l<loops>\n\
110 : -t print timing stats to stderr\n\
111 : \n\
112 : " USAGE_TESTS, \
113 : basename(argv[0]), basename(argv[0]), DFLT_TEST_COMPARE_ITERS, /*USAGE_TESTS PARAMS*/ NUMTHREADS
114 : #define USAGE_TESTS \
115 : "\
116 : test1 [-lloops] a single TRACE lvl 2 [or more if loops != 0]\n\
117 : test-ro test first page of mmap read-only (for kernel module)\n\
118 : test-compare [-lloops] [test [modes [outfile]]] compare TRACE fmt+args vs. format+args converted (via sprintf). dflt loops=%d\n\
119 : NOTE: \"loops\" gets multipled: \"disabled\" mode - 10x; \"mem only\" - 5x\n\
120 : for ff in '' ' %%F:';do TRACE_PRINT=\"%%T %%n %%*L$ff %%M\" tcntl test-compare 1 2;done\n\
121 : tcntl test-compare 8 0xa t.t; tshow|tdelta -stats|tail -5; cat t.t|tdelta -d 1 -i -stats|tail -5\n\
122 : test-threads [-t] [-x<thread_opts>] [-lloops] [-bburst] [-ddly_ms] [num_threads] Tests threading. loops\n\
123 : of TRACEs: 2-info's, 1-debug, 1-dbg+1, 1-dbg+2 dflts: -x0 -l2 num_threads=%d\n\
124 : test various\n\
125 : example:\n\
126 : for tt in `seq 0 6`;do export TRACE_NUMENTS=500000\\\n\
127 : printf \"$tt: \";rm -f /tmp/trace_buffer_$USER;toffS info -ntrace_cntl;tcntl test-threads $tt -tl3000000;\\\n\
128 : done\n\
129 : TRACE [-t,-l<loops>,-L<linenum>,--] <lvl> <fmt> [ulong or double]... Note: double if contains any of \".INnPp\"; -\n\
130 : export LC_NUMERIC=en_US.UTF-8 for %%'[df] for TRACE(slow) and show(mem))\n\
131 : "
132 :
133 : // Started by copying macros from trace.h and adjusting (adding va_list ap = TRACE_VA_LIST_INIT(arrblk) in particular)
134 : #define VTRACE(lvl, nargs, msg, arrblk) \
135 : do { \
136 : struct { \
137 : char tn[TRACE_TN_BUFSZ]; \
138 : } _trc_; \
139 : if TRACE_INIT_CHECK (trace_name(TRACE_NAME, __FILE__, _trc_.tn, sizeof(_trc_.tn))) { \
140 : struct timeval lclTime; \
141 : uint8_t lvl_= (uint8_t)(lvl); \
142 : TRACE_SBUFDECL; \
143 : lclTime.tv_sec= 0; \
144 : if (traceControl_rwp->mode.bits.M && (traceLvls_p[traceTID].M & TLVLMSK(lvl_))) { \
145 : /* Note: "...NARGS...+2" don't know if any args are long doubles, so support 2 on 64 and 4 on 32 bit archs */ \
146 : va_list ap= TRACE_VA_LIST_INIT(arrblk); \
147 : vtrace(&lclTime, traceTID, lvl_, __TRACE_LINE__, __func__, nargs, msg, ap); \
148 : } \
149 : if (traceControl_rwp->mode.bits.S && (traceLvls_p[traceTID].S & TLVLMSK(lvl_))) { \
150 : TRACE_LIMIT_SLOW(lvl_, _insert, &lclTime) \
151 : { \
152 : va_list ap= TRACE_VA_LIST_INIT(arrblk); \
153 : vtrace_user(&lclTime, traceTID, lvl_, _insert, __FILE__, __TRACE_LINE__, __func__, nargs, msg, ap); \
154 : } \
155 : } \
156 : } \
157 : } while (0)
158 :
159 : #define VTRACEN(nam, lvl, nargs, msg, arrblk) \
160 : do { \
161 : struct { \
162 : char tn[TRACE_TN_BUFSZ]; \
163 : } _trc_; \
164 : if TRACE_INIT_CHECK (trace_name(TRACE_NAME, __FILE__, _trc_.tn, sizeof(_trc_.tn))) { \
165 : static TRACE_THREAD_LOCAL int tid_= -1; \
166 : struct timeval lclTime; \
167 : uint8_t lvl_= (uint8_t)(lvl); \
168 : TRACE_SBUFDECL; \
169 : if (tid_ == -1) tid_= trace_tlog_name_(&(nam)[0], TRACE_NAME, __TRACE_FILE__, __FILE__, _trc_.tn, sizeof(_trc_.tn)); \
170 : lclTime.tv_sec= 0; \
171 : if (traceControl_rwp->mode.bits.M && (traceLvls_p[tid_].M & TLVLMSK(lvl_))) { \
172 : /* Note: "...NARGS...+2" don't know if any args are long doubles, so support 2 on 64 and 4 on 32 bit archs */ \
173 : va_list ap= TRACE_VA_LIST_INIT(arrblk); \
174 : vtrace(&lclTime, tid_, lvl_, __TRACE_LINE__, __func__, nargs, msg, ap); \
175 : } \
176 : if (traceControl_rwp->mode.bits.S && (traceLvls_p[tid_].S & TLVLMSK(lvl_))) { \
177 : TRACE_LIMIT_SLOW(lvl_, _insert, &lclTime) \
178 : { \
179 : va_list ap= TRACE_VA_LIST_INIT(arrblk); \
180 : vtrace_user(&lclTime, tid_, lvl_, _insert, __FILE__, __TRACE_LINE__, __func__, nargs, msg, ap); \
181 : } \
182 : } \
183 : } \
184 : } while (0)
185 :
186 0 : unsigned long *add_ul_arg(unsigned long *args_ptr, unsigned long *end_ptr, unsigned long arg)
187 : {
188 0 : unsigned long *ret_val= args_ptr;
189 0 : unsigned long *vp= (unsigned long *)args_ptr;
190 0 : if ((vp + 1) <= (unsigned long *)end_ptr) {
191 0 : *vp++= arg;
192 0 : ret_val= (unsigned long *)vp;
193 : }
194 0 : return (ret_val);
195 : }
196 :
197 0 : unsigned long *add_double_arg(unsigned long *args_ptr, unsigned long *end_ptr, double arg)
198 : {
199 0 : unsigned long *ret_val= args_ptr;
200 0 : double *vp= (double *)args_ptr;
201 0 : if ((vp + 1) <= (double *)end_ptr) {
202 0 : *vp++= arg;
203 0 : ret_val= (unsigned long *)vp;
204 : }
205 0 : return (ret_val);
206 : }
207 :
208 : #define DFLT_TEST_COMPARE_ITERS 500000
209 : #define minw(a, b) ((b) < (a) ? a : b) /* min width -- really max of the 2 numbers - argh */
210 :
211 : #define DFLT_FILE_WIDTH ((int)sizeof("file") - 1)
212 : #ifdef __linux__
213 : //# define DFLT_SHOW "HxNTPiCnLR"
214 : # define DFLT_SHOW "%H%x%N %T %P %i %C %e %O%.3L%o %m"
215 : #else
216 : //# define DFLT_SHOW "HxNTPinLR"
217 : # define DFLT_SHOW "%H%x%N %T %P %i %e %.3L %m"
218 : #endif
219 :
220 : #define NUMTHREADS 4
221 : static int trace_thread_option= 0;
222 :
223 : typedef struct {
224 : unsigned tidx;
225 : unsigned loops;
226 : float dly_ms;
227 : unsigned burst;
228 : } args_t;
229 :
230 0 : void *thread_func(void *arg)
231 : {
232 0 : args_t *argsp= (args_t *)arg;
233 0 : unsigned tidx= argsp->tidx;
234 0 : float dly_ms= argsp->dly_ms;
235 0 : unsigned burst= argsp->burst;
236 0 : unsigned loops= argsp->loops;
237 0 : unsigned lp;
238 0 : char tmp[PATH_MAX];
239 0 : pid_t tid= trace_gettid();
240 :
241 0 : if (trace_thread_option & 1) {
242 : /* IF -std=c11 is NOT used, a seg fault usually occurs if default file does not exit */
243 0 : snprintf(tmp, sizeof(tmp), "/tmp/trace_buffer_%u", tidx);
244 0 : TRACE_CNTL("file", tmp);
245 : }
246 0 : if (trace_thread_option & 2) {
247 0 : snprintf(tmp, sizeof(tmp), "TEST_THR_%u", tidx); /* a name per thread...*/
248 : /*...-- may fill up namLvlTbl (w/ "larger" number of threads), but that should be OK */
249 0 : TRACE_CNTL("name", tmp);
250 : } else
251 0 : strcpy(tmp, "TEST_THR");
252 :
253 0 : for (lp= 0; lp < loops;) {
254 : /* NOTE: be mindful of classic issue with macro -- args could be evaluted multiple times! */
255 : /* BUT -- when TRACE-ing a value, there you would not likely operate on it */
256 0 : ++lp;
257 0 : TRACE(TLVL_INFO,
258 : "tidx=%u loop=%d of %d tid=%d I need to test longer messages. They need to be about 256 characters - longer than the "
259 : "circular memory message buffer size. This will check for message mangling",
260 : tidx, lp, loops, tid);
261 0 : if (trace_thread_option & 8) { /* do more TRACEs */
262 0 : TRACE(TLVL_INFO,
263 : "tidx=%u loop=%d of %d tid=%d this is the second long message - second0 second1 second2 second3 second4 second5 "
264 : "second6 second7 second8 second9",
265 : tidx, lp, loops, tid);
266 0 : TRACE(TLVL_DEBUG,
267 : "tidx=%u loop=%d of %d tid=%d this is the third long message - third0 third1 third2 third3 third4 third5 third6 "
268 : "third7 third8 third9",
269 : tidx, lp, loops, tid);
270 0 : TRACE(TLVL_DBG + 1,
271 : "tidx=%u loop=%d of %d tid=%d this is the fourth long message - fourth0 fourth1 fourth2 fourth3 fourth4 fourth5 "
272 : "fourth6 fourth7 fourth8 fourth9",
273 : tidx, lp, loops, tid);
274 0 : TRACEN(tmp, TLVL_DBG + 2, "tidx=%u loop=%d of %d tid=%d - extra, if enabled", tidx, lp, loops, tid);
275 : }
276 0 : if (dly_ms && (lp % burst) == 0) usleep(dly_ms * 1000);
277 : }
278 0 : if (argsp->tidx) pthread_exit(NULL);
279 : else
280 0 : return (NULL);
281 : } /* thread_func */
282 :
283 0 : static uint64_t gettimeofday_us(void)
284 : {
285 0 : struct timeval tv;
286 0 : gettimeofday(&tv, NULL);
287 0 : return (uint64_t)(tv.tv_sec * 1000000 + tv.tv_usec);
288 : }
289 :
290 : /* This function searches the enum spec. One may think - just search the trace_lvlstrs[] array,
291 : but the issue is, some levels in the enum may have alternate designations.
292 : In the end, search both -- 1st, the enum spec and 2nd, the trace_lvlstrs
293 : which could be filled from the enum spec or the TRACE_LVLSTRS env.var.
294 : */
295 0 : int str2enum(const char *ss)
296 : {
297 0 : char lvlstr_a[]=
298 : TRACE_STR(TRACE_LVL_ENUM_0_9, TRACE_LVL_ENUM_10_63); /* the code below assumes the input compiles as an enum spec */
299 0 : char *lvlstr= lvlstr_a;
300 0 : int retidx;
301 0 : char *endptr, *savptr, *cp;
302 : // 1 of 3 - search for env.var. TLVL_<LVL> e.g. export TLVL_JONES=8
303 0 : if (strncmp(ss, "TLVL_", 5) == 0 && (cp= getenv(ss)) && (*cp)) { // See if the answer is in the env
304 0 : int val= (int)strtoul(cp, &endptr, 0);
305 0 : if (*endptr == '\0') return (val);
306 : }
307 : // 2 of 3 - enum tlvle_t definition
308 0 : for (retidx= 0; lvlstr; ++retidx, lvlstr= endptr) {
309 0 : savptr= endptr= strpbrk(lvlstr, ",= "); // savptr used below to delimit individual enum strings
310 0 : if (endptr) {
311 0 : if (*endptr == '=') {
312 0 : int rettmp;
313 0 : char *sav2= endptr + 1;
314 0 : while (*sav2 == ' ') ++sav2;
315 0 : rettmp= (int)strtoul(sav2, (char **)&endptr, 0); /* works only if value is not a previous enum enumerator */
316 0 : if (endptr == sav2) { // assume it's THE previous enum enumerator; need to adjust endptr
317 0 : --retidx;
318 0 : endptr= strpbrk(sav2, ", ");
319 0 : if (endptr == NULL)
320 0 : endptr= sav2 + strlen(sav2); // sav2 should be single token, just need endptr to point to the end of it.
321 : } else
322 0 : retidx= rettmp;
323 : }
324 0 : while (isspace(*endptr)) ++endptr; /* space before ',' */
325 0 : ++endptr; /* must be ',' */
326 0 : while (isspace(*endptr)) ++endptr; /* space after ',' */
327 0 : *savptr= '\0'; /* terminate to allow strcmp (e.g. lvlstr points to "TLVL_WARNING")*/
328 : }
329 0 : if (strcasecmp(ss, lvlstr) == 0) return (retidx);
330 0 : else if ((cp= index(lvlstr, '_')) != NULL && strcasecmp(ss, ++cp) == 0)
331 0 : return (retidx); // match when [^_]*_ prefix removed
332 : }
333 : // 3 of 3 - trace_lvlstrs, which could be loaded by TRACE_LVLSTRS (comma sep list of lvlstrs e.g export TRACE_LVLSTRS=mylvl0
334 0 : for (retidx= 0; retidx < 64; ++retidx) {
335 0 : if (strcasecmp(ss, trace_lvlstrs[0][retidx]) == 0) break;
336 : }
337 0 : if (retidx == 64) retidx= TLVL_ERROR;
338 : return (retidx);
339 : } /* str2enum */
340 :
341 : // 32bit 64bit
342 : // -------- --------
343 : // char 1 byte 1 bytes * always pushed as long - 4 on 32, 8 on 64
344 : // short 2 bytes 2 bytes * always pushed as long - 4 on 32, 8 on 64
345 : // int 4 bytes 4 bytes
346 : // float 4 bytes 4 bytes * always pushed as double/8 on both 32 and 64
347 : // long 4 bytes 8 bytes
348 : // void * 4 bytes 8 bytes
349 : // double 8 bytes 8 bytes
350 : // long long 8 bytes 8 bytes
351 : // long double 12 bytes 16 bytes * 80 bit extended precision in 96; 128 bits
352 : //
353 : // used to direct the copying of the trace entry parameters block as a part of "show"
354 : struct sizepush {
355 : unsigned
356 : size : 16; // size (number of bytes) an arg is when it's not pushed as determined by a combination of the traced fmt and fundamental param size of the executed TRACE statement (myEnt_p->param_bytes)
357 : unsigned push : 16; // size (number of bytes) an arg was pushed onto stack and copied into the trace entry parameters block
358 : unsigned positive; // 1=%d%p 0=%u
359 : }; // For example, '%hd' w/param_bytes=4 size=2, push=4
360 : // '%hd' w/param_bytes=8 size=2, push=8
361 : // usually args are ints size=sizeof(int) and pushed as long (the fundamental
362 : // size of the arch addresses) push=sizeof(long) (which is usually sizeof (void*))
363 :
364 : enum show_opts_e { filter_newline_= 0x1, quiet_= 0x2, indent_= 0x4, forward_= 0x8 };
365 :
366 0 : size_t get_arg_sizes(char *ofmt, char *ifmt, int opts, int param_bytes, struct sizepush *sizes_out, unsigned nargs)
367 : {
368 0 : char *in;
369 0 : char *percent_sav;
370 0 : unsigned numArgs= 0;
371 0 : unsigned maxArgs= (nargs < traceControl_p->num_params ? nargs : traceControl_p->num_params);
372 0 : int modifier= 0;
373 0 : int filter_newline= opts & filter_newline_;
374 0 : size_t slen= 0;
375 :
376 : /*strcpy( ofmt, ifmt );*/
377 0 : in= ifmt;
378 : /* strchrnul would be nice (_GNU_SOURCE, ref. man strchr) */
379 : /*while ((in=strchr(in,'%')))*/
380 0 : while (*in) {
381 0 : if (*in != '%') {
382 0 : if (*in != '\n') ofmt[slen++]= *in++;
383 : else {
384 0 : if (filter_newline) {
385 0 : ofmt[slen++]= ';';
386 0 : in++;
387 : } else {
388 0 : ofmt[slen++]= *in++;
389 : }
390 : }
391 0 : continue;
392 : }
393 : /* found '%' - process it */
394 0 : ofmt[slen]= *in;
395 0 : percent_sav= ofmt + slen; /* save in case we need to modify it (too many args) */
396 0 : ++in;
397 0 : ++slen; /* point to next char */
398 0 : sizes_out[numArgs].positive= 0;
399 : /*printf("next char=%c\n",*in);*/
400 0 : if ((*in == '%') || (*in == 'm')) {
401 0 : ofmt[slen++]= *in++;
402 0 : continue;
403 : } /* ingore %% which specified a % char */
404 0 : if (numArgs == maxArgs) {
405 0 : *percent_sav= '*';
406 0 : continue;
407 : } /* SAFETY - no args beyond max */
408 0 : while (((*in >= '0') && (*in <= '9')) || (*in == '.') || (*in == '-') || (*in == '#') || (*in == '+') || (*in == '\'') ||
409 0 : (*in == 'I') || (*in == '"'))
410 0 : ofmt[slen++]= *in++;
411 : chkChr:
412 0 : switch (*in) {
413 : /* Basic conversion specifiers */
414 0 : case 'd':
415 : case 'i':
416 : case 'o':
417 : case 'u':
418 : case 'x':
419 : case 'X':
420 : case 'c':
421 0 : switch (modifier) {
422 0 : case -2:
423 0 : sizes_out[numArgs].push= (unsigned short)param_bytes;
424 0 : sizes_out[numArgs].size= 4;
425 0 : break; /* char */
426 0 : case -1:
427 0 : sizes_out[numArgs].push= (unsigned short)param_bytes;
428 0 : sizes_out[numArgs].size= 4;
429 0 : break; /* short */
430 0 : case 0:
431 0 : sizes_out[numArgs].push= (unsigned short)param_bytes;
432 0 : sizes_out[numArgs].size= 4;
433 0 : break; /* int */
434 0 : case 1:
435 0 : sizes_out[numArgs].push= (unsigned short)param_bytes;
436 0 : sizes_out[numArgs].size= (unsigned short)param_bytes; /* long */
437 0 : if ((param_bytes == 8) && (sizeof(long) == 4)) ofmt[slen++]= 'l';
438 0 : break;
439 0 : case 2:
440 0 : sizes_out[numArgs].push= 8;
441 0 : sizes_out[numArgs].size= 8;
442 0 : break; /* long long */
443 0 : default: printf("error\n");
444 : }
445 0 : if (strchr("di", *in)) sizes_out[numArgs].positive= 1;
446 0 : ofmt[slen++]= *in++;
447 0 : modifier= 0;
448 0 : break;
449 0 : case 'e':
450 : case 'E':
451 : case 'f':
452 : case 'F':
453 : case 'g':
454 : case 'G':
455 : case 'a':
456 : case 'A':
457 0 : if (modifier) { /* long double */
458 0 : sizes_out[numArgs].push= (param_bytes == 4) ? 12 : 16;
459 0 : sizes_out[numArgs].size= (param_bytes == 4) ? 12 : 16;
460 0 : modifier= 0;
461 : } else { /* double */
462 0 : sizes_out[numArgs].push= 8;
463 0 : sizes_out[numArgs].size= 8;
464 : }
465 0 : ofmt[slen++]= *in++;
466 0 : modifier= 0;
467 0 : break;
468 :
469 : /* length modifiers */
470 0 : case 'h':
471 0 : --modifier;
472 0 : ofmt[slen++]= *in++;
473 0 : goto chkChr;
474 0 : case 'l':
475 0 : ++modifier;
476 0 : ofmt[slen++]= *in++;
477 0 : goto chkChr;
478 0 : case 'L':
479 0 : ++modifier;
480 0 : ofmt[slen++]= *in++;
481 0 : goto chkChr;
482 0 : case 'z':
483 0 : ++modifier;
484 0 : ofmt[slen++]= *in++;
485 0 : goto chkChr;
486 :
487 0 : case 's':
488 : case 'n':
489 : case 'p': /* SAFETY -- CONVERT %s to %p */
490 : // for 's', first need to remove "-.0123456789" b/c %p take no flags
491 0 : if (*in == 's')
492 0 : while (strchr("-.0123456789", ofmt[slen - 1])) --slen;
493 0 : sizes_out[numArgs].push= (unsigned short)param_bytes;
494 0 : sizes_out[numArgs].size= (unsigned short)param_bytes;
495 0 : if ((param_bytes == 8) && (sizeof(long) == 4)) { // traced on 64, showing on 32
496 : strcpy(&ofmt[slen - 1], "0x%llx"); // overwrite the original '%'
497 : slen+= 5;
498 : } else
499 0 : ofmt[slen++]= 'p';
500 0 : in++;
501 0 : break;
502 0 : case 'm': // (Glibc extension.) Print output of strerror(errno). No argument is required.
503 0 : ofmt[slen++]= *in++;
504 0 : continue;
505 : break;
506 0 : case '*': /* special -- "arg" in an arg */
507 0 : sizes_out[numArgs].push= (unsigned short)param_bytes;
508 0 : sizes_out[numArgs].size= 4 /*int*/;
509 0 : sizes_out[numArgs].positive= 1;
510 0 : if (++numArgs == maxArgs) {
511 0 : ofmt[slen++]= *in++;
512 0 : break;
513 : }
514 0 : ofmt[slen++]= *in++;
515 0 : goto chkChr;
516 0 : default:
517 0 : if (!(opts & quiet_)) printf("tshow: unknown format spec char \"%c\" encountered with nargs=%u.\n", *in, nargs);
518 0 : ofmt[slen++]= *in++;
519 : }
520 0 : ++numArgs;
521 : }
522 0 : while (*in) ofmt[slen++]= *in++;
523 0 : ofmt[slen]= '\0';
524 : //while (*ofmt == '\n') *ofmt-- = '\0';
525 0 : if (numArgs < maxArgs) sizes_out[numArgs].push= 0;
526 0 : return (slen);
527 : } /* get_arg_sizes */
528 :
529 0 : int countDigits(int n) { return (n >= 10) ? 1 + countDigits(n / 10) : 1; }
530 :
531 : /*
532 : terse medium verbose Important: for "verbose"*, must allow %*L for "auto right justify"
533 : 1 3 6 8 * verbose would be the default, to get "terse", use TRACE_LVLSTRS
534 : F FTL FATAL FATAL use TRACE_LVLSTRS vs "trace_lvlstrs[altidx]" ???
535 : E ERR ERROR ERROR
536 : W WRN WARN WARNING
537 : I NFO INFO INFO super verbose: INFORMATION???
538 : L LOG LOG LOG
539 : D D55 DBG_55 DEBUG_55
540 :
541 :
542 :
543 : mem/fast cout/slow valid flags
544 : TRACE_SHOW TRACE_PRINT -----------
545 : %a nargs <->
546 : %b tbuf fileName %-7b => print last 7 chars of file; %7b => print first 7 chars of file
547 : %B paramBytes
548 : %C cpu *<->
549 : %D inDent
550 : %e combined name:ln#
551 : %F [prepend to msg?] function %nF 0(or none)=abbreviated; 1=long to get "short/consistent" C/C++ (__func__/__PRETTY_FUNCTION__)????
552 : %f raw_message_format file_name %nf n=#path_components #=strstr_search_follows
553 : %H Header *
554 : %I TrcID <->
555 : %i threadID *<->
556 : %L lvStr *<->* lvStr %*L *=auto_width %.1L gets rid of %S (below)
557 : %l lvNum <->
558 : %M * msgInsert+Msg
559 : %m message *<->
560 : %N idx * TrcName_nopad
561 : %n TrcNamePadded *<->* TrcNamePadded %*n *auto_padded %n would be no padding
562 : %P processID *<->
563 : %R retries *
564 : %S severity_char <-> CAN GET RID OF THIS. REPLACED BY %.1L
565 : %s slt
566 : %T time *<->* time
567 : %t tsc insert (if !null)
568 : %u linenum <->
569 : %x filterNewline
570 : %X eXamine arg data for tshow <file>...
571 :
572 : <-> indicates same for both
573 : * indicates default
574 : default TRACE_SHOW="%H%x%N %T %P %i %C %n %L %R %m"
575 : default TRACE_PRINT="%T %n %L %M"
576 : SHOW others: a:nargs b:fileName B:paramBytes D:inDent f:convertedMsgfmt_only I:trcId l:lvl_int s:slot t:tsc
577 : PRINT others: C:core s:severity I:trcId i:threadID f:file N:unpadded_trcName P:pid u:line
578 :
579 :
580 : TRACE_NAME=%-f <----<<< could add program name here (via %p???), but should still have a way to put it in TRACE_PRINT also???
581 : TRACE_NAME=%p-%-f NOTE: TRACE_NAME is not meant to be a "big" string (so the filename is usually the basename
582 : */
583 :
584 0 : void printEnt(const char *ospec, int opts, struct traceEntryHdr_s *myEnt_p, char *local_msg, uint8_t *local_params,
585 : struct sizepush *params_sizes, int bufSlot_width, int N_width, int name_width, unsigned printed, uint32_t rdIdx,
586 : char *tfmt, int tfmt_len, const char *file)
587 : {
588 0 : unsigned uu;
589 0 : char *msg_p;
590 0 : uint64_t *params_p;
591 0 : size_t slen;
592 0 : time_t seconds;
593 0 : int useconds;
594 0 : void *param_va_ptr;
595 0 : uint8_t *ent_param_ptr;
596 0 : uint8_t *lcl_param_ptr;
597 0 : const char *sp; /*spec ptr*/
598 0 : int msg_spec_included;
599 0 : char *cp;
600 0 : char *indent= " ";
601 :
602 0 : msg_p= (char *)(myEnt_p + 1);
603 0 : params_p= (uint64_t *)(msg_p + traceControl_p->siz_msg);
604 :
605 0 : msg_p[traceControl_p->siz_msg - 1]= '\0';
606 0 : if (myEnt_p->nargs) slen= get_arg_sizes(local_msg, msg_p, opts, myEnt_p->param_bytes, params_sizes, myEnt_p->nargs);
607 : else {
608 0 : if (opts & filter_newline_) {
609 0 : for (slen= 0; slen < traceControl_p->siz_msg && msg_p[slen] != '\0'; ++slen)
610 0 : local_msg[slen]= (msg_p[slen] != '\n') ? msg_p[slen] : ';';
611 : } else {
612 : /* strncpy( local_msg, msg_p, traceControl_p->siz_msg ); */
613 0 : for (slen= 0; slen < traceControl_p->siz_msg && msg_p[slen] != '\0'; ++slen) local_msg[slen]= msg_p[slen];
614 : }
615 0 : if (slen < traceControl_p->siz_msg) local_msg[slen]= '\0';
616 0 : else if (traceControl_p->siz_msg > 0) // if no term in 1st n bytes
617 0 : local_msg[--slen]= '\0'; // adjust strlen and terminate
618 : }
619 :
620 : /* Determine if args need to be copied/converted (32/64-bit)
621 : Because timestamps need to be adjusted and compared in traceShow,
622 : they have already been 32/64-bit adjusted
623 : */
624 0 : seconds= myEnt_p->time.tv_sec;
625 0 : useconds= (int)myEnt_p->time.tv_usec;
626 0 : if (((myEnt_p->param_bytes == 4) && (sizeof(long) == 4)) || ((myEnt_p->param_bytes == 8) && (sizeof(long) == 8))) {
627 : param_va_ptr= (void *)params_p;
628 0 : } else if (((myEnt_p->param_bytes == 4) && (sizeof(long) == 8))) { // Entry made by 32bit program, being readout by 64bit
629 : ent_param_ptr= (uint8_t *)params_p;
630 : lcl_param_ptr= local_params;
631 0 : for (uu= 0; uu < myEnt_p->nargs && params_sizes[uu].push != 0; ++uu) {
632 0 : if (params_sizes[uu].push == 4) {
633 0 : if (params_sizes[uu].positive) {
634 0 : *(long *)lcl_param_ptr= (long)*(int *)ent_param_ptr;
635 : //printf( "converting from 0x%x\n", *(int*)ent_param_ptr );
636 : } else {
637 0 : *(unsigned long *)lcl_param_ptr= (unsigned long)*(unsigned *)ent_param_ptr;
638 : //printf( "converting to 0x%lx\n", *(unsigned long*)lcl_param_ptr );
639 : }
640 0 : lcl_param_ptr+= sizeof(long);
641 0 : } else if (params_sizes[uu].push == 12) // i.e. i686 long double - arm and ppc do not have 12 byte (long) double
642 : {
643 : #if defined(__arm__) || defined(__powerpc__) || defined(__aarch64__) || (__GNUC__ < 4)
644 : *(long double *)lcl_param_ptr= 0.0; // __arm__: error: unable to emulate 'XC';
645 : #else
646 0 : typedef _Complex float __attribute__((mode(XC))) _float80;
647 0 : *(long double *)lcl_param_ptr= *(long double *)(_float80 *)ent_param_ptr;
648 : #endif
649 0 : lcl_param_ptr+= sizeof(long double);
650 : } else /* (params_sizes[uu].push == 8) */
651 : {
652 0 : *(long *)lcl_param_ptr= *(long *)ent_param_ptr;
653 0 : lcl_param_ptr+= sizeof(long);
654 : }
655 0 : ent_param_ptr+= params_sizes[uu].push;
656 : }
657 : param_va_ptr= (void *)local_params;
658 : } else /* ( ((myEnt_p->param_bytes==8) && (sizeof(long)==4)) ) */
659 : { // Entry made by 64bit program, being shown by 32bit
660 : ent_param_ptr= (uint8_t *)params_p;
661 : lcl_param_ptr= local_params;
662 0 : for (uu= 0; uu < myEnt_p->nargs && params_sizes[uu].push != 0; ++uu) {
663 0 : if (params_sizes[uu].size == 4) // i.e. char, short, int
664 : {
665 0 : *(unsigned *)lcl_param_ptr= (unsigned)*(unsigned long long *)ent_param_ptr;
666 0 : lcl_param_ptr+= sizeof(long);
667 0 : } else if (params_sizes[uu].size == 16) // i.e. long double
668 : {
669 : #if defined(__APPLE__) || defined(__arm__) || \
670 : defined(__powerpc__) // Basically mixed 32/64 env w/ long double is not supported on Mac/clang; shouldn't be a big deal
671 : *(long double *)lcl_param_ptr= 0.0;
672 : #elif defined(__aarch64__)
673 : *(long double *)lcl_param_ptr= *(long double *)ent_param_ptr;
674 : #else
675 0 : *(long double *)lcl_param_ptr= *(long double *)(__float128 *)ent_param_ptr;
676 : #endif
677 0 : lcl_param_ptr+= sizeof(long double);
678 : } else // (params_sizes[uu].size == 8) i.e.
679 : {
680 0 : *(unsigned long long *)lcl_param_ptr= *(unsigned long long *)ent_param_ptr;
681 0 : lcl_param_ptr+= sizeof(long long);
682 : }
683 0 : ent_param_ptr+= params_sizes[uu].push;
684 : }
685 : param_va_ptr= (void *)local_params;
686 : }
687 :
688 0 : msg_spec_included= 0;
689 0 : for (sp= ospec; *sp; ++sp) {
690 0 : char tbuf[0x100];
691 0 : int width_state, width_ia[2]= {0}; /* allow up to 3 widths w[.w] */
692 0 : char width_ca[2][9]; /* need to save the string for leading "0" (e.g. "-04"*/
693 0 : char flags_ca[4], *sncp; /* */
694 0 : int snlen;
695 0 : size_t flags_sz;
696 : //const char *default_unknown_sav;
697 0 : char *endptr;
698 :
699 0 : if (*sp != '%') {
700 0 : printf("%c", *sp);
701 0 : continue;
702 : }
703 0 : if (*++sp == '\0') {
704 0 : printf("%%"); // print the trailing/ending %
705 0 : break;
706 : }
707 :
708 : /* look for a width/flag spec - see trace.h:vtrace_user(...) */
709 0 : flags_sz= strspn(sp, "#*."); /* originally:"#-+ '*." %#n.mf#/src# */
710 0 : if (flags_sz) {
711 0 : snprintf(flags_ca, TRACE_MIN(flags_sz + 1, sizeof(flags_ca)), "%s", sp); // snprintf always terminates
712 0 : sp+= flags_sz; // use just flags_sz here to ignore wacky +++++++
713 : } else
714 0 : flags_ca[0]= '\0';
715 0 : for (width_state= 0; width_state < 2; ++width_state) {
716 0 : width_ia[width_state]= (int)TRACE_STRTOL(sp, &endptr, 10);
717 0 : if (endptr == sp || endptr > (sp + 4)) { /* check if no num or num too big (allow "-099") */
718 0 : width_ca[width_state][0]= '\0';
719 0 : break;
720 : }
721 0 : snprintf(width_ca[width_state], sizeof(width_ca[0]), "%.*s", (int)(endptr - sp), sp);
722 0 : sp= endptr;
723 0 : if (*sp == '.') ++sp;
724 : }
725 :
726 0 : switch (*sp) {
727 0 : case '%': printf("%%"); break;
728 0 : case 'a': printf("%4u", myEnt_p->nargs); break;
729 0 : case 'b':
730 0 : if (!width_state) {
731 0 : int last= DFLT_FILE_WIDTH;
732 0 : if (last > (int)strlen(file)) last= strlen(file);
733 0 : printf("%*.*s", DFLT_FILE_WIDTH, DFLT_FILE_WIDTH, file + strlen(file) - last);
734 : } else {
735 0 : int field_width= abs(width_ia[0]);
736 0 : if (field_width < DFLT_FILE_WIDTH) field_width= DFLT_FILE_WIDTH;
737 0 : if (width_ia[0] < 0) {
738 : /* print the end of the filename */
739 0 : width_ia[0]= abs(width_ia[0]);
740 0 : if (width_ia[0] > (int)strlen(file)) width_ia[0]= strlen(file);
741 0 : printf("%*.*s", field_width, width_ia[0], file + strlen(file) - width_ia[0]);
742 : } else
743 0 : printf("%*.*s", field_width, abs(width_ia[0]), file);
744 : }
745 : break;
746 0 : case 'B': printf("%u", myEnt_p->param_bytes); break;
747 0 : case 'C': printf("%" TRACE_STR(TRACE_CPU_WIDTH) "d", myEnt_p->cpu); break;
748 : case 'D': /* ignore this "control" */ break;
749 0 : case 'e':
750 0 : snlen= snprintf(tbuf, sizeof(tbuf), "%s:%d", TRACE_TID2NAME(myEnt_p->TrcId), myEnt_p->linenum);
751 0 : if (!width_state) printf("%*.*s", name_width + 1 + TRACE_LINENUM_WIDTH, name_width + 1 + TRACE_LINENUM_WIDTH, tbuf);
752 : else {
753 0 : if (width_ia[0] < (TRACE_LINENUM_WIDTH + 1)) width_ia[0]= (TRACE_LINENUM_WIDTH + 1);
754 0 : if (width_ia[0] > (int)(traceControl_p->nam_arr_sz + TRACE_LINENUM_WIDTH))
755 0 : width_ia[0]= (int)(traceControl_p->nam_arr_sz + TRACE_LINENUM_WIDTH);
756 0 : if (snlen > width_ia[0]) sncp= &tbuf[snlen - width_ia[0]];
757 : else
758 : sncp= &tbuf[0];
759 0 : printf("%*s", width_ia[0], sncp);
760 : }
761 : break;
762 0 : case 'f':
763 0 : printf("%s", local_msg);
764 0 : msg_spec_included= 1;
765 0 : break;
766 : case 'H': /* ignore this "control" */ break;
767 0 : case 'I': printf("%4u", myEnt_p->TrcId); break;
768 0 : case 'i': printf("%*d", TRACE_TID_WIDTH, myEnt_p->tid); break; /* darwin 16 tids are routinely 7 digit */
769 0 : case 'L': {
770 0 : char altbuf[0x100], l3buf[4], *lvlcp= trace_lvlstrs[0][(myEnt_p->lvl) & TLVLBITSMSK];
771 0 : int vwidth;
772 0 : if (trace_build_L_fmt(tbuf, altbuf, l3buf, &lvlcp, &vwidth, flags_ca, width_ia, width_ca)) printf(tbuf, vwidth, lvlcp);
773 : else
774 0 : printf(tbuf, lvlcp);
775 0 : } break;
776 0 : case 'l': /*printf("%2d", myEnt_p->lvl); break;*/
777 0 : if (!width_state) printf("%2d", myEnt_p->lvl);
778 : else {
779 0 : if (width_ia[0] < 2) width_ia[0]= 2;
780 0 : printf("%*d", width_ia[0], myEnt_p->lvl);
781 : }
782 : break;
783 0 : case 'm':
784 0 : if (local_msg[slen - 1] == '\n') local_msg[--slen]= '\0'; // strip off the trailing newline
785 0 : if (opts & indent_) printf("%s", &indent[TLVLBITSMSK - (myEnt_p->lvl & TLVLBITSMSK)]);
786 0 : if (myEnt_p->nargs) {
787 0 : va_list ap_= TRACE_VA_LIST_INIT(param_va_ptr);
788 0 : vprintf(local_msg, ap_);
789 : } else
790 0 : printf("%s", local_msg);
791 : msg_spec_included= 1;
792 : break;
793 0 : case 'N': printf("%*u", N_width, printed); break;
794 0 : case 'n':
795 0 : if (!width_state) printf("%*.*s", name_width, name_width, TRACE_TID2NAME(myEnt_p->TrcId));
796 : else {
797 0 : if (width_ia[0] < (int)strlen("trcname")) width_ia[0]= strlen("trcname");
798 0 : if (width_ia[0] > (int)(traceControl_p->nam_arr_sz - 1)) width_ia[0]= (int)(traceControl_p->nam_arr_sz - 1);
799 0 : sncp= TRACE_TID2NAME(myEnt_p->TrcId);
800 0 : snlen= strlen(sncp);
801 0 : if (snlen > width_ia[0]) sncp= &sncp[snlen - width_ia[0]];
802 0 : printf("%*.*s", width_ia[0], width_ia[0], sncp);
803 : }
804 : break;
805 0 : case 'O': printf("%s", trace_lvlcolors[(myEnt_p->lvl) & TLVLBITSMSK][0]); break;
806 0 : case 'o': printf("%s", trace_lvlcolors[(myEnt_p->lvl) & TLVLBITSMSK][1]); break;
807 0 : case 'P':
808 0 : printf("%*d", TRACE_TID_WIDTH, myEnt_p->pid);
809 0 : break; /* /proc/sys/kernel/pid_max has 32768 or 458752 (on mu2edaq13) */
810 0 : case 'R':
811 0 : if (myEnt_p->get_idxCnt_retries) printf("%u", myEnt_p->get_idxCnt_retries);
812 : else
813 0 : printf(".");
814 : break;
815 0 : case 'S': printf("%c", trace_lvlstrs[0][(myEnt_p->lvl) & TLVLBITSMSK][0]); break;
816 0 : case 's': printf("%*u", bufSlot_width, rdIdx % traceControl_p->num_entries); break;
817 0 : case 'T':
818 0 : if (tfmt_len) {
819 0 : strftime(tbuf, sizeof(tbuf), tfmt, localtime(&seconds)); // strcat(tbuf," ");
820 0 : if ((cp= strstr(tbuf, "%0")) && *(cp + 2) >= '1' && *(cp + 2) <= '5' && *(cp + 3) == 'd') {
821 : // NOTE: if *(cp+2)==6, don't do anything.
822 : // Example: fmt originally had "%%04d" which got changed by strftime to "%04d";
823 : // grab the character '4' and adjust useconds accordingly.
824 0 : int div= 1;
825 0 : switch (*(cp + 2)) {
826 : case '1': div= 100000; break;
827 : case '2': div= 10000; break;
828 : case '3': div= 1000; break;
829 : case '4': div= 100; break;
830 : case '5': div= 10; break;
831 : }
832 0 : useconds= (int)((double)useconds / div + 0.5); // div, round and cast back to unsigned
833 : }
834 0 : printf(tbuf, useconds);
835 : }
836 : break;
837 0 : case 't':
838 0 : if (!width_state) printf("%10u", (unsigned)myEnt_p->tsc);
839 : else
840 0 : printf("%20llu", (unsigned long long)myEnt_p->tsc);
841 : break;
842 0 : case 'u':
843 0 : if (!width_state) printf("%" TRACE_STR(TRACE_LINENUM_WIDTH) "u", myEnt_p->linenum);
844 : else
845 0 : printf("%*u", width_ia[0], myEnt_p->linenum);
846 : break;
847 : case 'x': /* ignore this "control" (filter_newline_ is set in opts) */ break;
848 : case 'X':
849 0 : params_p= (uint64_t *)param_va_ptr;
850 0 : for (uu= 0; uu < myEnt_p->nargs; ++uu) printf("0x%016llx ", (unsigned long long)params_p[uu]);
851 : msg_spec_included= 1;
852 : break;
853 0 : default: printf("%%%c", *sp); break; // print unknown %<blah> sequence
854 : }
855 : }
856 :
857 0 : if (!msg_spec_included) {
858 0 : if (local_msg[slen - 1] == '\n') local_msg[--slen]= '\0'; // strip off the trailing newline
859 0 : if (opts & indent_) printf("%s", &indent[TLVLBITSMSK - (myEnt_p->lvl & TLVLBITSMSK)]);
860 0 : if (ospec[0] != '\0' && !strchr(" \t:-|", *(sp - 1))) // if the specification does not end in a "separator"
861 0 : printf(" ");
862 0 : if (myEnt_p->nargs) { /* Ref. http://andrewl.dreamhosters.com/blog/variadic_functions_in_amd64_linux/index.html
863 : Here, I need an initializer so I must have this inside braces { } */
864 0 : va_list ap_= TRACE_VA_LIST_INIT(param_va_ptr);
865 0 : vprintf(local_msg, ap_);
866 : } else
867 0 : printf("%s", local_msg);
868 : }
869 0 : printf("\n");
870 0 : fflush(stdout);
871 0 : } /* printEnt */
872 :
873 : typedef struct trace_ptrs {
874 : struct trace_ptrs *next;
875 : struct trace_ptrs *prev;
876 : char file[PATH_MAX];
877 : int file_idx;
878 : struct traceControl_s *ro_p;
879 : struct traceControl_rw *rw_p;
880 : struct traceLvls_s *lvls_p;
881 : char *nams_p;
882 : struct traceEntryHdr_s *entries_p;
883 : uint32_t rdIdx;
884 : uint32_t max;
885 : int32_t offset_adjust; /* for trace file from different nodes */
886 : struct timeval ref_tv; // last/previously processed
887 : } trace_ptrs_t;
888 :
889 0 : void trace_ptrs_store(int idx, trace_ptrs_t *trace_ptrs, const char *file, int32_t off_adjust)
890 : {
891 0 : if (idx == 0) trace_ptrs[idx].prev= NULL;
892 : else {
893 0 : trace_ptrs[idx].prev= &trace_ptrs[idx - 1];
894 0 : trace_ptrs[idx - 1].next= &trace_ptrs[idx];
895 : }
896 0 : strcpy(trace_ptrs[idx].file, file);
897 0 : trace_ptrs[idx].file_idx= idx;
898 0 : trace_ptrs[idx].ro_p= traceControl_p;
899 0 : trace_ptrs[idx].rw_p= traceControl_rwp;
900 0 : trace_ptrs[idx].lvls_p= traceLvls_p;
901 0 : trace_ptrs[idx].nams_p= traceNams_p;
902 0 : trace_ptrs[idx].entries_p= traceEntries_p;
903 0 : trace_ptrs[idx].offset_adjust= off_adjust;
904 :
905 0 : trace_ptrs[idx].next= NULL;
906 0 : } /* trace_ptrs_store */
907 :
908 : /* Designed to work in the code:
909 : for (t_ptrs=trace_ptrs_list_start; t_ptrs!=NULL; t_ptrs=t_ptrs->next )
910 : trace_ptrs_discard( t_ptrs, &trace_ptrs_list_start );
911 : */
912 0 : void trace_ptrs_discard(trace_ptrs_t *tptr, trace_ptrs_t **list_start)
913 : {
914 0 : trace_ptrs_t *prev= tptr->prev;
915 0 : trace_ptrs_t *next= tptr->next;
916 0 : if (next) next->prev= prev;
917 0 : if (prev) prev->next= next;
918 0 : if (prev == NULL && next == NULL) *list_start= NULL;
919 0 : } /* trace_ptrs_discard */
920 :
921 0 : const char *trace_ptrs_restore(trace_ptrs_t *tptr)
922 : {
923 0 : traceControl_p= tptr->ro_p;
924 0 : traceControl_rwp= tptr->rw_p;
925 0 : traceLvls_p= tptr->lvls_p;
926 0 : traceNams_p= tptr->nams_p;
927 0 : traceEntries_p= tptr->entries_p;
928 0 : return tptr->file;
929 : } /* trace_ptrs_restore */
930 :
931 : /* rdIdx and wrIdxCnt are BOTH non-modulo.
932 : For forward read: simple delta
933 : For reverse read: num_entries(or wrIdxCnt) - delta
934 :
935 : NOT WORRYING ABOUT wrIdxCnt,rdIdx wrap yet -- Work-In-Progress!!!
936 : */
937 0 : uint32_t rdIdx_has_lines(uint32_t wrIdxCnt, uint32_t rdIdx, int for_rev)
938 : {
939 0 : uint32_t lines;
940 0 : if (for_rev == -1) {
941 : // READING in reverse -- from large numbers to smaller; reading and writing are on head on collision course
942 0 : if (wrIdxCnt <= traceControl_p->num_entries) {
943 : // 0*********<-rw->_______End
944 0 : lines= wrIdxCnt - TRACE_IDXCNT_DELTA(wrIdxCnt, rdIdx + 1);
945 : //return ((lines<=wrIdxCnt)?lines:0);
946 0 : return (lines);
947 : } else {
948 0 : lines= traceControl_p->num_entries - TRACE_IDXCNT_DELTA(wrIdxCnt, rdIdx + 1);
949 : //return ((lines<=traceControl_p->num_entries)?lines:0);
950 0 : return (lines);
951 : }
952 : } else {
953 : // READING forward (i.e from 0 up; read chasing writing; writing may overrun reading)
954 0 : if (wrIdxCnt <= traceControl_p->num_entries) {
955 0 : lines= TRACE_IDXCNT_DELTA(wrIdxCnt, rdIdx);
956 : //return ((lines<=wrIdxCnt)?lines:0);
957 0 : return (lines);
958 : } else {
959 0 : lines= TRACE_IDXCNT_DELTA(wrIdxCnt, rdIdx);
960 : //return ((lines<=traceControl_p->num_entries)?lines:0);
961 0 : return (lines);
962 : }
963 : }
964 : } /* rdIdx_has_lines */
965 :
966 0 : void tv_from_ent(struct timeval *tvp, struct traceEntryHdr_s *myEnt_p)
967 : {
968 0 : if (((myEnt_p->param_bytes == 4) && (sizeof(long) == 4)) ||
969 0 : ((myEnt_p->param_bytes == 8) && (sizeof(long) == 8))) { // 32, 32 or 64, 64 - no translation needed
970 0 : tvp->tv_sec= myEnt_p->time.tv_sec;
971 0 : tvp->tv_usec= myEnt_p->time.tv_usec;
972 0 : } else if (((myEnt_p->param_bytes == 4) && (sizeof(long) == 8))) { // Entry made by 32bit program, being readout by 64bit
973 0 : void *xx= &(myEnt_p->time);
974 0 : int *ptr= (int *)xx;
975 0 : tvp->tv_sec= *ptr++;
976 0 : tvp->tv_usec= (int)*ptr;
977 : } else /* ( ((myEnt_p->param_bytes==8) && (sizeof(long)==4)) ) */
978 : { // Entry made by 64bit program, being shown by 32bit
979 0 : void *xx= &myEnt_p->time;
980 0 : long long *ptr= (long long *)xx;
981 0 : tvp->tv_sec= (time_t)*ptr++;
982 0 : tvp->tv_usec= (int)*ptr;
983 : }
984 0 : }
985 :
986 0 : int tvcmp(struct timeval *tvp1, int32_t off1, struct timeval *tvp2, int32_t off2)
987 : {
988 0 : uint64_t t1= tvp1->tv_sec * 1000000 + tvp1->tv_usec;
989 0 : uint64_t t2= tvp2->tv_sec * 1000000 + tvp2->tv_usec;
990 0 : t1+= off1; // off1 could be negative
991 0 : t2+= off2; // off1 could be negative
992 0 : if (t1 < t2) return (-1);
993 0 : else if (t1 == t2)
994 0 : return (0);
995 : return (1);
996 : } /* tvcmp */
997 :
998 0 : char *check_off_adjust(char *fname)
999 : {
1000 0 : char *cp= fname + strlen(fname);
1001 0 : char *last_ch= --cp;
1002 0 : while (*cp >= '0' && *cp <= '9' && *cp != ':' && *cp != '-' && cp != fname) --cp;
1003 0 : if (*cp == '-' && cp != fname) --cp;
1004 0 : if (cp != last_ch && *cp == ':' && cp != fname) return cp; /* watch out for fname=':123" */
1005 : return NULL;
1006 : } /* check_off_adjust */
1007 :
1008 : /*
1009 : ospec is TRACE_SHOW or DFLT_SHOW
1010 : count==-1, slotStart==-1 ==> DEFAULT - reverse print from wrIdx to zero (if not
1011 : full) or for num_entrie
1012 : count>=0, slotStart==-1 ==> reverse print "count" entries (including 0, which
1013 : doesn't seem to useful
1014 : count>=0, slotStart>=0 ==> reverse print count entries starting at "slot" idx
1015 : "slotStart" I.e. if 8 traces have occurred, these 2
1016 : are equivalent:
1017 : traceShow( ospec, -1, -1, 0 ) and
1018 : traceShow( ospec, 8, 7, 0 )
1019 : show_opts = 0[|quiet_][|forward_]
1020 : arg, argv -- list of files to show
1021 : */
1022 0 : void traceShow(const char *ospec, int count, int slotStart, int show_opts, int argc, char *argv[])
1023 : {
1024 0 : uint32_t rdIdx;
1025 0 : uint32_t max;
1026 0 : unsigned printed= 0;
1027 0 : int ii;
1028 0 : int bufSlot_width;
1029 0 : int opts= 0;
1030 0 : struct traceEntryHdr_s *myEnt_p;
1031 :
1032 0 : uint8_t *local_params;
1033 0 : char *local_msg;
1034 0 : struct sizepush *params_sizes;
1035 0 : const char *sp; /*spec ptr*/
1036 0 : char *tfmt;
1037 0 : int tfmt_len;
1038 0 : time_t tt= time(NULL);
1039 0 : char tbuf[0x100], ttbuf[0x100];
1040 0 : int for_rev= -1; /* default is reverse print */
1041 0 : int forward_continuous= 0; /* continuous==1 will force for_rev=1 */
1042 0 : int msg_spec_included;
1043 0 : int files_to_show= 0;
1044 0 : const char *file= "";
1045 0 : int memlen_out_unused __attribute__((__unused__));
1046 0 : trace_ptrs_t *t_ptrs, *trace_ptrs_list_start, *t_ptrs_use;
1047 0 : struct timeval tv_use= {0}, tv2;
1048 0 : int32_t off_use;
1049 0 : uint32_t name_width= 0;
1050 0 : uint32_t num_entries_total;
1051 0 : uint32_t siz_msg_largest;
1052 0 : uint32_t siz_entry_largest= 0;
1053 0 : uint32_t num_params_largest;
1054 0 : int N_width;
1055 0 : char file_during_fun[PATH_MAX];
1056 :
1057 0 : if ((slotStart != -1) && (argc >= 2)) {
1058 0 : fprintf(stderr, "-s<startSlotIndex> invalid with multiple files\n");
1059 0 : exit(EXIT_FAILURE);
1060 0 : } else if ((slotStart != -1) && (show_opts & forward_)) {
1061 0 : fprintf(stderr, "-s<startSlotIndex> invalid with -F (Forward/poll mode)\n");
1062 0 : exit(EXIT_FAILURE);
1063 : }
1064 0 : opts|= show_opts; /* see enum show_opts_e above */
1065 0 : opts|= (trace_strflg(ospec, 'D') ? indent_ : 0);
1066 0 : opts|= (trace_strflg(ospec, 'x') ? filter_newline_ : 0);
1067 :
1068 0 : if (argc == 0) {
1069 0 : traceInit(
1070 : "_TRACE_",
1071 : 1); /* init traceControl_p, traceControl_rwp, etc. Use "_TRACE_" to assure non-write to corrupted TRACE_FILE where "TRACE" may not exist */
1072 0 : (void)tsnprintf(file_during_fun, sizeof(file_during_fun), getenv("TRACE_FILE") ? getenv("TRACE_FILE") : traceFile);
1073 0 : trace_ptrs_list_start= (trace_ptrs_t *)malloc(sizeof(trace_ptrs_t) * 1);
1074 0 : trace_ptrs_store(files_to_show++, trace_ptrs_list_start, file_during_fun, 0);
1075 : } else {
1076 0 : trace_ptrs_list_start= (trace_ptrs_t *)malloc(sizeof(trace_ptrs_t) * (unsigned)argc);
1077 0 : memset(trace_ptrs_list_start, 0, sizeof(trace_ptrs_t) * (unsigned)argc);
1078 0 : trace_ptrs_list_start[0].prev= NULL;
1079 0 : for (ii= 0; ii < argc; ++ii) {
1080 0 : char *off_adjust_ptr= check_off_adjust(argv[ii]); /* check for :[0-9]+$ */
1081 0 : if (off_adjust_ptr) *off_adjust_ptr++= '\0'; /* overwrite ':' with '\0' */
1082 0 : (void)tsnprintf(file_during_fun, sizeof(file_during_fun), argv[ii]);
1083 0 : if (access(file_during_fun, R_OK) != 0) {
1084 0 : fprintf(stderr, "Warning: cannot access %s\n", file_during_fun);
1085 0 : continue;
1086 : }
1087 0 : setenv("TRACE_FILE", file_during_fun, 1);
1088 : #if 1
1089 0 : traceControl_p= traceControl_p_static= NULL; // trick to allow traceInit to (re)mmap (another) file
1090 0 : traceInit(
1091 : "_TRACE_",
1092 : 1); /* init traceControl_p, traceControl_rwp, etc. NOTE: multiple "register_atfork" will occur, but should be tolerated be this non-forking app */
1093 : #else
1094 : trace_mmap_file(file_during_fun, &memlen_out_unused, &traceControl_p,
1095 : &traceControl_rwp // NOTE: traceNamLvls_p, traceEntries_p are iniitalized
1096 : ,
1097 : 0, 0, 0, 0, 1);
1098 : #endif
1099 0 : if (TRACE_CNTL("mapped")) {
1100 : int jj;
1101 0 : for (jj= 0; jj < files_to_show; ++jj) {
1102 0 : if (traceControl_p == trace_ptrs_list_start[jj].ro_p)
1103 : // found
1104 : break;
1105 : }
1106 0 : if (jj == files_to_show) {
1107 0 : int32_t off_adjust= off_adjust_ptr ? atoi(off_adjust_ptr) : 0;
1108 0 : trace_ptrs_store(files_to_show++, trace_ptrs_list_start, file_during_fun, off_adjust);
1109 : }
1110 : }
1111 : }
1112 0 : if (files_to_show == 0) {
1113 0 : fprintf(stderr, "Error: no files to show\n");
1114 0 : return;
1115 : }
1116 : }
1117 :
1118 : /* get the time format and the length of the format (using example time (now) */
1119 0 : if ((tfmt= getenv("TRACE_TIME_FMT")) == NULL)
1120 : //tfmt=(char*)TRACE_DFLT_TIME_FMT;
1121 0 : tfmt= (char *)"%10s%%06d"; // %s - The number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).
1122 0 : strftime(ttbuf, sizeof(ttbuf), tfmt, localtime(&tt));
1123 0 : tfmt_len= snprintf(tbuf, sizeof(tbuf), ttbuf, 0); /* possibly (probably) add usecs (i.e. FMT has %%06d) */
1124 :
1125 0 : if (trace_strflg(ospec, 'n') || trace_strflg(ospec, 'e'))
1126 0 : for (name_width= 0, t_ptrs= trace_ptrs_list_start; t_ptrs != NULL; t_ptrs= t_ptrs->next)
1127 0 : if (name_width < t_ptrs->rw_p->longest_name) {
1128 0 : name_width= t_ptrs->rw_p->longest_name;
1129 0 : if (name_width > t_ptrs->ro_p->nam_arr_sz) name_width= t_ptrs->ro_p->nam_arr_sz;
1130 : }
1131 0 : for (num_entries_total= 0, t_ptrs= trace_ptrs_list_start; t_ptrs != NULL; t_ptrs= t_ptrs->next)
1132 0 : num_entries_total+= t_ptrs->ro_p->num_entries;
1133 0 : for (siz_msg_largest= 0, num_params_largest= 0, t_ptrs= trace_ptrs_list_start; t_ptrs != NULL; t_ptrs= t_ptrs->next) {
1134 0 : if (siz_msg_largest < t_ptrs->ro_p->siz_msg) siz_msg_largest= t_ptrs->ro_p->siz_msg;
1135 0 : if (num_params_largest < t_ptrs->ro_p->num_params) num_params_largest= t_ptrs->ro_p->num_params;
1136 0 : if (siz_entry_largest < t_ptrs->ro_p->siz_entry) siz_entry_largest= t_ptrs->ro_p->siz_entry;
1137 : }
1138 :
1139 : /* If just a count is given, it will be a way to limit the number printed;
1140 : a short hand version of "... | head -count". (so if there are not entries,
1141 : none will be printed.
1142 : If however, count and slotStart are given, the count (up to numents) will be
1143 : printed regardless of how many entries are actually filled with real
1144 : data. This gives a way to look at data after a "treset".
1145 : */
1146 0 : if (slotStart >= 0) // only applicable with one file
1147 : {
1148 0 : slotStart++; /* startSlot index needs to be turned into a "count" */
1149 0 : if ((unsigned)slotStart > num_entries_total) {
1150 0 : slotStart= (int)num_entries_total;
1151 0 : printf("specified slotStart index too large, adjusting to %d\n", slotStart);
1152 : }
1153 0 : trace_ptrs_list_start->rdIdx= (uint32_t)slotStart; // in "startSlot mode" rdIdx is not realative to wrIdxCnt
1154 0 : trace_ptrs_list_start->ref_tv.tv_sec= 0;
1155 : } else { /* here, rdIdx starts as a count (see max = rdIdx below), but it is
1156 : used as an index after first being decremented (assuming reverse
1157 : printing) */
1158 0 : for (t_ptrs= trace_ptrs_list_start; t_ptrs != NULL; t_ptrs= t_ptrs->next) {
1159 0 : t_ptrs->rdIdx= TRACE_ATOMIC_LOAD(&t_ptrs->rw_p->wrIdxCnt); // % t_ptrs->ro_p->num_entries;
1160 0 : t_ptrs->ref_tv.tv_sec= 0;
1161 : }
1162 : }
1163 :
1164 0 : if ((count >= 0) && (slotStart >= 0)) { // only applicable with one file (the slotStart>=0 part)
1165 0 : t_ptrs= trace_ptrs_list_start;
1166 0 : if ((unsigned)count > num_entries_total) {
1167 0 : max= t_ptrs->max= t_ptrs->ro_p->num_entries;
1168 0 : count= (int)max;
1169 : } else {
1170 0 : max= t_ptrs->max= (uint32_t)count;
1171 : }
1172 : } else {
1173 0 : for (max= 0, t_ptrs= trace_ptrs_list_start; t_ptrs != NULL; t_ptrs= t_ptrs->next)
1174 0 : if (TRACE_ATOMIC_LOAD(&t_ptrs->rw_p->wrIdxCnt) >= t_ptrs->ro_p->num_entries)
1175 : //max = traceControl_p->num_entries;
1176 0 : max+= t_ptrs->max= t_ptrs->ro_p->num_entries;
1177 : else
1178 : //max = rdIdx;
1179 0 : max+= t_ptrs->max= t_ptrs->rdIdx;
1180 : }
1181 :
1182 0 : if ((count >= 0) && (slotStart < 0) && (count < (int)max))
1183 0 : for (t_ptrs= trace_ptrs_list_start; t_ptrs != NULL; t_ptrs= t_ptrs->next) t_ptrs->max= (uint32_t)count;
1184 0 : if (opts & forward_) {
1185 0 : for (t_ptrs= trace_ptrs_list_start; t_ptrs != NULL; t_ptrs= t_ptrs->next) {
1186 0 : int32_t add= -(int32_t)t_ptrs->max;
1187 0 : t_ptrs->rdIdx= TRACE_IDXCNT_ADD(TRACE_ATOMIC_LOAD(&t_ptrs->rw_p->wrIdxCnt), add);
1188 : /* subtract 1 b/c during loop (below) rdIdx is incremented first */
1189 0 : t_ptrs->rdIdx= TRACE_IDXCNT_ADD(t_ptrs->rdIdx, -1);
1190 : }
1191 : forward_continuous= 1;
1192 : for_rev= 1;
1193 : }
1194 :
1195 : //printf("for_rev=%d slotStart=%d count=%d max=%d siz_msg_largest=%u num_params_largest=%u files_to_show=%d\n"
1196 : // ,for_rev,slotStart,count,max,siz_msg_largest,num_params_largest, files_to_show);
1197 :
1198 0 : N_width= minw(3, countDigits((int32_t)max - 1));
1199 0 : bufSlot_width= minw(3, countDigits((int32_t)num_entries_total - 1));
1200 :
1201 0 : myEnt_p= (struct traceEntryHdr_s *)malloc(siz_entry_largest);
1202 0 : local_msg= (char *)malloc(siz_msg_largest * 3); /* in case an %ld needs change to %lld */
1203 0 : local_params= (uint8_t *)malloc(num_params_largest * sizeof(uint64_t));
1204 0 : params_sizes= (struct sizepush *)malloc(num_params_largest * sizeof(struct sizepush));
1205 :
1206 0 : if (trace_strflg(ospec, 'H')) {
1207 : msg_spec_included= 0;
1208 : /* 123456789112345678921234*/
1209 : #define TRACE_MSG_DASHES "------------------------"
1210 : #define TRACE_LONG_DASHES "------------------------------------------------------------------------------"
1211 0 : for (sp= ospec; *sp; ++sp) {
1212 0 : char tbuf[0x100];
1213 0 : int width_state, width_ia[2]= {0}; /* allow up to 3 widths w[.w] */
1214 0 : char width_ca[2][9]; /* need to save the string for leading "0" (e.g. "-04"*/
1215 0 : char flags_ca[4]; /* */
1216 0 : const char *sncp;
1217 0 : int snlen;
1218 0 : size_t flags_sz;
1219 : //const char *default_unknown_sav;
1220 0 : char *endptr;
1221 :
1222 0 : if (*sp != '%') {
1223 0 : printf("%c", *sp);
1224 0 : continue;
1225 : }
1226 0 : if (*++sp == '\0') {
1227 0 : printf("%%"); // print the trailing/ending %
1228 0 : break;
1229 : }
1230 :
1231 : /* look for a width/flag spec - see trace.h:vtrace_user(...) */
1232 0 : flags_sz= strspn(sp, "#*."); /* originally:"#-+ '*." %#n.mf#/src# */
1233 0 : if (flags_sz) {
1234 0 : snprintf(flags_ca, TRACE_MIN(flags_sz + 1, sizeof(flags_ca)), "%s", sp); // snprintf always terminates
1235 0 : sp+= flags_sz; // use just flags_sz here to ignore wacky +++++++
1236 : } else
1237 0 : flags_ca[0]= '\0';
1238 0 : for (width_state= 0; width_state < 2; ++width_state) {
1239 0 : width_ia[width_state]= (int)TRACE_STRTOL(sp, &endptr, 10);
1240 0 : if (endptr == sp || endptr > (sp + 4)) { /* check if no num or num too big (allow "-099") */
1241 0 : width_ca[width_state][0]= '\0';
1242 0 : break;
1243 : }
1244 0 : snprintf(width_ca[width_state], sizeof(width_ca[0]), "%.*s", (int)(endptr - sp), sp);
1245 0 : sp= endptr;
1246 0 : if (*sp == '.') ++sp;
1247 : }
1248 :
1249 0 : switch (*sp) {
1250 0 : case '%': printf("%%"); break;
1251 0 : case 'a': printf("args"); break;
1252 0 : case 'b':
1253 0 : if (!width_state) printf("%*.*s", DFLT_FILE_WIDTH, DFLT_FILE_WIDTH, "file");
1254 : else {
1255 0 : if (width_ia[0] < 0 && width_ia[0] > -DFLT_FILE_WIDTH) width_ia[0]= -DFLT_FILE_WIDTH;
1256 0 : else if (width_ia[0] >= 0 && width_ia[0] < DFLT_FILE_WIDTH)
1257 0 : width_ia[0]= DFLT_FILE_WIDTH;
1258 0 : printf("%*.*s", abs(width_ia[0]), abs(width_ia[0]), "file");
1259 : }
1260 : break;
1261 0 : case 'B': printf("B"); break;
1262 0 : case 'C': printf("cpu"); break;
1263 : case 'D': /* ignore this "control" */ break;
1264 0 : case 'e':
1265 0 : sncp= "trcname:ln#";
1266 0 : snlen= strlen(sncp);
1267 0 : if (!width_state) printf("%*s", name_width + 1 + TRACE_LINENUM_WIDTH, sncp); // +1 for ':'
1268 : else {
1269 0 : if (width_ia[0] < (TRACE_LINENUM_WIDTH + 1)) width_ia[0]= TRACE_LINENUM_WIDTH + 1;
1270 0 : if (width_ia[0] > (int)(traceControl_p->nam_arr_sz + TRACE_LINENUM_WIDTH))
1271 0 : width_ia[0]= (int)(traceControl_p->nam_arr_sz + TRACE_LINENUM_WIDTH);
1272 0 : if (snlen > width_ia[0]) sncp= &sncp[snlen - width_ia[0]];
1273 0 : printf("%*s", width_ia[0], sncp);
1274 : }
1275 : break;
1276 0 : case 'f':
1277 0 : printf("%-*s", (int)strlen(TRACE_MSG_DASHES), "msg");
1278 0 : msg_spec_included= 1;
1279 0 : break;
1280 : case 'H': /* ignore this "control" */ break;
1281 0 : case 'I': printf(" TID"); break;
1282 0 : case 'i': printf("%" TRACE_STR(TRACE_TID_WIDTH) "s", "tid"); break; /* darwin 16 tids are routinely 7 digit */
1283 0 : case 'L':
1284 0 : if (strchr(flags_ca, '*')) printf("%*s", trace_lvlwidth, "lvl");
1285 : else {
1286 0 : strcpy(tbuf, "%");
1287 0 : if (strchr(flags_ca, '.')) strcpy(&tbuf[1], ".");
1288 0 : if (width_state >= 1) strcat(&tbuf[1], width_ca[0]);
1289 0 : if (width_state >= 2) {
1290 0 : strcat(&tbuf[1], ".");
1291 0 : strcat(&tbuf[2], width_ca[1]);
1292 : }
1293 0 : strcat(&tbuf[1], "s");
1294 0 : printf(tbuf, "lvl");
1295 : }
1296 : break;
1297 0 : case 'l': /*printf("lv"); break;*/
1298 0 : if (!width_state) printf("lv");
1299 : else {
1300 0 : if (width_ia[0] < 2) width_ia[0]= 2;
1301 0 : printf("%*.*s", width_ia[0], width_ia[0], "lvlnum");
1302 : }
1303 : break;
1304 0 : case 'm':
1305 0 : printf("%-*s", (int)strlen(TRACE_MSG_DASHES), "msg");
1306 0 : msg_spec_included= 1;
1307 0 : break;
1308 0 : case 'N': printf("%*s", N_width, "idx"); break;
1309 0 : case 'n':
1310 0 : if (!width_state) printf("%*s", name_width + 1 + TRACE_LINENUM_WIDTH, "trcname");
1311 : else {
1312 0 : if (width_ia[0] < 7) width_ia[0]= 7;
1313 0 : if (width_ia[0] > (int)(traceControl_p->nam_arr_sz - 1)) width_ia[0]= (int)(traceControl_p->nam_arr_sz - 1);
1314 0 : printf("%*s", width_ia[0], "trcname");
1315 : }
1316 : break;
1317 : case 'O': /* ignore here */ break;
1318 : case 'o': /* ignore here */ break;
1319 0 : case 'P': printf("%" TRACE_STR(TRACE_TID_WIDTH) "s", "pid"); break;
1320 0 : case 'R': printf("r"); break;
1321 0 : case 'S': printf("%c", 'S'); break; /* Severity (1st character of lvlstr) */
1322 0 : case 's': printf("%*s", bufSlot_width, "slt"); break;
1323 0 : case 'T':
1324 0 : if (tfmt_len) printf("%*.*s", tfmt_len, tfmt_len, &("us_tod"[tfmt_len >= 6 ? 0 : 6 - tfmt_len]));
1325 : break;
1326 0 : case 't':
1327 0 : if (!width_state) printf(" tsc");
1328 : else
1329 0 : printf(" tsc");
1330 : break;
1331 0 : case 'u':
1332 0 : if (!width_state) printf("%" TRACE_STR(TRACE_LINENUM_WIDTH) "s", "ln#");
1333 : else
1334 0 : printf("%*s", width_ia[0], "ln#");
1335 : break;
1336 : case 'x': /* ignore this "control" */ break;
1337 0 : case 'X':
1338 0 : printf("%-*s", (int)strlen(TRACE_MSG_DASHES), "msg");
1339 0 : msg_spec_included= 1;
1340 0 : break;
1341 0 : default: printf("%%%c", *sp); break; // print unknown %<blah> sequence
1342 : }
1343 : }
1344 0 : if (!msg_spec_included) {
1345 0 : if (!strchr(" \t:-|", *(sp - 1))) // if the specification does not end in a "separator"
1346 0 : printf(" ");
1347 0 : printf("%-*s\n", (int)strlen(TRACE_MSG_DASHES), "msg");
1348 0 : } else if (*(sp - 1) != '\n')
1349 0 : printf("\n");
1350 :
1351 : msg_spec_included= 0; // reset for the ---- processing
1352 0 : for (sp= ospec; *sp; ++sp) {
1353 0 : char tbuf[0x100];
1354 0 : int width_state, width_ia[2]= {0}; /* allow up to 3 widths w[.w] */
1355 0 : char width_ca[2][9]; /* need to save the string for leading "0" (e.g. "-04"*/
1356 0 : char flags_ca[4]; /* */
1357 0 : size_t flags_sz;
1358 : //const char *default_unknown_sav;
1359 0 : char *endptr;
1360 :
1361 0 : if (*sp != '%') {
1362 0 : if (strchr(" \t\n", *sp)) printf("%c", *sp);
1363 : else
1364 0 : printf("-");
1365 0 : continue;
1366 : }
1367 0 : if (*++sp == '\0') {
1368 0 : printf("%%"); // print the trailing/ending %
1369 0 : break;
1370 : }
1371 :
1372 : /* look for a width/flag spec - see trace.h:vtrace_user(...) */
1373 0 : flags_sz= strspn(sp, "#*."); /* originally:"#-+ '*." %#n.mf#/src# */
1374 0 : if (flags_sz) {
1375 0 : snprintf(flags_ca, TRACE_MIN(flags_sz + 1, sizeof(flags_ca)), "%s", sp); // snprintf always terminates
1376 0 : sp+= flags_sz; // use just flags_sz here to ignore wacky +++++++
1377 : } else
1378 0 : flags_ca[0]= '\0';
1379 0 : for (width_state= 0; width_state < 2; ++width_state) {
1380 0 : width_ia[width_state]= (int)TRACE_STRTOL(sp, &endptr, 10);
1381 0 : if (endptr == sp || endptr > (sp + 4)) { /* check if no num or num too big (allow "-099") */
1382 0 : width_ca[width_state][0]= '\0';
1383 0 : break;
1384 : }
1385 0 : snprintf(width_ca[width_state], sizeof(width_ca[0]), "%.*s", (int)(endptr - sp), sp);
1386 0 : sp= endptr;
1387 0 : if (*sp == '.') ++sp;
1388 : }
1389 :
1390 0 : switch (*sp) {
1391 0 : case '%': printf("-"); break;
1392 0 : case 'a': printf("----"); break;
1393 0 : case 'b':
1394 0 : if (!width_state) printf("%*.*s", DFLT_FILE_WIDTH, DFLT_FILE_WIDTH, TRACE_LONG_DASHES);
1395 : else {
1396 0 : if (width_ia[0] < 0 && width_ia[0] > -DFLT_FILE_WIDTH) width_ia[0]= -DFLT_FILE_WIDTH;
1397 0 : else if (width_ia[0] >= 0 && width_ia[0] < DFLT_FILE_WIDTH)
1398 0 : width_ia[0]= DFLT_FILE_WIDTH;
1399 0 : printf("%*.*s", abs(width_ia[0]), abs(width_ia[0]), TRACE_LONG_DASHES);
1400 : }
1401 : break;
1402 0 : case 'B': printf("-"); break;
1403 0 : case 'C': printf("---"); break;
1404 : case 'D': /* ignore this "control" */ break;
1405 0 : case 'e':
1406 0 : if (!width_state) printf("%.*s", name_width + 1 + TRACE_LINENUM_WIDTH, TRACE_LONG_DASHES);
1407 : else {
1408 0 : if (width_ia[0] < (TRACE_LINENUM_WIDTH + 1)) width_ia[0]= (TRACE_LINENUM_WIDTH + 1);
1409 0 : if (width_ia[0] > (int)(traceControl_p->nam_arr_sz + TRACE_LINENUM_WIDTH))
1410 0 : width_ia[0]= (int)(traceControl_p->nam_arr_sz + TRACE_LINENUM_WIDTH);
1411 0 : printf("%.*s", width_ia[0], TRACE_LONG_DASHES);
1412 : }
1413 : break;
1414 0 : case 'f':
1415 0 : printf(TRACE_MSG_DASHES);
1416 0 : msg_spec_included= 1;
1417 0 : break;
1418 : case 'H': /* ignore this "control" */ break;
1419 0 : case 'I': printf("----"); break;
1420 0 : case 'i':
1421 0 : printf("%." TRACE_STR(TRACE_TID_WIDTH) "s", TRACE_LONG_DASHES);
1422 0 : break; /* darwin 16 tids are routinely 7 digit */
1423 0 : case 'L':
1424 0 : if (strchr(flags_ca, '*')) printf("%.*s", trace_lvlwidth, TRACE_LONG_DASHES);
1425 : else {
1426 0 : strcpy(tbuf, "%");
1427 0 : if (strchr(flags_ca, '.')) strcpy(&tbuf[1], ".");
1428 0 : if (width_state >= 1) strcat(&tbuf[1], width_ca[0]);
1429 0 : if (width_state >= 2) {
1430 0 : strcat(&tbuf[1], ".");
1431 0 : strcat(&tbuf[2], width_ca[1]);
1432 0 : } else if (tbuf[1] != '.') {
1433 0 : strcat(&tbuf[1], ".");
1434 0 : strcat(&tbuf[2], width_ca[0]);
1435 : }
1436 0 : strcat(&tbuf[1], "s");
1437 0 : printf(tbuf, TRACE_LONG_DASHES);
1438 : }
1439 : break;
1440 0 : case 'l': /*printf("--");*/
1441 0 : if (!width_state) printf("--");
1442 : else {
1443 0 : if (width_ia[0] < 2) width_ia[0]= 2;
1444 0 : printf("%.*s", width_ia[0], TRACE_LONG_DASHES);
1445 : }
1446 : break;
1447 0 : case 'm':
1448 0 : printf(TRACE_MSG_DASHES);
1449 0 : msg_spec_included= 1;
1450 0 : break;
1451 0 : case 'N': printf("%.*s", N_width, TRACE_LONG_DASHES); break;
1452 0 : case 'n':
1453 0 : if (!width_state) printf("%.*s", name_width, TRACE_LONG_DASHES);
1454 : else {
1455 0 : if (width_ia[0] < (int)strlen("trcname")) width_ia[0]= strlen("trcname");
1456 0 : if (width_ia[0] > (int)(traceControl_p->nam_arr_sz - 1)) width_ia[0]= (int)(traceControl_p->nam_arr_sz - 1);
1457 0 : printf("%.*s", width_ia[0], TRACE_LONG_DASHES);
1458 : }
1459 : break;
1460 : case 'O': /* ignore here */ break;
1461 : case 'o': /* ignore here */ break;
1462 0 : case 'P': printf("%." TRACE_STR(TRACE_TID_WIDTH) "s", TRACE_LONG_DASHES); break;
1463 0 : case 'R': printf("-"); break;
1464 0 : case 'S': printf("-"); break; /* Severity (1st character of lvlstr) */
1465 0 : case 's': printf("%.*s", bufSlot_width, TRACE_LONG_DASHES); break;
1466 0 : case 'T':
1467 0 : if (tfmt_len) printf("%.*s", tfmt_len, TRACE_LONG_DASHES);
1468 : break;
1469 0 : case 't':
1470 0 : if (!width_state) printf("----------");
1471 : else
1472 0 : printf("--------------------");
1473 : break;
1474 0 : case 'u':
1475 0 : if (!width_state) printf("%." TRACE_STR(TRACE_LINENUM_WIDTH) "s", TRACE_LONG_DASHES);
1476 : else
1477 0 : printf("%.*s", width_ia[0], TRACE_LONG_DASHES);
1478 : break;
1479 :
1480 : case 'x': /* ignore this "control" */ break;
1481 0 : case 'X':
1482 0 : printf(TRACE_MSG_DASHES);
1483 0 : msg_spec_included= 1;
1484 0 : break;
1485 0 : default: printf("%%%c", *sp); break; // print unknown %<blah> sequence
1486 : }
1487 : }
1488 0 : if (!msg_spec_included) {
1489 0 : if (!strchr(" \t:-|", *(sp - 1))) // if the specification does not end in a "separator"
1490 0 : printf(" ");
1491 0 : printf(TRACE_MSG_DASHES "\n");
1492 0 : } else if (*(sp - 1) != '\n')
1493 0 : printf("\n");
1494 : }
1495 :
1496 : // Change count to idx
1497 0 : for (t_ptrs= trace_ptrs_list_start; t_ptrs != NULL; t_ptrs= t_ptrs->next) {
1498 0 : rdIdx= TRACE_IDXCNT_ADD(t_ptrs->rdIdx, for_rev);
1499 0 : t_ptrs->rdIdx= rdIdx;
1500 : }
1501 :
1502 : printed= 0;
1503 0 : while (1) {
1504 0 : uint32_t lines= 0;
1505 : /* loop trhough the files looking for
1506 : a) ones that have something to print and
1507 : b) the one that is earliest or latest depending on for_rev
1508 : */
1509 0 : if (count >= 0 && (int)printed >= count && !forward_continuous) /* yes, can elect to print zero lines w/ -c0 */
1510 : break;
1511 :
1512 0 : forward_check:
1513 0 : t_ptrs_use= 0;
1514 0 : off_use= 0;
1515 0 : for (t_ptrs= trace_ptrs_list_start; t_ptrs != NULL; t_ptrs= t_ptrs->next) {
1516 0 : traceControl_p= t_ptrs->ro_p; // used in rdIdx_has_lines
1517 : reset_check:
1518 0 : if (slotStart >= 0) {
1519 : // "startSlot mode"
1520 : t_ptrs_use= t_ptrs;
1521 0 : } else if ((lines= rdIdx_has_lines(t_ptrs->rw_p->wrIdxCnt, t_ptrs->rdIdx, for_rev))) {
1522 0 : if (forward_continuous && lines > traceControl_p->num_entries) { // reset detect
1523 0 : if (t_ptrs->rw_p->wrIdxCnt == 0 && t_ptrs->rw_p->full == 0) t_ptrs->rdIdx= 0;
1524 : else {
1525 0 : uint32_t rdIdxsav= t_ptrs->rdIdx, wrIdxCntsav= t_ptrs->rw_p->wrIdxCnt;
1526 0 : int num_entries= (int)(traceControl_p->num_entries);
1527 : //if (num_entries > 400) num_entries -= 250; // incase significant burst (maybe from multiple threads)
1528 0 : t_ptrs->rdIdx= TRACE_IDXCNT_ADD(wrIdxCntsav, -num_entries);
1529 0 : if (!(show_opts & quiet_))
1530 0 : fprintf(stderr, "skipped %9u wrIdxCntsav=0x%08x rdIdx=%u rdIdxsav=%u\n",
1531 0 : TRACE_IDXCNT_DELTA(t_ptrs->rdIdx, rdIdxsav), wrIdxCntsav, t_ptrs->rdIdx, rdIdxsav);
1532 : }
1533 0 : t_ptrs->ref_tv.tv_sec= 0;
1534 0 : goto reset_check;
1535 : }
1536 0 : file= trace_ptrs_restore(t_ptrs); // for idxCnt2entPtr to get time
1537 0 : if (!t_ptrs_use) {
1538 0 : t_ptrs_use= t_ptrs;
1539 0 : tv_from_ent(&tv_use, idxCnt2entPtr(t_ptrs->rdIdx));
1540 0 : off_use= t_ptrs->offset_adjust;
1541 : } else {
1542 : // Compare differently depending on whether forward or reverse.
1543 : // Race condition -- rdIdx might have just changed and time could be being overwritten :(
1544 : // Frozen trace buffers are the safest!
1545 0 : tv_from_ent(&tv2, idxCnt2entPtr(t_ptrs->rdIdx));
1546 0 : if (tvcmp(&tv_use, off_use, &tv2, t_ptrs->offset_adjust) == for_rev) {
1547 0 : t_ptrs_use= t_ptrs;
1548 0 : tv_use= tv2;
1549 0 : off_use= t_ptrs->offset_adjust;
1550 : }
1551 : }
1552 0 : } else if (forward_continuous) { /* and lines==0 (obviously) */
1553 : /* special check where, e.g.: treset; tcntl test */
1554 0 : if (t_ptrs->ref_tv.tv_sec) {
1555 0 : uint32_t prvIdx= TRACE_IDXCNT_ADD(t_ptrs->rdIdx, -1);
1556 0 : tv_from_ent(&tv2, idxCnt2entPtr(prvIdx));
1557 0 : if (tvcmp(&t_ptrs->ref_tv, 0, &tv2, 0)) {
1558 0 : t_ptrs->rdIdx= 0;
1559 0 : t_ptrs->ref_tv.tv_sec= 0;
1560 0 : t_ptrs->offset_adjust= 0;
1561 0 : goto reset_check;
1562 : }
1563 : }
1564 0 : } else if (!forward_continuous) // not strictly needed, but a little efficiency gain
1565 0 : trace_ptrs_discard(t_ptrs, &trace_ptrs_list_start);
1566 : }
1567 0 : if (!forward_continuous && (t_ptrs_use == NULL || lines > traceControl_p->num_entries)) {
1568 : break;
1569 0 : } else if (t_ptrs_use == NULL && forward_continuous) {
1570 0 : usleep(10000);
1571 0 : goto forward_check;
1572 : }
1573 :
1574 : // had (possibly just changed) t_ptrs_use->rdIdx for a while -- race condition -
1575 : // could be being written to as we are copying :( -- frozen buffers are the safest
1576 0 : file= trace_ptrs_restore(t_ptrs_use);
1577 0 : memcpy(myEnt_p, idxCnt2entPtr(t_ptrs_use->rdIdx), traceControl_p->siz_entry);
1578 :
1579 : // adjust myEnt_p->time
1580 0 : tv_from_ent(&tv2, idxCnt2entPtr(t_ptrs_use->rdIdx));
1581 0 : if (t_ptrs_use->offset_adjust != 0) { // FIXME - currently does not work for mixed 32/64 bit environment!
1582 0 : uint64_t tt;
1583 0 : tt= tv2.tv_sec * 1000000 + tv2.tv_usec;
1584 0 : tt+= t_ptrs_use->offset_adjust;
1585 0 : myEnt_p->time.tv_sec= tt / 1000000;
1586 0 : myEnt_p->time.tv_usec= tt % 1000000;
1587 : } else
1588 0 : myEnt_p->time= tv2;
1589 0 : printEnt(ospec, opts, myEnt_p, local_msg, local_params, params_sizes, bufSlot_width, N_width, (int)name_width, printed,
1590 : t_ptrs_use->rdIdx, tfmt, tfmt_len, file);
1591 0 : ++printed;
1592 0 : t_ptrs_use->ref_tv= myEnt_p->time;
1593 0 : t_ptrs_use->rdIdx= TRACE_IDXCNT_ADD(t_ptrs_use->rdIdx, for_rev);
1594 : }
1595 : } /*traceShow*/
1596 :
1597 0 : void traceInfo(int quiet)
1598 : {
1599 0 : uint32_t used;
1600 0 : uint32_t wrCopy;
1601 0 : uint32_t nameLockCopy;
1602 0 : char outstr[200];
1603 0 : struct tm *tmp;
1604 0 : time_t tt= (time_t)traceControl_p->create_tv_sec;
1605 0 : int memlen= (int)traceMemLen(
1606 : TRACE_cntlPagesSiz(), traceControl_p->num_namLvlTblEnts, traceControl_p->nam_arr_sz, traceControl_p->siz_msg,
1607 : traceControl_p->num_params,
1608 : traceControl_p->num_entries); /* for when buffer is vmalloc (kernel) or file mmapped (user), but not userspace inactive */
1609 0 : tmp= localtime(&tt);
1610 0 : if (tmp == NULL) {
1611 0 : perror("localtime");
1612 0 : exit(EXIT_FAILURE);
1613 : }
1614 0 : if (strftime(outstr, sizeof(outstr), "%a %b %d %H:%M:%S %Z %Y", tmp) == 0) {
1615 0 : perror("strftime");
1616 0 : exit(EXIT_FAILURE);
1617 : }
1618 0 : nameLockCopy= TRACE_ATOMIC_LOAD(&traceControl_rwp->namelock);
1619 0 : wrCopy= TRACE_ATOMIC_LOAD(&traceControl_rwp->wrIdxCnt);
1620 0 : used= ((traceControl_rwp->full) ? traceControl_p->num_entries
1621 0 : : wrCopy); /* Race Condition - if not full this shouldn't be > traceControl_p->num_entries */
1622 0 : if (quiet)
1623 0 : printf("mode = 0x%-8x %s%s\n"
1624 : "writeIdxCount = 0x%08x entries used: %u\n"
1625 : "full = %d\n"
1626 : "num_entries = %u\n",
1627 0 : traceControl_rwp->mode.words.mode, traceControl_rwp->mode.bits.S ? "Slow:ON, " : "Slow:off",
1628 0 : traceControl_rwp->mode.bits.M ? " Mem:ON" : " Mem:off", wrCopy, used, traceControl_rwp->full,
1629 0 : traceControl_p->num_entries);
1630 : else
1631 0 : printf("trace.h rev = %s\n"
1632 : "revision = %s\n"
1633 : "create time = %s\n"
1634 : "trace_initialized = %d\n"
1635 : "fast/mem __func__ = %-8d 1=force on, 0=TRACE_PRINT, -1=force off\n"
1636 : #ifdef __arm__
1637 : "fast/mem getcpu = %d\n"
1638 : #endif
1639 : "mode = 0x%-8x %s%s\n"
1640 : "writeIdxCount = 0x%08x entries used: %u\n"
1641 : "full = %d\n"
1642 : "nameLock = %u\n"
1643 : "largestMultiple = 0x%08x\n"
1644 : "largestZeroOffset = 0x%08x\n"
1645 : "trigIdxCnt = 0x%08x\n"
1646 : "triggered = %d\n"
1647 : "trigActivePost = %u\n"
1648 : "limit_cnt_limit = %u\n"
1649 : "limit_span_on_ms = %llu\n"
1650 : "limit_span_off_ms = %llu\n"
1651 : "traceLevel = 0x%0*llx 0x%0*llx 0x%0*llx\n"
1652 : "num_entries = %u\n"
1653 : "max_msg_sz = %u includes system enforced terminator\n"
1654 : "max_params = %u\n"
1655 : "entry_size = %u\n"
1656 : "namLvlTbl_ents = %u\n"
1657 : "namLvlTbl_name_sz = %u not including null terminator\n"
1658 : "longest_name = %u\n"
1659 : "wrIdxCnt offset = %p traceControl_rw start\n"
1660 : "lvls offset = 0x%lx\n"
1661 : "nams offset = 0x%lx\n"
1662 : "buffer_offset = 0x%lx\n"
1663 : "memlen = 0x%x %s\n"
1664 : "default TRACE_TIME_FMT=\"%s\"\n"
1665 : "default TRACE_SHOW=\"%s\" others: a:nargs b:fileName B:paramBytes D:inDent e:nam:ln# n:nam f:convertedMsgfmt_only "
1666 : "I:trcId l:lvlNum O/o:color R:retry S:severity s:slot t:tsc u:line x:filtNl X:examineArgData\n"
1667 : "default TRACE_PRINT=\"%s\" others: C:core e:nam:ln# n:nam [n]f:file F:func I:trcId i:threadID l:lvlNum "
1668 : "m:msg-insert N:unpadded_trcName O/o:color P:pid S:severity t:insert u:line\n"
1669 : "Some SHOW/PRINT specifiers take optional modifiers. E.g. PRINT: %%#n.mf#/src#\n",
1670 0 : TRACE_REV, traceControl_p->version_string, outstr, traceControl_p->trace_initialized,
1671 0 : traceControl_rwp->mode.bits.func
1672 : #ifdef __arm__
1673 : ,
1674 : traceControl_rwp->mode.bits.fast_do_getcpu
1675 : #endif
1676 : ,
1677 0 : traceControl_rwp->mode.words.mode, traceControl_rwp->mode.bits.S ? "Slow:ON, " : "Slow:off",
1678 0 : traceControl_rwp->mode.bits.M ? " Mem:ON" : " Mem:off", wrCopy, used, traceControl_rwp->full, nameLockCopy,
1679 : traceControl_p->largest_multiple, traceControl_p->largest_zero_offset, traceControl_rwp->trigIdxCnt,
1680 : traceControl_rwp->triggered, traceControl_rwp->trigActivePost, traceControl_rwp->limit_cnt_limit,
1681 0 : (unsigned long long)traceControl_rwp->limit_span_on_ms, (unsigned long long)traceControl_rwp->limit_span_off_ms,
1682 0 : (int)sizeof(uint64_t) * 2, (unsigned long long)traceLvls_p[traceTID].M /* sizeof(uint64_t)*2 is "nibbles" */
1683 : ,
1684 0 : (int)sizeof(uint64_t) * 2, (unsigned long long)traceLvls_p[traceTID].S, (int)sizeof(uint64_t) * 2,
1685 0 : (unsigned long long)traceLvls_p[traceTID].T, traceControl_p->num_entries, traceControl_p->siz_msg,
1686 : traceControl_p->num_params, traceControl_p->siz_entry, traceControl_p->num_namLvlTblEnts,
1687 0 : (int)traceControl_p->nam_arr_sz - 1, traceControl_rwp->longest_name,
1688 0 : (void *)&((struct traceControl_s *)0)->rw.wrIdxCnt, (unsigned long)traceLvls_p - (unsigned long)traceControl_rwp,
1689 0 : (unsigned long)traceNams_p - (unsigned long)traceControl_rwp,
1690 0 : (unsigned long)traceEntries_p - (unsigned long)traceControl_rwp, traceControl_p->memlen,
1691 0 : (traceControl_p->memlen != (uint32_t)memlen) ? "not for mmap" : "", TRACE_DFLT_TIME_FMT, DFLT_SHOW, TRACE_PRINT__);
1692 0 : } /* traceInfo */
1693 :
1694 0 : void traceMemReport(int do_heading, int use_sys_levels, int32_t max_reverse_us)
1695 : {
1696 0 : uint32_t wrCopy= TRACE_ATOMIC_LOAD(&traceControl_rwp->wrIdxCnt);
1697 0 : uint32_t num_entries= traceControl_p->num_entries;
1698 0 : uint32_t num_tids= traceControl_p->num_namLvlTblEnts;
1699 0 : uint32_t used= traceControl_rwp->full ? num_entries : wrCopy;
1700 0 : uint32_t longest_name= traceControl_rwp->longest_name;
1701 0 : uint32_t *lvl_counts;
1702 0 : uint8_t *has_report;
1703 0 : uint32_t analyzed= 0;
1704 0 : struct timeval first_tv= {0};
1705 0 : struct timeval last_tv= {0};
1706 0 : int tid_digits;
1707 0 : uint32_t ii;
1708 :
1709 0 : if (used > num_entries) used= num_entries;
1710 0 : if (longest_name > (traceControl_p->nam_arr_sz - 1)) longest_name= traceControl_p->nam_arr_sz - 1;
1711 0 : if (longest_name < (uint32_t)strlen("NAME")) longest_name= (uint32_t)strlen("NAME");
1712 0 : tid_digits= countDigits((int)num_tids - 1);
1713 :
1714 0 : lvl_counts= (uint32_t *)calloc((size_t)num_tids * 64U, sizeof(uint32_t));
1715 0 : has_report= (uint8_t *)calloc((size_t)num_tids, sizeof(uint8_t));
1716 0 : if (!lvl_counts || !has_report) {
1717 0 : fprintf(stderr, "memreport: allocation failure\n");
1718 0 : free(lvl_counts);
1719 0 : free(has_report);
1720 0 : return;
1721 : }
1722 :
1723 0 : if (used) {
1724 0 : uint32_t rdIdx= TRACE_IDXCNT_ADD(wrCopy, -1);
1725 : struct timeval prev_tv;
1726 : int have_prev= 0;
1727 : const int32_t forward_jump_tol_us= max_reverse_us;
1728 :
1729 0 : for (ii= 0; ii < used; ++ii) {
1730 0 : struct traceEntryHdr_s *ent_p= idxCnt2entPtr(rdIdx);
1731 0 : struct timeval cur_tv;
1732 0 : int32_t tid;
1733 0 : uint8_t lvl;
1734 :
1735 0 : tv_from_ent(&cur_tv, ent_p);
1736 : /* While walking backward in time, stop only on forward jumps above jitter tolerance. */
1737 0 : if (have_prev && tvcmp(&prev_tv, 0, &cur_tv, -forward_jump_tol_us) == -1) break;
1738 :
1739 0 : tid= ent_p->TrcId;
1740 0 : if (tid >= 0 && (uint32_t)tid < num_tids) {
1741 0 : lvl= (uint8_t)(ent_p->lvl & TLVLBITSMSK);
1742 0 : ++lvl_counts[(size_t)tid * 64U + lvl];
1743 0 : has_report[tid]= 1;
1744 : }
1745 :
1746 0 : if (analyzed == 0) first_tv= cur_tv;
1747 0 : last_tv= cur_tv;
1748 :
1749 0 : prev_tv= cur_tv;
1750 0 : have_prev= 1;
1751 0 : ++analyzed;
1752 0 : rdIdx= TRACE_IDXCNT_ADD(rdIdx, -1);
1753 : }
1754 : }
1755 :
1756 0 : if (do_heading) {
1757 0 : printf("%*s %*s %*s %s\n", minw(3, tid_digits), "TID", longest_name, "NAME", 18, "maskM", "report");
1758 0 : printf("%.*s %.*s %.*s %.*s\n", minw(3, tid_digits), TRACE_LONG_DASHES, longest_name, TRACE_LONG_DASHES, 18,
1759 : TRACE_LONG_DASHES, 4, TRACE_LONG_DASHES);
1760 : }
1761 :
1762 0 : for (ii= 0; ii < num_tids; ++ii) {
1763 0 : int first= 1;
1764 0 : uint8_t lvl;
1765 0 : if (!has_report[ii]) continue;
1766 :
1767 0 : printf("%*u %*.*s 0x%016llx ", minw(3, tid_digits), ii, longest_name, longest_name, TRACE_TID2NAME((int32_t)ii),
1768 0 : (unsigned long long)traceLvls_p[ii].M);
1769 0 : for (lvl= 0; lvl < 64; ++lvl) {
1770 0 : uint32_t cnt= lvl_counts[(size_t)ii * 64U + lvl];
1771 0 : if (!cnt) continue;
1772 0 : if (!use_sys_levels && lvl >= 8)
1773 0 : printf("%sD%02u:%u", first ? "" : " ", (unsigned)(lvl - 8), cnt);
1774 : else
1775 0 : printf("%s%u:%u", first ? "" : " ", lvl, cnt);
1776 : first= 0;
1777 : }
1778 0 : printf("\n");
1779 : }
1780 0 : if (analyzed) {
1781 0 : long span_sec= (long)(first_tv.tv_sec - last_tv.tv_sec);
1782 0 : int span_usec= (int)(first_tv.tv_usec - last_tv.tv_usec);
1783 0 : if (span_usec < 0) {
1784 0 : span_usec+= 1000000;
1785 0 : --span_sec;
1786 : }
1787 0 : printf("entries analyzed: %u spanning %ld.%06d seconds\n", analyzed, span_sec, span_usec);
1788 : } else
1789 0 : printf("entries analyzed: %u spanning %ld.%06d seconds\n", analyzed, 0L, 0);
1790 :
1791 0 : free(lvl_counts);
1792 0 : free(has_report);
1793 : }
1794 :
1795 0 : void do_help(const char *cmd)
1796 : {
1797 0 : unsigned uu= 0;
1798 0 : if (strcmp(cmd, "") != 0) printf(" usage: tcntl");
1799 0 : for (uu= 0; uu < (sizeof(subcmdhlp) / sizeof(subcmdhlp[0])); ++uu) {
1800 0 : size_t slen= strlen(subcmdhlp[uu].cmd);
1801 0 : size_t slen2= strcspn(subcmdhlp[uu].cmd, " [{");
1802 0 : if (strcmp(cmd, "") == 0 || (slen2 < slen && strncmp(subcmdhlp[uu].cmd, cmd, slen2) == 0) ||
1803 0 : strcmp(subcmdhlp[uu].cmd, cmd) == 0) {
1804 0 : printf(" %s %s\n", subcmdhlp[uu].cmd, subcmdhlp[uu].hlp);
1805 0 : if (strcmp(cmd, "") != 0) exit(0);
1806 : }
1807 : }
1808 0 : exit(0);
1809 : } /* do_help */
1810 :
1811 : /* for "test-compare" printf buffer (could use write) and (more so) "TRACE" */
1812 : #pragma GCC diagnostic ignored "-Wformat-security"
1813 :
1814 0 : int main(int argc, char *argv[])
1815 : {
1816 0 : int ret= 0;
1817 0 : const char *cmd;
1818 0 : static const struct option long_options[]= {
1819 : {"sys", no_argument, 0, 1000},
1820 : {"max-reverse-us", required_argument, 0, 1001},
1821 : {0, 0, 0, 0}};
1822 0 : extern char *optarg; /* for getopt */
1823 0 : extern int optind; /* for getopt */
1824 0 : int opt; /* for how I use getopt */
1825 0 : int opt_memreport_sys= 0;
1826 0 : int32_t opt_memreport_max_reverse_us= 10000;
1827 0 : int do_heading= 1;
1828 0 : int show_opts= 0;
1829 0 : int ii= 0;
1830 0 : unsigned uu= 0;
1831 0 : char test_name[0x100];
1832 0 : int opt_loops= -1, opt_timing_stats= 0, opt_help= 0;
1833 0 : float opt_dly_ms= 0;
1834 0 : unsigned opt_burst= 1;
1835 0 : int opt_quiet= 0;
1836 0 : int opt_all= 0;
1837 0 : int opt_count= -2, opt_start= -1; // -2 to indicate "not specified" (less likely to be specfied than -1)
1838 0 : int opt_line= 0;
1839 0 : const char *opt_Name= NULL; /* -N<wild> */
1840 0 : char *cp;
1841 0 : uint64_t t0_us= 0;
1842 0 : uint32_t tdelta_us;
1843 :
1844 0 : opterr= 0; /* turn of getopt (automatic) error output */
1845 0 : while ((opt= getopt_long(argc, argv, "?hab:c:d:Ff:HL:l:N:n:qs:tVx:", long_options, NULL)) != -1) {
1846 0 : switch (opt) {
1847 0 : case 1000: opt_memreport_sys= 1; break;
1848 0 : case 1001: {
1849 0 : char *endp= NULL;
1850 0 : unsigned long tmp= strtoul(optarg, &endp, 0);
1851 0 : if (optarg[0] == '\0' || endp == optarg || *endp != '\0' || tmp > INT32_MAX) {
1852 0 : fprintf(stderr, "invalid --max-reverse-us value: %s\n", optarg);
1853 0 : exit(1);
1854 : }
1855 0 : opt_memreport_max_reverse_us= (int32_t)tmp;
1856 0 : break;
1857 : }
1858 : /* '?' is also what you get w/ "invalid option -- -" */
1859 0 : case '?':
1860 : case 'h':
1861 0 : if (!strchr("?h", optopt)) {
1862 0 : printf("Invalid option: -%c\n", optopt);
1863 0 : exit(0);
1864 : }
1865 : opt_help= 1;
1866 : break;
1867 0 : case 'a': opt_all= 1; break;
1868 0 : case 'b': opt_burst= (unsigned)strtoul(optarg, NULL, 0); break;
1869 0 : case 'c': opt_count= (int)strtoul(optarg, NULL, 0); break;
1870 0 : case 'd': opt_dly_ms= strtof(optarg, NULL); break;
1871 0 : case 'F': show_opts|= forward_; break;
1872 0 : case 'f': setenv("TRACE_FILE", optarg, 1); break;
1873 0 : case 'H': do_heading= 0; break;
1874 0 : case 'L': opt_line= (int)strtoul(optarg, NULL, 0); break;
1875 0 : case 'l': opt_loops= (int)strtoul(optarg, NULL, 0); break;
1876 0 : case 'N': opt_Name= optarg; break;
1877 0 : case 'n':
1878 0 : setenv("TRACE_NAME", optarg, 1);
1879 0 : break; /*note: TRACE_CNTL "file" or "name" doesn't allow setting the other (order becomes dependent)*/
1880 0 : case 'q': opt_quiet= 1; break;
1881 0 : case 's': opt_start= (int)strtoul(optarg, NULL, 0); break;
1882 0 : case 't': opt_timing_stats= 1; break;
1883 0 : case 'V':
1884 0 : printf("%s\n", TRACE_CNTL_REV);
1885 0 : if (argc == 2) exit(0);
1886 : break;
1887 0 : case 'x': trace_thread_option= (int)strtoul(optarg, NULL, 0); break;
1888 : }
1889 : }
1890 0 : if (argc - optind < 1) {
1891 0 : printf("Need cmd\n");
1892 : #define DO_HELP 0
1893 : #if DO_HELP
1894 : do_help("");
1895 : #else
1896 0 : printf(USAGE);
1897 0 : exit(0);
1898 : #endif
1899 : }
1900 0 : cmd= argv[optind++];
1901 0 : if (opt_help) {
1902 : #if DO_HELP
1903 : do_help(cmd);
1904 : #else
1905 0 : printf(USAGE);
1906 0 : exit(0);
1907 : #endif
1908 : }
1909 :
1910 0 : if (opt_Name && !(strncmp(cmd, "lvl", 3) == 0)) { // a name (which may be wildcard) will get added to lvl command below
1911 0 : setenv("TRACE_NAME", opt_Name, 1);
1912 : //traceInit(opt_Name,0);
1913 : }
1914 :
1915 0 : if ((cp= getenv("LC_NUMERIC"))) // IFF LC_NUMERIC is set in the environment (i.e to "en_US" or "en_US.UTF-8")...
1916 0 : setlocale(LC_NUMERIC, cp); // this is needed for (e.g.) %'d to produce output with thousands grouped
1917 :
1918 0 : if (strcmp(cmd, "test1") == 0) {
1919 0 : int loops= 1, trace_cnt;
1920 0 : char msg_str[200];
1921 0 : char addr_str[100];
1922 0 : void *vp= addr_str;
1923 0 : setenv("TRACE_NAME", "TRACE", 0); // allow this to be overridden by -n and env.var. (and define TRACE_NAME)
1924 0 : if (opt_loops > -1) loops= opt_loops;
1925 0 : sprintf(addr_str, "%p", vp);
1926 0 : sprintf(msg_str, "Hi %%d. \"c 2.5 -5 5000000000 0x87654321 2.6 %p\" ", vp);
1927 0 : strcat(msg_str, "should be repeated here: %c %.1f %hd %lld %p %.1Lf %s");
1928 0 : tdelta_us= (uint32_t)(opt_dly_ms * 1000);
1929 0 : for (trace_cnt= 1; trace_cnt <= loops; ++trace_cnt) {
1930 0 : TRACE(TLVL_INFO, msg_str, trace_cnt, 'c', 2.5, (short)-5, (long long)5000000000LL, (void *)0x87654321, (long double)2.6,
1931 : addr_str);
1932 0 : if (tdelta_us) usleep(tdelta_us);
1933 : }
1934 0 : } else if (strcmp(cmd, "file") == 0) {
1935 0 : char file[PATH_MAX];
1936 0 : if (argc - optind < 1) {
1937 0 : printf("Need file\n");
1938 : #if DO_HELP
1939 : do_help("");
1940 : #else
1941 0 : printf(USAGE);
1942 0 : exit(0);
1943 : #endif
1944 : }
1945 0 : (void)tsnprintf(file, sizeof(file), argv[optind++]);
1946 0 : printf("%s\n", file);
1947 0 : } else if (strcmp(cmd, "test") == 0) {
1948 0 : float ff[10];
1949 0 : pid_t tid;
1950 0 : uint32_t desired, myIdx;
1951 0 : int32_t xx;
1952 :
1953 0 : tid= trace_gettid();
1954 0 : if (tid == -1) perror("trace_gettid");
1955 0 : printf("tid=%ld\n", (long int)tid);
1956 :
1957 0 : printf("sizeof: int:%u long:%u pid_t:%u pthread_t:%u timeval:%u "
1958 : "double:%u traceControl_s:%u traceEntryHdr_s:%u traceControl(for disabled):%u\n",
1959 : (unsigned)sizeof(unsigned), (unsigned)sizeof(long), (unsigned)sizeof(pid_t), (unsigned)sizeof(pthread_t),
1960 : (unsigned)sizeof(struct timeval), (unsigned)sizeof(double), (unsigned)sizeof(struct traceControl_s),
1961 : (unsigned)sizeof(struct traceEntryHdr_s), (unsigned)sizeof(traceControl));
1962 0 : printf("offset: trigIdxCount =%p\n"
1963 : " trigActivePost =%p\n"
1964 : " full =%p\n"
1965 : " lvl =%p\n"
1966 : " pid =%p\n"
1967 : " TID =%p\n"
1968 : " get_retries =%p\n"
1969 : " param_bytes =%p\n"
1970 : " tsc =%p\n",
1971 : (void *)&((struct traceControl_s *)0)->rw.trigIdxCnt, (void *)&((struct traceControl_s *)0)->rw.trigActivePost,
1972 : (void *)&((struct traceControl_s *)0)->rw.full, (void *)&((struct traceEntryHdr_s *)0)->lvl,
1973 : (void *)&((struct traceEntryHdr_s *)0)->pid, (void *)&((struct traceEntryHdr_s *)0)->TrcId,
1974 : (void *)&((struct traceEntryHdr_s *)0)->get_idxCnt_retries, (void *)&((struct traceEntryHdr_s *)0)->param_bytes,
1975 : (void *)&((struct traceEntryHdr_s *)0)->tsc);
1976 :
1977 0 : for (uu= 0; uu < sizeof(ff) / sizeof(ff[0]); ++uu) ff[uu]= (float)(2.5 * uu);
1978 :
1979 : /* NOTE: using setenv method works in threading env where as additional
1980 : threads initializing via TRACE will disable/undo the levels
1981 : set by the initialization via TRACE_CNTL("lvlsetS",0xffLL) */
1982 0 : setenv("TRACE_LVLS", "0xff", 0); /*does TRACE_CNTL("lvlsetS",0xffLL);TRACE_CNTL("modeS",1);*/
1983 : /* NOTE/Recall - _LVLS does not "activate" like TRACE_{FILE,NAME,MSGMAX,NUMENTS,NAMTBLENTS} */
1984 :
1985 : /* _at_least_ set bit 0 (lvl=0) in the "M" mask and turn on the "M" mode
1986 : bit -- this is what is on by default when the file is created */
1987 : /* Mem Slow Trig */
1988 0 : TRACE_CNTL("lvlset", 0xfLL, 0LL, 0LL);
1989 0 : TRACE_CNTL("modeM", 1);
1990 :
1991 0 : TRACE(TLVL_INFO, "hello");
1992 :
1993 0 : myIdx= traceControl_p->largest_multiple - 3;
1994 0 : printf("myIdx=0x%08x\n", myIdx);
1995 0 : for (ii= 0; ii < 6; ++ii) {
1996 0 : desired= TRACE_IDXCNT_ADD(myIdx, 1);
1997 0 : printf("myIdx==>TRACE_IDXCNT_ADD(myIdx,1): 0x%08x 0x%08x\n", myIdx, desired);
1998 0 : myIdx= desired;
1999 : }
2000 0 : for (ii= 0; ii < 6; ++ii) {
2001 0 : desired= TRACE_IDXCNT_ADD(myIdx, -1);
2002 0 : printf("myIdx==>TRACE_IDXCNT_ADD(myIdx,-1): 0x%08x 0x%08x\n", myIdx, desired);
2003 0 : myIdx= desired;
2004 : }
2005 0 : printf("myIdx=0x%08x\n", myIdx);
2006 :
2007 0 : xx= 5;
2008 0 : myIdx= traceControl_p->largest_multiple - (uint32_t)(3 * xx);
2009 0 : printf("myIdx=0x%08x\n", myIdx);
2010 0 : for (ii= 0; ii < 6; ++ii) {
2011 0 : desired= TRACE_IDXCNT_ADD(myIdx, xx);
2012 0 : printf("myIdx==>TRACE_IDXCNT_ADD(myIdx,5): 0x%08x 0x%08x\n", myIdx, desired);
2013 0 : myIdx= desired;
2014 : }
2015 0 : for (ii= 0; ii < 6; ++ii) {
2016 0 : desired= TRACE_IDXCNT_ADD(myIdx, -xx); /* NOTE: this will not work if xx is of type uint32_t; it must be of int32_t */
2017 0 : printf("myIdx==>TRACE_IDXCNT_ADD(myIdx,-5): 0x%08x 0x%08x\n", myIdx, desired);
2018 0 : myIdx= desired;
2019 : }
2020 0 : printf("myIdx=0x%08x\n", myIdx);
2021 :
2022 0 : printf("For forward read... simple delta\n");
2023 0 : printf("TRACE_IDXCNT_DELTA(wr=5,rd=4)=%u\n", TRACE_IDXCNT_DELTA(5, 4));
2024 0 : printf("TRACE_IDXCNT_DELTA(wr=5,rd=0)=%u\n", TRACE_IDXCNT_DELTA(5, 0));
2025 0 : printf("TRACE_IDXCNT_DELTA(wr=5,rd=TRACE_IDXCNT_ADD(0,-1)=0x%08x)=%u\n", TRACE_IDXCNT_ADD(0, -1),
2026 0 : TRACE_IDXCNT_DELTA(5, TRACE_IDXCNT_ADD(0, -1)));
2027 0 : printf("For reverse read... numents(or wrIdxCnt) - delta\n");
2028 0 : printf(
2029 : "(num_entries=%u) - TRACE_IDXCNT_DELTA(wr=TRACE_IDXCNT_ADD(num_entries,10),rd=TRACE_IDXCNT_ADD(num_entries,5)) = %u\n",
2030 : traceControl_p->num_entries,
2031 0 : traceControl_p->num_entries - TRACE_IDXCNT_DELTA(TRACE_IDXCNT_ADD(traceControl_p->num_entries, 10),
2032 : TRACE_IDXCNT_ADD(traceControl_p->num_entries, 5)));
2033 0 : printf("rdIdx_has_lines(TRACE_IDXCNT_ADD(num_entries,10),TRACE_IDXCNT_ADD(num_entries,5),-1) = %u\n",
2034 0 : rdIdx_has_lines(TRACE_IDXCNT_ADD(traceControl_p->num_entries, 10), TRACE_IDXCNT_ADD(traceControl_p->num_entries, 5),
2035 : -1));
2036 0 : printf("(num_entries=%u) - "
2037 : "TRACE_IDXCNT_DELTA(wr=TRACE_IDXCNT_ADD(num_entries,10+num_entries)=%u,rd=TRACE_IDXCNT_ADD(num_entries,5)) = %u\n",
2038 0 : traceControl_p->num_entries, TRACE_IDXCNT_ADD(traceControl_p->num_entries, (int)(10 + traceControl_p->num_entries)),
2039 0 : traceControl_p->num_entries -
2040 0 : TRACE_IDXCNT_DELTA(TRACE_IDXCNT_ADD(traceControl_p->num_entries, (int)(10 + traceControl_p->num_entries)),
2041 : TRACE_IDXCNT_ADD(traceControl_p->num_entries, 5)));
2042 0 : printf("rdIdx_has_lines(TRACE_IDXCNT_ADD(num_entries,10+num_entries),TRACE_IDXCNT_ADD(num_entries,5),-1) = %u\n",
2043 0 : rdIdx_has_lines(TRACE_IDXCNT_ADD(traceControl_p->num_entries, (int)(10 + traceControl_p->num_entries)),
2044 0 : TRACE_IDXCNT_ADD(traceControl_p->num_entries, 5), -1));
2045 :
2046 0 : printf("\n");
2047 0 : TRACE(TLVL_WARNING, "hello %d\n\tthere\n", 1);
2048 0 : TRACE(TLVL_INFO, "hello %d %d", 1, 2);
2049 0 : TRACE(TLVL_DEBUG, "hello %d %d %d - make sure debug+1 is enabled", 1, 2, 3);
2050 0 : TRACE(TLVL_DEBUG, "hello %d %d %d %d %d %d %d %d %d %d %d - should be 1 2 3 4 5 6 7 8 9 10 *d", 1, 2, 3, 4, 5, 6, 7, 8, 9,
2051 : 10, 11); /* extra param does not get saved in buffer */
2052 0 : TRACE(TLVL_DEBUG,
2053 : "hello %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f - should be 1.00 2.00 3.00 4.00 12.50 6.00 7.00 8.00 9.00 "
2054 : "10.00",
2055 : 1.0, 2.0, 3.0, 4.0, ff[5], 6.0, 7.0, 8.0, 9.0, 10.0);
2056 0 : TRACE(TLVL_DBG + 1,
2057 : "hello %d %d %.1f %d %d %.1f %d %d %.1f %.1f. Should be 1 2 3.3 4 5 6.6 7 8 9.0 10.0 there is tabs after "
2058 : "2nd float",
2059 : 1, 2, 3.3, 4, 5, 6.6, 7, 8, 9.0, 10.0);
2060 0 : TRACE(TLVL_DBG + 1,
2061 : TSPRINTF("%s:%%d- int=%%d __FILE__=%s", strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__, __FILE__),
2062 : __LINE__, 5);
2063 0 : TRACE(TLVL_DBG + 1,
2064 : TSPRINTF("%s%s%s", strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__, ":%d- int=%d __FILE__=", __FILE__),
2065 : __LINE__, 5);
2066 :
2067 : #ifndef TEST_UNUSED_FUNCTION
2068 0 : TRACE_CNTL("trig", -1, 5LL);
2069 : #endif
2070 0 : for (ii= 0; ii < 20; ++ii) {
2071 0 : if (ii % 5 == 0) {
2072 : /* this shows how TRACE (which does not have a "name" argument)
2073 : could "TRACE with different names." The other trace macros
2074 : (TRACEN,TRACEN_,TLOG) can't do this. (They can have a name per thread.)
2075 : This is just a technical detail -- I have yet to think of a
2076 : real use for this feature/functionality.
2077 : */
2078 0 : sprintf(test_name, "%s_%d", "TRACE", ii);
2079 0 : TRACE_CNTL("name", test_name); /* this is a realatively slow operation - changes the value of traceTID */
2080 : }
2081 0 : TRACE(TLVL_INFO, "ii=%u", ii);
2082 : }
2083 0 : } else if (strcmp(cmd, "test-ro") == 0) {
2084 : /* To test normal userspace, can use: TRACE_FILE= trace_cntl test-ro
2085 : */
2086 0 : setenv("TRACE_FILE", "/proc/trace/buffer", 0);
2087 0 : printf("TRACE_FILE=%s\n", getenv("TRACE_FILE"));
2088 0 : traceInit(NULL, 0);
2089 0 : printf("try write to (presumably kernel memory) write-protected 1st page...\n");
2090 0 : traceControl_p->trace_initialized= 2;
2091 0 : printf("write succeeded.\n");
2092 : #if defined(TEST_WRITE_PAST_END)
2093 : *(((uint8_t *)traceControl_p) + traceControl_p->memlen)= 6;
2094 : #endif
2095 0 : } else if (strcmp(cmd, "test-compare") == 0) {
2096 0 : char buffer[200];
2097 0 : uint64_t mark;
2098 0 : uint32_t delta;
2099 0 : unsigned loops= DFLT_TEST_COMPARE_ITERS;
2100 0 : unsigned test_mask= 0xff; /* all tests */
2101 0 : unsigned modes_msk= 0xf; /* all mode combinations */
2102 0 : int fd;
2103 0 : int jj;
2104 :
2105 0 : if (opt_loops > -1) loops= (unsigned)opt_loops;
2106 0 : opt_loops= (int)loops; /* save */
2107 :
2108 0 : if (argc - optind >= 1) test_mask= (unsigned)strtoul(argv[optind], NULL, 0);
2109 0 : if (argc - optind >= 2) modes_msk= (unsigned)strtoul(argv[optind + 1], NULL, 0);
2110 0 : if (argc - optind == 3) {
2111 0 : fd= open(argv[optind + 2], O_WRONLY | O_CREAT | O_TRUNC, 0666);
2112 0 : if (fd == -1) {
2113 0 : perror("outputfile");
2114 0 : exit(1);
2115 : }
2116 : } else
2117 0 : fd= open("/dev/null", O_WRONLY);
2118 0 : dup2(fd, 1); /* redirect stdout to /dev/null */
2119 0 : setlocale(LC_NUMERIC, "en_US"); /* make ' printf flag work -- setting LC_NUMERIC in env does not seem to work */
2120 :
2121 0 : TRACE_CNTL("mode", 3);
2122 0 : traceControl_rwp->mode.bits.M= 1; // NOTE: TRACE_CNTL("modeM",1) hardwired to NOT enable when not mapped!
2123 :
2124 : #define STRT_PRN(s1, sA, sB) \
2125 : sprintf(buffer, s1, sA, sB); \
2126 : fprintf(stderr, "%-46s", buffer); \
2127 : fflush(stderr)
2128 : // ELF 6/6/18: GCC v6_3_0 does not like %', removing the '...
2129 : #define END_FMT "%10u us, %5.3f us/TRACE, %7.3f Mtraces/s\n", delta, (double)delta / loops, (double)loops / delta
2130 : #define CONTINUE \
2131 : fprintf(stderr, "Continuing.\n"); \
2132 : continue
2133 0 : for (jj= 0; jj < 4; ++jj) {
2134 0 : unsigned tstmod= (1U << jj) & modes_msk;
2135 0 : switch (tstmod) {
2136 0 : case 1: {
2137 0 : TRACE_CNTL("lvlclrM", 1LL << TLVL_INFO);
2138 0 : TRACE_CNTL("lvlclrS", 1LL << TLVL_INFO);
2139 0 : loops= (unsigned)opt_loops * 10;
2140 0 : fprintf(stderr, "0x1 M0S0 - Testing with M and S lvl disabled. loops=%u\n", loops);
2141 0 : } break;
2142 0 : case 2: {
2143 0 : TRACE_CNTL("lvlsetM", 1LL << TLVL_INFO);
2144 0 : TRACE_CNTL("lvlclrS", 1LL << TLVL_INFO);
2145 0 : loops= (unsigned)opt_loops * 5;
2146 0 : fprintf(stderr, "0x2 M1S0 - First testing with S lvl disabled (mem only). loops=%u\n", loops);
2147 0 : break;
2148 : } break;
2149 0 : case 4: {
2150 0 : TRACE_CNTL("lvlsetM", 1LL << TLVL_INFO);
2151 0 : TRACE_CNTL("lvlsetS", 1LL << TLVL_INFO);
2152 0 : loops= (unsigned)opt_loops;
2153 0 : fprintf(stderr, "0x4 M1S1 - Testing with M and S lvl enabled (stdout>/dev/null). loops=%u\n", loops);
2154 0 : break;
2155 : } break;
2156 0 : case 8: {
2157 0 : TRACE_CNTL("lvlclrM", 1LL << TLVL_INFO);
2158 0 : TRACE_CNTL("lvlsetS", 1LL << TLVL_INFO);
2159 0 : loops= (unsigned)opt_loops;
2160 0 : fprintf(stderr, "0x8 M0S1 - Testing with just S lvl enabled. Unusual (freeze). loops=%u\n", loops);
2161 0 : break;
2162 : } break;
2163 0 : case 0: continue;
2164 : }
2165 :
2166 0 : if (1 & test_mask) {
2167 0 : STRT_PRN(" 0x01 -%s const short msg %s", "", (tstmod & 0xc) ? "(NO snprintf)" : "");
2168 : //fprintf(stderr,STRT_FMT," 0x01 - const short msg (NO snprintf)");fflush(stderr);
2169 0 : if (tstmod & 6) { TRACE_CNTL("reset"); }
2170 0 : mark= gettimeofday_us();
2171 0 : for (uu= 0; uu < loops; ++uu) { TRACE(TLVL_INFO, "any msg"); }
2172 0 : delta= (uint32_t)(gettimeofday_us() - mark);
2173 0 : fprintf(stderr, END_FMT);
2174 : }
2175 :
2176 0 : if (2 & test_mask) {
2177 0 : STRT_PRN(" 0x02 - 1 arg%s%s", "", "");
2178 0 : if (tstmod & 6) { TRACE_CNTL("reset"); }
2179 0 : mark= gettimeofday_us();
2180 0 : for (uu= 0; uu < loops; ++uu) { TRACE(TLVL_INFO, "this is one small param: %u", 12345678); }
2181 0 : delta= (uint32_t)(gettimeofday_us() - mark);
2182 0 : fprintf(stderr, END_FMT);
2183 : }
2184 :
2185 0 : if (4 & test_mask) {
2186 0 : STRT_PRN(" 0x04 - 2 args%s%s", "", "");
2187 0 : if (tstmod & 6) { TRACE_CNTL("reset"); }
2188 0 : mark= gettimeofday_us();
2189 0 : for (uu= 0; uu < loops; ++uu) { TRACE(TLVL_INFO, "this is 2 params: %u %u", 12345678, uu); }
2190 0 : delta= (uint32_t)(gettimeofday_us() - mark);
2191 0 : fprintf(stderr, END_FMT);
2192 : }
2193 :
2194 0 : if (8 & test_mask) {
2195 0 : STRT_PRN(" 0x08 - 8 args (7 ints, 1 float)%s%s", "", "");
2196 0 : if (tstmod & 6) { TRACE_CNTL("reset"); }
2197 0 : mark= gettimeofday_us();
2198 0 : for (uu= 0; uu < loops; ++uu) {
2199 0 : TRACE(TLVL_INFO, "this is 8 params: %u %u %u %u %u %u %u %g", 12345678, uu, uu * 2, uu + 6, 12345679, uu,
2200 : uu - 7, (float)uu * 1.5);
2201 : }
2202 0 : delta= (uint32_t)(gettimeofday_us() - mark);
2203 0 : fprintf(stderr, END_FMT);
2204 : }
2205 :
2206 0 : if (0x10 & test_mask) {
2207 0 : STRT_PRN(" 0x10 - 8 args (1 ints, 7 float)%s%s", "", "");
2208 0 : if (tstmod & 6) { TRACE_CNTL("reset"); }
2209 0 : mark= gettimeofday_us();
2210 0 : for (uu= 0; uu < loops; ++uu) {
2211 0 : TRACE(TLVL_INFO, "this is 8 params: %u %g %g %g %g %g %g %g", 12345678, (float)uu, (float)uu * 2.5,
2212 : (float)uu + 3.14, (float)12345679, (float)uu / .25, (float)uu - 2 * 3.14, (float)uu * 1.5);
2213 : }
2214 0 : delta= (uint32_t)(gettimeofday_us() - mark);
2215 0 : fprintf(stderr, END_FMT);
2216 : }
2217 :
2218 0 : if (0x20 & test_mask) {
2219 0 : STRT_PRN(" 0x20 - snprintf of same 8 args%s%s", "", "");
2220 0 : if (tstmod & 6) { TRACE_CNTL("reset"); }
2221 0 : mark= gettimeofday_us();
2222 0 : for (uu= 0; uu < loops; ++uu) {
2223 0 : snprintf(buffer, sizeof(buffer), "this is 8 params: %u %g %g %g %g %g %g %g", 12345678, (float)uu,
2224 : (float)uu * 2.5, (float)uu + 3.14, (float)12345679, (float)uu / .25, (float)uu - 2 * 3.14,
2225 0 : (float)uu * 1.5);
2226 0 : TRACE(TLVL_INFO, buffer);
2227 : }
2228 0 : delta= (uint32_t)(gettimeofday_us() - mark);
2229 0 : fprintf(stderr, END_FMT);
2230 : }
2231 :
2232 0 : if (0x40 & test_mask) {
2233 0 : STRT_PRN(" 0x40 -%s const short msg %s", (1 & test_mask) ? " (repeat)" : "", (tstmod & 0xc) ? "(NO snprintf)" : "");
2234 0 : if (tstmod & 6) { TRACE_CNTL("reset"); }
2235 0 : mark= gettimeofday_us();
2236 0 : for (uu= 0; uu < loops; ++uu) { TRACE(TLVL_INFO, "any msg"); }
2237 0 : delta= (uint32_t)(gettimeofday_us() - mark);
2238 0 : fprintf(stderr, END_FMT);
2239 : }
2240 :
2241 0 : if (0x80 & test_mask) {
2242 0 : STRT_PRN(" 0x80 - 2 args%s%s traceTID=-1", "", "");
2243 0 : if (tstmod & 6) { TRACE_CNTL("reset"); }
2244 0 : mark= gettimeofday_us();
2245 0 : for (uu= 0; uu < loops; ++uu) {
2246 0 : TRACE(TLVL_INFO, "this is 2 params: %u %u", 12345678, uu);
2247 0 : traceTID= -1;
2248 : }
2249 0 : delta= (uint32_t)(gettimeofday_us() - mark);
2250 0 : fprintf(stderr, END_FMT);
2251 : }
2252 : #ifdef DO_THREADS /* requires linking with -lpthreads */
2253 0 : if (0x100 & test_mask) {
2254 0 : pthread_t thread;
2255 0 : args_t argsp[1];
2256 0 : argsp[0].loops= 1;
2257 0 : argsp[0].dly_ms= 0;
2258 0 : argsp[0].burst= 0;
2259 0 : loops /= 100; /* OK to do if last test */
2260 0 : STRT_PRN(" 0x100 - %swith loops=%u thread", "", loops);
2261 0 : if (tstmod & 6) { TRACE_CNTL("reset"); }
2262 0 : mark= gettimeofday_us();
2263 0 : for (uu= 0; uu < loops; ++uu) {
2264 0 : argsp[0].tidx= uu;
2265 0 : pthread_create(&thread, NULL, thread_func, (void *)&argsp[0]);
2266 0 : pthread_join(thread, NULL);
2267 : }
2268 0 : delta= (uint32_t)(gettimeofday_us() - mark);
2269 0 : fprintf(stderr, END_FMT);
2270 : }
2271 : #endif
2272 : }
2273 : }
2274 : #ifdef DO_THREADS /* requires linking with -lpthreads */
2275 0 : else if (strcmp(cmd, "test-threads") == 0) {
2276 0 : pthread_t *threads;
2277 0 : unsigned loops= 2, num_threads= NUMTHREADS;
2278 0 : args_t *argsp, args0;
2279 0 : int sts= 0;
2280 0 : pid_t main_tid= trace_gettid();
2281 0 : unsigned total_traces= 0;
2282 0 : if (opt_loops > -1) loops= (unsigned)opt_loops;
2283 0 : if (opt_burst == 0) {
2284 0 : printf("adjusting burst==0 to min burst=1\n");
2285 0 : opt_burst= 1;
2286 : }
2287 0 : if ((argc - optind) == 1) {
2288 0 : num_threads= (unsigned)strtoul(argv[optind], NULL, 0);
2289 0 : if (num_threads > 4095) num_threads= 4095;
2290 : }
2291 0 : threads= (pthread_t *)malloc(num_threads * sizeof(pthread_t));
2292 0 : argsp= (args_t *)malloc(num_threads * sizeof(args_t));
2293 :
2294 0 : TRACE(TLVL_INFO, "before pthread_create - main_tid=%u loops=%d, threads=%u, dly_ms=%.3f traceControl_p=%p", main_tid, loops,
2295 : num_threads, opt_dly_ms, (void *)traceControl_p);
2296 0 : if (opt_timing_stats) {
2297 0 : TRACE_CNTL("lvlsetMg", 0xfLL << TLVL_INFO); /* set INFO,DBG,DBG+1, and DBG+2 */
2298 0 : t0_us= gettimeofday_us();
2299 : }
2300 0 : for (uu= 0; uu < num_threads; uu++) {
2301 0 : argsp[uu].tidx= uu + 1;
2302 0 : argsp[uu].loops= (unsigned)loops;
2303 0 : argsp[uu].dly_ms= opt_dly_ms;
2304 0 : argsp[uu].burst= opt_burst;
2305 0 : pthread_create(&(threads[uu]), NULL, thread_func, (void *)&argsp[uu]);
2306 : }
2307 :
2308 0 : args0.tidx= 0;
2309 0 : args0.loops= (unsigned)loops;
2310 0 : args0.dly_ms= opt_dly_ms;
2311 0 : args0.burst= opt_burst;
2312 0 : thread_func(&args0);
2313 :
2314 0 : if (trace_thread_option & 4) {
2315 0 : char cmd[200];
2316 0 : sprintf(cmd, "echo trace_buffer mappings before join '(#1)' = `cat /proc/%d/maps | grep trace_buffer | wc -l`",
2317 : getpid());
2318 0 : sts= system(cmd);
2319 0 : sprintf(cmd, "echo trace_buffer mappings before join '(#2)' = `cat /proc/%d/maps | grep trace_buffer | wc -l`",
2320 : getpid());
2321 0 : sts= system(cmd);
2322 : }
2323 0 : for (uu= 0; uu < num_threads; uu++) { pthread_join(threads[uu], NULL); }
2324 0 : if (opt_timing_stats) {
2325 0 : tdelta_us= (uint32_t)(gettimeofday_us() - t0_us);
2326 0 : total_traces= loops * (num_threads + 1) * 5;
2327 0 : fprintf(stderr, "%lld usec, %.3f usec/TRACE, %.3f Mtraces/s\n", (unsigned long long)tdelta_us,
2328 0 : (double)tdelta_us / total_traces, (double)total_traces / tdelta_us);
2329 : }
2330 0 : if (trace_thread_option & 4) {
2331 0 : char cmd[200];
2332 0 : sprintf(cmd, "echo trace_buffer mappings after join '(#1)' = `cat /proc/%d/maps | grep trace_buffer | wc -l`",
2333 : getpid());
2334 0 : sts= system(cmd);
2335 0 : sprintf(cmd, "echo trace_buffer mappings after join '(#2)' = `cat /proc/%d/maps | grep trace_buffer | wc -l`",
2336 : getpid());
2337 0 : sts= system(cmd);
2338 : }
2339 0 : TRACE(TLVL_INFO, "after pthread_join - main_tid=%u traceControl_p=%p sts=%d", main_tid, (void *)traceControl_p, sts);
2340 0 : free(threads);
2341 : }
2342 : #endif
2343 0 : else if (strcmp(cmd, "show") == 0) {
2344 0 : const char *ospec= getenv("TRACE_SHOW");
2345 0 : if (!ospec) ospec= DFLT_SHOW;
2346 0 : if ((do_heading == 0) && (strncmp("%H", ospec, 2) == 0)) ospec+= 2; /* skip "%H" */
2347 0 : if (show_opts & forward_ && opt_count == -2) opt_count= 10; /* like head and tail default */
2348 0 : if (opt_quiet) show_opts|= quiet_;
2349 0 : traceShow(ospec, opt_count, opt_start, show_opts, argc - optind, &argv[optind]);
2350 0 : } else if (strncmp(cmd, "info", 4) == 0) {
2351 0 : traceInit("_TRACE_", 1); /* Use "_TRACE_" incase corrupted TRACE_FILE where "TRACE" may not exist and write undesired */
2352 0 : traceInfo(opt_quiet);
2353 0 : } else if (strcmp(cmd, "tids") == 0) {
2354 0 : uint32_t longest_name;
2355 0 : int namLvlTblEnts_digits;
2356 0 : traceInit("_TRACE_", 1);
2357 0 : longest_name= traceControl_rwp->longest_name;
2358 0 : if (longest_name > traceControl_p->nam_arr_sz) longest_name= traceControl_p->nam_arr_sz;
2359 : /*printf("longest_name=%d\n",longest_name);*/
2360 0 : namLvlTblEnts_digits= countDigits((int)traceControl_p->num_namLvlTblEnts - 1);
2361 0 : if (do_heading) {
2362 0 : printf("mode:%*s%*s M=%d S=%d\n", minw(3, namLvlTblEnts_digits), "", longest_name, "",
2363 0 : traceControl_rwp->mode.bits.M, traceControl_rwp->mode.bits.S);
2364 0 : printf("%*s %*s %*s %*s %*s\n", minw(3, namLvlTblEnts_digits), "TID", longest_name, "NAME", 18, "maskM", 18, "maskS",
2365 : 18, "maskT");
2366 0 : printf("%.*s %.*s %.*s %.*s %.*s\n", minw(3, namLvlTblEnts_digits), TRACE_LONG_DASHES, longest_name, TRACE_LONG_DASHES,
2367 : 18, TRACE_LONG_DASHES, 18, TRACE_LONG_DASHES, 18, TRACE_LONG_DASHES);
2368 : }
2369 0 : for (uu= 0; uu < traceControl_p->num_namLvlTblEnts; ++uu) {
2370 0 : if (opt_all || TRACE_TID2NAME((int32_t)uu)[0] != '\0') {
2371 0 : printf("%*d %*.*s 0x%016llx 0x%016llx 0x%016llx\n", minw(3, namLvlTblEnts_digits), uu, longest_name, longest_name,
2372 0 : TRACE_TID2NAME((int32_t)uu), (unsigned long long)traceLvls_p[uu].M, (unsigned long long)traceLvls_p[uu].S,
2373 0 : (unsigned long long)traceLvls_p[uu].T);
2374 : }
2375 : }
2376 0 : } else if (strcmp(cmd, "memreport") == 0) {
2377 0 : int use_sys_levels= opt_memreport_sys;
2378 0 : int32_t max_reverse_us= opt_memreport_max_reverse_us;
2379 : /* Re-scan trailing args for memreport-specific options and reject unexpected leftovers. */
2380 0 : for (ii= optind; ii < argc; ++ii) {
2381 0 : if (strcmp(argv[ii], "--sys") == 0) use_sys_levels= 1;
2382 0 : else if (strncmp(argv[ii], "--max-reverse-us=", 17) == 0) {
2383 0 : char *endp= NULL;
2384 0 : unsigned long tmp= strtoul(argv[ii] + 17, &endp, 0);
2385 0 : if (argv[ii][17] == '\0' || endp == (argv[ii] + 17) || *endp != '\0' || tmp > INT32_MAX) {
2386 0 : fprintf(stderr, "memreport: invalid --max-reverse-us value: %s\n", argv[ii] + 17);
2387 0 : ret= 1;
2388 0 : goto done;
2389 : }
2390 0 : max_reverse_us= (int32_t)tmp;
2391 0 : } else if (strcmp(argv[ii], "--max-reverse-us") == 0) {
2392 0 : char *endp= NULL;
2393 0 : unsigned long tmp;
2394 0 : if ((ii + 1) >= argc) {
2395 0 : fprintf(stderr, "memreport: missing argument for --max-reverse-us\n");
2396 0 : ret= 1;
2397 0 : goto done;
2398 : }
2399 0 : tmp= strtoul(argv[ii + 1], &endp, 0);
2400 0 : if (argv[ii + 1][0] == '\0' || endp == argv[ii + 1] || *endp != '\0' || tmp > INT32_MAX) {
2401 0 : fprintf(stderr, "memreport: invalid --max-reverse-us value: %s\n", argv[ii + 1]);
2402 0 : ret= 1;
2403 0 : goto done;
2404 : }
2405 0 : max_reverse_us= (int32_t)tmp;
2406 0 : ++ii;
2407 : }
2408 : else {
2409 0 : fprintf(stderr, "memreport: unknown option: %s\n", argv[ii]);
2410 0 : ret= 1;
2411 0 : goto done;
2412 : }
2413 : }
2414 0 : traceInit("_TRACE_", 1);
2415 0 : traceMemReport(do_heading, use_sys_levels, max_reverse_us);
2416 0 : } else if (strcmp(cmd, "unlock") == 0) {
2417 0 : traceInit(NULL, 0);
2418 0 : trace_unlock(&traceControl_rwp->namelock);
2419 0 : } else if (strcmp(cmd, "lvlstrs") == 0) {
2420 0 : TRACE_CNTL("init"); /* for potential TRACE_LVLSTRS */
2421 0 : for (ii= 0; ii < 64; ++ii) {
2422 0 : int aa;
2423 0 : printf("%2d ", ii);
2424 0 : for (aa= 0; aa < trace_lvlstrs_aliases; ++aa) printf("%*s ", trace_lvlwidth, trace_lvlstrs[aa][ii]);
2425 0 : printf("\n");
2426 : }
2427 0 : } else if (strncmp(cmd, "TRACE", 5) == 0) {
2428 : #define ARGS_SZ 35 * sizeof(uint64_t) / sizeof(unsigned long) /* works for 32 and 64 bit archs */
2429 0 : unsigned long args[ARGS_SZ] __attribute__((aligned(16))); // needed for delayed long double formatting
2430 0 : unsigned long *args_ptr= args;
2431 0 : unsigned long *end_ptr= &args[ARGS_SZ];
2432 0 : unsigned long *nxt_ptr;
2433 0 : uint8_t nargs= 0, lvl;
2434 0 : int line;
2435 0 : int cc, required= (cmd[5] == 'N') ? 3 : 2;
2436 0 : char *eptr, *lvlptr= (cmd[5] == 'N') ? argv[optind + 1] : argv[optind];
2437 0 : if (opt_loops == -1) opt_loops= 1;
2438 0 : if (argc - optind < required) {
2439 0 : printf("\"TRACE\" cmd requires at least lvl and fmt arguments.");
2440 0 : printf("\"TRACE\" cmd requires at least name, lvl and fmt arguments.");
2441 0 : printf(USAGE);
2442 0 : exit(0);
2443 : }
2444 0 : for (cc= optind + required; cc < argc; ++cc) {
2445 : /* do a quick check for certain character that would only be in a double - see man strtod */
2446 0 : if (strpbrk(argv[cc], ".INnPp")) nxt_ptr= add_double_arg(args_ptr, end_ptr, strtod(argv[cc], NULL));
2447 : else {
2448 : /* then actually see if a full unsigned long parse occurs */
2449 0 : unsigned long uu= strtoul(argv[cc], &eptr, 0);
2450 0 : if (eptr == (argv[cc] + strlen(argv[cc]))) nxt_ptr= add_ul_arg(args_ptr, end_ptr, uu);
2451 : else
2452 0 : nxt_ptr= add_double_arg(args_ptr, end_ptr, strtod(argv[cc], NULL));
2453 : }
2454 0 : if (nxt_ptr == args_ptr) break;
2455 0 : args_ptr= nxt_ptr;
2456 0 : ++nargs;
2457 : }
2458 :
2459 0 : TRACE_CNTL("init"); /* for potential TRACE_LVLSTRS */
2460 0 : tdelta_us= opt_dly_ms * 1000;
2461 0 : lvl= (uint8_t)strtoul(lvlptr, &eptr, 0); /* traceInit should be called before this to pickup any trace_lvlstrs changes */
2462 0 : if (lvlptr == eptr) lvl= (uint8_t)str2enum(lvlptr);
2463 0 : if (opt_timing_stats) t0_us= gettimeofday_us();
2464 0 : line= opt_line;
2465 : #ifdef __TRACE_LINE__
2466 : # undef __TRACE_LINE__
2467 : #endif
2468 : #define __TRACE_LINE__ line
2469 0 : if (cmd[5] == 'N')
2470 0 : for (ii= 0; ii < opt_loops; ++ii) {
2471 0 : if (tdelta_us && ii) usleep(tdelta_us);
2472 0 : if (!line) { line= __LINE__; }
2473 0 : VTRACEN(argv[optind], lvl, nargs, argv[optind + 2], args);
2474 : }
2475 : else
2476 0 : for (ii= 0; ii < opt_loops; ++ii) {
2477 0 : if (tdelta_us && ii) usleep(tdelta_us);
2478 0 : if (!line) { line= __LINE__; }
2479 0 : VTRACE(lvl, nargs, argv[optind + 1], args);
2480 : }
2481 : #undef __TRACE_LINE__
2482 : #define __TRACE_LINE__ __LINE__
2483 0 : if (opt_timing_stats) {
2484 0 : tdelta_us= (uint32_t)(gettimeofday_us() - t0_us);
2485 0 : fprintf(stderr, "%lld usec, %.3f usec/TRACE, %.3f Mtraces/s\n", (unsigned long long)tdelta_us,
2486 0 : (double)tdelta_us / opt_loops, (double)opt_loops / tdelta_us);
2487 : }
2488 0 : } else if (strncmp(cmd, "sleep", 4) == 0) { /* this allows time to look at /proc/fd/ and /proc/maps */
2489 0 : TRACE(TLVL_INFO, "starting sleep");
2490 0 : if ((argc - optind) == 1) sleep((unsigned)strtoul(argv[optind], NULL, 0));
2491 : else
2492 0 : sleep(10);
2493 0 : TRACE(TLVL_INFO, "done sleeping");
2494 : }
2495 : /* now deal with traceCntl commands using TRACE_CNTL */
2496 0 : else if (strncmp(cmd, "mode", 4) == 0) { /* mode is special -- may _return_ old mode */
2497 0 : if ((argc - optind) == 0) {
2498 0 : ret= (int)TRACE_CNTL(cmd);
2499 0 : printf("%d\n", ret); /* print the old mode */
2500 : } else
2501 0 : ret= (int)TRACE_CNTL(cmd, strtoul(argv[optind], NULL, 0));
2502 0 : if (ret == -1) ret= 1;
2503 : else
2504 0 : ret= 0;
2505 0 : } else if ((strncmp(cmd, "lvlmsk", 6) == 0) || (strncmp(cmd, "lvlset", 6) == 0) || (strncmp(cmd, "lvlclr", 6) == 0)) {
2506 : /* lvl msk,set,clr are special primarily in that the -n<name> will no
2507 : longer create a non-existent name b/c it could be a wildcard spec.
2508 : Way to create a non-existent name:
2509 : trace_cntl mode -n<name>
2510 : TRACE_NAME=<name> trace_cntl lvlsetM <msk>
2511 : others? */
2512 0 : size_t slen= strlen(&cmd[6]);
2513 0 : long long msk= 0;
2514 0 : if (opt_Name) { /* this is the Name given via the -N which may be a WILDCARDed name */
2515 0 : char cmdn[8];
2516 0 : if (slen > 1 || (slen == 1 && !strpbrk(&cmd[6], "MST"))) {
2517 0 : printf("invalid command: %s\n", cmd);
2518 0 : printf(USAGE);
2519 : }
2520 0 : strncpy(cmdn, cmd, 6);
2521 0 : cmdn[6]= 'n';
2522 0 : strcpy(&cmdn[7], &cmd[6]); /* insert the 'n' */
2523 0 : switch (argc - optind) {
2524 0 : case 0:
2525 0 : msk= TRACE_CNTL(cmdn, opt_Name);
2526 0 : printf("mask=0x%llx\n", msk);
2527 0 : break;
2528 0 : case 1:
2529 0 : msk= TRACE_CNTL(cmdn, opt_Name, strtoull(argv[optind], NULL, 0));
2530 : /*printf("previous mask=0x%x\n",sts);*/
2531 0 : break;
2532 0 : case 3:
2533 0 : TRACE_CNTL(cmdn, opt_Name, strtoull(argv[optind], NULL, 0), strtoull(argv[optind + 1], NULL, 0),
2534 : strtoull(argv[optind + 2], NULL, 0));
2535 0 : break;
2536 0 : default:
2537 0 : printf("invalid command: %s\n", cmd);
2538 0 : printf(USAGE);
2539 0 : break;
2540 : }
2541 : } else {
2542 0 : if (slen > 2 || (slen >= 1 && !strpbrk(&cmd[6], "MSTgG"))) {
2543 0 : printf("invalid command: %s\n", cmd);
2544 0 : printf(USAGE);
2545 : }
2546 0 : switch (argc - optind) {
2547 0 : case 0:
2548 0 : msk= TRACE_CNTL(cmd);
2549 0 : printf("mask=0x%llx\n", msk);
2550 0 : break;
2551 0 : case 1: msk= TRACE_CNTL(cmd, strtoull(argv[optind], NULL, 0)); break;
2552 0 : case 3:
2553 0 : TRACE_CNTL(cmd, strtoull(argv[optind], NULL, 0), strtoull(argv[optind + 1], NULL, 0),
2554 : strtoull(argv[optind + 2], NULL, 0));
2555 0 : break;
2556 0 : default:
2557 0 : printf("invalid command: %s\n", cmd);
2558 0 : printf(USAGE);
2559 0 : break;
2560 : }
2561 : }
2562 : } else { /* all other traceCntl commands */
2563 0 : long long sts= 0;
2564 : /*printf("argc - optind = %d\n", argc - optind );*/
2565 0 : switch (argc - optind) {
2566 0 : case 0: sts= TRACE_CNTL(cmd); break;
2567 0 : case 1: sts= TRACE_CNTL(cmd, strtoull(argv[optind], NULL, 0)); break;
2568 0 : case 2: sts= TRACE_CNTL(cmd, strtoull(argv[optind], NULL, 0), strtoull(argv[optind + 1], NULL, 0)); break;
2569 0 : case 3:
2570 0 : sts= TRACE_CNTL(cmd, strtoull(argv[optind], NULL, 0), strtoull(argv[optind + 1], NULL, 0),
2571 : strtoull(argv[optind + 2], NULL, 0));
2572 0 : break;
2573 : }
2574 0 : if (sts < 0) {
2575 0 : printf("invalid command: %s\n", cmd);
2576 0 : printf(USAGE);
2577 0 : ret= 1;
2578 0 : } else if (sts == 0) /* WHY?? */
2579 : ret= 1;
2580 : else
2581 0 : ret= 0;
2582 : }
2583 0 : done:
2584 0 : return (ret);
2585 : } /* main */
|