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

nickg / nvc / 6318698303

26 Sep 2023 09:30PM UTC coverage: 91.068%. Remained the same
6318698303

push

github

nickg
Respect Linux CPU affinity when calculating maximum number of threads

3 of 3 new or added lines in 1 file covered. (100.0%)

48581 of 53346 relevant lines covered (91.07%)

941296.57 hits per line

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

77.86
/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)
10,490,500✔
166
{
167
   void *p = malloc(size);
10,490,500✔
168
   if (p == NULL)
10,490,500✔
169
      fatal("memory exhausted (malloc %lu)", (long unsigned)size);
×
170
   return p;
10,490,500✔
171
}
172

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

180
   return xmalloc(fixed + bytes);
8,153,740✔
181
}
182

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

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

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

203
   return xcalloc(fixed + bytes);
801,667✔
204
}
205

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

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

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

226
   return xrealloc(ptr, bytes);
1,564,100✔
227
}
228

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

236
   return xrealloc(ptr, bytes + fixed);
27,883✔
237
}
238

239
char *xstrdup(const char *str)
242,520✔
240
{
241
   char *copy = strdup(str);
242,520✔
242
   if (copy == NULL)
242,520✔
243
      fatal("memory exhausted (strdup)");
×
244
   return copy;
242,520✔
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)
346,615✔
256
{
257
   char *strp = NULL;
346,615✔
258
   if (vasprintf(&strp, fmt, ap) < 0)
346,615✔
259
      fatal("memory exhausted (vasprintf)");
×
260
   return strp;
346,615✔
261
}
262

