• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nickg / nvc / 6535337722

16 Oct 2023 02:45PM UTC coverage: 91.176% (+0.009%) from 91.167%
6535337722

push

github

nickg
Add configure check for gettid. Fixes #774

49047 of 53794 relevant lines covered (91.18%)

585902.76 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.97
/src/util.c
1
//
2
//  Copyright (C) 2011-2023  Nick Gasson
3
//
4
//  This program is free software: you can redistribute it and/or modify
5
//  it under the terms of the GNU General Public License as published by
6
//  the Free Software Foundation, either version 3 of the License, or
7
//  (at your option) any later version.
8
//
9
//  This program is distributed in the hope that it will be useful,
10
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
//  GNU General Public License for more details.
13
//
14
//  You should have received a copy of the GNU General Public License
15
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
//
17

18
#if defined(__MINGW32__)
19
#define WINVER 0x0A00
20
#define _WIN32_WINNT 0x0A00
21
#include <windows.h>
22
#include <fileapi.h>
23
#include <psapi.h>
24
#include <io.h>
25
#endif
26

27
#include "util.h"
28
#include "array.h"
29
#include "cpustate.h"
30
#include "debug.h"
31
#include "diag.h"
32
#include "option.h"
33
#include "thread.h"
34

35
#include <stdlib.h>
36
#include <stdio.h>
37
#include <stdarg.h>
38
#include <errno.h>
39
#include <string.h>
40
#include <stdbool.h>
41
#include <signal.h>
42
#include <stdint.h>
43
#include <inttypes.h>
44
#include <math.h>
45
#include <unistd.h>
46
#include <ctype.h>
47
#include <assert.h>
48
#include <limits.h>
49
#include <time.h>
50
#include <libgen.h>
51
#include <fcntl.h>
52

53
#include <sys/types.h>
54
#include <sys/stat.h>
55
#include <sys/time.h>
56
#ifdef HAVE_SYS_PTRACE_H
57
#include <sys/ptrace.h>
58
#endif
59
#ifdef __APPLE__
60
#include <sys/sysctl.h>
61
#include <libproc.h>
62
#endif
63
#ifdef __FreeBSD__
64
#include <sys/sysctl.h>
65
#endif
66
#ifndef __MINGW32__
67
#include <sys/mman.h>
68
#include <sys/wait.h>
69
#include <sys/resource.h>
70
#include <sys/file.h>
71
#include <sys/ioctl.h>
72
#include <termios.h>
73
#endif
74

75
#ifdef HAVE_PTHREAD
76
#include <pthread.h>
77
#endif
78

79
#ifdef HAVE_SYS_PRCTL_H
80
#include <sys/prctl.h>
81
#endif
82

83
#ifdef __CYGWIN__
84
#include <process.h>
85
#endif
86

87
#if defined(HAVE_UCONTEXT_H)
88
#include <ucontext.h>
89
#elif defined(HAVE_SYS_UCONTEXT_H)
90
#include <sys/ucontext.h>
91
#endif
92

93
#define N_TRACE_DEPTH   16
94
#define ERROR_SZ        1024
95
#define PAGINATE_RIGHT  72
96
#define TRACE_MAX_LINE  256
97

98
#define ANSI_RESET      0
99
#define ANSI_BOLD       1
100
#define ANSI_FG_BLACK   30
101
#define ANSI_FG_RED     31
102
#define ANSI_FG_GREEN   32
103
#define ANSI_FG_YELLOW  33
104
#define ANSI_FG_BLUE    34
105
#define ANSI_FG_MAGENTA 35
106
#define ANSI_FG_CYAN    36
107
#define ANSI_FG_WHITE   37
108

109
#define MAX_FMT_BUFS    32
110
#define MAX_PRINTF_BUFS 8
111

112
#define HUGE_PAGE_SIZE  0x200000
113

114
typedef void (*print_fn_t)(const char *fmt, ...);
115

116
static char *ansi_vasprintf(const char *fmt, va_list ap, bool force_plain);
117

118
typedef struct _fault_handler fault_handler_t;
119

120
struct color_escape {
121
   const char *name;
122
   int         value;
123
};
124

125
struct text_buf {
126
   char  *buf;
127
   size_t alloc;
128
   size_t len;
129
};
130

131
struct _fault_handler {
132
   fault_handler_t *next;
133
   fault_fn_t       fn;
134
   void            *context;
135
};
136

137
static bool             want_color = false;
138
static bool             want_links = false;
139
static bool             want_utf8 = false;
140
static message_style_t  message_style = MESSAGE_FULL;
141
static sig_atomic_t     crashing = SIG_ATOMIC_MAX;
142
static int              term_width = 0;
143
static void            *ctrl_c_arg = NULL;
144
static fault_handler_t *fault_handlers = NULL;
145

146
#ifdef __MINGW32__
147
static UINT win32_codepage = 0;
148
#endif
149

150
static void (*ctrl_c_fn)(void *) = NULL;
151

152
static const struct color_escape escapes[] = {
153
   { "",        ANSI_RESET },
154
   { "bold",    ANSI_BOLD },
155
   { "black",   ANSI_FG_BLACK },
156
   { "red",     ANSI_FG_RED },
157
   { "green",   ANSI_FG_GREEN },
158
   { "yellow",  ANSI_FG_YELLOW },
159
   { "blue",    ANSI_FG_BLUE },
160
   { "magenta", ANSI_FG_MAGENTA },
161
   { "cyan",    ANSI_FG_CYAN },
162
   { "white",   ANSI_FG_WHITE },
163
};
164

165
void *xmalloc(size_t size)
8,761,760✔
166
{
167
   void *p = malloc(size);
8,761,760✔
168
   if (p == NULL)
8,761,760✔
169
      fatal("memory exhausted (malloc %lu)", (long unsigned)size);
×
170
   return p;
8,761,760✔
171
}
172

173
void *xmalloc_flex(size_t fixed, size_t nelems, size_t size)
6,532,620✔
174
{
175
   size_t bytes;
6,532,620✔
176
   if (__builtin_mul_overflow(nelems, size, &bytes))
6,532,620✔
177
      fatal_trace("array size overflow: requested %zd * %zd bytes",
×
178
                  nelems, size);
179

180
   return xmalloc(fixed + bytes);
6,532,620✔
181
}
182

183
void *xmalloc_array(size_t nelems, size_t size)
388,480✔
184
{
185
   return xmalloc_flex(0, nelems, size);
388,480✔
186
}
187

188
void *xcalloc(size_t size)
2,994,000✔
189
{
190
   void *p = calloc(1, size);
2,994,000✔
191
   if (p == NULL)
2,994,000✔
192
      fatal("memory exhausted (calloc %lu)", (long unsigned)size);
×
193
   return p;
2,994,000✔
194
}
195

196
void *xcalloc_flex(size_t fixed, size_t nelems, size_t size)
933,308✔
197
{
198
   size_t bytes;
933,308✔
199
   if (__builtin_mul_overflow(nelems, size, &bytes))
933,308✔
200
      fatal_trace("array size overflow: requested %zd * %zd bytes",
×
201
                  nelems, size);
202

203
   return xcalloc(fixed + bytes);
933,308✔
204
}
205

206
void *xcalloc_array(size_t nelems, size_t size)
883,811✔
207
{
208
   return xcalloc_flex(0, nelems, size);
883,811✔
209
}
210

211
void *xrealloc(void *ptr, size_t size)
1,892,700✔
212
{
213
   ptr = realloc(ptr, size);
1,892,700✔
214
   if (ptr == NULL)
1,892,700✔
215
      fatal("memory exhausted (realloc %lu)", (long unsigned)size);
×
216
   return ptr;
1,892,700✔
217
}
218

219
void *xrealloc_array(void *ptr, size_t nelems, size_t size)
1,541,710✔
220
{
221
   size_t bytes;
1,541,710✔
222
   if (__builtin_mul_overflow(nelems, size, &bytes))
1,541,710✔
223
      fatal_trace("array size overflow: requested %zd * %zd bytes",
×
224
                  nelems, size);
225

226
   return xrealloc(ptr, bytes);
1,541,710✔
227
}
228

229
void *xrealloc_flex(void *ptr, size_t fixed, size_t nelems, size_t size)
28,215✔
230
{
231
   size_t bytes;
28,215✔
232
   if (__builtin_mul_overflow(nelems, size, &bytes))
28,215✔
233
      fatal_trace("array size overflow: requested %zd * %zd bytes",
×
234
                  nelems, size);
235

236
   return xrealloc(ptr, bytes + fixed);
28,215✔
237
}
238

239
char *xstrdup(const char *str)
241,078✔
240
{
241
   char *copy = strdup(str);
241,078✔
242
   if (copy == NULL)
241,078✔
243
      fatal("memory exhausted (strdup)");
×
244
   return copy;
241,078✔
245
}
246

247
char *xstrndup(const char *str, size_t n)
42✔
248
{
249
   char *copy = strndup(str, n);
42✔
250
   if (copy == NULL)
42✔
251
      fatal("memory exhausted (strndup)");
×
252
   return copy;
42✔
253
}
254

255
char *xvasprintf(const char *fmt, va_list ap)
229,207✔
256
{
257
   char *strp = NULL;
229,207✔
258
   if (vasprintf(&strp, fmt, ap) < 0)
229,207✔
259
      fatal("memory exhausted (vasprintf)");
×
260
   return strp;
229,207✔
261
}
262

263
char *xasprintf(const char *fmt, ...)
86,627✔
264
{
265
   va_list ap;
86,627✔
266
   va_start(ap, fmt);
86,627✔
267
   char *strp = xvasprintf(fmt, ap);
86,627✔
268
   va_end(ap);
86,627✔
269
   return strp;
86,627✔
270
}
271

272
void errorf(const char *fmt, ...)
12✔
273
{
274
   diag_t *d = diag_new(DIAG_ERROR, NULL);
12✔
275
   va_list ap;
12✔
276
   va_start(ap, fmt);
12✔
277
   diag_vprintf(d, fmt, ap);
12✔
278
   va_end(ap);
12✔
279
   diag_emit(d);
12✔
280
}
12✔
281

282
void warnf(const char *fmt, ...)
17✔
283
{
284
   diag_t *d = diag_new(DIAG_WARN, NULL);
17✔
285
   va_list ap;
17✔
286
   va_start(ap, fmt);
17✔
287
   diag_vprintf(d, fmt, ap);
17✔
288
   va_end(ap);
17✔
289
   diag_emit(d);
17✔
290
}
17✔
291

292
void notef(const char *fmt, ...)
3,876✔
293
{
294
   diag_t *d = diag_new(DIAG_NOTE, NULL);
3,876✔
295
   va_list ap;
3,876✔
296
   va_start(ap, fmt);
3,876✔
297
   diag_vprintf(d, fmt, ap);
3,876✔
298
   va_end(ap);
3,876✔
299
   diag_emit(d);
3,876✔
300
}
3,876✔
301

302
void debugf(const char *fmt, ...)
3,119✔
303
{
304
   diag_t *d = diag_new(DIAG_DEBUG, NULL);
3,119✔
305
   va_list ap;
3,119✔
306
   va_start(ap, fmt);
3,119✔
307
   diag_vprintf(d, fmt, ap);
3,119✔
308
   va_end(ap);
3,119✔
309
   diag_emit(d);
3,119✔
310
}
3,119✔
311

