Line data Source code
1 : #ifndef FD_LOG_STYLE
2 : #if FD_HAS_HOSTED
3 : #define FD_LOG_STYLE 0
4 : #else
5 : #error "Define FD_LOG_STYLE for this platform"
6 : #endif
7 : #endif
8 :
9 : #if FD_LOG_STYLE==0 /* POSIX style */
10 :
11 : #if !defined(FD_HAS_BACKTRACE)
12 : #if __has_include( <execinfo.h> ) && !FD_HAS_ASAN && !FD_HAS_MSAN && FD_HAS_HOSTED
13 : #define FD_HAS_BACKTRACE 1
14 : #else
15 : #define FD_HAS_BACKTRACE 0
16 : #endif
17 : #endif
18 :
19 : /* FIXME: SANITIZE VARIOUS USER SET STRINGS */
20 :
21 : #define _GNU_SOURCE
22 :
23 : #include "fd_log.h"
24 : #include "fd_backtrace.h"
25 :
26 : #include <stdio.h>
27 : #include <stdlib.h>
28 : #include <stdarg.h>
29 : #include <ctype.h>
30 : #include <errno.h>
31 : #include <fcntl.h>
32 : #include <unistd.h>
33 : #include <signal.h>
34 : #include <sched.h>
35 : #include <time.h>
36 : #if defined(__linux__)
37 : #include <syscall.h>
38 : #endif
39 : #include <sys/mman.h>
40 :
41 : #if FD_HAS_BACKTRACE
42 : #include <execinfo.h>
43 : #endif
44 :
45 : #if defined(__FreeBSD__)
46 : #include <sys/stat.h> /* S_IRUSR */
47 : #endif /* defined(__FreeBSD__) */
48 :
49 : #include "../tile/fd_tile_private.h"
50 : #include "../fd_version.h"
51 :
52 : #ifdef FD_BUILD_INFO
53 : FD_IMPORT_CSTR( fd_log_build_info, FD_BUILD_INFO );
54 : #else
55 : char const fd_log_build_info[1] __attribute__((aligned(1))) = { '\0' };
56 : ulong const fd_log_build_info_sz = 1UL;
57 : #endif
58 :
59 : /* TEXT_* are quick-and-dirty color terminal hacks. Probably should
60 : do something more robust longer term. */
61 :
62 79584 : #define TEXT_NORMAL "\033[0m"
63 0 : #define TEXT_BOLD "\033[1m"
64 0 : #define TEXT_DIM "\033[2m"
65 : #define TEXT_UNDERLINE "\033[4m"
66 : #define TEXT_BLINK "\033[5m"
67 :
68 9948 : #define TEXT_BLUE "\033[34m"
69 9948 : #define TEXT_GREEN "\033[32m"
70 9948 : #define TEXT_YELLOW "\033[93m"
71 39792 : #define TEXT_RED "\033[31m"
72 :
73 : /* APPLICATION LOGICAL ID APIS ****************************************/
74 :
75 : /* App id */
76 :
77 : static ulong fd_log_private_app_id; /* 0 outside boot/halt, init on boot */
78 :
79 2701 : void fd_log_private_app_id_set( ulong app_id ) { fd_log_private_app_id = app_id; }
80 :
81 5468 : ulong fd_log_app_id( void ) { return fd_log_private_app_id; }
82 :
83 : /* App */
84 :
85 : static char fd_log_private_app[ FD_LOG_NAME_MAX ]; /* "" outside boot/halt, init on boot */
86 :
87 : void
88 2701 : fd_log_private_app_set( char const * app ) {
89 2701 : if( FD_UNLIKELY( !app ) ) app = "[app]";
90 2701 : if( FD_LIKELY( app!=fd_log_private_app ) )
91 2701 : fd_cstr_fini( fd_cstr_append_cstr_safe( fd_cstr_init( fd_log_private_app ), app, FD_LOG_NAME_MAX-1UL ) );
92 2701 : }
93 :
94 118342 : char const * fd_log_app( void ) { return fd_log_private_app; }
95 :
96 : /* Thread ID */
97 :
98 : #if FD_HAS_THREADS
99 : static ulong fd_log_private_thread_id_ctr; /* 0 outside boot/halt, init on boot */
100 :
101 : static ulong
102 2740 : fd_log_private_thread_id_next( void ) {
103 2740 : return FD_ATOMIC_FETCH_AND_ADD( &fd_log_private_thread_id_ctr, 1UL );
104 2740 : }
105 : #endif
106 :
107 : static FD_TL ulong fd_log_private_thread_id; /* 0 at thread start */
108 : static FD_TL int fd_log_private_thread_id_init; /* 0 at thread start */
109 :
110 : void
111 2740 : fd_log_private_thread_id_set( ulong thread_id ) {
112 2740 : fd_log_private_thread_id = thread_id;
113 2740 : fd_log_private_thread_id_init = 1;
114 2740 : }
115 :
116 : ulong
117 8235 : fd_log_thread_id( void ) {
118 8235 : # if FD_HAS_THREADS
119 8235 : if( FD_UNLIKELY( !fd_log_private_thread_id_init ) ) fd_log_private_thread_id_set( fd_log_private_thread_id_next() );
120 : # else
121 : FD_COMPILER_MFENCE(); /* Work around FD_FN_CONST */
122 : # endif
123 8235 : return fd_log_private_thread_id;
124 8235 : }
125 :
126 : /* Thread */
127 :
128 : /* Initialize name to a reasonable default thread description (FIXME:
129 : WEEN THIS OFF STDLIB) */
130 :
131 : static void
132 2749 : fd_log_private_thread_default( char * name ) { /* FD_LOG_NAME_MAX bytes */
133 2749 : sprintf( name, "%lu", fd_log_thread_id() );
134 2749 : }
135 :
136 : static FD_TL char fd_log_private_thread[ FD_LOG_NAME_MAX ]; /* "" at thread start */
137 : static FD_TL int fd_log_private_thread_init; /* 0 at thread start */
138 :
139 : void
140 2752 : fd_log_thread_set( char const * thread ) {
141 2752 : if( FD_UNLIKELY( !thread ) || FD_UNLIKELY( thread[0]=='\0') ) {
142 2749 : fd_log_private_thread_default( fd_log_private_thread );
143 2749 : fd_log_private_thread_init = 1;
144 2749 : } else if( FD_LIKELY( thread!=fd_log_private_thread ) ) {
145 3 : fd_cstr_fini( fd_cstr_append_cstr_safe( fd_cstr_init( fd_log_private_thread ), thread, FD_LOG_NAME_MAX-1UL ) );
146 3 : fd_log_private_thread_init = 1;
147 3 : }
148 2752 : }
149 :
150 : char const *
151 60384448 : fd_log_thread( void ) {
152 60384448 : if( FD_UNLIKELY( !fd_log_private_thread_init ) ) fd_log_thread_set( NULL );
153 60384448 : return fd_log_private_thread;
154 60384448 : }
155 :
156 : /* APPLICATION PHYSICAL ID APIS ***************************************/
157 :
158 : /* Host ID */
159 :
160 : static ulong fd_log_private_host_id; /* 0 outside boot/halt, initialized on boot */
161 :
162 2701 : void fd_log_private_host_id_set( ulong host_id ) { fd_log_private_host_id = host_id; }
163 :
164 5432 : ulong fd_log_host_id( void ) { return fd_log_private_host_id; }
165 :
166 : /* Host */
167 :
168 : static char fd_log_private_host[ FD_LOG_NAME_MAX ]; /* "" outside boot/halt, initialized on boot */
169 :
170 118342 : char const * fd_log_host( void ) { return fd_log_private_host; }
171 :
172 : void
173 2701 : fd_log_private_host_set( char const * host ) {
174 2701 : if( FD_UNLIKELY( !host ) || FD_UNLIKELY( host[0]=='\0') ) host = "[host]";
175 2701 : if( FD_LIKELY( host!=fd_log_private_host ) )
176 2701 : fd_cstr_fini( fd_cstr_append_cstr_safe( fd_cstr_init( fd_log_private_host ), host, FD_LOG_NAME_MAX-1UL ) );
177 2701 : }
178 :
179 : /* CPU ID */
180 :
181 : /* First CPU scheduled to run on or ULONG_MAX on failure */
182 :
183 : ulong
184 2701 : fd_log_private_cpu_id_default( void ) {
185 2701 : FD_CPUSET_DECL( cpu_set );
186 2701 : if( FD_UNLIKELY( fd_cpuset_getaffinity( (pid_t)0, cpu_set ) ) ) return ULONG_MAX;
187 2701 : ulong idx = fd_cpuset_first( cpu_set );
188 2701 : idx = fd_ulong_if( idx<FD_TILE_MAX, idx, ULONG_MAX );
189 2701 : return idx;
190 2701 : }
191 :
192 : static FD_TL ulong fd_log_private_cpu_id; /* 0 at thread start */
193 : static FD_TL int fd_log_private_cpu_id_init; /* 0 at thread start */
194 :
195 : void
196 2710 : fd_log_private_cpu_id_set( ulong cpu_id ) {
197 2710 : fd_log_private_cpu_id = cpu_id;
198 2710 : fd_log_private_cpu_id_init = 1;
199 2710 : }
200 :
201 : ulong
202 2839 : fd_log_cpu_id( void ) {
203 2839 : if( FD_UNLIKELY( !fd_log_private_cpu_id_init ) ) fd_log_private_cpu_id_set( fd_log_private_cpu_id_default() );
204 2839 : return fd_log_private_cpu_id;
205 2839 : }
206 :
207 : /* CPU */
208 :
209 : /* Initialize name to a reasonable default CPU description (FIXME: WEEN
210 : THIS OFF STDLIB) */
211 :
212 : static void
213 2749 : fd_log_private_cpu_default( char * name ) { /* FD_LOG_NAME_MAX bytes */
214 2749 : FD_CPUSET_DECL( set );
215 :
216 2749 : int err = fd_cpuset_getaffinity( (pid_t)0, set );
217 2749 : if( FD_UNLIKELY( err ) ) { sprintf( name, "e%i", err ); return; }
218 :
219 2749 : ulong cnt = fd_cpuset_cnt( set );
220 2749 : if( FD_UNLIKELY( !((0UL<cnt) & (cnt<=FD_TILE_MAX)) ) ) { sprintf( name, "ec" ); return; }
221 :
222 2749 : ulong idx = fd_cpuset_first( set );
223 2749 : sprintf( name, (cnt>1) ? "f%lu" : "%lu", idx );
224 2749 : }
225 :
226 : static FD_TL char fd_log_private_cpu[ FD_LOG_NAME_MAX ]; /* "" at thread start */
227 : static FD_TL int fd_log_private_cpu_init; /* 0 at thread start */
228 :
229 : void
230 2752 : fd_log_cpu_set( char const * cpu ) {
231 2752 : if( FD_UNLIKELY( !cpu ) || FD_UNLIKELY( cpu[0]=='\0') ) {
232 2749 : fd_log_private_cpu_default( fd_log_private_cpu );
233 2749 : fd_log_private_cpu_init = 1;
234 2749 : } else if( FD_LIKELY( cpu!=fd_log_private_cpu ) ) {
235 3 : fd_cstr_fini( fd_cstr_append_cstr_safe( fd_cstr_init( fd_log_private_cpu ), cpu, FD_LOG_NAME_MAX-1UL ) );
236 3 : fd_log_private_cpu_init = 1;
237 3 : }
238 2752 : }
239 :
240 : char const *
241 60384451 : fd_log_cpu( void ) {
242 60384451 : if( FD_UNLIKELY( !fd_log_private_cpu_init ) ) fd_log_cpu_set( NULL );
243 60384451 : return fd_log_private_cpu;
244 60384451 : }
245 :
246 : /* THREAD GROUP ID APIS ***********************************************/
247 :
248 : /* Group id */
249 :
250 : static ulong fd_log_private_group_id; /* 0 outside boot/halt, init on boot */
251 :
252 2701 : void fd_log_private_group_id_set( ulong group_id ) { fd_log_private_group_id = group_id; }
253 :
254 60512919 : ulong fd_log_group_id( void ) { return fd_log_private_group_id; }
255 :
256 : /* Group */
257 :
258 : static char fd_log_private_group[ FD_LOG_NAME_MAX ]; /* "" outside boot/halt, init on boot */
259 :
260 118343 : char const * fd_log_group( void ) { return fd_log_private_group; }
261 :
262 : void
263 2701 : fd_log_private_group_set( char const * group ) {
264 2701 : if( FD_UNLIKELY( !group ) || FD_UNLIKELY( group[0]=='\0') ) group = "[group]";
265 2701 : if( FD_LIKELY( group!=fd_log_private_group ) )
266 2701 : fd_cstr_fini( fd_cstr_append_cstr_safe( fd_cstr_init( fd_log_private_group ), group, FD_LOG_NAME_MAX-1UL ) );
267 2701 : }
268 :
269 : /* System TID or ULONG_MAX on failure */
270 :
271 : ulong
272 2740 : fd_log_private_tid_default( void ) {
273 2740 : # if defined(__linux__)
274 2740 : long tid = syscall( SYS_gettid );
275 : # else
276 : long tid = getpid();
277 : # endif
278 2740 : return fd_ulong_if( tid>0L, (ulong)tid, ULONG_MAX );
279 2740 : }
280 :
281 : static FD_TL ulong fd_log_private_tid; /* 0 at thread start */
282 : static FD_TL int fd_log_private_tid_init; /* 0 at thread start */
283 :
284 : void
285 2740 : fd_log_private_tid_set( ulong tid ) {
286 2740 : fd_log_private_tid = tid;
287 2740 : fd_log_private_tid_init = 1;
288 2740 : }
289 :
290 : ulong
291 60387104 : fd_log_tid( void ) {
292 60387104 : if( FD_UNLIKELY( !fd_log_private_tid_init ) ) fd_log_private_tid_set( fd_log_private_tid_default() );
293 60387104 : return fd_log_private_tid;
294 60387104 : }
295 :
296 : /* User id */
297 :
298 : static ulong
299 2701 : fd_log_private_user_id_default( void ) {
300 2701 : return (ulong)getuid(); /* POSIX spec seems ambiguous as to whether or not this is a signed type */
301 2701 : }
302 :
303 : static ulong fd_log_private_user_id; /* 0 outside boot/halt, init on boot */
304 : static int fd_log_private_user_id_init;
305 :
306 : void
307 2701 : fd_log_private_user_id_set( ulong user_id ) {
308 2701 : fd_log_private_user_id = user_id;
309 2701 : fd_log_private_user_id_init = 1;
310 2701 : }
311 :
312 : ulong
313 2740 : fd_log_user_id( void ) {
314 2740 : if( FD_UNLIKELY( !fd_log_private_user_id_init ) ) {
315 0 : fd_log_private_user_id = fd_log_private_user_id_default();
316 0 : fd_log_private_user_id_init = 1;
317 0 : }
318 2740 : return fd_log_private_user_id;
319 2740 : }
320 :
321 : /* User */
322 :
323 : static char fd_log_private_user[ FD_LOG_NAME_MAX ]; /* "" outside boot/halt, init on boot */
324 :
325 118343 : char const * fd_log_user( void ) { return fd_log_private_user; }
326 :
327 : void
328 2701 : fd_log_private_user_set( char const * user ) {
329 2701 : if( FD_UNLIKELY( !user ) || FD_UNLIKELY( user[0]=='\0') ) user = "[user]";
330 2701 : if( FD_LIKELY( user!=fd_log_private_user ) )
331 2701 : fd_cstr_fini( fd_cstr_append_cstr_safe( fd_cstr_init( fd_log_private_user ), user, FD_LOG_NAME_MAX-1UL ) );
332 2701 : }
333 :
334 : int
335 9 : fd_log_group_id_query( ulong group_id ) {
336 9 : if( group_id==fd_log_group_id() ) return FD_LOG_GROUP_ID_QUERY_LIVE; /* Avoid O/S call for self queries */
337 6 : pid_t pid = (pid_t)group_id;
338 6 : if( FD_UNLIKELY( ((group_id!=(ulong)pid) | (pid<=(pid_t)0)) ) ) return FD_LOG_GROUP_ID_QUERY_INVAL;
339 0 : if( !kill( (pid_t)group_id, 0 ) ) return FD_LOG_GROUP_ID_QUERY_LIVE;
340 0 : if( FD_LIKELY( errno==ESRCH ) ) return FD_LOG_GROUP_ID_QUERY_DEAD;
341 0 : if( FD_LIKELY( errno==EPERM ) ) return FD_LOG_GROUP_ID_QUERY_PERM;
342 0 : return FD_LOG_GROUP_ID_QUERY_FAIL;
343 0 : }
344 :
345 : /* WALLCLOCK APIS *****************************************************/
346 :
347 : long
348 429857595 : fd_log_wallclock_host( void const * _ ) {
349 429857595 : (void)_;
350 429857595 : struct timespec ts[1];
351 429857595 : clock_gettime( CLOCK_REALTIME, ts );
352 429857595 : return ((long)1e9)*((long)ts->tv_sec) + (long)ts->tv_nsec;
353 429857595 : }
354 :
355 : static fd_clock_func_t fd_log_private_clock_func = fd_log_wallclock_host;
356 : static void const * fd_log_private_clock_args = NULL;
357 :
358 : long
359 429493355 : fd_log_wallclock( void ) {
360 429493355 : return fd_log_private_clock_func( fd_log_private_clock_args );
361 429493355 : }
362 :
363 : void
364 : fd_log_wallclock_set( fd_clock_func_t clock,
365 3 : void const * args ) {
366 3 : fd_log_private_clock_func = clock;
367 3 : fd_log_private_clock_args = args;
368 3 : }
369 :
370 : char *
371 : fd_log_wallclock_cstr( long now,
372 122560 : char * buf ) {
373 122560 : uint YYYY;
374 122560 : uint MM;
375 122560 : uint DD;
376 122560 : uint hh;
377 122560 : uint mm;
378 122560 : ulong ns;
379 122560 : int tz;
380 :
381 122560 : static long const ns_per_m = 60000000000L;
382 122560 : static long const ns_per_s = 1000000000L;
383 :
384 122560 : static FD_TL long now_ref = 1262325600000000000L; /* 2010-01-01 00:00:00.000000000 GMT-06 */
385 122560 : static FD_TL uint YYYY_ref = 2010U; /* Initialized to what now0 corresponds to */
386 122560 : static FD_TL uint MM_ref = 1U; /* " */
387 122560 : static FD_TL uint DD_ref = 1U; /* " */
388 122560 : static FD_TL uint hh_ref = 0U; /* " */
389 122560 : static FD_TL uint mm_ref = 0U; /* " */
390 122560 : static FD_TL int tz_ref = -6; /* " */
391 :
392 122560 : if( FD_LIKELY( (now_ref<=now) & (now<(now_ref+ns_per_m)) ) ) {
393 :
394 : /* now is near the reference timestamp so we reuse the reference
395 : calculation timestamp. */
396 :
397 119797 : YYYY = YYYY_ref;
398 119797 : MM = MM_ref;
399 119797 : DD = DD_ref;
400 119797 : hh = hh_ref;
401 119797 : mm = mm_ref;
402 119797 : ns = (ulong)(now - now_ref);
403 119797 : tz = tz_ref;
404 :
405 119797 : } else {
406 :
407 2763 : long _t = now / ns_per_s;
408 2763 : long _ns = now - ns_per_s*_t;
409 2763 : if( _ns<0L ) _ns += ns_per_s, _t--;
410 2763 : time_t t = (time_t)_t;
411 :
412 2763 : struct tm tm[1];
413 2763 : static FD_TL int localtime_broken = 0;
414 2763 : if( FD_UNLIKELY( !localtime_broken && !localtime_r( &t, tm ) ) ) localtime_broken = 1;
415 2763 : if( FD_UNLIKELY( localtime_broken ) ) { /* If localtime_r doesn't work, pretty print as a raw UNIX time */
416 : /* Note: These can all run in parallel */
417 0 : fd_cstr_append_fxp10_as_text( buf, ' ', fd_char_if( now<0L, '-', '\0' ), 9UL, fd_long_abs( now ), 29UL );
418 0 : fd_cstr_append_text ( buf+29, " s UNIX", 7UL );
419 0 : fd_cstr_append_char ( buf+36, '\0' );
420 0 : return buf;
421 0 : }
422 :
423 2763 : YYYY = (uint)(1900+tm->tm_year);
424 2763 : MM = (uint)( 1+tm->tm_mon );
425 2763 : DD = (uint)tm->tm_mday;
426 2763 : hh = (uint)tm->tm_hour;
427 2763 : mm = (uint)tm->tm_min;
428 2763 : ns = ((ulong)((uint)tm->tm_sec))*((ulong)ns_per_s) + ((ulong)_ns);
429 2763 : # if defined(__linux__)
430 2763 : tz = (int)(-timezone/3600L+(long)tm->tm_isdst);
431 : # else
432 : tz = 0;
433 : # endif
434 :
435 2763 : now_ref = now - (long)ns;
436 2763 : YYYY_ref = YYYY;
437 2763 : MM_ref = MM;
438 2763 : DD_ref = DD;
439 2763 : hh_ref = hh;
440 2763 : mm_ref = mm;
441 2763 : tz_ref = tz;
442 :
443 2763 : }
444 :
445 : /* Note: These can all run in parallel! */
446 122560 : fd_cstr_append_uint_as_text ( buf, '0', '\0', YYYY, 4UL );
447 122560 : fd_cstr_append_char ( buf+ 4, '-' );
448 122560 : fd_cstr_append_uint_as_text ( buf+ 5, '0', '\0', MM, 2UL );
449 122560 : fd_cstr_append_char ( buf+ 7, '-' );
450 122560 : fd_cstr_append_uint_as_text ( buf+ 8, '0', '\0', DD, 2UL );
451 122560 : fd_cstr_append_char ( buf+10, ' ' );
452 122560 : fd_cstr_append_uint_as_text ( buf+11, '0', '\0', hh, 2UL );
453 122560 : fd_cstr_append_char ( buf+13, ':' );
454 122560 : fd_cstr_append_uint_as_text ( buf+14, '0', '\0', mm, 2UL );
455 122560 : fd_cstr_append_char ( buf+16, ':' );
456 122560 : fd_cstr_append_fxp10_as_text( buf+17, '0', '\0', 9UL, ns, 12UL );
457 122560 : fd_cstr_append_text ( buf+29, " GMT", 4UL );
458 122560 : fd_cstr_append_char ( buf+33, fd_char_if( tz<0, '-', '+' ) );
459 122560 : fd_cstr_append_uint_as_text ( buf+34, '0', '\0', fd_int_abs( tz ), 2UL );
460 122560 : fd_cstr_append_char ( buf+36, '\0' );
461 122560 : return buf;
462 122560 : }
463 :
464 : long
465 513 : fd_log_sleep( long dt ) {
466 513 : if( FD_UNLIKELY( dt < 1L ) ) {
467 0 : sched_yield();
468 0 : return 0L;
469 0 : }
470 :
471 : /* dt is in [1,LONG_MAX] at this point */
472 513 : long ns_dt = fd_long_min( dt, (((long)1e9)<<31)-1L ); /* in [1,2^31*1e9) and <= dt at this point */
473 513 : dt -= ns_dt;
474 :
475 513 : struct timespec req[1];
476 513 : struct timespec rem[1];
477 513 : req->tv_sec = (time_t)( ((ulong)ns_dt) / ((ulong)1e9) ); /* in [0,2^31-1] */
478 513 : req->tv_nsec = (long) ( ((ulong)ns_dt) % ((ulong)1e9) ); /* in [0,1e9) */
479 513 : int sleep_res;
480 513 : #if defined(__linux__)
481 : /* Always use clock_nanosleep on Linux to be predictable */
482 513 : sleep_res = clock_nanosleep( CLOCK_REALTIME, 0, req, rem );
483 : #else
484 : sleep_res = nanosleep( req, rem );
485 : #endif
486 513 : if( FD_UNLIKELY( sleep_res ) && FD_LIKELY( errno==EINTR ) ) dt += ((long)1e9)*((long)rem->tv_sec) + rem->tv_nsec;
487 513 : return dt;
488 513 : }
489 :
490 : long
491 6 : fd_log_wait_until( long then ) {
492 6 : long now;
493 14103674 : for(;;) {
494 14103674 : now = fd_log_wallclock();
495 14103674 : long rem = then - now;
496 14103674 : if( FD_LIKELY( rem<=0L ) ) break; /* we've waited long enough */
497 14103668 : if( FD_UNLIKELY( rem>(long)1e9 ) ) { /* long wait (over ~1 s) ... sleep until medium long */
498 0 : fd_log_sleep( rem-(long)0.1e9 );
499 0 : continue;
500 0 : }
501 14103668 : if( FD_UNLIKELY( rem>(long)0.1e9 ) ) { /* medium long wait (over ~0.1 s) ... yield */
502 3054143 : FD_YIELD();
503 3054143 : continue;
504 3054143 : }
505 11049525 : if( FD_UNLIKELY( rem>(long)1e3 ) ) { /* medium short wait (over ~1 us) ... hyperthreading friendly spin */
506 11049282 : FD_SPIN_PAUSE();
507 11049282 : continue;
508 11049282 : }
509 : /* short wait ... spin on fd_log_wallclock */
510 11049525 : }
511 6 : return now;
512 6 : }
513 :
514 : /* LOG APIS ***********************************************************/
515 :
516 : char fd_log_private_path[ 1024 ]; /* "" outside boot/halt, init at boot */
517 : static int fd_log_private_fileno = -1; /* -1 outside boot/halt, init at boot */
518 : static int fd_log_private_dedup; /* 0 outside boot/halt, init at boot */
519 :
520 : void
521 4347 : fd_log_flush( void ) {
522 4347 : int log_fileno = FD_VOLATILE_CONST( fd_log_private_fileno );
523 4347 : if( FD_LIKELY( log_fileno!=-1 ) ) fsync( log_fileno );
524 4347 : }
525 :
526 : static int fd_log_private_colorize; /* 0 outside boot/halt, init at boot */
527 : static int fd_log_private_level_logfile; /* 0 outside boot/halt, init at boot */
528 : static int fd_log_private_level_stderr; /* 0 outside boot/halt, init at boot */
529 : static int fd_log_private_level_flush; /* 0 outside boot/halt, init at boot */
530 : static int fd_log_private_level_core; /* 0 outside boot/halt, init at boot */
531 : static int fd_log_private_unclean_exit;
532 : static int fd_log_private_signal_handler;
533 :
534 4649 : int fd_log_colorize ( void ) { return FD_VOLATILE_CONST( fd_log_private_colorize ); }
535 :
536 267 : char const * fd_log_style_bold ( void ) { return fd_log_colorize() ? TEXT_BOLD : ""; }
537 267 : char const * fd_log_style_dim ( void ) { return fd_log_colorize() ? TEXT_DIM : ""; }
538 534 : char const * fd_log_style_normal( void ) { return fd_log_colorize() ? TEXT_NORMAL : ""; }
539 60538325 : int fd_log_level_logfile( void ) { return FD_VOLATILE_CONST( fd_log_private_level_logfile ); }
540 60384382 : int fd_log_level_stderr ( void ) { return FD_VOLATILE_CONST( fd_log_private_level_stderr ); }
541 124996 : int fd_log_level_flush ( void ) { return FD_VOLATILE_CONST( fd_log_private_level_flush ); }
542 4966 : int fd_log_level_core ( void ) { return FD_VOLATILE_CONST( fd_log_private_level_core ); }
543 :
544 2701 : void fd_log_colorize_set ( int mode ) { FD_VOLATILE( fd_log_private_colorize ) = mode; }
545 2713 : void fd_log_level_logfile_set( int level ) { FD_VOLATILE( fd_log_private_level_logfile ) = level; }
546 2707 : void fd_log_level_stderr_set ( int level ) { FD_VOLATILE( fd_log_private_level_stderr ) = level; }
547 2707 : void fd_log_level_flush_set ( int level ) { FD_VOLATILE( fd_log_private_level_flush ) = level; }
548 2710 : void fd_log_level_core_set ( int level ) { FD_VOLATILE( fd_log_private_level_core ) = level; }
549 :
550 12 : int fd_log_private_logfile_fd( void ) { return FD_VOLATILE_CONST( fd_log_private_fileno ); }
551 :
552 0 : void fd_log_enable_signal_handler( void ) { fd_log_private_signal_handler = 1; }
553 0 : void fd_log_enable_unclean_exit( void ) { fd_log_private_unclean_exit = 1; }
554 :
555 : /* Buffer size used for vsnprintf calls (this is also one more than the
556 : maximum size that this can passed to fd_io_write) */
557 :
558 121319780 : #define FD_LOG_BUF_SZ (32UL*4096UL)
559 :
560 : /* File descriptor to restore when logging a message that will terminate
561 : the application, incase it was redirected to some other consumer in
562 : the process which will not be able to process it in time */
563 : static int fd_log_private_stderr_fileno = STDERR_FILENO;
564 : int fd_log_private_restore_stderr = STDERR_FILENO;
565 :
566 : __attribute__((no_sanitize_address)) void
567 : fd_log_private_fprintf_0( int fd,
568 130969 : char const * fmt, ... ) {
569 :
570 : /* Note: while this function superficially looks vdprintf-ish, we don't
571 : use that as it can do all sorts of unpleasantness under the hood
572 : (fflush, mutex / futex on fd, non-AS-safe buffering, ...) that this
573 : function deliberately avoids. Also, the function uses the shared
574 : lock to help keep messages generated from processes that share the
575 : same log fd sane. */
576 :
577 : /* TODO:
578 : - Consider moving to util/io as fd_io_printf or renaming to
579 : fd_log_printf?
580 : - Is msg better to have on stack or in thread local storage?
581 : - Allow partial write to fd_io_write? (e.g. src_min=0 such that
582 : the fd_io_write below is guaranteed to be a single system call) */
583 :
584 130969 : char msg[ FD_LOG_BUF_SZ ];
585 :
586 130969 : va_list ap;
587 130969 : va_start( ap, fmt );
588 130969 : int len = vsnprintf( msg, FD_LOG_BUF_SZ, fmt, ap );
589 130969 : if( len<0 ) len = 0; /* cmov */
590 130969 : if( len>(int)(FD_LOG_BUF_SZ-1UL) ) len = (int)(FD_LOG_BUF_SZ-1UL); /* cmov */
591 130969 : msg[ len ] = '\0';
592 130969 : va_end( ap );
593 :
594 130969 : ulong wsz;
595 130969 : fd_io_write( fd, msg, (ulong)len, (ulong)len, &wsz ); /* Note: we ignore errors because what are we doing to do? log them? */
596 :
597 130969 : }
598 :
599 : /* Log buffer used by fd_log_private_0 and fd_log_private_hexdump_msg */
600 :
601 : static FD_TL char fd_log_private_log_msg[ FD_LOG_BUF_SZ ];
602 :
603 : char const *
604 60528921 : fd_log_private_0( char const * fmt, ... ) {
605 60528921 : va_list ap;
606 60528921 : va_start( ap, fmt );
607 60528921 : int len = vsnprintf( fd_log_private_log_msg, FD_LOG_BUF_SZ, fmt, ap );
608 60528921 : if( len<0 ) len = 0; /* cmov */
609 60528921 : if( len>(int)(FD_LOG_BUF_SZ-1UL) ) len = (int)(FD_LOG_BUF_SZ-1UL); /* cmov */
610 60528921 : fd_log_private_log_msg[ len ] = '\0';
611 60528921 : va_end( ap );
612 60528921 : return fd_log_private_log_msg;
613 60528921 : }
614 :
615 : char const *
616 : fd_log_private_hexdump_msg( char const * descr,
617 : void const * mem,
618 6684 : ulong sz ) {
619 :
620 3175953 : # define FD_LOG_HEXDUMP_BYTES_PER_LINE (16UL)
621 6684 : # define FD_LOG_HEXDUMP_BLOB_DESCRIPTION_MAX_LEN (32UL)
622 6684 : # define FD_LOG_HEXDUMP_MAX_INPUT_BLOB_SZ (1664UL) /* multiple of 128 >= 1542 */
623 :
624 3578856 : # define FD_LOG_HEXDUMP_ADD_TO_LOG_BUF(...) do { log_buf_ptr += fd_int_max( sprintf( log_buf_ptr, __VA_ARGS__ ), 0 ); } while(0)
625 6684 : char * log_buf_ptr = fd_log_private_log_msg; /* used by FD_LOG_HEXDUMP_ADD_TO_LOG_BUF macro */
626 :
627 : /* Print the hexdump header */
628 : /* FIXME: consider additional sanitization of descr or using compiler
629 : tricks to prevent user from passing a non-const-char string (i.e.
630 : data they got from somewhere else that might not be sanitized). */
631 :
632 6684 : if( FD_UNLIKELY( !descr ) ) {
633 :
634 24 : FD_LOG_HEXDUMP_ADD_TO_LOG_BUF( "HEXDUMP - (%lu bytes at 0x%lx)", sz, (ulong)mem );
635 :
636 6660 : } else if( FD_UNLIKELY( strlen( descr )>FD_LOG_HEXDUMP_BLOB_DESCRIPTION_MAX_LEN ) ) {
637 :
638 27 : char tmp[ FD_LOG_HEXDUMP_BLOB_DESCRIPTION_MAX_LEN + 1UL ];
639 27 : fd_cstr_fini( fd_cstr_append_text( fd_cstr_init( tmp ), descr, FD_LOG_HEXDUMP_BLOB_DESCRIPTION_MAX_LEN ) );
640 27 : FD_LOG_HEXDUMP_ADD_TO_LOG_BUF( "HEXDUMP \"%s\"... (%lu bytes at 0x%lx)", tmp, sz, (ulong)mem );
641 :
642 6633 : } else {
643 :
644 6633 : FD_LOG_HEXDUMP_ADD_TO_LOG_BUF( "HEXDUMP \"%s\" (%lu bytes at 0x%lx)", descr, sz, (ulong)mem );
645 :
646 6633 : }
647 :
648 6684 : if( FD_UNLIKELY( !sz ) ) return fd_log_private_log_msg;
649 :
650 6633 : FD_LOG_HEXDUMP_ADD_TO_LOG_BUF( "\n" );
651 :
652 6633 : if( FD_UNLIKELY( !mem ) ) {
653 24 : FD_LOG_HEXDUMP_ADD_TO_LOG_BUF( "\t... snip (unreadable memory) ..." );
654 24 : return fd_log_private_log_msg;
655 24 : }
656 :
657 6609 : char line_buf[ FD_LOG_HEXDUMP_BYTES_PER_LINE+1 ];
658 6609 : char const * blob = (char const *)mem;
659 6609 : ulong blob_off = 0UL;
660 6609 : ulong blob_sz = fd_ulong_min( sz, FD_LOG_HEXDUMP_MAX_INPUT_BLOB_SZ );
661 :
662 3172398 : for( ; blob_off<blob_sz; blob_off++ ) {
663 3165789 : ulong col_idx = blob_off % FD_LOG_HEXDUMP_BYTES_PER_LINE;
664 :
665 : /* New line. Print previous line's ASCII representation and then print the offset. */
666 3165789 : if( FD_UNLIKELY( !col_idx ) ) {
667 198084 : if( FD_LIKELY( blob_off ) ) FD_LOG_HEXDUMP_ADD_TO_LOG_BUF( " %s\n", line_buf );
668 198084 : FD_LOG_HEXDUMP_ADD_TO_LOG_BUF( "\t%04lx: ", blob_off );
669 198084 : }
670 : /* FIXME: consider extra space between col 7 and 8 to make easier
671 : for visual inspection */
672 :
673 3165789 : char c = blob[blob_off];
674 3165789 : FD_LOG_HEXDUMP_ADD_TO_LOG_BUF( " %02x", (uint)(uchar)c );
675 :
676 : /* If not a printable ASCII character, output a dot. */
677 3165789 : line_buf[ col_idx ] = fd_char_if( fd_isalnum( (int)c ) | fd_ispunct( (int)c ) | (c==' '), c, '.' );
678 3165789 : line_buf[ col_idx+1UL ] = '\0';
679 3165789 : }
680 :
681 : /* Print the 2nd column of last blob line */
682 10164 : while( blob_off % FD_LOG_HEXDUMP_BYTES_PER_LINE ) {
683 3555 : FD_LOG_HEXDUMP_ADD_TO_LOG_BUF( " " );
684 3555 : blob_off++;
685 3555 : }
686 6609 : FD_LOG_HEXDUMP_ADD_TO_LOG_BUF( " %s", line_buf );
687 :
688 6609 : if( FD_UNLIKELY( blob_sz < sz ) )
689 3 : FD_LOG_HEXDUMP_ADD_TO_LOG_BUF( "\n\t... snip (printed %lu bytes, omitted %lu bytes) ...", blob_sz, sz-blob_sz );
690 :
691 6609 : return fd_log_private_log_msg;
692 :
693 6633 : # undef FD_LOG_HEXDUMP_BYTES_PER_LINE
694 6633 : # undef FD_LOG_HEXDUMP_BLOB_DESCRIPTION_MAX_LEN
695 6633 : # undef FD_LOG_HEXDUMP_MAX_INPUT_BLOB_SZ
696 6633 : # undef FD_LOG_HEXDUMP_ADD_TO_LOG_BUF
697 6633 : }
698 :
699 : void
700 : fd_log_private_1( int level,
701 : long now,
702 : char const * file,
703 : int line,
704 : char const * func,
705 60535614 : char const * msg ) {
706 :
707 60535614 : if( level<fd_log_level_logfile() ) return;
708 :
709 : /* These are thread init so we call them regardless of permanent log
710 : enabled to their initialization time is guaranteed independent of
711 : whether the permanent log is enabled. */
712 :
713 60381675 : char const * thread = fd_log_thread();
714 60381675 : char const * cpu = fd_log_cpu();
715 60381675 : ulong tid = fd_log_tid();
716 :
717 60381675 : int log_fileno = FD_VOLATILE_CONST( fd_log_private_fileno );
718 60381675 : int to_logfile = (log_fileno!=-1);
719 60381675 : int to_stderr = (level>=fd_log_level_stderr());
720 60381675 : if( !(to_logfile | to_stderr) ) return;
721 :
722 : /* Deduplicate the log if requested */
723 :
724 60316893 : if( fd_log_private_dedup ) {
725 :
726 : /* Compute if this message appears to be a recent duplicate of
727 : a previous log message */
728 :
729 60316891 : ulong hash = fd_cstr_hash_append( fd_cstr_hash_append( fd_cstr_hash_append( fd_ulong_hash(
730 60316891 : (ulong)(8L*(long)line+(long)level) ), file ), func ), msg );
731 :
732 60316891 : static long const dedup_interval = 20000000L; /* 1/50 s */
733 :
734 60316891 : static FD_TL int init; /* 0 on thread start */
735 60316891 : static FD_TL ulong last_hash; /* 0UL on thread start */
736 60316891 : static FD_TL long then; /* 0L on thread start */
737 :
738 60316891 : int is_dup = init & (hash==last_hash) & ((now-then)<dedup_interval);
739 60316891 : init = 1;
740 :
741 : /* Update how many messages from this thread in row have been
742 : duplicates */
743 :
744 60316891 : static FD_TL ulong dedup_cnt; /* 0UL on thread start */
745 60316891 : static FD_TL int in_dedup; /* 0 on thread start */
746 :
747 60316891 : if( is_dup ) dedup_cnt++;
748 121826 : else {
749 121826 : if( in_dedup ) {
750 :
751 : /* This message appears to end a long string of duplicates.
752 : Log the end of the deduplication. */
753 :
754 98 : char then_cstr[ FD_LOG_WALLCLOCK_CSTR_BUF_SZ ];
755 98 : fd_log_wallclock_cstr( then, then_cstr );
756 :
757 98 : if( to_logfile )
758 98 : fd_log_private_fprintf_0( log_fileno, "SNIP %s %6lu:%-6lu %s:%s:%-4s %s:%s:%-4s "
759 98 : "stopped repeating (%lu identical messages)\n",
760 98 : then_cstr, fd_log_group_id(),tid, fd_log_user(),fd_log_host(),cpu,
761 98 : fd_log_app(),fd_log_group(),thread, dedup_cnt+1UL );
762 :
763 98 : if( to_stderr ) {
764 48 : char * then_short_cstr = then_cstr+5; then_short_cstr[21] = '\0'; /* Lop off the year, ns resolution and timezone */
765 48 : fd_log_private_fprintf_0( fd_log_private_stderr_fileno, "SNIP %s %-6lu %-4s %-4s stopped repeating (%lu identical messages)\n",
766 48 : then_short_cstr, tid,cpu,thread, dedup_cnt+1UL );
767 48 : }
768 :
769 98 : in_dedup = 0;
770 98 : }
771 :
772 121826 : dedup_cnt = 0UL;
773 121826 : }
774 :
775 : /* dedup_cnt previous messages from this thread appear to be
776 : duplicates. Decide whether to let the raw message print or
777 : deduplicate to the log. FIXME: CONSIDER RANDOMIZING THE
778 : THROTTLE. */
779 :
780 60316891 : static ulong const dedup_thresh = 3UL; /* let initial dedup_thresh duplicates go out the door */
781 60316891 : static long const dedup_throttle = 1000000000L; /* ~1s, how often to update status on current duplication */
782 :
783 60316891 : static FD_TL long dedup_last; /* 0L on thread start */
784 :
785 60316891 : if( dedup_cnt < dedup_thresh ) dedup_last = now;
786 60194609 : else {
787 60194609 : if( (now-dedup_last) >= dedup_throttle ) {
788 10 : char now_cstr[ FD_LOG_WALLCLOCK_CSTR_BUF_SZ ];
789 10 : fd_log_wallclock_cstr( now, now_cstr );
790 10 : if( to_logfile )
791 10 : fd_log_private_fprintf_0( log_fileno, "SNIP %s %6lu:%-6lu %s:%s:%-4s %s:%s:%-4s repeating (%lu identical messages)\n",
792 10 : now_cstr, fd_log_group_id(),tid, fd_log_user(),fd_log_host(),cpu,
793 10 : fd_log_app(),fd_log_group(),thread, dedup_cnt+1UL );
794 10 : if( to_stderr ) {
795 6 : char * now_short_cstr = now_cstr+5; now_short_cstr[21] = '\0'; /* Lop off the year, ns resolution and timezone */
796 6 : fd_log_private_fprintf_0( fd_log_private_stderr_fileno, "SNIP %s %-6lu %-4s %-4s repeating (%lu identical messages)\n",
797 6 : now_short_cstr, tid,cpu,thread, dedup_cnt+1UL );
798 6 : }
799 10 : dedup_last = now;
800 10 : }
801 60194609 : in_dedup = 1;
802 60194609 : }
803 :
804 60316891 : last_hash = hash;
805 60316891 : then = now;
806 :
807 60316891 : if( in_dedup ) return;
808 60316891 : }
809 :
810 122286 : char now_cstr[ FD_LOG_WALLCLOCK_CSTR_BUF_SZ ];
811 122286 : fd_log_wallclock_cstr( now, now_cstr );
812 :
813 122286 : static char const * level_cstr[] = {
814 122286 : /* 0 */ "DEBUG ",
815 122286 : /* 1 */ "INFO ",
816 122286 : /* 2 */ "NOTICE ",
817 122286 : /* 3 */ "WARNING",
818 122286 : /* 4 */ "ERR ",
819 122286 : /* 5 */ "CRIT ",
820 122286 : /* 6 */ "ALERT ",
821 122286 : /* 7 */ "EMERG "
822 122286 : };
823 :
824 : /* Messages may carry terminal escapes for stderr, keep the logfile
825 : plain text. */
826 122286 : char const * logfile_msg = msg;
827 122286 : static FD_TL char stripped[ FD_LOG_BUF_SZ ];
828 122286 : if( FD_UNLIKELY( strchr( msg, '\033' ) ) ) {
829 0 : char const * s = msg;
830 0 : char * w = stripped;
831 0 : while( *s ) {
832 0 : if( FD_UNLIKELY( s[0]=='\033' && s[1]=='[' ) ) {
833 0 : s += 2;
834 0 : while( *s && !( (*s>='@') & (*s<='~') ) ) s++;
835 0 : if( *s ) s++;
836 0 : } else {
837 0 : *w++ = *s++;
838 0 : }
839 0 : }
840 0 : *w = '\0';
841 0 : logfile_msg = stripped;
842 0 : }
843 :
844 122286 : if( to_logfile )
845 115457 : fd_log_private_fprintf_0( log_fileno, "%s %s %6lu:%-6lu %s:%s:%-4s %s:%s:%-4s %s(%i)[%s]: %s\n",
846 115457 : level_cstr[level], now_cstr, fd_log_group_id(),tid, fd_log_user(),fd_log_host(),cpu,
847 115457 : fd_log_app(),fd_log_group(),thread, file,line,func, logfile_msg );
848 :
849 122286 : if( to_stderr ) {
850 9948 : static char const * color_level_cstr[] = {
851 9948 : /* 0 */ TEXT_NORMAL "DEBUG ",
852 9948 : /* 1 */ TEXT_BLUE "INFO " TEXT_NORMAL,
853 9948 : /* 2 */ TEXT_GREEN "NOTICE " TEXT_NORMAL,
854 9948 : /* 3 */ TEXT_YELLOW "WARNING" TEXT_NORMAL,
855 9948 : /* 4 */ TEXT_RED "ERR " TEXT_NORMAL,
856 9948 : /* 5 */ TEXT_RED TEXT_BOLD "CRIT " TEXT_NORMAL,
857 9948 : /* 6 */ TEXT_RED TEXT_BOLD TEXT_UNDERLINE "ALERT " TEXT_NORMAL,
858 9948 : /* 7 */ TEXT_RED TEXT_BOLD TEXT_UNDERLINE TEXT_BLINK "EMERG " TEXT_NORMAL
859 9948 : };
860 9948 : char * now_short_cstr = now_cstr+5; now_short_cstr[21] = '\0'; /* Lop off the year, ns resolution and timezone */
861 9948 : char const * stem = strrchr( file, '/' );
862 9948 : char const * file_name = stem ? stem+1 : file;
863 9948 : if( FD_LIKELY( fd_log_private_colorize ) )
864 0 : fd_log_private_fprintf_0( fd_log_private_stderr_fileno, "%s " TEXT_DIM "%s" TEXT_NORMAL " %-9s " TEXT_DIM "%s(%i):" TEXT_NORMAL " %s\n",
865 0 : color_level_cstr[level], now_short_cstr, thread, file_name, line, msg );
866 9948 : else
867 9948 : fd_log_private_fprintf_0( fd_log_private_stderr_fileno, "%s %s %-9s %s(%i): %s\n",
868 9948 : level_cstr[level], now_short_cstr, thread, file_name, line, msg );
869 9948 : }
870 :
871 122286 : if( level<fd_log_level_flush() ) return;
872 :
873 4312 : fd_log_flush();
874 4312 : }
875 :
876 : void
877 : fd_log_private_2( int level,
878 : long now,
879 : char const * file,
880 : int line,
881 : char const * func,
882 1128 : char const * msg ) {
883 1128 : if( level<fd_log_level_core() && fd_log_private_restore_stderr!=-1 ) {
884 : /* Restore stderr to original fd in case it was redirected to
885 : something that won't be able to process the fatal message */
886 :
887 1128 : fd_log_private_stderr_fileno = fd_log_private_restore_stderr;
888 1128 : fd_log_private_restore_stderr = -1;
889 :
890 1128 : }
891 :
892 1128 : fd_log_private_1( level, now, file, line, func, msg );
893 :
894 1128 : if( level<fd_log_level_core() ) {
895 1128 : # if defined(__linux__)
896 1128 : if( fd_log_private_unclean_exit ) {
897 0 : syscall( SYS_exit_group, 1 );
898 0 : }
899 1128 : # endif /* defined(__linux__) */
900 1128 : exit(1); /* atexit will call fd_log_private_cleanup implicitly */
901 1128 : }
902 :
903 0 : abort();
904 1128 : }
905 :
906 : /* BOOT/HALT APIS *****************************************************/
907 :
908 : static void
909 4250 : fd_log_private_cleanup( void ) {
910 :
911 : /* The atexit below means that all calls to "exit();" implicitly
912 : become "fd_log_private_cleanup(); exit();". It also implies that
913 : programs that terminate via a top level return from main implicitly
914 : call fd_log_private_cleanup().
915 :
916 : As such it is possible that a thread other than the booter will
917 : trigger cleanup either by triggering this directly (e.g. calling
918 : exit) or indirectly (e.g. by logging a message with an exit
919 : triggering priority) and that the booter itself might call this
920 : more than once sequentially (e.g. fd_halt() calling cleanup
921 : explicitly followed by return from main triggering it again.
922 :
923 : Accordingly we protect this with a ONCE block so it only will
924 : execute once per program. Further, if cleanup gets triggered by
925 : multiple threads concurrently, the ONCE block will prevent them
926 : from progressing until the first thread that hits the once block
927 : has completed cleanup. */
928 :
929 13902 : FD_ONCE_BEGIN {
930 2701 : int log_fileno = FD_VOLATILE_CONST( fd_log_private_fileno );
931 2701 : if( log_fileno==-1 ) fd_log_private_fprintf_0( STDERR_FILENO, "No log\n" );
932 889 : else if( !strcmp( fd_log_private_path, "-" ) ) fd_log_private_fprintf_0( STDERR_FILENO, "Log to stdout\n" );
933 877 : else {
934 877 : # if FD_HAS_THREADS
935 877 : if( fd_log_private_thread_id_ctr>1UL ) { /* There are potentially other log users running */
936 : /* Just closing the permanent log file is not multithreading
937 : safe in the case where other threads are still running
938 : normally and thus potentially logging to the permanent log.
939 : Such should not happen in a correctly written and functioning
940 : application but logging exists in large part to help
941 : understand when applications misbehave. So we try to be as
942 : robust and informative as we can here. FIXME: THE SECOND
943 : USLEEP IS AN UGLY HACK TO REDUCE (BUT NOT FULLY ELIMINATE)
944 : THE RISK OF USE AFTER CLOSE BY THOSE OTHER THREADS. IT IS
945 : POSSIBLE WITH A MORE INVASIVE CHANGES TO FULLY ELIMINATE THIS
946 : RISK. */
947 3 : usleep( (useconds_t)40000 ); /* Give potentially concurrent users a chance to get their dying messages out */
948 3 : FD_COMPILER_MFENCE();
949 3 : FD_VOLATILE( fd_log_private_fileno ) = -1; /* Turn off the permanent log for concurrent users */
950 3 : FD_COMPILER_MFENCE();
951 3 : usleep( (useconds_t)40000 ); /* Give any concurrent log operations progress at turn off a chance to wrap */
952 3 : }
953 : # else
954 : FD_VOLATILE( fd_log_private_fileno ) = -1;
955 : # endif
956 :
957 877 : fsync( log_fileno );
958 877 : sync();
959 877 : fd_log_private_fprintf_0( STDERR_FILENO, "Log at \"%s\"\n", fd_log_private_path );
960 877 : }
961 2701 : } FD_ONCE_END;
962 4250 : }
963 :
964 : static void
965 : fd_log_private_sig_abort( int sig,
966 : siginfo_t * info,
967 0 : void * context ) {
968 0 : (void)sig; (void)info; (void)context;
969 :
970 0 : #define FD_LOG_ERR_NOEXIT(a) do { long _fd_log_msg_now = fd_log_wallclock(); fd_log_private_1( 4, _fd_log_msg_now, __FILE__, __LINE__, __func__, fd_log_private_0 a ); } while(0)
971 0 : FD_LOG_ERR_NOEXIT(( "Received signal %s%s%s %s(%s)%s", fd_log_style_bold(), fd_io_strsignal_name( sig ), fd_log_style_normal(), fd_log_style_dim(), fd_io_strsignal_desc( sig ), fd_log_style_normal() ));
972 0 : #undef FD_LOG_ERR_NOEXIT
973 :
974 0 : # if FD_HAS_BACKTRACE
975 :
976 0 : void * btrace[ 128UL ];
977 0 : int btrace_cnt = backtrace( btrace, 128 );
978 :
979 0 : fd_backtrace_log( btrace, (ulong)btrace_cnt );
980 :
981 0 : # endif
982 :
983 : /* Returning is going to cause SIGSYS since it's probably not allowed
984 : in the sandbox, which is OK. The parent process will terminate
985 : everything anyway. If we allow rt_sigreturn to be called in the
986 : sandbox, then we will get a correct signal. */
987 0 : return;
988 0 : }
989 :
990 : static void
991 48564 : fd_log_private_sig_trap( int sig ) {
992 48564 : struct sigaction act[1];
993 : /* FIXME: CONSIDER NOT OVERRIDING IF THE SIGNAL HANDLER HAS ALREADY
994 : BEEN SET BY THE USER. */
995 48564 : act->sa_sigaction = fd_log_private_sig_abort;
996 48564 : if( sigemptyset( &act->sa_mask ) ) FD_LOG_ERR(( "sigempty set failed" ));
997 48564 : act->sa_flags = (int)(SA_SIGINFO | SA_RESETHAND);
998 48564 : if( sigaction( sig, act, NULL ) ) FD_LOG_ERR(( "unable to override signal %i", sig ));
999 48564 : }
1000 :
1001 : static int
1002 : fd_log_private_open_path( int cmdline,
1003 2701 : char const * log_path ) {
1004 2701 : ulong log_path_sz = log_path ? (strlen( log_path )+1UL) : 0UL;
1005 :
1006 2701 : if( !log_path_sz ) { /* Use default log path */
1007 0 : char tag[ FD_LOG_WALLCLOCK_CSTR_BUF_SZ ];
1008 0 : fd_log_wallclock_cstr( fd_log_wallclock(), tag );
1009 0 : for( ulong b=0UL; tag[b]; b++ ) if( tag[b]==' ' || tag[b]=='-' || tag[b]=='.' || tag[b]==':' ) tag[b] = '_';
1010 0 : char version[ 32UL ];
1011 0 : fd_version_cstr_format( version, sizeof(version), fd_major_version, fd_minor_version, fd_patch_version );
1012 0 : ulong len; fd_cstr_printf( fd_log_private_path, 1024UL, &len, "/tmp/fd-%s_%lu_%s_%s_%s",
1013 0 : version, fd_log_group_id(), fd_log_user(), fd_log_host(), tag );
1014 0 : if( len==1023UL ) { fd_log_private_fprintf_0( STDERR_FILENO, "default log path too long; unable to boot\n" ); exit(1); }
1015 0 : }
1016 2701 : else if( log_path_sz==1UL ) fd_log_private_path[0] = '\0'; /* User disabled */
1017 889 : else if( log_path_sz<=1024UL ) fd_memcpy( fd_log_private_path, log_path, log_path_sz ); /* User specified */
1018 0 : else { fd_log_private_fprintf_0( STDERR_FILENO, "log path too long; unable to boot\n" ); exit(1); } /* Invalid */
1019 :
1020 2701 : int log_fileno;
1021 2701 : if( fd_log_private_path[0]=='\0' ) {
1022 1812 : if( cmdline ) fd_log_private_fprintf_0( STDERR_FILENO, "--log-path \"\"\nNo log\n" );
1023 0 : else fd_log_private_fprintf_0( STDERR_FILENO, "No log\n" );
1024 1812 : log_fileno = -1;
1025 1812 : } else if( !strcmp( fd_log_private_path, "-" ) ) {
1026 12 : if( cmdline ) fd_log_private_fprintf_0( STDERR_FILENO, "--log-path \"-\"\nLog to stdout\n" );
1027 0 : else fd_log_private_fprintf_0( STDERR_FILENO, "Log to stdout\n" );
1028 12 : log_fileno = STDOUT_FILENO;
1029 877 : } else {
1030 877 : if( cmdline && !log_path_sz ) fd_log_private_fprintf_0( STDERR_FILENO, "--log-path not specified; using autogenerated path\n" );
1031 877 : log_fileno = open( fd_log_private_path, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH );
1032 877 : if( log_fileno==-1 ) {
1033 0 : fd_log_private_fprintf_0( STDERR_FILENO, "open failed (--log-path \"%s\"); unable to boot (%d-%s)\n", fd_log_private_path, errno, fd_io_strerror( errno ) );
1034 0 : exit(1);
1035 0 : }
1036 : /* When a process reruns itself (e.g. elevating to root) it passes
1037 : the already announced log path along, don't print it twice. */
1038 877 : char const * announced = fd_env_strip_cmdline_cstr( NULL, NULL, NULL, "FD_LOG_PATH_ANNOUNCED", NULL );
1039 877 : if( FD_LIKELY( !announced || strcmp( announced, fd_log_private_path ) ) ) {
1040 877 : if( fd_log_colorize() ) fd_log_private_fprintf_0( STDERR_FILENO, TEXT_DIM "Log at \"%s\"" TEXT_NORMAL "\n", fd_log_private_path );
1041 877 : else fd_log_private_fprintf_0( STDERR_FILENO, "Log at \"%s\"\n", fd_log_private_path );
1042 877 : }
1043 877 : }
1044 2701 : return log_fileno;
1045 2701 : }
1046 :
1047 : void
1048 : fd_log_private_boot( int * pargc,
1049 2701 : char *** pargv ) {
1050 : //FD_LOG_INFO(( "fd_log: booting" )); /* Log not online yet */
1051 :
1052 2701 : char buf[ FD_LOG_NAME_MAX ];
1053 :
1054 : /* Init our our application logical ids */
1055 : /* FIXME: CONSIDER EXPLICIT SPECIFICATION OF RANGE OF THREADS
1056 : INSTEAD OF ATOMIC COUNTER FROM BASE */
1057 :
1058 2701 : fd_log_private_app_id_set( fd_env_strip_cmdline_ulong( pargc, pargv, "--log-app-id", "FD_LOG_APP_ID", 0UL ) );
1059 :
1060 2701 : fd_log_private_app_set( fd_env_strip_cmdline_cstr( pargc, pargv, "--log-app", "FD_LOG_APP", NULL ) );
1061 :
1062 2701 : # if FD_HAS_THREADS
1063 2701 : fd_log_private_thread_id_ctr = fd_env_strip_cmdline_ulong( pargc, pargv, "--log-thread-id", "FD_LOG_THREAD_ID", 0UL );
1064 2701 : ulong thread_id = fd_log_private_thread_id_next();
1065 : # else
1066 : ulong thread_id = fd_env_strip_cmdline_ulong( pargc, pargv, "--log-thread-id", "FD_LOG_THREAD_ID", 0UL );
1067 : # endif
1068 2701 : fd_log_private_thread_id_set( thread_id );
1069 :
1070 2701 : fd_log_thread_set( fd_env_strip_cmdline_cstr( pargc, pargv, "--log-thread", "FD_LOG_THREAD", NULL ) );
1071 :
1072 : /* Init our application physical ids */
1073 : /* We ignore any user specified cpu-id in favor of the actual core
1074 : assigned by the host OS. We strip it from the command line so
1075 : downstream command line handling is identical from user's point of
1076 : view. */
1077 :
1078 2701 : fd_log_private_host_id_set( fd_env_strip_cmdline_ulong( pargc, pargv, "--log-host-id", "FD_LOG_HOST_ID", 0UL ) );
1079 :
1080 2701 : char const * host = fd_env_strip_cmdline_cstr( pargc, pargv, "--log-host", "FD_LOG_HOST", NULL );
1081 2701 : if( !host ) { if( !gethostname( buf, FD_LOG_NAME_MAX ) ) buf[ FD_LOG_NAME_MAX-1UL ] = '\0', host = buf; }
1082 2701 : fd_msan_unpoison( (void *)host, FD_LOG_NAME_MAX );
1083 2701 : fd_log_private_host_set( host );
1084 :
1085 2701 : fd_env_strip_cmdline_ulong( pargc, pargv, "--log-cpu-id", "FD_LOG_CPU_ID", 0UL ); /* FIXME: LOG IGNORING? */
1086 2701 : fd_log_private_cpu_id_set( fd_log_private_cpu_id_default() );
1087 :
1088 2701 : fd_log_cpu_set( fd_env_strip_cmdline_cstr( pargc, pargv, "--log-cpu", "FD_LOG_CPU", NULL ) );
1089 :
1090 : /* Init our thread group ids */
1091 : /* We ignore any user specified group id and tid in favor of the actual
1092 : group id and tid assigned by the host OS. We strip it from the
1093 : command line so downstream command line handling is identical from
1094 : user's point of view. */
1095 :
1096 2701 : fd_env_strip_cmdline_ulong( pargc, pargv, "--log-group-id", "FD_LOG_GROUP_ID", 0UL ); /* FIXME: LOG IGNORING? */
1097 2701 : pid_t pid = getpid();
1098 2701 : fd_log_private_group_id_set( fd_ulong_if( pid>(pid_t)0, (ulong)pid, ULONG_MAX ) );
1099 :
1100 2701 : char const * group = fd_env_strip_cmdline_cstr( pargc, pargv, "--log-group", "FD_LOG_GROUP", NULL );
1101 2701 : # if defined(__linux__)
1102 2701 : if( !group ) group = program_invocation_short_name;
1103 : # elif defined(__FreeBSD__)
1104 : if( !group ) group = getprogname();
1105 : # endif
1106 2701 : if( !group ) group = (pargc && pargv && (*pargc)>0) ? (*pargv)[0] : NULL;
1107 2701 : fd_log_private_group_set( group );
1108 :
1109 2701 : fd_env_strip_cmdline_ulong( pargc, pargv, "--log-tid", "FD_LOG_TID", 0UL ); /* FIXME: LOG IGNORING? */
1110 2701 : fd_log_private_tid_set( fd_log_private_tid_default() );
1111 :
1112 2701 : fd_env_strip_cmdline_ulong( pargc, pargv, "--log-user-id", "FD_LOG_USER_ID", 0UL ); /* FIXME: LOG IGNORING? */
1113 2701 : fd_log_private_user_id_set( fd_log_private_user_id_default() );
1114 :
1115 2701 : char const * user = fd_env_strip_cmdline_cstr( pargc, pargv, "--log-user", "FD_LOG_USER", NULL );
1116 2701 : if( !user ) user = getenv( "LOGNAME" );
1117 2701 : if( !user ) user = getlogin();
1118 2701 : fd_log_private_user_set( user );
1119 :
1120 : /* Configure the log */
1121 :
1122 2701 : fd_log_private_dedup = fd_env_strip_cmdline_int( pargc, pargv, "--log-dedup", "FD_LOG_DEDUP", 1 );
1123 :
1124 2701 : int colorize = 0;
1125 2701 : do {
1126 2701 : char const * cstr = fd_env_strip_cmdline_cstr( pargc, pargv, "--log-colorize", "FD_LOG_COLORIZE", NULL );
1127 2701 : if( cstr ) { colorize = fd_cstr_to_int( cstr ); break; }
1128 :
1129 2701 : colorize = fd_log_should_colorize();
1130 2701 : } while(0);
1131 2701 : fd_log_colorize_set( colorize );
1132 :
1133 2701 : fd_log_level_logfile_set( fd_env_strip_cmdline_int( pargc, pargv, "--log-level-logfile", "FD_LOG_LEVEL_LOGFILE", 1 ) );
1134 2701 : fd_log_level_stderr_set ( fd_env_strip_cmdline_int( pargc, pargv, "--log-level-stderr", "FD_LOG_LEVEL_STDERR", 2 ) );
1135 2701 : fd_log_level_flush_set ( fd_env_strip_cmdline_int( pargc, pargv, "--log-level-flush", "FD_LOG_LEVEL_FLUSH", 3 ) );
1136 2701 : fd_log_level_core_set ( fd_env_strip_cmdline_int( pargc, pargv, "--log-level-core", "FD_LOG_LEVEL_CORE", 5 ) );
1137 :
1138 : /* Hook up signal handlers */
1139 :
1140 2701 : int log_backtrace = fd_env_strip_cmdline_int( pargc, pargv, "--log-backtrace", "FD_LOG_BACKTRACE", 1 );
1141 2701 : if( log_backtrace || fd_log_private_signal_handler ) {
1142 :
1143 2698 : # if FD_HAS_BACKTRACE
1144 : /* If libgcc isn't already linked into the program when a trapped
1145 : signal is received by an application, calls to backtrace and
1146 : backtrace_symbols_fd within the signal handler can silently
1147 : invoke the dynamic linker, which in turn can do silent async
1148 : signal unsafe behavior behind our back. We do dummy calls to
1149 : backtrace and backtrace_symbols_fd here to avoid dynamic linking
1150 : surprises in the signal handler. (Hat tip to runtimeverification
1151 : for finding this.) */
1152 :
1153 2698 : void * btrace[128];
1154 2698 : (void)backtrace( btrace, 128 );
1155 2698 : # endif /* FD_HAS_BACKTRACE */
1156 :
1157 : /* This is all overridable POSIX sigs whose default behavior is to
1158 : abort the program. It will backtrace and then fallback to the
1159 : default behavior. */
1160 2698 : fd_log_private_sig_trap( SIGABRT );
1161 2698 : fd_log_private_sig_trap( SIGALRM );
1162 2698 : fd_log_private_sig_trap( SIGFPE );
1163 2698 : fd_log_private_sig_trap( SIGHUP );
1164 2698 : fd_log_private_sig_trap( SIGILL );
1165 2698 : fd_log_private_sig_trap( SIGQUIT );
1166 2698 : fd_log_private_sig_trap( SIGPIPE );
1167 2698 : fd_log_private_sig_trap( SIGSEGV );
1168 2698 : fd_log_private_sig_trap( SIGUSR1 );
1169 2698 : fd_log_private_sig_trap( SIGUSR2 );
1170 2698 : fd_log_private_sig_trap( SIGBUS );
1171 2698 : fd_log_private_sig_trap( SIGPOLL );
1172 2698 : fd_log_private_sig_trap( SIGPROF );
1173 2698 : fd_log_private_sig_trap( SIGSYS );
1174 2698 : fd_log_private_sig_trap( SIGTRAP );
1175 2698 : fd_log_private_sig_trap( SIGVTALRM );
1176 2698 : fd_log_private_sig_trap( SIGXCPU );
1177 2698 : fd_log_private_sig_trap( SIGXFSZ );
1178 2698 : }
1179 :
1180 : /* Hook up the permanent log */
1181 2701 : char const * log_path = fd_env_strip_cmdline_cstr( pargc, pargv, "--log-path", "FD_LOG_PATH", NULL );
1182 2701 : FD_VOLATILE( fd_log_private_fileno ) = fd_log_private_open_path( 1, log_path );
1183 :
1184 2701 : if( !fd_log_private_unclean_exit ) {
1185 2701 : if( atexit( fd_log_private_cleanup ) ) { fd_log_private_fprintf_0( STDERR_FILENO, "atexit failed; unable to boot\n" ); exit(1); }
1186 2701 : }
1187 :
1188 : /* At this point, logging online */
1189 2701 : if( fd_log_build_info_sz>1UL ) FD_LOG_INFO(( "fd_log: build info:\n%s", fd_log_build_info ));
1190 0 : else FD_LOG_INFO(( "fd_log: build info not available" ));
1191 2701 : FD_LOG_INFO(( "fd_log: --log-path %s", fd_log_private_path ));
1192 2701 : FD_LOG_INFO(( "fd_log: --log-dedup %i", fd_log_private_dedup ));
1193 2701 : FD_LOG_INFO(( "fd_log: --log-colorize %i", fd_log_colorize() ));
1194 2701 : FD_LOG_INFO(( "fd_log: --log-level-logfile %i", fd_log_level_logfile() ));
1195 2701 : FD_LOG_INFO(( "fd_log: --log-level-stderr %i", fd_log_level_stderr() ));
1196 2701 : FD_LOG_INFO(( "fd_log: --log-level-flush %i", fd_log_level_flush() ));
1197 2701 : FD_LOG_INFO(( "fd_log: --log-level-core %i", fd_log_level_core() ));
1198 2701 : FD_LOG_INFO(( "fd_log: --log-app-id %lu", fd_log_app_id() ));
1199 2701 : FD_LOG_INFO(( "fd_log: --log-app %s", fd_log_app() ));
1200 2701 : FD_LOG_INFO(( "fd_log: --log-thread-id %lu", fd_log_thread_id() ));
1201 2701 : FD_LOG_INFO(( "fd_log: --log-thread %s", fd_log_thread() ));
1202 2701 : FD_LOG_INFO(( "fd_log: --log-host-id %lu", fd_log_host_id() ));
1203 2701 : FD_LOG_INFO(( "fd_log: --log-host %s", fd_log_host() ));
1204 2701 : FD_LOG_INFO(( "fd_log: --log-cpu-id %lu", fd_log_cpu_id() ));
1205 2701 : FD_LOG_INFO(( "fd_log: --log-cpu %s", fd_log_cpu() ));
1206 2701 : FD_LOG_INFO(( "fd_log: --log-group-id %lu", fd_log_group_id() ));
1207 2701 : FD_LOG_INFO(( "fd_log: --log-group %s", fd_log_group() ));
1208 2701 : FD_LOG_INFO(( "fd_log: --log-tid %lu", fd_log_tid() ));
1209 2701 : FD_LOG_INFO(( "fd_log: --log-user-id %lu", fd_log_user_id() ));
1210 2701 : FD_LOG_INFO(( "fd_log: --log-user %s", fd_log_user() ));
1211 :
1212 2701 : FD_LOG_INFO(( "fd_log: boot success" ));
1213 2701 : }
1214 :
1215 : void
1216 : fd_log_private_boot_custom( ulong app_id,
1217 : char const * app,
1218 : ulong thread_id,
1219 : char const * thread,
1220 : ulong host_id,
1221 : char const * host,
1222 : ulong cpu_id,
1223 : char const * cpu,
1224 : ulong group_id,
1225 : char const * group,
1226 : ulong tid,
1227 : ulong user_id,
1228 : char const * user,
1229 : int dedup,
1230 : int colorize,
1231 : int level_logfile,
1232 : int level_stderr,
1233 : int level_flush,
1234 : int level_core,
1235 : int log_fd,
1236 0 : char const * log_path ) {
1237 0 : fd_log_private_app_id_set( app_id );
1238 0 : fd_log_private_app_set( app );
1239 0 : fd_log_private_thread_id_set( thread_id );
1240 0 : fd_log_thread_set( thread );
1241 0 : fd_log_private_host_id_set( host_id );
1242 0 : fd_log_private_host_set( host );
1243 0 : fd_log_private_cpu_id_set( cpu_id );
1244 0 : fd_log_cpu_set( cpu );
1245 0 : fd_log_private_group_id_set( group_id );
1246 0 : fd_log_private_group_set( group );
1247 0 : fd_log_private_tid_set( tid );
1248 0 : fd_log_private_user_id_set( user_id );
1249 0 : fd_log_private_user_set( user );
1250 :
1251 0 : fd_log_private_dedup = dedup;
1252 0 : fd_log_colorize_set( colorize );
1253 :
1254 0 : fd_log_level_logfile_set( level_logfile );
1255 0 : fd_log_level_stderr_set ( level_stderr );
1256 0 : fd_log_level_flush_set ( level_flush );
1257 0 : fd_log_level_core_set ( level_core );
1258 :
1259 : /* Hook up the permanent log */
1260 0 : if( -1!=log_fd ) {
1261 : /* Log file descriptor was set up before boot, user wishes to log there directly. */
1262 0 : fd_log_private_path[0] = '\0';
1263 0 : FD_VOLATILE( fd_log_private_fileno ) = log_fd;
1264 0 : } else {
1265 0 : FD_VOLATILE( fd_log_private_fileno ) = fd_log_private_open_path( 0, log_path );
1266 0 : }
1267 :
1268 0 : if( FD_UNLIKELY( fd_log_private_signal_handler ) ) {
1269 0 : # if FD_HAS_BACKTRACE
1270 : /* See note above about needing to prime backtrace */
1271 0 : void * btrace[128];
1272 0 : (void)backtrace( btrace, 128 );
1273 0 : # endif
1274 :
1275 : /* This is all overridable POSIX sigs whose default behavior is to
1276 : abort the program. It will backtrace and then fallback to the
1277 : default behavior. */
1278 0 : fd_log_private_sig_trap( SIGABRT );
1279 0 : fd_log_private_sig_trap( SIGALRM );
1280 0 : fd_log_private_sig_trap( SIGFPE );
1281 0 : fd_log_private_sig_trap( SIGHUP );
1282 0 : fd_log_private_sig_trap( SIGILL );
1283 0 : fd_log_private_sig_trap( SIGQUIT );
1284 0 : fd_log_private_sig_trap( SIGPIPE );
1285 0 : fd_log_private_sig_trap( SIGSEGV );
1286 0 : fd_log_private_sig_trap( SIGUSR1 );
1287 0 : fd_log_private_sig_trap( SIGUSR2 );
1288 0 : fd_log_private_sig_trap( SIGBUS );
1289 0 : fd_log_private_sig_trap( SIGPOLL );
1290 0 : fd_log_private_sig_trap( SIGPROF );
1291 0 : fd_log_private_sig_trap( SIGSYS );
1292 0 : fd_log_private_sig_trap( SIGTRAP );
1293 0 : fd_log_private_sig_trap( SIGVTALRM );
1294 0 : fd_log_private_sig_trap( SIGXCPU );
1295 0 : fd_log_private_sig_trap( SIGXFSZ );
1296 0 : }
1297 :
1298 : /* At this point, logging online */
1299 0 : if( fd_log_build_info_sz>1UL ) FD_LOG_INFO(( "fd_log: build info:\n%s", fd_log_build_info ));
1300 0 : else FD_LOG_INFO(( "fd_log: build info not available" ));
1301 0 : FD_LOG_INFO(( "fd_log: --log-path %s", fd_log_private_path ));
1302 0 : FD_LOG_INFO(( "fd_log: --log-dedup %i", fd_log_private_dedup ));
1303 0 : FD_LOG_INFO(( "fd_log: --log-colorize %i", fd_log_colorize() ));
1304 0 : FD_LOG_INFO(( "fd_log: --log-level-logfile %i", fd_log_level_logfile() ));
1305 0 : FD_LOG_INFO(( "fd_log: --log-level-logfile %i", fd_log_level_logfile() ));
1306 0 : FD_LOG_INFO(( "fd_log: --log-level-stderr %i", fd_log_level_stderr() ));
1307 0 : FD_LOG_INFO(( "fd_log: --log-level-flush %i", fd_log_level_flush() ));
1308 0 : FD_LOG_INFO(( "fd_log: --log-level-core %i", fd_log_level_core() ));
1309 0 : FD_LOG_INFO(( "fd_log: --log-app-id %lu", fd_log_app_id() ));
1310 0 : FD_LOG_INFO(( "fd_log: --log-app %s", fd_log_app() ));
1311 0 : FD_LOG_INFO(( "fd_log: --log-thread-id %lu", fd_log_thread_id() ));
1312 0 : FD_LOG_INFO(( "fd_log: --log-thread %s", fd_log_thread() ));
1313 0 : FD_LOG_INFO(( "fd_log: --log-host-id %lu", fd_log_host_id() ));
1314 0 : FD_LOG_INFO(( "fd_log: --log-host %s", fd_log_host() ));
1315 0 : FD_LOG_INFO(( "fd_log: --log-cpu-id %lu", fd_log_cpu_id() ));
1316 0 : FD_LOG_INFO(( "fd_log: --log-cpu %s", fd_log_cpu() ));
1317 0 : FD_LOG_INFO(( "fd_log: --log-group-id %lu", fd_log_group_id() ));
1318 0 : FD_LOG_INFO(( "fd_log: --log-group %s", fd_log_group() ));
1319 0 : FD_LOG_INFO(( "fd_log: --log-tid %lu", fd_log_tid() ));
1320 0 : FD_LOG_INFO(( "fd_log: --log-user-id %lu", fd_log_user_id() ));
1321 0 : FD_LOG_INFO(( "fd_log: --log-user %s", fd_log_user() ));
1322 :
1323 0 : FD_LOG_INFO(( "fd_log: boot success" ));
1324 0 : }
1325 :
1326 : void
1327 1549 : fd_log_private_halt( void ) {
1328 1549 : FD_LOG_INFO(( "fd_log: halting" ));
1329 :
1330 1549 : fd_log_private_cleanup();
1331 :
1332 : /* At this point, log is offline */
1333 :
1334 1549 : fd_log_private_path[0] = '\0';
1335 : //fd_log_private_fileno = -1; /* Already handled by cleanup */
1336 1549 : fd_log_private_dedup = 0;
1337 :
1338 1549 : fd_log_private_level_core = 0;
1339 1549 : fd_log_private_level_flush = 0;
1340 1549 : fd_log_private_level_stderr = 0;
1341 1549 : fd_log_private_level_logfile = 0;
1342 1549 : fd_log_private_colorize = 0;
1343 :
1344 1549 : fd_log_private_clock_func = fd_log_wallclock_host;
1345 1549 : fd_log_private_clock_args = NULL;
1346 :
1347 1549 : fd_log_private_user[0] = '\0';
1348 1549 : fd_log_private_user_id_init = 0;
1349 1549 : fd_log_private_user_id = 0UL;
1350 1549 : fd_log_private_tid_init = 0;
1351 1549 : fd_log_private_tid = 0UL;
1352 1549 : fd_log_private_group[0] = '\0';
1353 1549 : fd_log_private_group_id = 0UL;
1354 :
1355 1549 : fd_log_private_cpu_init = 0;
1356 1549 : fd_log_private_cpu[0] = '\0';
1357 1549 : fd_log_private_cpu_id_init = 0;
1358 1549 : fd_log_private_cpu_id = 0UL;
1359 1549 : fd_log_private_host[0] = '\0';
1360 1549 : fd_log_private_host_id = 0UL;
1361 :
1362 1549 : fd_log_private_thread_init = 0;
1363 1549 : fd_log_private_thread[0] = '\0';
1364 1549 : fd_log_private_thread_id_init = 0;
1365 1549 : fd_log_private_thread_id = 0UL;
1366 1549 : # if FD_HAS_THREADS
1367 1549 : fd_log_private_thread_id_ctr = 0UL;
1368 1549 : # endif
1369 1549 : fd_log_private_app[0] = '\0';
1370 1549 : fd_log_private_app_id = 0UL;
1371 :
1372 : //FD_LOG_INFO(( "fd_log: halt success" )); /* Log not online anymore */
1373 1549 : }
1374 :
1375 : #include <sys/resource.h>
1376 :
1377 : ulong
1378 2692 : fd_log_private_main_stack_sz( void ) {
1379 :
1380 : /* We are extra paranoid about what rlimit returns and we don't trust
1381 : environments that claim an unlimited stack size (because it just
1382 : isn't unlimited ... even if rlimit says otherwise ... which it will
1383 : if a user tries to be clever with a "ulimit -s unlimited" ... e.g.
1384 : tile0's stack highest address is at 128 TiB-4KiB typically on
1385 : modern Linux and grows down while the text / data / heap grow up
1386 : from 0B ... so stack size is practically always going to be << 128
1387 : TiB irrespective of any getrlimit claim). TODO: It looks like
1388 : pthead_attr_getstack might be getrlimit based under the hood, so
1389 : maybe just use pthread_attr_getstack here too? */
1390 :
1391 2692 : struct rlimit rlim[1];
1392 2692 : int err = getrlimit( RLIMIT_STACK, rlim );
1393 2692 : if( FD_UNLIKELY( err ) ) {
1394 0 : FD_LOG_WARNING(( "fd_log: getrlimit failed (%i-%s)", errno, fd_io_strerror( errno ) ));
1395 0 : return 0UL;
1396 0 : }
1397 :
1398 2692 : ulong stack_sz = (ulong)rlim->rlim_cur;
1399 2692 : if( FD_UNLIKELY( (rlim->rlim_cur>rlim->rlim_max) | (rlim->rlim_max>RLIM_INFINITY ) |
1400 2692 : (rlim->rlim_cur==RLIM_INFINITY) | (rlim->rlim_cur!=(rlim_t)stack_sz) ) ) {
1401 0 : FD_LOG_WARNING(( "fd_log: unexpected stack limits (rlim_cur %lu, rlim_max %lu)",
1402 0 : (ulong)rlim->rlim_cur, (ulong)rlim->rlim_max ));
1403 0 : return 0UL;
1404 0 : }
1405 :
1406 2692 : return stack_sz;
1407 2692 : }
1408 :
1409 : /* When pthread_setstack is not used to explicitly set the memory region
1410 : for a new thread's stack, pthread_create will create a memory region
1411 : (using either the requested size or a default size). And, while
1412 : pthread allows us to set and get the size of the stack region it
1413 : creates and we can get a pointer into a thread's stack by just
1414 : declaring a stack variable in that thread and we obviously know where
1415 : a thread's stack is when we explicitly specify it to pthread create,
1416 : pthreads does not seem to provide a simple way to get the extents of
1417 : the stacks it creates.
1418 :
1419 : But the relationship between a pointer in the stack and the stack
1420 : extents is non-trival because pthreads will use some of the stack for
1421 : things like thread local storage (and it will not tell us how much
1422 : stack was used by that and this is practically only known after
1423 : linking is complete and then it is not simply exposed to the
1424 : application).
1425 :
1426 : Similar uncertainty applies to the first thread's stack. We can
1427 : learn how large the stack is and get a pointer into the stack via a
1428 : stack variable but we have no simple way to get the extents. And, in
1429 : this case on recent Linux, things like command line strings and
1430 : environment strings are typically allowed to consume up to 1/4 of
1431 : main's thread stack ... these are only known at application load
1432 : time. (There is the additional caveat that the main stack might be
1433 : dynamically allocated such that the address space reserved for it
1434 : might not be backed by memory yet.)
1435 :
1436 : But, if we want to do useful run-time stack diagnostics (e.g. alloca
1437 : bounds checking / stack overflow prevention / etc), having explicit
1438 : knowledge of a thread's stack extents is very useful. Hence the
1439 : below. It would be nice if there was portable and non-horrific way
1440 : to do this (an even more horrific way is trigger seg faults by
1441 : scanning for the guard pages and then recover from the seg fault via
1442 : a longjmp ... shivers). */
1443 :
1444 : void
1445 : fd_log_private_stack_discover( ulong stack_sz,
1446 : ulong * _stack0,
1447 2692 : ulong * _stack1 ) {
1448 :
1449 2692 : if( FD_UNLIKELY( !stack_sz ) ) {
1450 0 : *_stack0 = 0UL;
1451 0 : *_stack1 = 0UL;
1452 0 : return;
1453 0 : }
1454 :
1455 2692 : ulong stack0 = 0UL;
1456 2692 : ulong stack1 = 0UL;
1457 :
1458 : /* Create a variable on the caller's stack and scan the thread group's
1459 : memory map for the memory region holding the variable. That should
1460 : be the caller's stack. */
1461 :
1462 2692 : uchar stack_mem[1];
1463 2692 : FD_VOLATILE( stack_mem[0] ) = (uchar)1; /* Paranoia to make sure compiler puts this in stack */
1464 2692 : ulong stack_addr = (ulong)stack_mem;
1465 :
1466 2692 : int filefd;
1467 2692 : if( FD_UNLIKELY( ( filefd = open( "/proc/self/maps", O_RDONLY ) ) < 0 ) ) {
1468 0 : FD_LOG_WARNING(( "open( \"/proc/self/maps\" ) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
1469 0 : *_stack0 = 0UL;
1470 0 : *_stack1 = 0UL;
1471 0 : return;
1472 0 : }
1473 :
1474 2692 : char filebuf[1<<12];
1475 2692 : ulong filelen = 0;
1476 2692 : char * p = filebuf;
1477 2692 : int found = 0;
1478 83962 : while( !found ) {
1479 :
1480 : /* Scan a line */
1481 :
1482 83962 : int full_line = 0;
1483 83962 : char * nextp;
1484 8538809 : for( nextp = p; nextp < filebuf + filelen; ) {
1485 8536114 : if( *(nextp++) == '\n' ) {
1486 81267 : full_line = 1;
1487 81267 : break;
1488 81267 : }
1489 8536114 : }
1490 83962 : if( !full_line ) {
1491 : /* We need to read more data. First shift the old data down. */
1492 2695 : filelen = (ulong)((filebuf + filelen) - p);
1493 2695 : if( filelen )
1494 0 : memmove( filebuf, p, filelen );
1495 2695 : p = filebuf;
1496 : /* Now read data */
1497 2695 : ssize_t tlen;
1498 2695 : if( FD_UNLIKELY( ( tlen = read( filefd, filebuf + filelen, sizeof(filebuf)-1U-filelen ) ) < 0 ) ) {
1499 0 : FD_LOG_WARNING(( "read( \"/proc/self/maps\" ) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
1500 0 : break;
1501 0 : }
1502 2695 : if( tlen == 0 ) break; /* End of file */
1503 2695 : filelen += (ulong)tlen;
1504 2695 : filebuf[filelen] = '\0'; /* For sscanf */
1505 2695 : continue;
1506 2695 : }
1507 :
1508 81267 : ulong m0;
1509 81267 : ulong m1;
1510 81267 : int r = sscanf( p, "%lx-%lx", &m0, &m1 );
1511 81267 : p = nextp;
1512 81267 : if( FD_UNLIKELY( r !=2 ) ) continue;
1513 :
1514 : /* Test if the stack allocation is in the discovered region */
1515 :
1516 81267 : if( FD_UNLIKELY( (m0<=stack_addr) & (stack_addr<m1) ) ) {
1517 2692 : found = 1;
1518 2692 : ulong msz = m1 - m0;
1519 2692 : if( msz==stack_sz ) { /* Memory region matches expectations */
1520 0 : stack0 = m0;
1521 0 : stack1 = m1;
1522 2692 : } else if( ((fd_log_group_id()==fd_log_tid()) & (msz<stack_sz)) ) {
1523 : /* This is the main thread, which, on recent Linux, seems to
1524 : just reserve address space for main's stack at program
1525 : start up to the application stack size limits then uses
1526 : page faults to dynamically back the stack with DRAM as the
1527 : stack grows (which is awful for performance, jitter and
1528 : reliability ... sigh). This assumes stack grows down such
1529 : that m1 is the fixed value in this process. */
1530 2692 : stack0 = m1 - stack_sz;
1531 2692 : stack1 = m1;
1532 2692 : } else {
1533 0 : FD_LOG_WARNING(( "unexpected caller stack memory region size (got %lu bytes, expected %lu bytes)", msz, stack_sz ));
1534 : /* don't trust the discovered region */
1535 0 : }
1536 2692 : break;
1537 2692 : }
1538 :
1539 81267 : }
1540 2692 : if( !found )
1541 0 : FD_LOG_WARNING(( "unable to find stack size around address 0x%lx", stack_addr ));
1542 :
1543 2692 : close(filefd);
1544 :
1545 2692 : *_stack0 = stack0;
1546 2692 : *_stack1 = stack1;
1547 2692 : }
1548 :
1549 :
1550 : int
1551 2701 : fd_log_should_colorize( void ) {
1552 2701 : char const * no_color = fd_env_strip_cmdline_cstr( NULL, NULL, NULL, "NO_COLOR", NULL );
1553 2701 : if( FD_UNLIKELY( no_color && no_color[0]!='\0' ) ) return 0;
1554 :
1555 2701 : if( FD_UNLIKELY( !isatty( STDERR_FILENO ) ) ) return 0;
1556 :
1557 0 : char const * term = fd_env_strip_cmdline_cstr( NULL, NULL, NULL, "TERM", NULL );
1558 0 : if( FD_UNLIKELY( !term || !strcmp( term, "dumb" ) ) ) return 0;
1559 :
1560 0 : char const * dirs[] = {
1561 0 : fd_env_strip_cmdline_cstr( NULL, NULL, NULL, "TERMINFO", NULL ),
1562 0 : NULL,
1563 0 : "/usr/share/terminfo",
1564 0 : "/usr/lib/terminfo",
1565 0 : "/etc/terminfo",
1566 0 : };
1567 0 : char homebuf[ PATH_MAX ];
1568 0 : char const * home = fd_env_strip_cmdline_cstr( NULL, NULL, NULL, "HOME", NULL );
1569 0 : if( FD_LIKELY( home ) ) {
1570 0 : snprintf( homebuf, sizeof(homebuf), "%s/.terminfo", home );
1571 0 : dirs[1] = homebuf;
1572 0 : }
1573 :
1574 0 : FILE * f = NULL;
1575 0 : char path[ PATH_MAX ];
1576 0 : for( ulong i=0UL; i<(sizeof(dirs)/sizeof(dirs[0])); i++ ) {
1577 0 : if( FD_UNLIKELY( !dirs[i] ) ) continue;
1578 0 : snprintf( path, sizeof(path), "%s/%c/%s", dirs[i], term[0], term );
1579 0 : f = fopen( path, "rb" );
1580 0 : if( f ) break;
1581 0 : snprintf( path, sizeof(path), "%s/%02x/%s", dirs[i], (uchar)term[0], term );
1582 0 : f = fopen( path, "rb" );
1583 0 : if( f ) break;
1584 0 : }
1585 0 : if( FD_UNLIKELY( !f ) ) return 0;
1586 :
1587 0 : ushort hdr[6];
1588 0 : if( FD_UNLIKELY( fread( hdr, 2, 6, f )!=6 ) ) goto fail;
1589 :
1590 0 : ushort magic = hdr[0];
1591 0 : ushort name_sz = hdr[1];
1592 0 : ushort bool_cnt = hdr[2];
1593 0 : ushort num_cnt = hdr[3];
1594 :
1595 0 : uint num_width;
1596 0 : if( magic==0x011A ) num_width = 2;
1597 0 : else if( magic==0x021E ) num_width = 4;
1598 0 : else goto fail;
1599 :
1600 0 : if( FD_UNLIKELY( 13>=num_cnt ) ) goto fail;
1601 :
1602 0 : long skip = (long)name_sz + (long)bool_cnt;
1603 0 : if( skip%2 ) skip++;
1604 0 : skip += 13L * (long)num_width;
1605 :
1606 0 : if( FD_UNLIKELY( fseek( f, skip, SEEK_CUR ) ) ) goto fail;
1607 :
1608 0 : int colors;
1609 0 : if( num_width==2 ) {
1610 0 : short v;
1611 0 : if( FD_UNLIKELY( fread( &v, 2, 1, f )!=1 ) ) goto fail;
1612 0 : colors = v;
1613 0 : } else {
1614 0 : if( FD_UNLIKELY( fread( &colors, 4, 1, f )!=1 ) ) goto fail;
1615 0 : }
1616 :
1617 0 : fclose( f );
1618 0 : return colors>0;
1619 :
1620 0 : fail:
1621 0 : fclose( f );
1622 0 : return 0;
1623 0 : }
1624 :
1625 : #elif FD_LOG_STYLE==1 /* generic embedded target */
1626 :
1627 : #include "fd_log.h"
1628 :
1629 : FD_FN_UNUSED static inline void _unused( void ) {} /* required due to -Wpedantic */
1630 :
1631 : #else
1632 : #error "Unknown FD_LOG_STYLE"
1633 : #endif
|