263
char *xasprintf(const char *fmt, ...)
164,197✔
264
{
265
   va_list ap;
164,197✔
266
   va_start(ap, fmt);
164,197✔
267
   char *strp = xvasprintf(fmt, ap);
164,197✔
268
   va_end(ap);
164,197✔
269
   return strp;
164,197✔
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,816✔
293
{
294
   diag_t *d = diag_new(DIAG_NOTE, NULL);
3,816✔
295
   va_list ap;
3,816✔
296
   va_start(ap, fmt);
3,816✔
297
   diag_vprintf(d, fmt, ap);
3,816✔
298
   va_end(ap);
3,816✔
299
   diag_emit(d);
3,816✔
300
}
3,816✔
301

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

312
static char *ansi_vasprintf(const char *fmt, va_list ap, bool force_plain)
78,896✔
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;
78,896✔
318

319
   if (strchr(fmt, '$') == NULL)
78,896✔
320
      return xvasprintf(fmt, ap);
18,756✔
321

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

325
   while (*fmt != '\0') {
981,260✔
326
      if (*fmt == '$') {
921,120✔
327
         if (escape_start == NULL)
195,957✔
328
            escape_start = fmt;
329
         else {
330
            const char *e = escape_start + 1;
97,975✔
331
            size_t len = fmt - e;
97,975✔
332

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

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

341
            if ((*e == '<' || *e == '>') && *(e + 1) == '$') {
97,975✔
342
               override += *e == '<' ? -1 : 1;
2✔
343
               escape_start = NULL;
2✔
344
            }
345
            else if (strncmp(e, "link:", 5) == 0) {
97,973✔
346
               if (want_links && !force_plain) {
10,949✔
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,949✔
353
                  if (bel && bel < e + len)
10,949✔
354
                     tb_catn(tb, bel + 1, e + len - bel - 1);
10,949✔
355
               }
356
               escape_start = NULL;
357
            }
358
            else if (want_color && !force_plain && override >= 0) {
87,024✔
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)
725,163✔
403
         tb_append(tb, *fmt);
314,818✔
404

405
      ++fmt;
921,120✔
406
   }
407

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

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

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

418
   bool escape = false;
67,280✔
419
   int len = 0;
67,280✔
420
   for (const char *p = strp; *p != '\0'; p++) {
953,785✔
421
      if (*p == '\033')
886,505✔
422
         escape = true;
423
      if (escape)
886,505✔
424
         escape = (*p != 'm');
×
425
      else
426
         len += 1;
886,505✔
427
   }
428

429
   fputs(strp, f);
67,280✔
430
   return len;
67,280✔
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,590✔
439
{
440
   return ansi_vasprintf(fmt, ap, true);
11,590✔
441
}
442

443
int color_fprintf(FILE *f, const char *fmt, ...)
67,261✔
444
{
445
   va_list ap;
67,261✔
446
   va_start(ap, fmt);
67,261✔
447
   const int len = color_vfprintf(f, fmt, ap);
67,261✔
448
   va_end(ap);
67,261✔
449
   return len;
67,261✔
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)
15,455✔
476
{
477
   return want_color;
15,455✔
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, ...)
636✔
508
{
509
   diag_t *d = diag_new(DIAG_ERROR, loc);
636✔
510

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

516
   diag_emit(d);
636✔
517
}
633✔
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,027✔
869
{
870
#ifdef __MINGW32__
871
   SetUnhandledExceptionFilter(win32_exception_handler);
872
#else
873

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

880
#ifndef __SANITIZE_THREAD__
881
   sigaction(SIGSEGV, &sa, NULL);
4,027✔
882
   sigaction(SIGUSR1, &sa, NULL);
4,027✔
883
   sigaction(SIGFPE, &sa, NULL);
4,027✔
884
   sigaction(SIGBUS, &sa, NULL);
4,027✔
885
   sigaction(SIGILL, &sa, NULL);
4,027✔
886
   sigaction(SIGABRT, &sa, NULL);
4,027✔
887
   sigaction(SIGTRAP, &sa, NULL);
4,027✔
888
#endif  // !__SANITIZE_THREAD__
889
   sigaction(SIGUSR2, &sa, NULL);
4,027✔
890
#endif  // !__MINGW32__
891
}
4,027✔
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,270✔
912
{
913
   ctrl_c_arg = arg;
5,270✔
914
   atomic_store(&ctrl_c_fn, fn);
5,270✔
915

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

923
      sigaction(SIGINT, &sa, NULL);
2,635✔
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,635✔
932
      sa.sa_handler = SIG_DFL;
2,635✔
933
      sigaction(SIGINT, &sa, NULL);
2,635✔
934
#else
935
      if (!SetConsoleCtrlHandler(win32_ctrl_c_handler, FALSE))
936
         fatal_trace("SetConsoleCtrlHandler");
937
#endif
938
   }
939
}
5,270✔
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,028✔
950
{
951
   const char *nvc_colors = getenv("NVC_COLORS");
4,028✔
952
   const char *term = getenv("TERM") ?: "";
4,028✔
953

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

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

960
   bool is_tty = isatty(STDERR_FILENO);
4,028✔
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,028✔
981
      want_color = true;
1✔
982
   else if (nvc_colors && strcmp(nvc_colors, "never") == 0)
4,027✔
983
      want_color = false;
×
984
   else {
985
      want_color = is_tty;
4,027✔
986

987
      if (want_color && (term != NULL)) {
4,027✔
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,028✔
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,028✔
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,028✔
1037

1038
   const char *lang = getenv("LANG");
4,028✔
1039
   if (lang != NULL && *lang != '\0' && strcasestr(lang, "utf-8") == NULL)
4,028✔
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,028✔
1053
}
4,028✔
1054

1055
int terminal_width(void)
13,670✔
1056
{
1057
   return term_width;
13,670✔
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,555,820✔
1094
{
1095
   if (ch >= 'a' && ch <= 'z')
2,555,820✔
1096
      return ch - 'a' + 'A';
1,534,890✔
1097
   else if ((ch >= 0xe0 && ch <= 0xf6) || (ch >= 0xf8 && ch <= 0xfe))
1,020,930✔
1098
      return ch - 0x20;
39✔
1099
   else
1100
      return ch;
1,020,890✔
1101
}
1102

1103
char tolower_iso88591(unsigned char ch)
245,521✔
1104
{
1105
   if (ch >= 'A' && ch <= 'Z')
245,521✔
1106
      return ch + 'a' - 'A';
137,015✔
1107
   else if ((ch >= 0xc0 && ch <= 0xd6) || (ch >= 0xd8 && ch <= 0xde))
108,506✔
1108
      return ch + 0x20;
90✔
1109
   else
1110
      return ch;
108,416✔
1111
}
1112

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

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

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

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

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

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

1159
int ilog2(int64_t n)
10,025✔
1160
{
1161
   if (n <= 1)
10,025✔
1162
      return 0;
1163
   else {
1164
      int r = 0;
1165
      int64_t c = 1;
1166
      while (c < n) {
38,280✔
1167
         r += 1;
28,284✔
1168
         c <<= 1;
28,284✔
1169
      }
1170
      return r;
9,996✔
1171
   }
1172
}
1173

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

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

1195
   return result;
57✔
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,168✔
1212
{
1213
#if __SANITIZE_ADDRESS__
1214
   free(ptr);
11,168✔
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,168✔
1223

1224
void *nvc_memalign(size_t align, size_t sz)
32,097✔
1225
{
1226
#if __SANITIZE_ADDRESS__
1227
   void *ptr;
32,097✔
1228
   if (posix_memalign(&ptr, align, sz) != 0)
64,194✔
1229
      fatal_errno("posix_memalign");
×
1230

1231
   memset(ptr, '\0', sz);
32,097✔
1232
   return ptr;
32,097✔
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)
40,963✔
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)
40,963✔
1280
      return;
1281
#endif
1282
   static const int map[] = {
23✔
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)
23✔
1287
      fatal_errno("mprotect");
×
1288
#endif
1289
}
1290

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

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

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

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

1308
void *map_jit_pages(size_t align, size_t sz)
23✔
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);
23✔
1317
   nvc_memprotect(ptr, sz, MEM_RWX);
23✔
1318
#endif
1319

1320
   return ptr;
23✔
1321
}
1322

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

1327
   va_list ap;
838,100✔
1328
   va_start(ap, fmt);
838,100✔
1329

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

1335
   va_end(ap);
838,100✔
1336

1337
   return nbytes;
838,100✔
1338
}
1339

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

1347
   tb->buf[0] = '\0';
354,398✔
1348

1349
   return tb;
354,398✔
1350
}
1351

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

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

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

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