312
static char *ansi_vasprintf(const char *fmt, va_list ap, bool force_plain)
81,604✔
313
{
314
   // Replace color strings like "$red$foo$$bar" with ANSI escaped
315
   // strings like "\033[31mfoo\033[0mbar"
316

317
   static int override = 0;
81,604✔
318

319
   if (strchr(fmt, '$') == NULL)
81,604✔
320
      return xvasprintf(fmt, ap);
21,594✔
321

322
   LOCAL_TEXT_BUF tb = tb_new();
120,020✔
323
   const char *escape_start = NULL;
60,010✔
324

325
   while (*fmt != '\0') {
979,438✔
326
      if (*fmt == '$') {
919,428✔
327
         if (escape_start == NULL)
195,547✔
328
            escape_start = fmt;
329
         else {
330
            const char *e = escape_start + 1;
97,770✔
331
            size_t len = fmt - e;
97,770✔
332

333
            bool bold;
97,770✔
334
            if ((bold = (*e == '!')))
97,770✔
335
               ++e, --len;
15✔
336

337
            bool bright;
97,770✔
338
            if ((bright = (*e == '+')))
97,770✔
339
               ++e, --len;
13✔
340

341
            if ((*e == '<' || *e == '>') && *(e + 1) == '$') {
97,770✔
342
               override += *e == '<' ? -1 : 1;
2✔
343
               escape_start = NULL;
2✔
344
            }
345
            else if (strncmp(e, "link:", 5) == 0) {
97,768✔
346
               if (want_links && !force_plain) {
10,955✔
347
                  tb_cat(tb, "\033]8;;");
×
348
                  tb_catn(tb, e + 5, len - 5);
×
349
                  tb_cat(tb, "\033]8;;\07");
×
350
               }
351
               else {
352
                  const char *bel = strchr(e, '\07');
10,955✔
353
                  if (bel && bel < e + len)
10,955✔
354
                     tb_catn(tb, bel + 1, e + len - bel - 1);
10,955✔
355
               }
356
               escape_start = NULL;
357
            }
358
            else if (want_color && !force_plain && override >= 0) {
86,813✔
359
               bool found = false;
8✔
360

361
               if (*e == '#') {
8✔
362
                  char *eptr;
1✔
363
                  int code = strtoul(e + 1, &eptr, 10);
1✔
364
                  if (eptr == e + len) {
1✔
365
                     if (bold)
1✔
366
                        tb_printf(tb, "\033[1;38;5;%dm", code);
×
367
                     else
368
                        tb_printf(tb, "\033[38;5;%dm", code);
1✔
369
                     found = true;
370
                  }
371
               }
372
               else if (strncmp(e, "link:", 5) == 0) {
373
                  tb_cat(tb, "\033]8;;");
374
                  tb_catn(tb, e + 5, len - 5);
375
                  tb_cat(tb, "\033]8;;\07");
376
                  found = true;
377
               }
378

379
               for (int i = 0; !found && i < ARRAY_LEN(escapes); i++) {
37✔
380
                  if (strncmp(e, escapes[i].name, len) == 0) {
34✔
381
                     int code = escapes[i].value + (bright ? 60 : 0);
5✔
382
                     if (bold)
5✔
383
                        tb_printf(tb, "\033[1;%dm", code);
1✔
384
                     else
385
                        tb_printf(tb, "\033[%dm", code);
4✔
386
                     found = true;
387
                     break;
388
                  }
389
               }
390

391
               if (!found) {
3✔
392
                  tb_catn(tb, escape_start, len + 1 + bold);
2✔
393
                  escape_start = fmt;
2✔
394
               }
395
               else
396
                  escape_start = NULL;
397
            }
398
            else
399
               escape_start = NULL;
400
         }
401
      }
402
      else if (escape_start == NULL)
723,881✔
403
         tb_append(tb, *fmt);
313,791✔
404

405
      ++fmt;
919,428✔
406
   }
407

408
   if (escape_start != NULL)
60,010✔
409
      tb_cat(tb, escape_start);
9✔
410

411
   return xvasprintf(tb_get(tb), ap);
60,010✔
412
}
413

414
static int color_vfprintf(FILE *f, const char *fmt, va_list ap)
69,939✔
415
{
416
   char *strp LOCAL = ansi_vasprintf(fmt, ap, false);
69,939✔
417

418
   bool escape = false;
69,939✔
419
   int len = 0;
69,939✔
420
   for (const char *p = strp; *p != '\0'; p++) {
984,691✔
421
      if (*p == '\033')
914,752✔
422
         escape = true;
423
      if (escape)
914,752✔
424
         escape = (*p != 'm');
×
425
      else
426
         len += 1;
914,752✔
427
   }
428

429
   fputs(strp, f);
69,939✔
430
   return len;
69,939✔
431
}
432

433
char *color_vasprintf(const char *fmt, va_list ap)
6✔
434
{
435
   return ansi_vasprintf(fmt, ap, false);
6✔
436
}
437

438
char *strip_color(const char *fmt, va_list ap)
11,639✔
439
{
440
   return ansi_vasprintf(fmt, ap, true);
11,639✔
441
}
442

443
int color_fprintf(FILE *f, const char *fmt, ...)
69,920✔
444
{
445
   va_list ap;
69,920✔
446
   va_start(ap, fmt);
69,920✔
447
   const int len = color_vfprintf(f, fmt, ap);
69,920✔
448
   va_end(ap);
69,920✔
449
   return len;
69,920✔
450
}
451

452
int color_printf(const char *fmt, ...)
19✔
453
{
454
   va_list ap;
19✔
455
   va_start(ap, fmt);
19✔
456
   int rc = color_vprintf(fmt, ap);
19✔
457
   va_end(ap);
19✔
458
   return rc;
19✔
459
}
460

461
int color_vprintf(const char *fmt, va_list ap)
19✔
462
{
463
   return color_vfprintf(stdout, fmt, ap);
19✔
464
}
465

466
char *color_asprintf(const char *fmt, ...)
20✔
467
{
468
   va_list ap;
20✔
469
   va_start(ap, fmt);
20✔
470
   char *str = ansi_vasprintf(fmt, ap, false);
20✔
471
   va_end(ap);
20✔
472
   return str;
20✔
473
}
474

475
bool color_terminal(void)
18,329✔
476
{
477
   return want_color;
18,329✔
478
}
479

480
bool utf8_terminal(void)
1,314✔
481
{
482
   return want_utf8;
1,314✔
483
}
484

485
void print_centred(const char *text)
×
486
{
487
   if (term_width == 0)
×
488
      fputs(text, stdout);
×
489
   else {
490
      const int pad = (term_width - strlen(text)) / 2;
×
491
      printf("%*s%s", pad, "", text);
×
492
   }
493
}
×
494

495
void fatal_exit(int status)
42✔
496
{
497
   async_barrier();
42✔
498

499
   if (atomic_load(&crashing) != SIG_ATOMIC_MAX)
42✔
500
      _exit(status);   // Exit during crash
×
501
   else if (!thread_attached() || thread_id() != 0)
42✔
502
      _exit(status);
×
503
   else
504
      exit(status);
42✔
505
}
506

507
void error_at(const loc_t *loc, const char *fmt, ...)
635✔
508
{
509
   diag_t *d = diag_new(DIAG_ERROR, loc);
635✔
510

511
   va_list ap;
635✔
512
   va_start(ap, fmt);
635✔
513
   diag_vprintf(d, fmt, ap);
635✔
514
   va_end(ap);
635✔
515

516
   diag_emit(d);
635✔
517
}
632✔
518

519
void warn_at(const loc_t *loc, const char *fmt, ...)
59✔
520
{
521
   diag_t *d = diag_new(DIAG_WARN, loc);
59✔
522

523
   va_list ap;
59✔
524
   va_start(ap, fmt);
59✔
525
   diag_vprintf(d, fmt, ap);
59✔
526
   va_end(ap);
59✔
527

528
   diag_emit(d);
59✔
529
}
59✔
530

531
void note_at(const loc_t *loc, const char *fmt, ...)
4,635✔
532
{
533
   diag_t *d = diag_new(DIAG_NOTE, loc);
4,635✔
534

535
   va_list ap;
4,635✔
536
   va_start(ap, fmt);
4,635✔
537
   diag_vprintf(d, fmt, ap);
4,635✔
538
   va_end(ap);
4,635✔
539

540
   diag_emit(d);
4,635✔
541
}
4,635✔
542

543
void fatal_at(const loc_t *loc, const char *fmt, ...)
12✔
544
{
545
   diag_t *d = diag_new(DIAG_FATAL, loc);
12✔
546
   diag_suppress(d, false);
12✔
547

548
   va_list ap;
12✔
549
   va_start(ap, fmt);
12✔
550
   diag_vprintf(d, fmt, ap);
12✔
551
   va_end(ap);
12✔
552

553
   diag_emit(d);
12✔
554
   fatal_exit(EXIT_FAILURE);
12✔
555
}
556

557
void fatal(const char *fmt, ...)
27✔
558
{
559
   diag_t *d = diag_new(DIAG_FATAL, NULL);
27✔
560
   diag_suppress(d, false);
27✔
561

562
   va_list ap;
27✔
563
   va_start(ap, fmt);
27✔
564
   diag_vprintf(d, fmt, ap);
27✔
565
   va_end(ap);
27✔
566

567
   diag_emit(d);
27✔
568
   fatal_exit(EXIT_FAILURE);
27✔
569
}
570

571
void fatal_trace(const char *fmt, ...)
×
572
{
573
   diag_t *d = diag_new(DIAG_FATAL, NULL);
×
574

575
   va_list ap;
×
576
   va_start(ap, fmt);
×
577
   diag_vprintf(d, fmt, ap);
×
578
   va_end(ap);
×
579

580
   diag_set_consumer(NULL, NULL);
×
581
   diag_suppress(d, false);
×
582
   diag_stacktrace(d, true);
×
583
   diag_emit(d);
×
584
   fatal_exit(EXIT_FAILURE);
×
585
}
586

587
void fatal_errno(const char *fmt, ...)
×
588
{
589
   diag_t *d = diag_new(DIAG_FATAL, NULL);
×
590
   diag_suppress(d, false);
×
591

592
   va_list ap;
×
593
   va_start(ap, fmt);
×
594
   diag_vprintf(d, fmt, ap);
×
595
   diag_printf(d, ": %s", last_os_error());
×
596
   va_end(ap);
×
597

598
   diag_emit(d);
×
599
   fatal_exit(EXIT_FAILURE);
×
600
}
601

602
const char *last_os_error(void)
×
603
{
604
#ifdef __MINGW32__
605
   static __thread LPSTR mbuf = NULL;
606

607
   if (mbuf != NULL)
608
      LocalFree(mbuf);
609

610
   FormatMessage(
611
      FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
612
      | FORMAT_MESSAGE_IGNORE_INSERTS,
613
      NULL,
614
      GetLastError(),
615
      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
616
      (LPSTR)&mbuf, 0, NULL);
617

618
   return mbuf;
619
#else
620
   return strerror(errno);
×
621
#endif
622
}
623

624
static void trace_one_frame(uintptr_t pc, const char *module,
625
                            const char *srcfile, const char *symbol,
626
                            unsigned lineno, unsigned colno,
627
                            ptrdiff_t disp, frame_kind_t kind)