1374
      va_end(aq);
197,142✔
1375

1376
      if (nchars + 1 < avail)
197,142✔
1377
         break;
1378

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

1383
   tb->len += nchars;
197,132✔
1384
}
197,132✔
1385

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

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

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

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

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

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

1423
void tb_cat(text_buf_t *tb, const char *str)
475,017✔
1424
{
1425
   tb_catn(tb, str, strlen(str));
475,017✔
1426
}
475,017✔
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)
197,798✔
1456
{
1457
   return tb->len;
197,798✔
1458
}
1459

1460
char *tb_claim(text_buf_t *tb)
20,422✔
1461
{
1462
   char *buf = tb->buf;
20,422✔
1463
   tb->buf = NULL;
20,422✔
1464
   return buf;
20,422✔
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)
527,878✔
1480
{
1481
   return tb->buf;
527,878✔
1482
}
1483

1484
void tb_rewind(text_buf_t *tb)
798✔
1485
{
1486
   tb->len = 0;
798✔
1487
   tb->buf[0] = '\0';
798✔
1488
}
798✔
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_downcase(text_buf_t *tb)
2,748✔
1498
{
1499
   for (size_t i = 0; i < tb->len; i++)
39,216✔
1500
      tb->buf[i] = tolower_iso88591(tb->buf[i]);
36,468✔
1501
}
2,748✔
1502

1503
void tb_upcase(text_buf_t *tb)
15✔
1504
{
1505
   for (size_t i = 0; i < tb->len; i++)
145✔
1506
      tb->buf[i] = toupper_iso88591(tb->buf[i]);
130✔
1507
}
15✔
1508

1509
void tb_replace(text_buf_t *tb, char old, char rep)
903✔
1510
{
1511
   for (size_t i = 0; i < tb->len; i++) {
26,289✔
1512
      if (tb->buf[i] == old)
25,386✔
1513
         tb->buf[i] = rep;
402✔
1514
   }
1515
}
903✔
1516

1517
void _local_free(void *ptr)
566,261✔
1518
{
1519
   free(*(void **)ptr);
566,261✔
1520
}
566,261✔
1521

1522
void set_message_style(message_style_t style)
×
1523
{
1524
   message_style = style;
×
1525

1526
   if (style == MESSAGE_COMPACT)
×
1527
      want_color = false;
×
1528
}
×
1529

1530
message_style_t get_message_style(void)
13,379✔
1531
{
1532
   return message_style;
13,379✔
1533
}
1534

1535
#ifndef __MINGW32__
1536
static uint64_t timeval_us(struct timeval *tv)
1537
{
1538
   return (tv->tv_sec * UINT64_C(1000000)) + tv->tv_usec;
1539
}
1540
#endif
1541

1542
void nvc_rusage(nvc_rusage_t *ru)
2,667✔
1543
{
1544
#ifndef __MINGW32__
1545
   static uint64_t last_user, last_sys;
2,667✔
1546

1547
   struct rusage buf;
2,667✔
1548
   if (getrusage(RUSAGE_SELF, &buf) < 0)
2,667✔
1549
      fatal_errno("getrusage");
×
1550

1551
   const uint64_t user = timeval_us(&(buf.ru_utime));
2,667✔
1552
   const uint64_t sys = timeval_us(&(buf.ru_stime));
2,667✔
1553

1554
   ru->user = (user - last_user) / 1000;
2,667✔
1555
   ru->sys = (sys - last_sys) / 1000;
2,667✔
1556

1557
   last_sys = sys;
2,667✔
1558
   last_user = user;
2,667✔
1559

1560
#ifdef __APPLE__
1561
   const int rss_units = 1024;
1562
#else
1563
   const int rss_units = 1;
2,667✔
1564
#endif
1565

1566
   ru->rss = buf.ru_maxrss / rss_units;
2,667✔
1567
#else
1568
   static ULARGE_INTEGER last_kernel, last_user;
1569
   ULARGE_INTEGER lv_Tkernel, lv_Tuser;
1570
   HANDLE hProcess = GetCurrentProcess();
1571

1572
   FILETIME ftCreation, ftExit, ftKernel, ftUser;
1573
   if (!GetProcessTimes(hProcess, &ftCreation, &ftExit, &ftKernel, &ftUser))
1574
      fatal_errno("GetProcessTimes");
1575

1576
   lv_Tkernel.LowPart = ftKernel.dwLowDateTime;
1577
   lv_Tkernel.HighPart = ftKernel.dwHighDateTime;
1578
   lv_Tuser.LowPart = ftUser.dwLowDateTime;
1579
   lv_Tuser.HighPart = ftUser.dwHighDateTime;
1580

1581
   ru->user = (lv_Tuser.QuadPart - last_user.QuadPart) / 10000;
1582
   ru->sys = (lv_Tkernel.QuadPart - last_kernel.QuadPart) / 10000;
1583

1584
   last_user = lv_Tuser;
1585
   last_kernel = lv_Tkernel;
1586

1587
   PROCESS_MEMORY_COUNTERS counters;
1588
   if (!GetProcessMemoryInfo(GetCurrentProcess(), &counters, sizeof(counters)))
1589
      fatal_errno("GetProcessMemoryInfo");
1590

1591
   ru->rss = counters.PeakWorkingSetSize / 1024;
1592
#endif
1593

1594
   static uint64_t last_ts;
2,667✔
1595
   const uint64_t ts = get_timestamp_us();
2,667✔
1596
   ru->ms = last_ts == 0 ? ru->user + ru->sys : (ts - last_ts) / 1000;
2,667✔
1597
   last_ts = ts;
2,667✔
1598
}
2,667✔
1599

1600
#ifdef __MINGW32__
1601
static uint64_t file_time_to_nanos(LPFILETIME ft)
1602
{
1603
   uint64_t nanos = (uint64_t)ft->dwHighDateTime << 32;
1604
   nanos |= ft->dwLowDateTime;
1605

1606
   // Windows file timestamps are in units of 100 nanoseconds since
1607
   // 1601-01-01T00:00:00Z: convert that to nanoseconds since the Unix
1608
   // epoch 1970-01-01T00:00:00Z
1609
   nanos -= UINT64_C(116444736000000000);
1610
   nanos *= 100;
1611

1612
   return nanos;
1613
}
1614

1615
static bool fill_file_info(file_info_t *info, HANDLE handle)
1616
{
1617
   memset(info, '\0', sizeof(file_info_t));
1618

1619
   BY_HANDLE_FILE_INFORMATION hinfo;
1620
   if (!GetFileInformationByHandle(handle, &hinfo))
1621
      fatal_errno("GetFileInformationByHandle");
1622

1623
   info->size = (uint64_t)hinfo.nFileSizeHigh << 32;
1624
   info->size |= hinfo.nFileSizeLow;
1625

1626
   if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1627
      info->type = FILE_DIR;
1628
   else
1629
      info->type = FILE_REGULAR;
1630

1631
   info->mtime = file_time_to_nanos(&(hinfo.ftLastWriteTime));
1632

1633
   return true;
1634
}
1635
#else  // !__MINGW32__
1636
static void fill_file_info(file_info_t *info, const struct stat *st)
43,673✔
1637
{
1638
   memset(info, '\0', sizeof(file_info_t));
43,673✔
1639

1640
   info->size = st->st_size;
43,673✔
1641

1642
   if (S_ISDIR(st->st_mode))
43,673✔
1643
      info->type = FILE_DIR;
101✔
1644
   else if (!S_ISREG(st->st_mode))
43,572✔
1645
      info->type = FILE_FIFO;
27✔
1646
   else
1647
      info->type = FILE_REGULAR;
43,545✔
1648

1649
   info->mtime = st->st_mtime * UINT64_C(1000000000);
43,673✔
1650

1651
#if defined HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
1652
   info->mtime += st->st_mtimespec.tv_nsec;
1653
#elif defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
1654
   info->mtime += st->st_mtim.tv_nsec;
43,673✔
1655
#endif
1656
}
43,673✔
1657
#endif  // !__MINGW32__
1658