628
{
629
   color_fprintf(stderr, "[$green$%p$$] ", (void *)pc);
630
   if (kind == FRAME_LIB)
631
      color_fprintf(stderr, "($red$%s$$) ", module);
632
   if (srcfile != NULL)
633
      color_fprintf(stderr, "%s:%d ", srcfile, lineno);
634
   if (symbol != NULL) {
635
      color_fprintf(stderr, "$yellow$%s$$", symbol);
636
      if (srcfile == NULL && disp != 0)
637
         color_fprintf(stderr, "$yellow$+0x%"PRIxPTR"$$", disp);
638
   }
639
   if (kind == FRAME_VHDL)
640
      color_fprintf(stderr, " $magenta$[VHDL]$$");
641
   fprintf(stderr, "\n");
642

643
#ifndef __MINGW32__
644
   if (srcfile != NULL) {
645
      FILE *f = fopen(srcfile, "r");
646
      if (f != NULL) {
647
         char *line LOCAL = NULL;
648
         size_t linesz = 0;
649
         for (int i = 0, len; i < lineno + 1
650
                 && (len = getline(&line, &linesz, f)) != -1; i++) {
651
            if (i < lineno - 2)
652
               continue;
653

654
            if (len <= 1)
655
               continue;
656
            else if (line[len - 1] == '\n')
657
               line[len - 1] = '\0';
658

659
            if (i == lineno - 1)
660
               color_fprintf(stderr, "$cyan$$bold$-->$$ $cyan$%s$$\n", line);
661
            else
662
               color_fprintf(stderr, "    $cyan$%s$$\n", line);
663
         }
664
         fclose(f);
665
      }
666
   }
667
#endif
668
}
669

670
__attribute__((noinline))
671
void show_stacktrace(void)
×
672
{
673
   debug_info_t *di = debug_capture();
×
674

675
   const int nframes = debug_count_frames(di);
×
676
   for (int n = 1; n < nframes; n++) {
×
677
      const debug_frame_t *f = debug_get_frame(di, n);
×
678

679
      for (debug_inline_t *inl = f->inlined; inl != NULL; inl = inl->next)
×
680
         trace_one_frame(f->pc, f->module, inl->srcfile, inl->symbol,
×
681
                         inl->lineno, inl->colno, f->disp, f->kind);
682

683
      trace_one_frame(f->pc, f->module, f->srcfile, f->symbol, f->lineno,
×
684
                      f->colno, f->disp, f->kind);
685

686
   }
687

688
   debug_free(di);
×
689

690
#if defined __linux__ && !defined HAVE_LIBDW && !defined HAVE_LIBDWARF
691
   color_fprintf(stderr, "\n$cyan$Hint: you can get better stack traces by "
692
                 "installing the libdw-dev package and reconfiguring$$\n");
693
#endif
694

695
   fflush(stderr);
×
696
}
×
697

698
#ifdef __MINGW32__
699

700
static const char *exception_name(DWORD code)
701
{
702
   switch (code) {
703
   case EXCEPTION_ACCESS_VIOLATION:
704
      return "EXCEPTION_ACCESS_VIOLATION";
705
   case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
706
      return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
707
   case EXCEPTION_BREAKPOINT:
708
      return "EXCEPTION_BREAKPOINT";
709
   case EXCEPTION_DATATYPE_MISALIGNMENT:
710
      return "EXCEPTION_DATATYPE_MISALIGNMENT";
711
   case EXCEPTION_ILLEGAL_INSTRUCTION:
712
      return "EXCEPTION_ILLEGAL_INSTRUCTION";
713
   case EXCEPTION_IN_PAGE_ERROR:
714
      return "EXCEPTION_IN_PAGE_ERROR";
715
   case EXCEPTION_INT_DIVIDE_BY_ZERO:
716
      return "EXCEPTION_INT_DIVIDE_BY_ZERO";
717
   case EXCEPTION_INT_OVERFLOW:
718
      return "EXCEPTION_INT_OVERFLOW";
719
   case EXCEPTION_PRIV_INSTRUCTION:
720
      return "EXCEPTION_PRIV_INSTRUCTION";
721
   case EXCEPTION_STACK_OVERFLOW:
722
      return "EXCEPTION_STACK_OVERFLOW";
723
   }
724

725
   return "???";
726
}
727

728
WINAPI
729
static LONG win32_exception_handler(EXCEPTION_POINTERS *ExceptionInfo)
730
{
731
   DWORD code = ExceptionInfo->ExceptionRecord->ExceptionCode;
732
   PVOID addr = ExceptionInfo->ExceptionRecord->ExceptionAddress;
733

734
#ifdef __WIN64
735
   DWORD64 ip = ExceptionInfo->ContextRecord->Rip;
736
#else
737
   DWORD ip = ExceptionInfo->ContextRecord->Eip;
738
#endif
739

740
   if (code == EXCEPTION_ACCESS_VIOLATION)
741
      addr = (PVOID)ExceptionInfo->ExceptionRecord->ExceptionInformation[1];
742

743
   color_fprintf(stderr, "\n$red$$bold$*** Caught exception %x (%s)",
744
                 (int)code, exception_name(code));
745

746
   switch (code) {
747
   case EXCEPTION_ACCESS_VIOLATION:
748
   case EXCEPTION_ILLEGAL_INSTRUCTION:
749
      fprintf(stderr, " [address=%p, ip=%p]", (void *)addr, (void*)ip);
750
      break;
751
   }
752

753
   color_fprintf(stderr, " ***$$\n\n");
754
   fflush(stderr);
755

756
#ifdef __WIN64
757
   if (code != EXCEPTION_STACK_OVERFLOW)
758
      show_stacktrace();
759
#endif
760

761
  return EXCEPTION_EXECUTE_HANDLER;
762
}
763

764
#else
765

766
#ifndef __SANITIZE_THREAD__
767
static const char *signame(int sig, siginfo_t *info)
768
{
769
   switch (sig) {
770
   case SIGSEGV:
771
#ifdef __linux__
772
      switch (info->si_code) {
773
      case SEGV_MAPERR: return "SEGV_MAPERR";
774
      case SEGV_ACCERR: return "SEGV_ACCERR";
775
      default: return "SIGSEGV";
776
      }
777
#else
778
      return "SIGSEGV";
779
#endif
780
   case SIGABRT: return "SIGABRT";
781
   case SIGILL: return "SIGILL";
782
   case SIGFPE: return "SIGFPE";
783
   case SIGUSR1: return "SIGUSR1";
784
   case SIGUSR2: return "SIGUSR2";
785
   case SIGBUS: return "SIGBUS";
786
   case SIGINT: return "SIGINT";
787
   case SIGTRAP: return "SIGTRAP";
788
   default: return "???";
789
   }
790
}
791

792
static void print_fatal_signal(int sig, siginfo_t *info, struct cpu_state *cpu)
793
{
794
   static volatile __thread sig_atomic_t recurse = 0;
795

796
   if (recurse++ > 1) {
797
      signal(SIGABRT, SIG_DFL);
798
      abort();
799
   }
800

801
   char buf[512], *p = buf, *s = buf, *end = buf + ARRAY_LEN(buf);
802
   p += checked_sprintf(p, end - p, "\n%s*** Caught signal %d (%s)%s",
803
                        want_color ? "\033[31m\033[1m" : "",
804
                        sig, signame(sig, info),
805
                        recurse > 1 ? " inside signal handler" : "");
806

807
   switch (sig) {
808
   case SIGSEGV:
809
   case SIGILL:
810
   case SIGFPE:
811
   case SIGBUS:
812
      p += checked_sprintf(p, end - p, " [address=%p, ip=%p]",
813
                           info->si_addr, (void*)cpu->pc);
814
      break;
815
   }
816

817
   p += checked_sprintf(p, end - p, " ***%s\n\n", want_color ? "\033[0m" : "");
818

819
   for (int n; s < p && (n = write(STDERR_FILENO, s, p - s)) > 0; s += n);
820

821
   if (sig != SIGUSR1 && !atomic_cas(&crashing, SIG_ATOMIC_MAX, thread_id())) {
822
      sleep(60);
823
      _exit(EXIT_FAILURE);
824
   }
825
}
826
#endif  // !__SANITIZE_THREAD__
827

828
static __thread struct cpu_state *thread_regs = NULL;
829

830
static void signal_handler(int sig, siginfo_t *info, void *context)
×
831
{
832
   ucontext_t *uc = (ucontext_t*)context;
×
833
   struct cpu_state cpu;
×
834
   fill_cpu_state(&cpu, uc);
×
835

836
   struct cpu_state *req;
×
837
   if (sig == SIGUSR2 && (req = atomic_load(&thread_regs)) != NULL) {
×
838
      // Fill in registers for capture_registers
839
      *req = cpu;
×
840
      atomic_store(&thread_regs, NULL);
×
841
      return;
×
842
   }
843
   else if (sig == SIGINT) {
×
844
      void (*fn)(void *) = atomic_load(&ctrl_c_fn);
×
845
      if (fn != NULL) {
×
846
         (*fn)(ctrl_c_arg);
×
847
         return;
×
848
      }
849
   }
850

851
#ifdef __SANITIZE_THREAD__
852
   abort();
853
#else
854

855
   for (fault_handler_t *f = fault_handlers; f; f = f->next)
×
856
      (*f->fn)(sig, info->si_addr, &cpu, f->context);
×
857

858
   print_fatal_signal(sig, info, &cpu);
×
859

860
   show_stacktrace();
×
861

862
   if (sig != SIGUSR1)
×
863
      _exit(2);
×
864
#endif  // !__SANITIZE_THREAD__
865
}
866
#endif  // ! __MINGW32__
867

868
void register_signal_handlers(void)
4,056✔
869
{
870
#ifdef __MINGW32__
871
   SetUnhandledExceptionFilter(win32_exception_handler);
872
#else
873

874
   struct sigaction sa = {
4,056✔
875
      .sa_sigaction = signal_handler,
876
      .sa_flags = SA_RESTART | SA_SIGINFO
877
   };
878
   sigemptyset(&sa.sa_mask);
4,056✔
879

880
#ifndef __SANITIZE_THREAD__
881
   sigaction(SIGSEGV, &sa, NULL);
4,056✔
882
   sigaction(SIGUSR1, &sa, NULL);
4,056✔
883
   sigaction(SIGFPE, &sa, NULL);
4,056✔
884
   sigaction(SIGBUS, &sa, NULL);
4,056✔
885
   sigaction(SIGILL, &sa, NULL);
4,056✔
886
   sigaction(SIGABRT, &sa, NULL);
4,056✔
887
   sigaction(SIGTRAP, &sa, NULL);
4,056✔
888
#endif  // !__SANITIZE_THREAD__
889
   sigaction(SIGUSR2, &sa, NULL);
4,056✔
890
#endif  // !__MINGW32__
891
}
4,056✔
892

893
#ifdef __MINGW32__
894
static BOOL win32_ctrl_c_handler(DWORD fdwCtrlType)
895
{
896
   switch (fdwCtrlType) {
897
   case CTRL_C_EVENT:
898
      {
899
         void (*fn)(void *) = atomic_load(&ctrl_c_fn);
900
         if (fn != NULL)
901
            (*fn)(ctrl_c_arg);
902
         return TRUE;
903
      }
904

905
   default:
906
      return FALSE;
907
   }
908
}
909
#endif
910