1659
bool get_file_info(const char *path, file_info_t *info)
24,276✔
1660
{
1661
#ifdef __MINGW32__
1662
   HANDLE handle = CreateFile(
1663
        path, FILE_READ_ATTRIBUTES,
1664
        FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1665
        NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1666

1667
   if (handle == INVALID_HANDLE_VALUE)
1668
      return false;
1669

1670
   fill_file_info(info, handle);
1671

1672
   if (!CloseHandle(handle))
1673
      fatal_errno("CloseHandle");
1674

1675
   return true;
1676
#else
1677
   struct stat st;
24,276✔
1678
   if (stat(path, &st) == 0) {
24,276✔
1679
      fill_file_info(info, &st);
12,894✔
1680
      return true;
12,894✔
1681
   }
1682
   else
1683
      return false;
1684
#endif
1685
}
1686

1687
bool get_handle_info(int fd, file_info_t *info)
30,779✔
1688
{
1689
#ifdef __MINGW32__
1690
   HANDLE handle = (HANDLE)_get_osfhandle(fd);
1691
   fill_file_info(info, handle);
1692
   return true;
1693
#else
1694
   struct stat st;
30,779✔
1695
   if (fstat(fd, &st) == 0) {
30,779✔
1696
      fill_file_info(info, &st);
30,779✔
1697
      return true;
30,779✔
1698
   }
1699
   else
1700
      return false;
1701
#endif
1702
}
1703

1704
bool get_handle_path(int fd, text_buf_t *tb)
3✔
1705
{
1706
#ifdef __MINGW32__
1707
   HANDLE handle = (HANDLE)_get_osfhandle(fd);
1708
   char buf[PATH_MAX];
1709

1710
   DWORD nchars = GetFinalPathNameByHandle(handle, buf, PATH_MAX, 0);
1711
   if (nchars == 0)
1712
      return false;
1713

1714
   tb_catn(tb, buf, nchars);
1715
   return true;
1716
#elif defined __linux__
1717
   char path[64], buf[PATH_MAX];
3✔
1718
   checked_sprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
3✔
1719

1720
   size_t nchars = readlink(path, buf, PATH_MAX);
3✔
1721
   if (nchars == -1)
3✔
1722
      return false;
1723

1724
   tb_catn(tb, buf, nchars);
3✔
1725
   return true;
3✔
1726
#elif defined F_GETPATH
1727
   char buf[PATH_MAX];
1728
   if (fcntl(fd, F_GETPATH, buf) == -1)
1729
      return false;
1730

1731
   tb_cat(tb, buf);
1732
   return true;
1733
#else
1734
   return false;
1735
#endif
1736
}
1737

1738
void run_program(const char *const *args)
1,132✔
1739
{
1740
#if defined __CYGWIN__ || defined __MINGW32__
1741
   int status = spawnvp(_P_WAIT, args[0], (char *const *)args);
1742
#else  // __CYGWIN__
1743
   pid_t pid = fork();
1,132✔
1744
   int status = 0;
2,264✔
1745
   if (pid == 0) {
2,264✔
1746
      execvp(args[0], (char *const *)args);
1,132✔
1747
      fatal_errno("execv");
1,132✔
1748
   }
1749
   else if (pid > 0) {
1,132✔
1750
      if (waitpid(pid, &status, 0) != pid)
1,132✔
1751
         fatal_errno("waitpid");
×
1752

1753
      status = WEXITSTATUS(status);
1,132✔
1754
   }
1755
   else
1756
      fatal_errno("fork");
×
1757
#endif  // __CYGWIN__
1758

1759
   if (status != 0) {
1,132✔
1760
      LOCAL_TEXT_BUF tb = tb_new();
×
1761
      for (size_t i = 0; args[i] != NULL; i++)
×
1762
         tb_printf(tb, "%s%s", i > 0 ? " " : "", args[i]);
×
1763
      fatal("$bold$%s$$ failed with status %d", tb_get(tb), status);
×
1764
   }
1765
}
1,132✔
1766

1767
char *nvc_temp_file(void)
3✔
1768
{
1769
   static const char *try[] = { "TMPDIR", "TEMP", "TMP" };
3✔
1770
   const char *tmpdir = NULL;
3✔
1771
   for (int i = 0; tmpdir == NULL && i < ARRAY_LEN(try); i++)
12✔
1772
      tmpdir = getenv(try[i]);
9✔
1773

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

1787
void file_read_lock(int fd)
17,916✔
1788
{
1789
#ifdef __MINGW32__
1790
   HANDLE hf = (HANDLE)_get_osfhandle(fd);
1791

1792
   LARGE_INTEGER li;
1793
   li.QuadPart = _filelengthi64(fd);
1794

1795
   OVERLAPPED ovlp;
1796
   memset(&ovlp, 0, sizeof ovlp);
1797

1798
   if (!LockFileEx(hf, 0, 0, li.LowPart, li.HighPart, &ovlp))
1799
      fatal_errno("LockFileEx");
1800
#else
1801
   if (flock(fd, LOCK_SH) < 0)
17,916✔
1802
      fatal_errno("flock");
×
1803
#endif
1804
}
17,916✔
1805

1806
void file_write_lock(int fd)
6,625✔
1807
{
1808
#ifdef __MINGW32__
1809
   HANDLE hf = (HANDLE)_get_osfhandle(fd);
1810

1811
   LARGE_INTEGER li;
1812
   li.QuadPart = _filelengthi64(fd);
1813

1814
   OVERLAPPED ovlp;
1815
   memset(&ovlp, 0, sizeof ovlp);
1816

1817
   if (!LockFileEx(hf, LOCKFILE_EXCLUSIVE_LOCK, 0,
1818
                   li.LowPart, li.HighPart, &ovlp))
1819
      fatal_errno("LockFileEx");
1820
#else
1821
   if (flock(fd, LOCK_EX) < 0)
6,625✔
1822
      fatal_errno("flock");
×
1823
#endif
1824
}
6,625✔
1825

1826
void file_unlock(int fd)
24,532✔
1827
{
1828
#ifdef __MINGW32__
1829
   HANDLE hf = (HANDLE)_get_osfhandle(fd);
1830

1831
   LARGE_INTEGER li;
1832
   li.QuadPart = _filelengthi64 (fd);
1833

1834
   UnlockFile(hf, 0, 0, li.LowPart, li.HighPart);
1835
#else
1836
   if (flock(fd, LOCK_UN) < 0)
24,532✔
1837
      fatal_errno("flock");
×
1838
#endif
1839
}
24,532✔
1840

1841
void *map_file(int fd, size_t size)
17,311✔
1842
{
1843
#ifdef __MINGW32__
1844
   HANDLE handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), NULL,
1845
                                     PAGE_READONLY, 0, size, NULL);
1846
   if (!handle)
1847
      fatal_errno("CreateFileMapping");
1848

1849
   void *ptr = MapViewOfFileEx(handle, FILE_MAP_READ, 0,
1850
                               0, (SIZE_T) size, (LPVOID) NULL);
1851
   CloseHandle(handle);
1852
   if (ptr == NULL)
1853
      fatal_errno("MapViewOfFileEx");
1854
#else
1855
   void *ptr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
17,311✔
1856
   if (ptr == MAP_FAILED)
17,311✔
1857
      fatal_errno("mmap");
×
1858
#endif
1859
   return ptr;
17,311✔
1860
}
1861