911
void set_ctrl_c_handler(void (*fn)(void *), void *arg)
5,300✔
912
{
913
   ctrl_c_arg = arg;
5,300✔
914
   atomic_store(&ctrl_c_fn, fn);
5,300✔
915

916
   if (fn != NULL) {
5,300✔
917
#ifndef __MINGW32__
918
      struct sigaction sa = {};
2,650✔
919
      sa.sa_sigaction = signal_handler;
2,650✔
920
      sigemptyset(&sa.sa_mask);
2,650✔
921
      sa.sa_flags = SA_RESTART | SA_SIGINFO;
2,650✔
922

923
      sigaction(SIGINT, &sa, NULL);
2,650✔
924
#else
925
      if (!SetConsoleCtrlHandler(win32_ctrl_c_handler, TRUE))
926
         fatal_trace("SetConsoleCtrlHandler");
927
#endif
928
   }
929
   else {
930
#ifndef __MINGW32__
931
      struct sigaction sa = {};
2,650✔
932
      sa.sa_handler = SIG_DFL;
2,650✔
933
      sigaction(SIGINT, &sa, NULL);
2,650✔
934
#else
935
      if (!SetConsoleCtrlHandler(win32_ctrl_c_handler, FALSE))
936
         fatal_trace("SetConsoleCtrlHandler");
937
#endif
938
   }
939
}
5,300✔
940

941
#ifdef __MINGW32__
942
static void restore_win32_codepage(void)
943
{
944
   assert(win32_codepage != 0);
945
   SetConsoleOutputCP(win32_codepage);
946
}
947
#endif
948

949
void term_init(void)
4,057✔
950
{
951
   const char *nvc_colors = getenv("NVC_COLORS");
4,057✔
952
   const char *term = getenv("TERM") ?: "";
4,057✔
953

954
   static const char *term_blacklist[] = {
4,057✔
955
      "dumb"
956
   };
957

958
   spin_wait();  // Dummy, to force linking thread.c
4,057✔
959

960
   bool is_tty = isatty(STDERR_FILENO);
4,057✔
961

962
#ifdef __MINGW32__
963
   if (!is_tty) {
964
      // Handle running under MinTty
965
      HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
966
      const size_t size = sizeof(FILE_NAME_INFO) + sizeof(WCHAR) * MAX_PATH;
967
      FILE_NAME_INFO *nameinfo = malloc(size);
968
      if (!GetFileInformationByHandleEx(hStdOut, FileNameInfo, nameinfo, size))
969
         fatal_errno("GetFileInformationByHandle");
970

971
      if ((wcsncmp(nameinfo->FileName, L"\\msys-", 6) == 0
972
           || wcsncmp(nameinfo->FileName, L"\\cygwin-", 8) == 0)
973
          && wcsstr(nameinfo->FileName, L"pty") != NULL)
974
         is_tty = true;
975

976
      free(nameinfo);
977
   }
978
#endif
979

980
   if (nvc_colors && strcmp(nvc_colors, "always") == 0)
4,057✔
981
      want_color = true;
1✔
982
   else if (nvc_colors && strcmp(nvc_colors, "never") == 0)
4,056✔
983
      want_color = false;
×
984
   else {
985
      want_color = is_tty;
4,056✔
986

987
      if (want_color && (term != NULL)) {
4,056✔
988
         for (size_t i = 0; i < ARRAY_LEN(term_blacklist); i++) {
×
989
            if (strcmp(term, term_blacklist[i]) == 0) {
×
990
               want_color = false;
×
991
               break;
×
992
            }
993
         }
994
      }
995
   }
996

997
#ifdef __MINGW32__
998
   HANDLE hConsole = GetStdHandle(STD_ERROR_HANDLE);
999
   DWORD mode;
1000
   if (GetConsoleMode(hConsole, &mode)) {
1001
      mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT;
1002
      if (!SetConsoleMode(hConsole, mode))
1003
         want_color = false;
1004

1005
      CONSOLE_SCREEN_BUFFER_INFO info;
1006
      if (GetConsoleScreenBufferInfo(hConsole, &info))
1007
         term_width = info.dwSize.X;
1008
      else
1009
         term_width = 80;
1010
   }
1011
#else
1012
   if (is_tty) {
4,057✔
1013
      // Try to find the terminal size with tcgetwinsize or TIOCGWINSZ
1014
      term_width = 80;
×
1015
#if defined HAVE_TCGETWINSIZE
1016
      struct winsize ws;
1017
      if (tcgetwinsize(STDIN_FILENO, &ws) == 0)
1018
         term_width = ws.ws_col;
1019
#elif defined TIOCGWINSZ
1020
      struct winsize ws;
×
1021
      if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0)
×
1022
         term_width = ws.ws_col;
×
1023
#endif
1024
   }
1025
#endif
1026

1027
#ifndef __MINGW32__
1028
   // Only print link escape codes if this is really a terminal
1029
   want_links = want_color && is_tty;
4,057✔
1030
#else
1031
   want_links = false;    // Winpty doesn't recognise these
1032
#endif
1033

1034
#ifndef __MINGW32__
1035
   // Assume the terminal is expecting UTF-8 by default
1036
   want_utf8 = true;
4,057✔
1037

1038
   const char *lang = getenv("LANG");
4,057✔
1039
   if (lang != NULL && *lang != '\0' && strcasestr(lang, "utf-8") == NULL)
4,057✔
1040
      want_utf8 = false;
×
1041
#else
1042
   win32_codepage = GetConsoleOutputCP();
1043
   if (win32_codepage == 65001)
1044
      want_utf8 = true;
1045
   else if (win32_codepage != 28591) {
1046
      SetConsoleOutputCP(28591);
1047
      atexit(restore_win32_codepage);
1048
   }
1049
#endif
1050

1051
   // Diagnostics are printed to stderr and explicitly flushed
1052
   setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
4,057✔
1053
}
4,057✔
1054

1055
int terminal_width(void)
16,508✔
1056
{
1057
   return term_width;
16,508✔
1058
}
1059

1060
const char *ordinal_str(int n)
7✔
1061
{
1062
   switch (n) {
7✔
1063
   case 1: return "first";
1064
   case 2: return "second";
1✔
1065
   case 3: return "third";
×
1066
   default:
×
1067
      {
1068
         static char buf[16];
×
1069
         if (n > 20 && n % 10 == 1)
×
1070
            checked_sprintf(buf, sizeof(buf), "%dst", n);
×
1071
         else if (n > 20 && n % 10 == 2)
×
1072
            checked_sprintf(buf, sizeof(buf), "%dnd", n);
×
1073
         else if (n > 20 && n % 10 == 2)
×
1074
            checked_sprintf(buf, sizeof(buf), "%drd", n);
×
1075
         else
1076
            checked_sprintf(buf, sizeof(buf), "%dth", n);
×
1077
         return buf;
1078
      }
1079
   }
1080
}
1081

1082
char *null_terminate(const uint8_t *data, size_t len)
153✔
1083
{
1084
   char *cstr = xmalloc(len + 1);
153✔
1085
   if (data != NULL)
153✔
1086
      memcpy(cstr, data, len);
153✔
1087
   else
1088
      assert(len == 0);
×
1089
   cstr[len] = '\0';
153✔
1090
   return cstr;
153✔
1091
}
1092

1093
char toupper_iso88591(unsigned char ch)
2,565,090✔
1094
{
1095
   if (ch >= 'a' && ch <= 'z')
2,565,090✔
1096
      return ch - 'a' + 'A';
1,541,510✔
1097
   else if ((ch >= 0xe0 && ch <= 0xf6) || (ch >= 0xf8 && ch <= 0xfe))
1,023,580✔
1098
      return ch - 0x20;
39✔
1099
   else
1100
      return ch;
1,023,540✔
1101
}
1102

1103
char tolower_iso88591(unsigned char ch)
286,777✔
1104
{
1105
   if (ch >= 'A' && ch <= 'Z')
286,777✔
1106
      return ch + 'a' - 'A';
143,496✔
1107
   else if ((ch >= 0xc0 && ch <= 0xd6) || (ch >= 0xd8 && ch <= 0xde))
143,281✔
1108
      return ch + 0x20;
90✔
1109
   else
1110
      return ch;
143,191✔
1111
}
1112

1113
bool isprint_iso88591(unsigned char ch)
154,997✔
1114
{
1115
   return (ch >= 0x20 && ch <= 0x7e) || (ch >= 0xa0 && ch <= 0xff);
154,997✔
1116
}
1117

1118
bool isspace_iso88591(unsigned char ch)
170✔
1119
{
1120
   return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\v' || ch == '\f'
129✔
1121
      || ch == '\r' || ch == 0xa0;
298✔
1122
}
1123

1124
bool isdigit_iso88591(unsigned char ch)
12,110✔
1125
{
1126
   return ch >= '0' && ch <= '9';
12,110✔
1127
}
1128

1129
bool isupper_iso88591(unsigned char ch)
1,151✔
1130
{
1131
   return (ch >= 'A' && ch <= 'Z')
1,151✔
1132
      || (ch >= 0xc0 && ch <= 0xd6)
1,151✔
1133
      || (ch >= 0xd8 && ch <= 0xde);
1,151✔
1134
}
1135

1136
bool isalnum_iso88591(unsigned char ch)
2,978✔
1137
{
1138
   return (ch >= 'A' && ch <= 'Z')
2,978✔
1139
      || (ch >= 'a' && ch <= 'z')
2,978✔
1140
      || (ch >= '0' && ch <= '9')
1,482✔
1141
      || (ch >= 0xc0 && ch <= 0xd6)
1,482✔
1142
      || (ch >= 0xd8 && ch <= 0xde)
1,482✔
1143
      || (ch >= 0xe0 && ch <= 0xf6)
1,482✔
1144
      || (ch >= 0xf8 && ch <= 0xfe);
4,460✔
1145
}
1146

1147
int next_power_of_2(int n)
719,297✔
1148
{
1149
   n--;
719,297✔
1150
   n |= n >> 1;
719,297✔
1151
   n |= n >> 2;
719,297✔
1152
   n |= n >> 4;
719,297✔
1153
   n |= n >> 8;
719,297✔
1154
   n |= n >> 16;
719,297✔
1155
   n++;
719,297✔
1156
   return n;
719,297✔
1157
}
1158

1159
int ilog2(int64_t n)
9,494✔
1160
{
1161
   if (n <= 1)
9,494✔
1162
      return 0;
1163
   else {
1164
      int r = 0;
1165
      int64_t c = 1;
1166
      while (c < n) {
35,765✔
1167
         r += 1;
26,303✔
1168
         c <<= 1;
26,303✔
1169
      }
1170
      return r;
9,462✔
1171
   }
1172
}
1173

1174
bool ipow_safe(int64_t x, int64_t y, int64_t *result)
909✔
1175
{
1176
   assert(y >= 0);
909✔
1177
   int overflow = 0, xo = 0;
1178
   int64_t r = 1;
1179
   while (y) {
1,827✔
1180
      if (y & 1)
918✔
1181
         overflow |= xo || __builtin_mul_overflow(r, x, &r);
623✔
1182
      y >>= 1;
918✔
1183
      xo |= __builtin_mul_overflow(x, x, &x);
918✔
1184
   }
1185
   *result = r;
909✔
1186
   return !overflow;
909✔
1187
}
1188

1189
int64_t ipow(int64_t x, int64_t y)
80✔
1190
{
1191
   int64_t result;
80✔
1192
   if (!ipow_safe(x, y, &result))
80✔
1193
      DEBUG_ONLY(fatal_trace("integer overflow in ipow"));
×
1194

1195
   return result;
80✔
1196
}
1197

1198
#ifndef __SANITIZE_ADDRESS__
1199
static long nvc_page_size(void)
1200
{
1201
#ifdef __MINGW32__
1202
   SYSTEM_INFO si;
1203
   GetSystemInfo(&si);
1204
   return si.dwPageSize;
1205
#else
1206
   return sysconf(_SC_PAGESIZE);
1207
#endif
1208
}
1209
#endif
1210

1211
void nvc_munmap(void *ptr, size_t length)
11,380✔
1212
{
1213
#if __SANITIZE_ADDRESS__
1214
   free(ptr);
11,380✔
1215
#elif !defined __MINGW32__
1216
   if (munmap(ptr, length) != 0)
1217
      fatal_errno("munmap");
1218
#else
1219
   if (!VirtualFree(ptr, length, MEM_DECOMMIT))
1220
      fatal_errno("VirtualFree");
1221
#endif
1222
}
11,380✔
1223

1224
void *nvc_memalign(size_t align, size_t sz)
30,973✔
1225
{
1226
#if __SANITIZE_ADDRESS__
1227
   void *ptr;
30,973✔
1228
   if (posix_memalign(&ptr, align, sz) != 0)
61,946✔
1229
      fatal_errno("posix_memalign");
×
1230

1231
   memset(ptr, '\0', sz);
30,973✔
1232
   return ptr;
30,973✔
1233
#else
1234
   assert(is_power_of_2(align));
1235
   const size_t mapalign = MAX(align, nvc_page_size());
1236
   const size_t mapsz = ALIGN_UP(sz + mapalign - 1, mapalign);
1237

1238
#if defined __MINGW32__
1239
   void *ptr = VirtualAlloc(NULL, mapsz, MEM_COMMIT | MEM_RESERVE,
1240
                            PAGE_READWRITE);
1241
   if (ptr == NULL)
1242
      fatal_errno("VirtualAlloc");
1243
#else
1244
   void *ptr = mmap(NULL, mapsz, PROT_READ | PROT_WRITE,
1245
                    MAP_PRIVATE | MAP_ANON, -1, 0);
1246
   if (ptr == MAP_FAILED)
1247
      fatal_errno("mmap");
1248
#endif
1249

1250
   void *aligned = ALIGN_UP(ptr, align);
1251
   void *limit = aligned + sz;
1252

1253
   if (align > nvc_page_size()) {
1254
      const size_t low_waste = aligned - ptr;
1255
      const size_t high_waste = ptr + mapsz - limit;
1256
      assert(low_waste + high_waste == align);
1257

1258
      if (low_waste > 0) nvc_munmap(ptr, low_waste);
1259
      if (high_waste > 0) nvc_munmap(limit, high_waste);
1260
   }
1261

1262
   return aligned;
1263
#endif
1264
}
1265

1266
void nvc_memprotect(void *ptr, size_t length, mem_access_t prot)
41,082✔
1267
{
1268
#if defined __MINGW32__
1269
   static const int map[] = {
1270
      PAGE_NOACCESS, PAGE_READONLY, PAGE_READWRITE, PAGE_EXECUTE_READ,
1271
      PAGE_EXECUTE_READWRITE
1272
   };
1273
   DWORD old_prot;
1274
   if (!VirtualProtect(ptr, length, map[prot], &old_prot))
1275
      fatal_errno("VirtualProtect");
1276
#else
1277
#if __SANITIZE_ADDRESS__
1278
   // LeakSanitizer will not detect leaks in regions mapped read-only
1279
   if (prot == MEM_RO || prot == MEM_NONE)
41,082✔
1280
      return;
1281
#endif
1282
   static const int map[] = {
2,828✔
1283
      PROT_NONE, PROT_READ, PROT_READ | PROT_WRITE, PROT_READ | PROT_EXEC,
1284
      PROT_READ | PROT_WRITE | PROT_EXEC
1285
   };
1286
   if (mprotect(ptr, length, map[prot]) < 0)
2,828✔
1287
      fatal_errno("mprotect");
×
1288
#endif
1289
}
1290

1291
void *map_huge_pages(size_t align, size_t sz)
11,389✔
1292
{
1293
#ifdef __linux__
1294
   if (sz >= HUGE_PAGE_SIZE) {
11,389✔
1295
      const size_t mapsz = ALIGN_UP(sz, HUGE_PAGE_SIZE);
10,931✔
1296
      void *mem = nvc_memalign(MAX(HUGE_PAGE_SIZE, align), mapsz);
10,931✔
1297

1298
      if (madvise(mem, mapsz, MADV_HUGEPAGE) < 0)
10,931✔
1299
         warnf("madvise: MADV_HUGEPAGE: %s", last_os_error());
×
1300

1301
      return mem;
10,931✔
1302
   }
1303
#endif
1304

1305
   return nvc_memalign(align, sz);
458✔
1306
}
1307

1308
void *map_jit_pages(size_t align, size_t sz)
2,828✔
1309
{
1310
#ifdef __APPLE__
1311
   void *ptr = mmap(NULL, sz, PROT_READ | PROT_WRITE | PROT_EXEC,
1312
                    MAP_PRIVATE | MAP_ANON | MAP_JIT, -1, 0);
1313
   if (ptr == MAP_FAILED)
1314
      fatal_errno("mmap");
1315
#else
1316
   void *ptr = map_huge_pages(align, sz);
2,828✔
1317
   nvc_memprotect(ptr, sz, MEM_RWX);
2,828✔
1318
#endif
1319

1320
   return ptr;
2,828✔
1321
}
1322

1323
int checked_sprintf(char *buf, int len, const char *fmt, ...)
1,085,000✔
1324
{
1325
   assert(len > 0);
1,085,000✔
1326

1327
   va_list ap;
1,085,000✔
1328
   va_start(ap, fmt);
1,085,000✔
1329

1330
   const int nbytes = vsnprintf(buf, len, fmt, ap);
1,085,000✔
1331
   if (nbytes >= len)
1,085,000✔
1332
      fatal_trace("checked_sprintf requires %d bytes but have %d",
×
1333
                  nbytes + 1, len);
1334

1335
   va_end(ap);
1,085,000✔
1336

1337
   return nbytes;
1,085,000✔
1338
}
1339

1340
text_buf_t *tb_new(void)
350,675✔
1341
{
1342
   text_buf_t *tb = xmalloc(sizeof(text_buf_t));
350,675✔
1343
   tb->alloc = 256;
350,675✔
1344
   tb->len   = 0;
350,675✔
1345
   tb->buf   = xmalloc(tb->alloc);
350,675✔
1346

1347
   tb->buf[0] = '\0';
350,675✔
1348

1349
   return tb;
350,675✔
1350
}
1351

1352
void tb_free(text_buf_t *tb)
349,542✔
1353
{
1354
   free(tb->buf);
349,542✔
1355
   free(tb);
349,542✔
1356
}
349,542✔
1357

1358
void _tb_cleanup(text_buf_t **tb)
329,965✔
1359
{
1360
   if (*tb != NULL)
329,965✔
1361
      tb_free(*tb);
329,965✔
1362
}
329,965✔
1363

1364
void tb_vprintf(text_buf_t *tb, const char *fmt, va_list ap)
194,072✔
1365
{
1366
   int nchars, avail;
194,082✔
1367
   for (;;) {
10✔
1368
      va_list aq;
194,082✔
1369
      va_copy(aq, ap);
194,082✔
1370

1371
      avail  = tb->alloc - tb->len;
194,082✔
1372
      nchars = vsnprintf(tb->buf + tb->len, avail, fmt, aq);
194,082✔
1373

1374
      va_end(aq);
194,082✔
1375

1376
      if (nchars + 1 < avail)
194,082✔
1377
         break;
1378

1379
      tb->alloc *= 2;
10✔
1380
      tb->buf = xrealloc(tb->buf, tb->alloc);
10✔
1381
   }
1382

1383
   tb->len += nchars;
194,072✔
1384
}
194,072✔
1385

1386
void tb_printf(text_buf_t *tb, const char *fmt, ...)
170,542✔
1387
{
1388
   va_list ap;
170,542✔
1389
   va_start(ap, fmt);
170,542✔
1390
   tb_vprintf(tb, fmt, ap);
170,542✔
1391
   va_end(ap);
170,542✔
1392
}
170,542✔
1393

1394
void tb_istr(text_buf_t *tb, ident_t ident)
334,285✔
1395
{
1396
   // TODO: this function seems useless now
1397
   tb_cat(tb, istr(ident));
334,285✔
1398
}
334,285✔
1399

1400
void tb_append(text_buf_t *tb, char ch)
530,377✔
1401
{
1402
   if (tb->len + 2 >= tb->alloc) {
530,377✔
1403
      tb->alloc *= 2;
12✔
1404
      tb->buf = xrealloc(tb->buf, tb->alloc);
12✔
1405
   }
1406

1407
   tb->buf[(tb->len)++] = ch;
530,377✔
1408
   tb->buf[tb->len] = '\0';
530,377✔
1409
}
530,377✔
1410

1411
void tb_catn(text_buf_t *tb, const char *str, size_t nchars)
513,340✔
1412
{
1413
   if (tb->len + nchars + 1 >= tb->alloc) {
513,340✔
1414
      tb->alloc = next_power_of_2(tb->alloc + nchars);
938✔
1415
      tb->buf = xrealloc(tb->buf, tb->alloc);
938✔
1416
   }
1417

1418
   memcpy(tb->buf + tb->len, str, nchars);
513,340✔
1419
   tb->len += nchars;
513,340✔
1420
   tb->buf[tb->len] = '\0';
513,340✔
1421
}
513,340✔
1422

1423
void tb_cat(text_buf_t *tb, const char *str)
474,371✔
1424
{
1425
   tb_catn(tb, str, strlen(str));
474,371✔
1426
}
474,371✔
1427

1428
void tb_repeat(text_buf_t *tb, char ch, size_t count)
×
1429
{
1430
   if (tb->len + count + 1 >= tb->alloc) {
×
1431
      tb->alloc = next_power_of_2(tb->alloc + count + 1);
×
1432
      tb->buf = xrealloc(tb->buf, tb->alloc);
×
1433
   }
1434

1435
   memset(tb->buf + tb->len, ch, count);
×
1436
   tb->len += count;
×
1437
   tb->buf[tb->len] = '\0';
×
1438
}
×
1439

1440
char *tb_reserve(text_buf_t *tb, size_t size)
×
1441
{
1442
   if (tb->len + size + 1 >= tb->alloc) {
×
1443
      tb->alloc = next_power_of_2(tb->alloc + size + 1);
×
1444
      tb->buf = xrealloc(tb->buf, tb->alloc);
×
1445
   }
1446

1447
   char *start = tb->buf + tb->len;
×
1448

1449
   tb->len += size;
×
1450
   tb->buf[tb->len] = '\0';
×
1451

1452
   return start;
×
1453
}
1454