1862
void unmap_file(void *ptr, size_t size)
13,603✔
1863
{
1864
#ifdef __MINGW32__
1865
   if (!UnmapViewOfFile((LPCVOID) ptr))
1866
      fatal_errno("UnmapViewOfFile");
1867
#else
1868
   munmap(ptr, size);
13,603✔
1869
#endif
1870
}
13,603✔
1871

1872
void make_dir(const char *path)
2,922✔
1873
{
1874
#ifdef __MINGW32__
1875
   if (!CreateDirectory(path, NULL) && (GetLastError() != ERROR_ALREADY_EXISTS))
1876
      fatal_errno("mkdir: %s", path);
1877
#else
1878
   if (mkdir(path, 0777) != 0 && errno != EEXIST)
2,922✔
1879
      fatal_errno("mkdir: %s", path);
×
1880
#endif
1881
}
2,922✔
1882

1883
uint64_t get_timestamp_ns(void)
342,889✔
1884
{
1885
#if defined __MINGW32__
1886
   static volatile uint64_t freq;
1887
   if (load_acquire(&freq) == 0) {
1888
      LARGE_INTEGER tmp;
1889
      if (!QueryPerformanceFrequency(&tmp))
1890
         fatal_errno("QueryPerformanceFrequency");
1891
      store_release(&freq, tmp.QuadPart);
1892
   }
1893

1894
   LARGE_INTEGER ticks;
1895
   if (!QueryPerformanceCounter(&ticks))
1896
      fatal_errno("QueryPerformanceCounter");
1897
   return (double)ticks.QuadPart * (1e9 / (double)freq);
1898
#else
1899
   struct timespec ts;
342,889✔
1900
   if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
342,889✔
1901
      fatal_errno("clock_gettime");
×
1902
   return ts.tv_nsec + (ts.tv_sec * UINT64_C(1000000000));
342,889✔
1903
#endif
1904
}
1905

1906
uint64_t get_timestamp_us(void)
342,889✔
1907
{
1908
   return get_timestamp_ns() / 1000;
342,889✔
1909
}
1910

1911
timestamp_t get_real_time(void)
12,615✔
1912
{
1913
#if defined __MINGW32__
1914
   FILETIME ft;
1915
   GetSystemTimeAsFileTime(&ft);
1916

1917
   return file_time_to_nanos(&ft);
1918
#else
1919
   struct timespec ts;
12,615✔
1920
   if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
12,615✔
1921
      fatal_errno("clock_gettime");
×
1922

1923
   return (uint64_t)ts.tv_nsec + ((uint64_t)ts.tv_sec * UINT64_C(1000000000));
12,615✔
1924
#endif
1925
}
1926

1927
void open_pipe(int *rfd, int *wfd)
×
1928
{
1929
   int fds[2];
×
1930
#ifdef __MINGW32__
1931
   const int rc = _pipe(fds, 4096, _O_BINARY);
1932
#else
1933
   const int rc = pipe(fds) < 0;
×
1934
#endif
1935
   if (rc < 0)
×
1936
      fatal_errno("failed to create pipe");
1937

1938
   *rfd = fds[0];
×
1939
   *wfd = fds[1];
×
1940
}
×
1941

1942
#if defined _WIN32 || defined __CYGWIN__
1943
static struct {
1944
   char illegal;
1945
   const char *rep;
1946
} symbol_replacements[] = {
1947
   { '(', "_lp_"   },
1948
   { ')', "_rp_"   },
1949
   { '"', "_q_"    },
1950
   { '[', "_ls_"   },
1951
   { ']', "_rs_"   },
1952
   { '*', "_mult_" },
1953
   { '+', "_plus_" },
1954
   { '=', "_eq_"   }
1955
};
1956

1957
static text_buf_t *safe_symbol_win32(const char *text)
1958
{
1959
   text_buf_t *tb = tb_new();
1960

1961
   for (const char *p = text; *p != '\0'; p++) {
1962
      bool replaced = false;
1963
      for (size_t j = 0; j < ARRAY_LEN(symbol_replacements); j++) {
1964
         if (*p == symbol_replacements[j].illegal) {
1965
            tb_cat(tb, symbol_replacements[j].rep);
1966
            replaced = true;
1967
            break;
1968
         }
1969
      }
1970

1971
      if (!replaced)
1972
         tb_append(tb, *p);
1973
   }
1974

1975
   return tb;
1976
}
1977

1978
#endif
1979

1980
text_buf_t *safe_symbol(ident_t id)
75,243✔
1981
{
1982
   // Return a string that is safe to use as a symbol name on this platform
1983

1984
   text_buf_t *tb = tb_new();
75,243✔
1985
   tb_istr(tb, id);
75,243✔
1986

1987
#if defined _WIN32 || defined __CYGWIN__
1988
   if (strpbrk(tb_get(tb), "()\"[]*+=") == NULL)
1989
      return tb;
1990
   else {
1991
      text_buf_t *new = safe_symbol_win32(tb_get(tb));
1992
      tb_free(tb);
1993
      return new;
1994
   }
1995
#else
1996
   return tb;
75,243✔
1997
#endif
1998
}
1999

2000
text_buf_t *unsafe_symbol(const char *text)
1✔
2001
{
2002
   // Restore original symbol from safe_symbol
2003

2004
   text_buf_t *tb = tb_new();
1✔
2005

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

2020
      if (!replaced)
2021
         tb_append(tb, *p++);
2022
   }
2023

2024
   return tb;
2025
#else
2026
   tb_cat(tb, text);
1✔
2027
#endif
2028

2029
   return tb;
1✔
2030
}
2031

2032
void __cleanup_array(void *ptr)
113,883✔
2033
{
2034
   A(void *) *a = ptr;
113,883✔
2035
   ACLEAR(*a);
113,883✔
2036
}
113,883✔
2037

2038
void __array_resize_slow(void **ptr, uint32_t *limit, uint32_t count,
1,219,110✔
2039
                         size_t size)
2040
{
2041
   if (count == 0) {
1,219,110✔
2042
      free(*ptr);
×
2043
      *ptr = NULL;
×
2044
      *limit = 0;
×
2045
   }
2046
   else {
2047
      if (*limit == 0)
1,219,110✔
2048
         *limit = count;  // Setting the initial size of the array
1,023,630✔
2049
      else
2050
         *limit = next_power_of_2(count);
195,476✔
2051
      *ptr = xrealloc_array(*ptr, *limit, size);
1,219,110✔
2052
   }
2053
}
1,219,110✔
2054

2055
char *search_path(const char *name)
×
2056
{
2057
   const char *path = getenv("PATH");
×
2058
   if (path == NULL)
×
2059
      return xstrdup(name);
×
2060

2061
   char LOCAL *tmp = xstrdup(path);
×
2062
   for (char *p = strtok(tmp, ":"); p; p = strtok(NULL, ":")) {
×
2063
      char *full = xasprintf("%s"DIR_SEP"%s", p, name);
×
2064

2065
      struct stat sb;
×
2066
      if (stat(full, &sb) == 0)
×
2067
         return full;
×
2068

2069
      free(full);
×
2070
   }
2071

2072
   return xstrdup(name);
×
2073
}
2074

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

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

2128
void get_libexec_dir(text_buf_t *tb)
×
2129
{
2130
#if defined __MINGW32__
2131
   get_relative_prefix(tb);
2132
   tb_cat(tb, DIR_SEP "libexec" DIR_SEP "nvc");
2133
#else
2134
   tb_cat(tb, LIBEXECDIR);
×
2135
#endif
2136
}
×
2137

2138
void get_lib_dir(text_buf_t *tb)
3,772✔
2139
{
2140
#if defined __MINGW32__
2141
   get_relative_prefix(tb);
2142
   tb_cat(tb, DIR_SEP "lib" DIR_SEP "nvc");
2143
#else
2144
   tb_cat(tb, LIBDIR);
3,772✔
2145
#endif
2146
}
3,772✔
2147

2148
void get_data_dir(text_buf_t *tb)
×
2149
{
2150
#if defined __MINGW32__
2151
   get_relative_prefix(tb);
2152
   tb_cat(tb, DIR_SEP "share" DIR_SEP "nvc");
2153
#else
2154
   tb_cat(tb, DATADIR);
×
2155
#endif
2156
}
×
2157

2158
bool is_absolute_path(const char *path)
27✔
2159
{
2160
   if (path[0] == DIR_SEP[0] || path[0] == '/')
27✔
2161
      return true;
27✔
2162

2163
#ifdef __MINGW32__
2164
   if (isalpha((int)path[0]) && path[1] == ':')
2165
      return true;
2166
#endif
2167

2168
   return false;
2169
}
2170

2171
void progress(const char *fmt, ...)
11,706✔
2172
{
2173
   if (opt_get_int(OPT_VERBOSE)) {
11,706✔
2174
      va_list ap;
9✔
2175
      va_start(ap, fmt);
9✔
2176
      char *msg LOCAL = xvasprintf(fmt, ap);
9✔
2177
      va_end(ap);
9✔
2178

2179
      static nvc_rusage_t last_ru;
9✔
2180

2181
      nvc_rusage_t ru;
9✔
2182
      nvc_rusage(&ru);
9✔
2183

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

2186
      if (!isinf(conc) && conc > 1.1)
9✔
2187
         notef("%s [%ums %.1fx %+dkB]", msg, ru.ms, conc,
×
2188
               ru.rss - last_ru.rss);
×
2189
      else
2190
         notef("%s [%ums %+dkB]", msg, ru.ms, ru.rss - last_ru.rss);
9✔
2191

2192
      last_ru = ru;
9✔
2193
   }
2194
}
11,706✔
2195