1455
size_t tb_len(text_buf_t *tb)
205,687✔
1456
{
1457
   return tb->len;
205,687✔
1458
}
1459

1460
char *tb_claim(text_buf_t *tb)
26,523✔
1461
{
1462
   char *buf = tb->buf;
26,523✔
1463
   tb->buf = NULL;
26,523✔
1464
   return buf;
26,523✔
1465
}
1466

1467
void tb_move(text_buf_t *to, text_buf_t *from)
48✔
1468
{
1469
   free(to->buf);
48✔
1470

1471
   to->buf = from->buf;
48✔
1472
   to->len = from->len;
48✔
1473
   to->alloc = from->alloc;
48✔
1474

1475
   from->alloc = from->len = 0;
48✔
1476
   from->buf = NULL;
48✔
1477
}
48✔
1478

1479
const char *tb_get(text_buf_t *tb)
526,096✔
1480
{
1481
   return tb->buf;
526,096✔
1482
}
1483

1484
void tb_rewind(text_buf_t *tb)
862✔
1485
{
1486
   tb->len = 0;
862✔
1487
   tb->buf[0] = '\0';
862✔
1488
}
862✔
1489

1490
void tb_trim(text_buf_t *tb, size_t newlen)
63✔
1491
{
1492
   assert(newlen <= tb->len);
63✔
1493
   tb->len = newlen;
63✔
1494
   tb->buf[tb->len] = '\0';
63✔
1495
}
63✔
1496

1497
void tb_strip(text_buf_t *tb)
1✔
1498
{
1499
   while (tb->len > 0 && isspace_iso88591(tb->buf[tb->len - 1]))
7✔
1500
      tb->buf[--(tb->len)] = '\0';
6✔
1501
}
1✔
1502

1503
void tb_downcase(text_buf_t *tb)
4,211✔
1504
{
1505
   for (size_t i = 0; i < tb->len; i++)
79,615✔
1506
      tb->buf[i] = tolower_iso88591(tb->buf[i]);
75,404✔
1507
}
4,211✔
1508

1509
void tb_upcase(text_buf_t *tb)
15✔
1510
{
1511
   for (size_t i = 0; i < tb->len; i++)
145✔
1512
      tb->buf[i] = toupper_iso88591(tb->buf[i]);
130✔
1513
}
15✔
1514

1515
void tb_replace(text_buf_t *tb, char old, char rep)
442✔
1516
{
1517
   for (size_t i = 0; i < tb->len; i++) {
9,437✔
1518
      if (tb->buf[i] == old)
8,995✔
1519
         tb->buf[i] = rep;
445✔
1520
   }
1521
}
442✔
1522

1523
void _local_free(void *ptr)
547,556✔
1524
{
1525
   free(*(void **)ptr);
547,556✔
1526
}
547,556✔
1527

1528
void set_message_style(message_style_t style)
×
1529
{
1530
   message_style = style;
×
1531

1532
   if (style == MESSAGE_COMPACT)
×
1533
      want_color = false;
×
1534
}
×
1535

1536
message_style_t get_message_style(void)
16,246✔
1537
{
1538
   return message_style;
16,246✔
1539
}
1540

1541
#ifndef __MINGW32__
1542
static uint64_t timeval_us(struct timeval *tv)
1543
{
1544
   return (tv->tv_sec * UINT64_C(1000000)) + tv->tv_usec;
1545
}
1546
#endif
1547

1548
void nvc_rusage(nvc_rusage_t *ru)
2,682✔
1549
{
1550
#ifndef __MINGW32__
1551
   static uint64_t last_user, last_sys;
2,682✔
1552

1553
   struct rusage buf;
2,682✔
1554
   if (getrusage(RUSAGE_SELF, &buf) < 0)
2,682✔
1555
      fatal_errno("getrusage");
×
1556

1557
   const uint64_t user = timeval_us(&(buf.ru_utime));
2,682✔
1558
   const uint64_t sys = timeval_us(&(buf.ru_stime));
2,682✔
1559

1560
   ru->user = (user - last_user) / 1000;
2,682✔
1561
   ru->sys = (sys - last_sys) / 1000;
2,682✔
1562

1563
   last_sys = sys;
2,682✔
1564
   last_user = user;
2,682✔
1565

1566
#ifdef __APPLE__
1567
   const int rss_units = 1024;
1568
#else
1569
   const int rss_units = 1;
2,682✔
1570
#endif
1571

1572
   ru->rss = buf.ru_maxrss / rss_units;
2,682✔
1573
#else
1574
   static ULARGE_INTEGER last_kernel, last_user;
1575
   ULARGE_INTEGER lv_Tkernel, lv_Tuser;
1576
   HANDLE hProcess = GetCurrentProcess();
1577

1578
   FILETIME ftCreation, ftExit, ftKernel, ftUser;
1579
   if (!GetProcessTimes(hProcess, &ftCreation, &ftExit, &ftKernel, &ftUser))
1580
      fatal_errno("GetProcessTimes");
1581

1582
   lv_Tkernel.LowPart = ftKernel.dwLowDateTime;
1583
   lv_Tkernel.HighPart = ftKernel.dwHighDateTime;
1584
   lv_Tuser.LowPart = ftUser.dwLowDateTime;
1585
   lv_Tuser.HighPart = ftUser.dwHighDateTime;
1586

1587
   ru->user = (lv_Tuser.QuadPart - last_user.QuadPart) / 10000;
1588
   ru->sys = (lv_Tkernel.QuadPart - last_kernel.QuadPart) / 10000;
1589

1590
   last_user = lv_Tuser;
1591
   last_kernel = lv_Tkernel;
1592

1593
   PROCESS_MEMORY_COUNTERS counters;
1594
   if (!GetProcessMemoryInfo(GetCurrentProcess(), &counters, sizeof(counters)))
1595
      fatal_errno("GetProcessMemoryInfo");
1596

1597
   ru->rss = counters.PeakWorkingSetSize / 1024;
1598
#endif
1599

1600
   static uint64_t last_ts;
2,682✔
1601
   const uint64_t ts = get_timestamp_us();
2,682✔
1602
   ru->ms = last_ts == 0 ? ru->user + ru->sys : (ts - last_ts) / 1000;
2,682✔
1603
   last_ts = ts;
2,682✔
1604
}
2,682✔
1605

1606
#ifdef __MINGW32__
1607
static uint64_t file_time_to_nanos(LPFILETIME ft)
1608
{
1609
   uint64_t nanos = (uint64_t)ft->dwHighDateTime << 32;
1610
   nanos |= ft->dwLowDateTime;
1611

1612
   // Windows file timestamps are in units of 100 nanoseconds since
1613
   // 1601-01-01T00:00:00Z: convert that to nanoseconds since the Unix
1614
   // epoch 1970-01-01T00:00:00Z
1615
   nanos -= UINT64_C(116444736000000000);
1616
   nanos *= 100;
1617

1618
   return nanos;
1619
}
1620

1621
static bool fill_file_info(file_info_t *info, HANDLE handle)
1622
{
1623
   memset(info, '\0', sizeof(file_info_t));
1624

1625
   BY_HANDLE_FILE_INFORMATION hinfo;
1626
   if (!GetFileInformationByHandle(handle, &hinfo))
1627
      fatal_errno("GetFileInformationByHandle");
1628

1629
   info->size = (uint64_t)hinfo.nFileSizeHigh << 32;
1630
   info->size |= hinfo.nFileSizeLow;
1631

1632
   if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1633
      info->type = FILE_DIR;
1634
   else
1635
      info->type = FILE_REGULAR;
1636

1637
   info->mtime = file_time_to_nanos(&(hinfo.ftLastWriteTime));
1638

1639
   return true;
1640
}
1641
#else  // !__MINGW32__
1642
static void fill_file_info(file_info_t *info, const struct stat *st)
38,832✔
1643
{
1644
   memset(info, '\0', sizeof(file_info_t));
38,832✔
1645

1646
   info->size = st->st_size;
38,832✔
1647

1648
   if (S_ISDIR(st->st_mode))
38,832✔
1649
      info->type = FILE_DIR;
101✔
1650
   else if (!S_ISREG(st->st_mode))
38,731✔
1651
      info->type = FILE_FIFO;
33✔
1652
   else
1653
      info->type = FILE_REGULAR;
38,698✔
1654

1655
   info->mtime = st->st_mtime * UINT64_C(1000000000);
38,832✔
1656

1657
#if defined HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
1658
   info->mtime += st->st_mtimespec.tv_nsec;
1659
#elif defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
1660
   info->mtime += st->st_mtim.tv_nsec;
38,832✔
1661
#endif
1662
}
38,832✔
1663
#endif  // !__MINGW32__
1664

1665
bool get_file_info(const char *path, file_info_t *info)
21,409✔
1666
{
1667
#ifdef __MINGW32__
1668
   HANDLE handle = CreateFile(
1669
        path, FILE_READ_ATTRIBUTES,
1670
        FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1671
        NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1672

1673
   if (handle == INVALID_HANDLE_VALUE)
1674
      return false;
1675

1676
   fill_file_info(info, handle);
1677

1678
   if (!CloseHandle(handle))
1679
      fatal_errno("CloseHandle");
1680

1681
   return true;
1682
#else
1683
   struct stat st;
21,409✔
1684
   if (stat(path, &st) == 0) {
21,409✔
1685
      fill_file_info(info, &st);
11,440✔
1686
      return true;
11,440✔
1687
   }
1688
   else
1689
      return false;
1690
#endif
1691
}
1692

1693
bool get_handle_info(int fd, file_info_t *info)
27,392✔
1694
{
1695
#ifdef __MINGW32__
1696
   HANDLE handle = (HANDLE)_get_osfhandle(fd);
1697
   fill_file_info(info, handle);
1698
   return true;
1699
#else
1700
   struct stat st;
27,392✔
1701
   if (fstat(fd, &st) == 0) {
27,392✔
1702
      fill_file_info(info, &st);
27,392✔
1703
      return true;
27,392✔
1704
   }
1705
   else
1706
      return false;
1707
#endif
1708
}
1709

1710
bool get_handle_path(int fd, text_buf_t *tb)
3✔
1711
{
1712
#ifdef __MINGW32__
1713
   HANDLE handle = (HANDLE)_get_osfhandle(fd);
1714
   char buf[PATH_MAX];
1715

1716
   DWORD nchars = GetFinalPathNameByHandle(handle, buf, PATH_MAX, 0);
1717
   if (nchars == 0)
1718
      return false;
1719

1720
   tb_catn(tb, buf, nchars);
1721
   return true;
1722
#elif defined __linux__
1723
   char path[64], buf[PATH_MAX];
3✔
1724
   checked_sprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
3✔
1725

1726
   size_t nchars = readlink(path, buf, PATH_MAX);
3✔
1727
   if (nchars == -1)
3✔
1728
      return false;
1729

1730
   tb_catn(tb, buf, nchars);
3✔
1731
   return true;
3✔
1732
#elif defined F_GETPATH
1733
   char buf[PATH_MAX];
1734
   if (fcntl(fd, F_GETPATH, buf) == -1)
1735
      return false;
1736

1737
   tb_cat(tb, buf);
1738
   return true;
1739
#else
1740
   return false;
1741
#endif
1742
}
1743

1744
void run_program(const char *const *args)
1,137✔
1745
{
1746
#if defined __CYGWIN__ || defined __MINGW32__
1747
   int status = spawnvp(_P_WAIT, args[0], (char *const *)args);
1748
#else  // __CYGWIN__
1749
   pid_t pid = fork();
1,137✔
1750
   int status = 0;
2,274✔
1751
   if (pid == 0) {
2,274✔
1752
      execvp(args[0], (char *const *)args);
1,137✔
1753
      fatal_errno("execv");
1,137✔
1754
   }
1755
   else if (pid > 0) {
1,137✔
1756
      if (waitpid(pid, &status, 0) != pid)
1,137✔
1757
         fatal_errno("waitpid");
×
1758

1759
      status = WEXITSTATUS(status);
1,137✔
1760
   }
1761
   else
1762
      fatal_errno("fork");
×
1763
#endif  // __CYGWIN__
1764

1765
   if (status != 0) {
1,137✔
1766
      LOCAL_TEXT_BUF tb = tb_new();
×
1767
      for (size_t i = 0; args[i] != NULL; i++)
×
1768
         tb_printf(tb, "%s%s", i > 0 ? " " : "", args[i]);
×
1769
      fatal("$bold$%s$$ failed with status %d", tb_get(tb), status);
×
1770
   }
1771
}
1,137✔
1772

1773
char *nvc_temp_file(void)
3✔
1774
{
1775
   static const char *try[] = { "TMPDIR", "TEMP", "TMP" };
3✔
1776
   const char *tmpdir = NULL;
3✔
1777
   for (int i = 0; tmpdir == NULL && i < ARRAY_LEN(try); i++)
12✔
1778
      tmpdir = getenv(try[i]);
9✔
1779

1780
#ifdef __MINGW32__
1781
   char *buf = xasprintf("%s\\nvc-XXXXXX", tmpdir ?: ".");
1782
   return _mktemp(buf);
1783
#else
1784
   char *buf = xasprintf("%s/nvc-XXXXXX", tmpdir ?: "/tmp");
6✔
1785
   int fd = mkstemp(buf);
3✔
1786
   if (fd < 0)
3✔
1787
      fatal_errno("mkstemp");
×
1788
   close(fd);
3✔
1789
   return buf;
3✔
1790
#endif
1791
}
1792

1793
void file_read_lock(int fd)
11,752✔
1794
{
1795
#ifdef __MINGW32__
1796
   HANDLE hf = (HANDLE)_get_osfhandle(fd);
1797

1798
   LARGE_INTEGER li;
1799
   li.QuadPart = _filelengthi64(fd);
1800

1801
   OVERLAPPED ovlp;
1802
   memset(&ovlp, 0, sizeof ovlp);
1803

1804
   if (!LockFileEx(hf, 0, 0, li.LowPart, li.HighPart, &ovlp))
1805
      fatal_errno("LockFileEx");
1806
#else
1807
   if (flock(fd, LOCK_SH) < 0)
11,752✔
1808
      fatal_errno("flock");
×
1809
#endif
1810
}
11,752✔
1811

1812
void file_write_lock(int fd)
6,669✔
1813
{
1814
#ifdef __MINGW32__
1815
   HANDLE hf = (HANDLE)_get_osfhandle(fd);
1816

1817
   LARGE_INTEGER li;
1818
   li.QuadPart = _filelengthi64(fd);
1819

1820
   OVERLAPPED ovlp;
1821
   memset(&ovlp, 0, sizeof ovlp);
1822

1823
   if (!LockFileEx(hf, LOCKFILE_EXCLUSIVE_LOCK, 0,
1824
                   li.LowPart, li.HighPart, &ovlp))
1825
      fatal_errno("LockFileEx");
1826
#else
1827
   if (flock(fd, LOCK_EX) < 0)
6,669✔
1828
      fatal_errno("flock");
×
1829
#endif
1830
}
6,669✔
1831

1832
void file_unlock(int fd)
18,412✔
1833
{
1834
#ifdef __MINGW32__
1835
   HANDLE hf = (HANDLE)_get_osfhandle(fd);
1836

1837
   LARGE_INTEGER li;
1838
   li.QuadPart = _filelengthi64 (fd);
1839

1840
   UnlockFile(hf, 0, 0, li.LowPart, li.HighPart);
1841
#else
1842
   if (flock(fd, LOCK_UN) < 0)
18,412✔
1843
      fatal_errno("flock");
×
1844
#endif
1845
}
18,412✔
1846

1847
void *map_file(int fd, size_t size)
15,625✔
1848
{
1849
#ifdef __MINGW32__
1850
   HANDLE handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), NULL,
1851
                                     PAGE_READONLY, 0, size, NULL);
1852
   if (!handle)
1853
      fatal_errno("CreateFileMapping");
1854

1855
   void *ptr = MapViewOfFileEx(handle, FILE_MAP_READ, 0,
1856
                               0, (SIZE_T) size, (LPVOID) NULL);
1857
   CloseHandle(handle);
1858
   if (ptr == NULL)
1859
      fatal_errno("MapViewOfFileEx");
1860
#else
1861
   void *ptr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
15,625✔
1862
   if (ptr == MAP_FAILED)
15,625✔
1863
      fatal_errno("mmap");
×
1864
#endif
1865
   return ptr;
15,625✔
1866
}
1867

1868
void unmap_file(void *ptr, size_t size)
11,896✔
1869
{
1870
#ifdef __MINGW32__
1871
   if (!UnmapViewOfFile((LPCVOID) ptr))
1872
      fatal_errno("UnmapViewOfFile");
1873
#else
1874
   munmap(ptr, size);
11,896✔
1875
#endif
1876
}
11,896✔
1877

1878
void make_dir(const char *path)
2,940✔
1879
{
1880
#ifdef __MINGW32__
1881
   if (!CreateDirectory(path, NULL) && (GetLastError() != ERROR_ALREADY_EXISTS))
1882
      fatal_errno("mkdir: %s", path);
1883
#else
1884
   if (mkdir(path, 0777) != 0 && errno != EEXIST)
2,940✔
1885
      fatal_errno("mkdir: %s", path);
×
1886
#endif
1887
}
2,940✔
1888

1889
uint64_t get_timestamp_ns(void)
19,832,200✔
1890
{
1891
#if defined __MINGW32__
1892
   static volatile uint64_t freq;
1893
   if (load_acquire(&freq) == 0) {
1894
      LARGE_INTEGER tmp;
1895
      if (!QueryPerformanceFrequency(&tmp))
1896
         fatal_errno("QueryPerformanceFrequency");
1897
      store_release(&freq, tmp.QuadPart);
1898
   }
1899

1900
   LARGE_INTEGER ticks;
1901
   if (!QueryPerformanceCounter(&ticks))
1902
      fatal_errno("QueryPerformanceCounter");
1903
   return (double)ticks.QuadPart * (1e9 / (double)freq);
1904
#else
1905
   struct timespec ts;
19,832,200✔
1906
   if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
19,832,200✔
1907
      fatal_errno("clock_gettime");
×
1908
   return ts.tv_nsec + (ts.tv_sec * UINT64_C(1000000000));
19,832,200✔
1909
#endif
1910
}
1911

1912
uint64_t get_timestamp_us(void)
19,832,200✔
1913
{
1914
   return get_timestamp_ns() / 1000;
19,832,200✔
1915
}
1916

1917
timestamp_t get_real_time(void)
12,712✔
1918
{
1919
#if defined __MINGW32__
1920
   FILETIME ft;
1921
   GetSystemTimeAsFileTime(&ft);
1922

1923
   return file_time_to_nanos(&ft);
1924
#else
1925
   struct timespec ts;
12,712✔
1926
   if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
12,712✔
1927
      fatal_errno("clock_gettime");
×
1928

1929
   return (uint64_t)ts.tv_nsec + ((uint64_t)ts.tv_sec * UINT64_C(1000000000));
12,712✔
1930
#endif
1931
}
1932

1933
void open_pipe(int *rfd, int *wfd)
×
1934
{
1935
   int fds[2];
×
1936
#ifdef __MINGW32__
1937
   const int rc = _pipe(fds, 4096, _O_BINARY);
1938
#else
1939
   const int rc = pipe(fds) < 0;
×
1940
#endif
1941
   if (rc < 0)
×
1942
      fatal_errno("failed to create pipe");
1943

1944
   *rfd = fds[0];
×
1945
   *wfd = fds[1];
×
1946
}
×
1947

1948
#if defined _WIN32 || defined __CYGWIN__
1949
static struct {
1950
   char illegal;
1951
   const char *rep;
1952
} symbol_replacements[] = {
1953
   { '(', "_lp_"   },
1954
   { ')', "_rp_"   },
1955
   { '"', "_q_"    },
1956
   { '[', "_ls_"   },
1957
   { ']', "_rs_"   },
1958
   { '*', "_mult_" },
1959
   { '+', "_plus_" },
1960
   { '=', "_eq_"   }
1961
};
1962

1963
static text_buf_t *safe_symbol_win32(const char *text)
1964
{
1965
   text_buf_t *tb = tb_new();
1966

1967
   for (const char *p = text; *p != '\0'; p++) {
1968
      bool replaced = false;
1969
      for (size_t j = 0; j < ARRAY_LEN(symbol_replacements); j++) {
1970
         if (*p == symbol_replacements[j].illegal) {
1971
            tb_cat(tb, symbol_replacements[j].rep);
1972
            replaced = true;
1973
            break;
1974
         }
1975
      }
1976

1977
      if (!replaced)
1978
         tb_append(tb, *p);
1979
   }
1980

1981
   return tb;
1982
}
1983

1984
#endif
1985

1986
text_buf_t *safe_symbol(ident_t id)
69,059✔
1987
{
1988
   // Return a string that is safe to use as a symbol name on this platform
1989

1990
   text_buf_t *tb = tb_new();
69,059✔
1991
   tb_istr(tb, id);
69,059✔
1992

1993
#if defined _WIN32 || defined __CYGWIN__
1994
   if (strpbrk(tb_get(tb), "()\"[]*+=") == NULL)
1995
      return tb;
1996
   else {
1997
      text_buf_t *new = safe_symbol_win32(tb_get(tb));
1998
      tb_free(tb);
1999
      return new;
2000
   }
2001
#else
2002
   return tb;
69,059✔
2003
#endif
2004
}
2005

2006
text_buf_t *unsafe_symbol(const char *text)
1✔
2007
{
2008
   // Restore original symbol from safe_symbol
2009

2010
   text_buf_t *tb = tb_new();
1✔
2011

2012
#if defined _WIN32 || defined __CYGWIN__
2013
   const char *p = text;
2014
   while (*p) {
2015
      bool replaced = false;
2016
      for (size_t j = 0; j < ARRAY_LEN(symbol_replacements); j++) {
2017
         size_t len = strlen(symbol_replacements[j].rep);
2018
         if (strncmp(p, symbol_replacements[j].rep, len) == 0) {
2019
            tb_append(tb, symbol_replacements[j].illegal);
2020
            p += len;
2021
            replaced = true;
2022
            break;
2023
         }
2024
      }
2025

2026
      if (!replaced)
2027
         tb_append(tb, *p++);
2028
   }
2029

2030
   return tb;
2031
#else
2032
   tb_cat(tb, text);
1✔
2033
#endif
2034

2035
   return tb;
1✔
2036
}
2037