2196
unsigned nvc_nprocs(void)
4,027✔
2197
{
2198
#if defined _WIN32
2199
   SYSTEM_INFO sysinfo;
2200
   GetSystemInfo(&sysinfo);
2201

2202
   return sysinfo.dwNumberOfProcessors;
2203
#elif defined _SC_NPROCESSORS_ONLN
2204
   long count = sysconf(_SC_NPROCESSORS_ONLN);
4,027✔
2205
   if (count == -1)
4,027✔
2206
      fatal_errno("sysconf(_SC_NPROCESSORS_ONLN)");
×
2207

2208
#ifdef __linux__
2209
   // Restrict to the number of CPUs we are allowed to run on
2210
   cpu_set_t s;
4,027✔
2211
   if (sched_getaffinity(gettid(), sizeof(cpu_set_t), &s) == 0)
4,027✔
2212
      return MAX(1, MIN(count, CPU_COUNT(&s)));
4,027✔
2213
#endif
2214

2215
   return count;
×
2216
#else
2217
#warning Cannot detect number of processors on this platform
2218
   return 1;
2219
#endif
2220
}
2221

2222
void capture_registers(struct cpu_state *cpu)
945✔
2223
{
2224
#if defined HAVE_GETCONTEXT
2225
   ucontext_t uc;
945✔
2226
   if (getcontext(&uc) != 0)
945✔
2227
      fatal_errno("getcontext");
×
2228

2229
   fill_cpu_state(cpu, &uc);
945✔
2230
#elif defined __MINGW32__
2231
   CONTEXT context;
2232
   RtlCaptureContext(&context);
2233
   fill_cpu_state(cpu, &context);
2234
#elif defined HAVE_PTHREAD
2235
   assert(atomic_load(&thread_regs) == NULL);
2236
   atomic_store(&thread_regs, cpu);
2237

2238
   if (pthread_kill(pthread_self(), SIGUSR2) != 0)
2239
      fatal_errno("pthread_kill");
2240

2241
   // Registers filled in by signal_handler
2242
   if (atomic_load(&thread_regs) != NULL)
2243
      fatal_trace("signal handler did not capture thread registers");
2244
#else
2245
#error cannot capture registers on this platform
2246
#endif
2247
}
945✔
2248

2249
void add_fault_handler(fault_fn_t fn, void *context)
3,808✔
2250
{
2251
   fault_handler_t *h = xmalloc(sizeof(fault_handler_t));
3,808✔
2252
   h->next    = fault_handlers;
3,808✔
2253
   h->fn      = fn;
3,808✔
2254
   h->context = context;
3,808✔
2255

2256
   fault_handlers = h;
3,808✔
2257
}
3,808✔
2258

2259
void remove_fault_handler(fault_fn_t fn, void *context)
23✔
2260
{
2261
   for (fault_handler_t **p = &fault_handlers; *p; p = &((*p)->next)) {
23✔
2262
      if ((*p)->fn == fn && (*p)->context == context) {
23✔
2263
         fault_handler_t *tmp = (*p)->next;
23✔
2264
         free(*p);
23✔
2265
         *p = tmp;
23✔
2266
         return;
23✔
2267
      }
2268
   }
2269

2270
   fatal_trace("no fault handler for %p with context %p", fn, context);
×
2271
}
2272

2273
void check_cpu_features(void)
3,251✔
2274
{
2275
#ifdef HAVE_POPCNT
2276
   if (!__builtin_cpu_supports("popcnt"))
3,251✔
2277
      fatal("CPU is missing support for POPCNT instruction, reconfigure "
×
2278
            "with $bold$--disable-popcnt$$");
2279
#endif
2280
}
3,251✔
2281

2282
void list_add(ptr_list_t *l, void *item)
75,063✔
2283
{
2284
   if (*l == NULL) {
75,063✔
2285
      *l = xmalloc_flex(sizeof(struct _ptr_list), 16, sizeof(void *));
22,230✔
2286
      (*l)->count = 0;
22,230✔
2287
      (*l)->max   = 16;
22,230✔
2288
   }
2289
   else if ((*l)->count == (*l)->max) {
52,833✔
2290
      (*l)->max *= 2;
364✔
2291
      *l = xrealloc_flex(*l, sizeof(struct _ptr_list),
364✔
2292
                         (*l)->max, sizeof(void *));
2293
   }
2294

2295
   (*l)->items[(*l)->count++] = item;
75,063✔
2296
}
75,063✔
2297

2298
void list_free(ptr_list_t *l)
97,320✔
2299
{
2300
   if (*l != NULL)
97,320✔
2301
      free(*l);
17,779✔
2302
   *l = NULL;
97,320✔
2303
}
97,320✔
2304

2305
void list_sort(ptr_list_t *l, list_cmp_fn_t cmp)
×
2306
{
2307
   if (*l != NULL)
×
2308
      qsort((*l)->items, (*l)->count, sizeof(void *), cmp);
×
2309
}
×
2310

2311
void list_clear(ptr_list_t *l)
×
2312
{
2313
   if (*l != NULL)
×
2314
      (*l)->count = 0;
×
2315
}
×
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