2038
void __cleanup_array(void *ptr)
114,638✔
2039
{
2040
   A(void *) *a = ptr;
114,638✔
2041
   ACLEAR(*a);
114,638✔
2042
}
114,638✔
2043

2044
void __array_resize_slow(void **ptr, uint32_t *limit, uint32_t count,
1,217,710✔
2045
                         size_t size)
2046
{
2047
   if (count == 0) {
1,217,710✔
2048
      free(*ptr);
×
2049
      *ptr = NULL;
×
2050
      *limit = 0;
×
2051
   }
2052
   else {
2053
      if (*limit == 0)
1,217,710✔
2054
         *limit = count;  // Setting the initial size of the array
1,027,700✔
2055
      else
2056
         *limit = next_power_of_2(count);
190,013✔
2057
      *ptr = xrealloc_array(*ptr, *limit, size);
1,217,710✔
2058
   }
2059
}
1,217,710✔
2060

2061
char *search_path(const char *name)
×
2062
{
2063
   const char *path = getenv("PATH");
×
2064
   if (path == NULL)
×
2065
      return xstrdup(name);
×
2066

2067
   char LOCAL *tmp = xstrdup(path);
×
2068
   for (char *p = strtok(tmp, ":"); p; p = strtok(NULL, ":")) {
×
2069
      char *full = xasprintf("%s"DIR_SEP"%s", p, name);
×
2070

2071
      struct stat sb;
×
2072
      if (stat(full, &sb) == 0)
×
2073
         return full;
×
2074

2075
      free(full);
×
2076
   }
2077

2078
   return xstrdup(name);
×
2079
}
2080

2081
bool get_exe_path(text_buf_t *tb)
3,543✔
2082
{
2083
#if defined __linux__
2084
   char buf[PATH_MAX];
3,543✔
2085
   ssize_t nchars = readlink("/proc/self/exe", buf, sizeof(buf));
3,543✔
2086
   if (nchars > 0) {   // Does not append '\0'
3,543✔
2087
      tb_catn(tb, buf, nchars);
3,543✔
2088
      return true;
3,543✔
2089
   }
2090
#elif defined __APPLE__
2091
   char buf[PATH_MAX];
2092
   if (proc_pidpath(getpid(), buf, sizeof(buf)) > 0) {
2093
      tb_cat(tb, buf);
2094
      return true;
2095
   }
2096
#elif defined __FreeBSD__
2097
   char buf[PATH_MAX];
2098
   size_t size = sizeof(buf);
2099
   const int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
2100
   if (sysctl(name, ARRAY_LEN(name), buf, &size, NULL, 0) == 0) {
2101
      tb_catn(tb, buf, size);
2102
      return true;
2103
   }
2104
#elif defined __MINGW32__
2105
   HANDLE hProc = GetCurrentProcess();
2106
   char buf[PATH_MAX];
2107
   DWORD size = sizeof(buf);
2108
   if (QueryFullProcessImageNameA(hProc, 0, buf, &size)) {
2109
      tb_cat(tb, buf);
2110
      return true;
2111
   }
2112
#endif
2113
   return false;
2114
}
2115

2116
#if defined __MINGW32__
2117
static void get_relative_prefix(text_buf_t *tb)
2118
{
2119
   if (get_exe_path(tb)) {
2120
      int len = tb_len(tb);
2121
      const char *str = tb_get(tb);
2122
      for (int i = 0; i < 2; i++) {
2123
         do {
2124
            len--;
2125
         } while (str[len] != DIR_SEP[0]);
2126
      }
2127
      tb_trim(tb, len);
2128
   }
2129
   else
2130
      fatal("failed to read executable path");
2131
}
2132
#endif
2133

2134
void get_libexec_dir(text_buf_t *tb)
×
2135
{
2136
#if defined __MINGW32__
2137
   get_relative_prefix(tb);
2138
   tb_cat(tb, DIR_SEP "libexec" DIR_SEP "nvc");
2139
#else
2140
   tb_cat(tb, LIBEXECDIR);
×
2141
#endif
2142
}
×
2143

2144
void get_lib_dir(text_buf_t *tb)
3,799✔
2145
{
2146
#if defined __MINGW32__
2147
   get_relative_prefix(tb);
2148
   tb_cat(tb, DIR_SEP "lib" DIR_SEP "nvc");
2149
#else
2150
   tb_cat(tb, LIBDIR);
3,799✔
2151
#endif
2152
}
3,799✔
2153

2154
void get_data_dir(text_buf_t *tb)
×
2155
{
2156
#if defined __MINGW32__
2157
   get_relative_prefix(tb);
2158
   tb_cat(tb, DIR_SEP "share" DIR_SEP "nvc");
2159
#else
2160
   tb_cat(tb, DATADIR);
×
2161
#endif
2162
}
×
2163

2164
bool is_absolute_path(const char *path)
27✔
2165
{
2166
   if (path[0] == DIR_SEP[0] || path[0] == '/')
27✔
2167
      return true;
27✔
2168

2169
#ifdef __MINGW32__
2170
   if (isalpha((int)path[0]) && path[1] == ':')
2171
      return true;
2172
#endif
2173

2174
   return false;
2175
}
2176

2177
void progress(const char *fmt, ...)
11,766✔
2178
{
2179
   if (opt_get_int(OPT_VERBOSE)) {
11,766✔
2180
      va_list ap;
9✔
2181
      va_start(ap, fmt);
9✔
2182
      char *msg LOCAL = xvasprintf(fmt, ap);
9✔
2183
      va_end(ap);
9✔
2184

2185
      static nvc_rusage_t last_ru;
9✔
2186

2187
      nvc_rusage_t ru;
9✔
2188
      nvc_rusage(&ru);
9✔
2189

2190
      const double conc = (double)(ru.user + ru.sys) / ru.ms;
9✔
2191

2192
      if (!isinf(conc) && conc > 1.1)
9✔
2193
         notef("%s [%ums %.1fx %+dkB]", msg, ru.ms, conc,
×
2194
               ru.rss - last_ru.rss);
×
2195
      else
2196
         notef("%s [%ums %+dkB]", msg, ru.ms, ru.rss - last_ru.rss);
9✔
2197

2198
      last_ru = ru;
9✔
2199
   }
2200
}
11,766✔
2201

2202
unsigned nvc_nprocs(void)
4,056✔
2203
{
2204
#if defined _WIN32
2205
   SYSTEM_INFO sysinfo;
2206
   GetSystemInfo(&sysinfo);
2207

2208
   return sysinfo.dwNumberOfProcessors;
2209
#elif defined _SC_NPROCESSORS_ONLN
2210
   long count = sysconf(_SC_NPROCESSORS_ONLN);
4,056✔
2211
   if (count == -1)
4,056✔
2212
      fatal_errno("sysconf(_SC_NPROCESSORS_ONLN)");
×
2213

2214
#if defined __linux__ && defined HAVE_GETTID
2215
   // Restrict to the number of CPUs we are allowed to run on
2216
   cpu_set_t s;
4,056✔
2217
   if (sched_getaffinity(gettid(), sizeof(cpu_set_t), &s) == 0)
4,056✔
2218
      return MAX(1, MIN(count, CPU_COUNT(&s)));
4,056✔
2219
#endif
2220

2221
   return count;
×
2222
#else
2223
#warning Cannot detect number of processors on this platform
2224
   return 1;
2225
#endif
2226
}
2227

2228
void capture_registers(struct cpu_state *cpu)
945✔
2229
{
2230
#if defined HAVE_GETCONTEXT
2231
   ucontext_t uc;
945✔
2232
   if (getcontext(&uc) != 0)
945✔
2233
      fatal_errno("getcontext");
×
2234

2235
   fill_cpu_state(cpu, &uc);
945✔
2236
#elif defined __MINGW32__
2237
   CONTEXT context;
2238
   RtlCaptureContext(&context);
2239
   fill_cpu_state(cpu, &context);
2240
#elif defined HAVE_PTHREAD
2241
   assert(atomic_load(&thread_regs) == NULL);
2242
   atomic_store(&thread_regs, cpu);
2243

2244
   if (pthread_kill(pthread_self(), SIGUSR2) != 0)
2245
      fatal_errno("pthread_kill");
2246

2247
   // Registers filled in by signal_handler
2248
   if (atomic_load(&thread_regs) != NULL)
2249
      fatal_trace("signal handler did not capture thread registers");
2250
#else
2251
#error cannot capture registers on this platform
2252
#endif
2253
}
945✔
2254

2255
void add_fault_handler(fault_fn_t fn, void *context)
6,640✔
2256
{
2257
   fault_handler_t *h = xmalloc(sizeof(fault_handler_t));
6,640✔
2258
   h->next    = fault_handlers;
6,640✔
2259
   h->fn      = fn;
6,640✔
2260
   h->context = context;
6,640✔
2261

2262
   fault_handlers = h;
6,640✔
2263
}
6,640✔
2264

2265
void remove_fault_handler(fault_fn_t fn, void *context)
2,826✔
2266
{
2267
   for (fault_handler_t **p = &fault_handlers; *p; p = &((*p)->next)) {
2,830✔
2268
      if ((*p)->fn == fn && (*p)->context == context) {
2,830✔
2269
         fault_handler_t *tmp = (*p)->next;
2,826✔
2270
         free(*p);
2,826✔
2271
         *p = tmp;
2,826✔
2272
         return;
2,826✔
2273
      }
2274
   }
2275

2276
   fatal_trace("no fault handler for %p with context %p", fn, context);
×
2277
}
2278

2279
void check_cpu_features(void)
3,272✔
2280
{
2281
#ifdef HAVE_POPCNT
2282
   if (!__builtin_cpu_supports("popcnt"))
3,272✔
2283
      fatal("CPU is missing support for POPCNT instruction, reconfigure "
×
2284
            "with $bold$--disable-popcnt$$");
2285
#endif
2286
}
3,272✔
2287

2288
void list_add(ptr_list_t *l, void *item)
70,784✔
2289
{
2290
   if (*l == NULL) {
70,784✔
2291
      *l = xmalloc_flex(sizeof(struct _ptr_list), 16, sizeof(void *));
22,500✔
2292
      (*l)->count = 0;
22,500✔
2293
      (*l)->max   = 16;
22,500✔
2294
   }
2295
   else if ((*l)->count == (*l)->max) {
48,284✔
2296
      (*l)->max *= 2;
364✔
2297
      *l = xrealloc_flex(*l, sizeof(struct _ptr_list),
364✔
2298
                         (*l)->max, sizeof(void *));
2299
   }
2300

2301
   (*l)->items[(*l)->count++] = item;
70,784✔
2302
}
70,784✔
2303

2304
void list_free(ptr_list_t *l)
74,133✔
2305
{
2306
   if (*l != NULL)
74,133✔
2307
      free(*l);
18,049✔
2308
   *l = NULL;
74,133✔
2309
}
74,133✔
2310

2311
void list_sort(ptr_list_t *l, list_cmp_fn_t cmp)
×
2312
{
2313
   if (*l != NULL)
×
2314
      qsort((*l)->items, (*l)->count, sizeof(void *), cmp);
×
2315
}
×
2316

2317
void list_clear(ptr_list_t *l)
×
2318
{
2319
   if (*l != NULL)
×
2320
      (*l)->count = 0;
×
2321
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc