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

nickg / nvc / 20116595352

10 Dec 2025 11:27PM UTC coverage: 92.599% (-0.006%) from 92.605%
20116595352

push

github

nickg
Add --relative analysis option to control saved paths

46 of 53 new or added lines in 5 files covered. (86.79%)

1 existing line in 1 file now uncovered.

75425 of 81453 relevant lines covered (92.6%)

444499.69 hits per line

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

72.82
/src/util.c
1
//
2
//  Copyright (C) 2011-2025  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
#include <shlwapi.h>
26
#endif
27

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

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

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

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

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

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

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

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

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

110
#define MAX_FMT_BUFS    32
111
#define MAX_PRINTF_BUFS 8
112

113
#define HUGE_PAGE_SIZE  0x200000
114

115
#define POOL_MIN_ALIGN  sizeof(double)
116
#define POOL_PAGE_4K    12
117
#define POOL_PAGE_1M    20
118
#define POOL_PAGE_MIN   POOL_PAGE_4K
119
#define POOL_PAGE_MAX   POOL_PAGE_1M
120

121
#if ASAN_ENABLED
122
#define POOL_REDZONE 16
123
#else
124
#define POOL_REDZONE 0
125
#endif
126

127
typedef void (*print_fn_t)(const char *fmt, ...);
128

129
static char *ansi_vasprintf(const char *fmt, va_list ap, bool force_plain);
130
static void show_bug_report(void);
131

132
typedef struct _fault_handler fault_handler_t;
133
typedef struct _pool_page pool_page_t;
134

135
struct color_escape {
136
   const char *name;
137
   int         value;
138
};
139

140
struct text_buf {
141
   char  *buf;
142
   size_t alloc;
143
   size_t len;
144
};
145

146
struct _fault_handler {
147
   fault_handler_t *next;
148
   fault_fn_t       fn;
149
   void            *context;
150
};
151

152
typedef struct _pool_page {
153
   pool_page_t *next;
154
   size_t       alloc;
155
   size_t       size;
156
   uint8_t      data[0];
157
} page_header_t;
158

159
typedef struct _mem_pool {
160
   pool_page_t *pages;
161
   size_t       pageshift;
162
} mem_pool_t;
163

164
static bool             want_color = false;
165
static bool             want_links = false;
166
static bool             want_utf8 = false;
167
static message_style_t  message_style = MESSAGE_FULL;
168
static sig_atomic_t     crashing = SIG_ATOMIC_MAX;
169
static int              term_width = 0;
170
static void            *ctrl_c_arg = NULL;
171
static fault_handler_t *fault_handlers = NULL;
172

173
#ifdef __MINGW32__
174
static UINT win32_codepage = 0;
175
#endif
176

177
static void (*ctrl_c_fn)(void *) = NULL;
178

179
static const struct color_escape escapes[] = {
180
   { "",        ANSI_RESET },
181
   { "bold",    ANSI_BOLD },
182
   { "black",   ANSI_FG_BLACK },
183
   { "red",     ANSI_FG_RED },
184
   { "green",   ANSI_FG_GREEN },
185
   { "yellow",  ANSI_FG_YELLOW },
186
   { "blue",    ANSI_FG_BLUE },
187
   { "magenta", ANSI_FG_MAGENTA },
188
   { "cyan",    ANSI_FG_CYAN },
189
   { "white",   ANSI_FG_WHITE },
190
};
191

192
void *xmalloc(size_t size)
10,511,715✔
193
{
194
   void *p = malloc(size);
10,511,715✔
195
   if (p == NULL)
10,511,715✔
196
      fatal("memory exhausted (malloc %lu)", (long unsigned)size);
×
197
   return p;
10,511,715✔
198
}
199

200
void *xmalloc_flex(size_t fixed, size_t nelems, size_t size)
7,813,656✔
201
{
202
   size_t bytes;
7,813,656✔
203
   if (__builtin_mul_overflow(nelems, size, &bytes))
7,813,656✔
204
      fatal_trace("array size overflow: requested %zd * %zd bytes",
205
                  nelems, size);
206

207
   return xmalloc(fixed + bytes);
7,813,656✔
208
}
209

210
void *xmalloc_array(size_t nelems, size_t size)
709,048✔
211
{
212
   return xmalloc_flex(0, nelems, size);
709,048✔
213
}
214

215
void *xcalloc(size_t size)
7,867,685✔
216
{
217
   void *p = calloc(1, size);
7,867,685✔
218
   if (p == NULL)
7,867,685✔
219
      fatal("memory exhausted (calloc %lu)", (long unsigned)size);
×
220
   return p;
7,867,685✔
221
}
222

223
void *xcalloc_flex(size_t fixed, size_t nelems, size_t size)
6,151,491✔
224
{
225
   size_t bytes;
6,151,491✔
226
   if (__builtin_mul_overflow(nelems, size, &bytes))
6,151,491✔
227
      fatal_trace("array size overflow: requested %zd * %zd bytes",
228
                  nelems, size);
229

230
   return xcalloc(fixed + bytes);
6,151,491✔
231
}
232

233
void *xcalloc_array(size_t nelems, size_t size)
902,937✔
234
{
235
   return xcalloc_flex(0, nelems, size);
902,937✔
236
}
237

238
void *xrealloc(void *ptr, size_t size)
2,968,990✔
239
{
240
   ptr = realloc(ptr, size);
2,968,990✔
241
   if (ptr == NULL)
2,968,990✔
242
      fatal("memory exhausted (realloc %lu)", (long unsigned)size);
×
243
   return ptr;
2,968,990✔
244
}
245

246
void *xrealloc_array(void *ptr, size_t nelems, size_t size)
2,454,578✔
247
{
248
   size_t bytes;
2,454,578✔
249
   if (__builtin_mul_overflow(nelems, size, &bytes))
2,454,578✔
250
      fatal_trace("array size overflow: requested %zd * %zd bytes",
251
                  nelems, size);
252

253
   return xrealloc(ptr, bytes);
2,454,578✔
254
}
255

256
void *xrealloc_flex(void *ptr, size_t fixed, size_t nelems, size_t size)
32,955✔
257
{
258
   size_t bytes;
32,955✔
259
   if (__builtin_mul_overflow(nelems, size, &bytes))
32,955✔
260
      fatal_trace("array size overflow: requested %zd * %zd bytes",
261
                  nelems, size);
262

263
   return xrealloc(ptr, bytes + fixed);
32,955✔
264
}
265

266
char *xstrdup(const char *str)
365,766✔
267
{
268
   char *copy = strdup(str);
365,766✔
269
   if (copy == NULL)
365,766✔
270
      fatal("memory exhausted (strdup)");
×
271
   return copy;
365,766✔
272
}
273

274
char *xstrndup(const char *str, size_t n)
42✔
275
{
276
   char *copy = strndup(str, n);
42✔
277
   if (copy == NULL)
42✔
278
      fatal("memory exhausted (strndup)");
×
279
   return copy;
42✔
280
}
281

282
char *xvasprintf(const char *fmt, va_list ap)
457,620✔
283
{
284
   char *strp = NULL;
457,620✔
285
   if (vasprintf(&strp, fmt, ap) < 0)
457,620✔
286
      fatal("memory exhausted (vasprintf)");
×
287
   return strp;
457,620✔
288
}
289

290
char *xasprintf(const char *fmt, ...)
148,113✔
291
{
292
   va_list ap;
148,113✔
293
   va_start(ap, fmt);
148,113✔
294
   char *strp = xvasprintf(fmt, ap);
148,113✔
295
   va_end(ap);
148,113✔
296
   return strp;
148,113✔
297
}
298

299
void errorf(const char *fmt, ...)
49✔
300
{
301
   diag_t *d = diag_new(DIAG_ERROR, NULL);
49✔
302
   va_list ap;
49✔
303
   va_start(ap, fmt);
49✔
304
   diag_vprintf(d, fmt, ap);
49✔
305
   va_end(ap);
49✔
306
   diag_emit(d);
49✔
307
}
49✔
308

309
void warnf(const char *fmt, ...)
35✔
310
{
311
   diag_t *d = diag_new(DIAG_WARN, NULL);
35✔
312
   va_list ap;
35✔
313
   va_start(ap, fmt);
35✔
314
   diag_vprintf(d, fmt, ap);
35✔
315
   va_end(ap);
35✔
316
   diag_emit(d);
35✔
317
}
35✔
318

319
void notef(const char *fmt, ...)
1,653✔
320
{
321
   diag_t *d = diag_new(DIAG_NOTE, NULL);
1,653✔
322
   va_list ap;
1,653✔
323
   va_start(ap, fmt);
1,653✔
324
   diag_vprintf(d, fmt, ap);
1,653✔
325
   va_end(ap);
1,653✔
326
   diag_emit(d);
1,653✔
327
}
1,653✔
328

329
void debugf(const char *fmt, ...)
2,003✔
330
{
331
   diag_t *d = diag_new(DIAG_DEBUG, NULL);
2,003✔
332
   va_list ap;
2,003✔
333
   va_start(ap, fmt);
2,003✔
334
   diag_vprintf(d, fmt, ap);
2,003✔
335
   va_end(ap);
2,003✔
336
   diag_emit(d);
2,003✔
337
}
2,003✔
338

339
static char *ansi_vasprintf(const char *fmt, va_list ap, bool force_plain)
136,699✔
340
{
341
   // Replace color strings like "$red$foo$$bar" with ANSI escaped
342
   // strings like "\033[31mfoo\033[0mbar"
343

344
   static int override = 0;
136,699✔
345

346
   if (strchr(fmt, '$') == NULL)
136,699✔
347
      return xvasprintf(fmt, ap);
39,462✔
348

349
   LOCAL_TEXT_BUF tb = tb_new();
194,474✔
350
   const char *escape_start = NULL;
97,237✔
351

352
   while (*fmt != '\0') {
1,501,661✔
353
      if (*fmt == '$') {
1,404,424✔
354
         if (escape_start == NULL)
303,531✔
355
            escape_start = fmt;
356
         else {
357
            const char *e = escape_start + 1;
151,729✔
358
            size_t len = fmt - e;
151,729✔
359

360
            bool bold;
151,729✔
361
            if ((bold = (*e == '!')))
151,729✔
362
               ++e, --len;
5✔
363

364
            bool bright;
151,729✔
365
            if ((bright = (*e == '+')))
151,729✔
366
               ++e, --len;
29✔
367

368
            if ((*e == '<' || *e == '>') && *(e + 1) == '$') {
151,729✔
369
               override += *e == '<' ? -1 : 1;
2✔
370
               escape_start = NULL;
2✔
371
            }
372
            else if (strncmp(e, "link:", 5) == 0) {
151,727✔
373
               if (want_links && !force_plain) {
16,276✔
374
                  tb_cat(tb, "\033]8;;");
×
375
                  tb_catn(tb, e + 5, len - 5);
×
376
                  tb_cat(tb, "\033]8;;\07");
×
377
               }
378
               else {
379
                  const char *bel = strchr(e, '\07');
16,276✔
380
                  if (bel && bel < e + len)
16,276✔
381
                     tb_catn(tb, bel + 1, e + len - bel - 1);
16,276✔
382
               }
383
               escape_start = NULL;
384
            }
385
            else if (want_color && !force_plain && override >= 0) {
135,451✔
386
               bool found = false;
8✔
387

388
               if (*e == '#') {
8✔
389
                  char *eptr;
1✔
390
                  int code = strtoul(e + 1, &eptr, 10);
1✔
391
                  if (eptr == e + len) {
1✔
392
                     if (bold)
1✔
393
                        tb_printf(tb, "\033[1;38;5;%dm", code);
×
394
                     else
395
                        tb_printf(tb, "\033[38;5;%dm", code);
1✔
396
                     found = true;
397
                  }
398
               }
399
               else if (strncmp(e, "link:", 5) == 0) {
400
                  tb_cat(tb, "\033]8;;");
401
                  tb_catn(tb, e + 5, len - 5);
402
                  tb_cat(tb, "\033]8;;\07");
403
                  found = true;
404
               }
405

406
               for (int i = 0; !found && i < ARRAY_LEN(escapes); i++) {
37✔
407
                  if (strncmp(e, escapes[i].name, len) == 0) {
34✔
408
                     int code = escapes[i].value + (bright ? 60 : 0);
5✔
409
                     if (bold)
5✔
410
                        tb_printf(tb, "\033[1;%dm", code);
1✔
411
                     else
412
                        tb_printf(tb, "\033[%dm", code);
4✔
413
                     found = true;
414
                     break;
415
                  }
416
               }
417

418
               if (!found) {
3✔
419
                  tb_catn(tb, escape_start, len + 1 + bold);
2✔
420
                  escape_start = fmt;
2✔
421
               }
422
               else
423
                  escape_start = NULL;
424
            }
425
            else
426
               escape_start = NULL;
427
         }
428
      }
429
      else if (escape_start == NULL)
1,100,893✔
430
         tb_append(tb, *fmt);
457,129✔
431

432
      ++fmt;
1,404,424✔
433
   }
434

435
   if (escape_start != NULL)
97,237✔
436
      tb_cat(tb, escape_start);
75✔
437

438
   return xvasprintf(tb_get(tb), ap);
97,237✔
439
}
440

441
static int color_vfprintf(FILE *f, const char *fmt, va_list ap)
126,472✔
442
{
443
   char *strp LOCAL = ansi_vasprintf(fmt, ap, false);
126,472✔
444

445
   bool escape = false;
126,472✔
446
   int len = 0;
126,472✔
447
   for (const char *p = strp; *p != '\0'; p++) {
1,341,522✔
448
      if (*p == '\033')
1,215,050✔
449
         escape = true;
450
      if (escape)
1,215,050✔
451
         escape = (*p != 'm');
×
452
      else
453
         len += 1;
1,215,050✔
454
   }
455

456
   fputs(strp, f);
126,472✔
457
   return len;
126,472✔
458
}
459

460
char *color_vasprintf(const char *fmt, va_list ap)
10✔
461
{
462
   return ansi_vasprintf(fmt, ap, false);
10✔
463
}
464

465
char *strip_color(const char *fmt, va_list ap)
10,187✔
466
{
467
   return ansi_vasprintf(fmt, ap, true);
10,187✔
468
}
469

470
int color_fprintf(FILE *f, const char *fmt, ...)
126,462✔
471
{
472
   va_list ap;
126,462✔
473
   va_start(ap, fmt);
126,462✔
474
   const int len = color_vfprintf(f, fmt, ap);
126,462✔
475
   va_end(ap);
126,462✔
476
   return len;
126,462✔
477
}
478

479
int color_printf(const char *fmt, ...)
10✔
480
{
481
   va_list ap;
10✔
482
   va_start(ap, fmt);
10✔
483
   int rc = color_vprintf(fmt, ap);
10✔
484
   va_end(ap);
10✔
485
   return rc;
10✔
486
}
487

488
int color_vprintf(const char *fmt, va_list ap)
10✔
489
{
490
   return color_vfprintf(stdout, fmt, ap);
10✔
491
}
492

493
char *color_asprintf(const char *fmt, ...)
30✔
494
{
495
   va_list ap;
30✔
496
   va_start(ap, fmt);
30✔
497
   char *str = ansi_vasprintf(fmt, ap, false);
30✔
498
   va_end(ap);
30✔
499
   return str;
30✔
500
}
501

502
bool color_terminal(void)
24,161✔
503
{
504
   return want_color;
24,161✔
505
}
506

507
bool utf8_terminal(void)
864✔
508
{
509
   return want_utf8;
864✔
510
}
511

512
void print_centred(const char *text)
3✔
513
{
514
   if (term_width == 0)
3✔
515
      fputs(text, stdout);
3✔
516
   else {
517
      const int pad = (term_width - strlen(text)) / 2;
×
518
      printf("%*s%s", pad, "", text);
×
519
   }
520
}
3✔
521

522
void fatal_exit(int status)
40✔
523
{
524
   async_barrier();
40✔
525

526
   if (atomic_load(&crashing) != SIG_ATOMIC_MAX)
40✔
527
      _exit(status);   // Exit during crash
×
528
   else if (!thread_attached() || thread_id() != 0)
40✔
529
      _exit(status);
×
530
   else
531
      exit(status);
40✔
532
}
533

534
void error_at(const loc_t *loc, const char *fmt, ...)
729✔
535
{
536
   diag_t *d = diag_new(DIAG_ERROR, loc);
729✔
537

538
   va_list ap;
729✔
539
   va_start(ap, fmt);
729✔
540
   diag_vprintf(d, fmt, ap);
729✔
541
   va_end(ap);
729✔
542

543
   diag_emit(d);
729✔
544
}
726✔
545

546
void warn_at(const loc_t *loc, const char *fmt, ...)
81✔
547
{
548
   diag_t *d = diag_new(DIAG_WARN, loc);
81✔
549

550
   va_list ap;
81✔
551
   va_start(ap, fmt);
81✔
552
   diag_vprintf(d, fmt, ap);
81✔
553
   va_end(ap);
81✔
554

555
   diag_emit(d);
81✔
556
}
81✔
557

558
void note_at(const loc_t *loc, const char *fmt, ...)
9,186✔
559
{
560
   diag_t *d = diag_new(DIAG_NOTE, loc);
9,186✔
561

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

567
   diag_emit(d);
9,186✔
568
}
9,186✔
569

570
void fatal_at(const loc_t *loc, const char *fmt, ...)
×
571
{
572
   diag_t *d = diag_new(DIAG_FATAL, loc);
×
573
   diag_suppress(d, false);
×
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_emit(d);
×
582
   fatal_exit(EXIT_FAILURE);
×
583
}
584

585
void fatal(const char *fmt, ...)
33✔
586
{
587
   diag_t *d = diag_new(DIAG_FATAL, NULL);
33✔
588
   diag_suppress(d, false);
33✔
589

590
   va_list ap;
33✔
591
   va_start(ap, fmt);
33✔
592
   diag_vprintf(d, fmt, ap);
33✔
593
   va_end(ap);
33✔
594

595
   diag_set_consumer(NULL, NULL);
33✔
596
   diag_emit(d);
33✔
597
   fatal_exit(EXIT_FAILURE);
33✔
598
}
599

600
void fatal_trace(const char *fmt, ...)
601
{
602
   diag_t *d = diag_new(DIAG_FATAL, NULL);
×
603
   diag_suppress(d, false);
×
604

605
   va_list ap;
×
606
   va_start(ap, fmt);
×
607
   diag_vprintf(d, fmt, ap);
×
608
   va_end(ap);
×
609

610
   diag_set_consumer(NULL, NULL);
×
611
   diag_emit(d);
×
612

613
   show_stacktrace();
×
614

615
   show_bug_report();
×
616

617
   fatal_exit(EXIT_FAILURE);
×
618
}
619

620
void fatal_errno(const char *fmt, ...)
3✔
621
{
622
   diag_t *d = diag_new(DIAG_FATAL, NULL);
3✔
623
   diag_suppress(d, false);
3✔
624

625
   va_list ap;
3✔
626
   va_start(ap, fmt);
3✔
627
   diag_vprintf(d, fmt, ap);
3✔
628
   diag_printf(d, ": %s", last_os_error());
3✔
629
   va_end(ap);
3✔
630

631
   diag_set_consumer(NULL, NULL);
3✔
632
   diag_emit(d);
3✔
633
   fatal_exit(EXIT_FAILURE);
3✔
634
}
635

636
const char *last_os_error(void)
3✔
637
{
638
#ifdef __MINGW32__
639
   static __thread LPSTR mbuf = NULL;
640

641
   if (mbuf != NULL)
642
      LocalFree(mbuf);
643

644
   FormatMessage(
645
      FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
646
      | FORMAT_MESSAGE_IGNORE_INSERTS,
647
      NULL,
648
      GetLastError(),
649
      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
650
      (LPSTR)&mbuf, 0, NULL);
651

652
   return mbuf;
653
#else
654
   return strerror(errno);
3✔
655
#endif
656
}
657

658
static void trace_one_frame(uintptr_t pc, const char *module,
×
659
                            const char *srcfile, const char *symbol,
660
                            unsigned lineno, unsigned colno,
661
                            ptrdiff_t disp, frame_kind_t kind)
662
{
663
   color_fprintf(stderr, "[$green$%p$$] ", (void *)pc);
×
664
   if (kind == FRAME_LIB)
×
665
      color_fprintf(stderr, "($red$%s$$) ", module);
×
666
   if (srcfile != NULL)
×
667
      color_fprintf(stderr, "%s:%d ", srcfile, lineno);
×
668
   if (symbol != NULL) {
×
669
      color_fprintf(stderr, "$yellow$%s$$", symbol);
×
670
      if (srcfile == NULL && disp != 0)
×
671
         color_fprintf(stderr, "$yellow$+0x%"PRIxPTR"$$", disp);
×
672
   }
673
   if (kind == FRAME_VHDL)
×
674
      color_fprintf(stderr, " $magenta$[VHDL]$$");
×
675
   fprintf(stderr, "\n");
×
676

677
#ifndef __MINGW32__
678
   if (srcfile != NULL) {
×
679
      FILE *f = fopen(srcfile, "r");
×
680
      if (f != NULL) {
×
681
         char *line LOCAL = NULL;
×
682
         size_t linesz = 0;
×
683
         for (int i = 0, len; i < lineno + 1
×
684
                 && (len = getline(&line, &linesz, f)) != -1; i++) {
×
685
            if (i < lineno - 2)
×
686
               continue;
×
687

688
            if (len <= 1)
×
689
               continue;
×
690
            else if (line[len - 1] == '\n')
×
691
               line[len - 1] = '\0';
×
692

693
            if (i == lineno - 1)
×
694
               color_fprintf(stderr, "$cyan$$bold$-->$$ $cyan$%s$$\n", line);
×
695
            else
696
               color_fprintf(stderr, "    $cyan$%s$$\n", line);
×
697
         }
698
         fclose(f);
×
699
      }
700
   }
701
#endif
702
}
×
703

704
__attribute__((noinline))
705
void show_stacktrace(void)
×
706
{
707
   debug_info_t *di = debug_capture();
×
708

709
   const int nframes = debug_count_frames(di);
×
710
   for (int n = 1; n < nframes; n++) {
×
711
      const debug_frame_t *f = debug_get_frame(di, n);
×
712

713
      for (debug_inline_t *inl = f->inlined; inl != NULL; inl = inl->next)
×
714
         trace_one_frame(f->pc, f->module, inl->srcfile, inl->symbol,
×
715
                         inl->lineno, inl->colno, f->disp, f->kind);
×
716

717
      trace_one_frame(f->pc, f->module, f->srcfile, f->symbol, f->lineno,
×
718
                      f->colno, f->disp, f->kind);
×
719

720
   }
721

722
   debug_free(di);
×
723

724
#if defined __linux__ && !defined HAVE_LIBDW && !defined HAVE_LIBDWARF
725
   color_fprintf(stderr, "\n$cyan$Hint: you can get better stack traces by "
726
                 "installing the libdw-dev package and reconfiguring$$\n");
727
#endif
728

729
   fflush(stderr);
×
730
}
×
731

732
static void show_bug_report(void)
733
{
734
#ifndef DEBUG
735
   extern const char version_string[];
736
   color_fprintf(stderr, "\n$!red$%s ["TARGET_SYSTEM"]\n\n"
737
                 "Please report this bug at "PACKAGE_BUGREPORT"$$\n\n",
738
                 version_string);
739
   fflush(stderr);
740
#endif
741
}
742

743
#ifdef __MINGW32__
744

745
static const char *exception_name(DWORD code)
746
{
747
   switch (code) {
748
   case EXCEPTION_ACCESS_VIOLATION:
749
      return "EXCEPTION_ACCESS_VIOLATION";
750
   case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
751
      return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
752
   case EXCEPTION_BREAKPOINT:
753
      return "EXCEPTION_BREAKPOINT";
754
   case EXCEPTION_DATATYPE_MISALIGNMENT:
755
      return "EXCEPTION_DATATYPE_MISALIGNMENT";
756
   case EXCEPTION_ILLEGAL_INSTRUCTION:
757
      return "EXCEPTION_ILLEGAL_INSTRUCTION";
758
   case EXCEPTION_IN_PAGE_ERROR:
759
      return "EXCEPTION_IN_PAGE_ERROR";
760
   case EXCEPTION_INT_DIVIDE_BY_ZERO:
761
      return "EXCEPTION_INT_DIVIDE_BY_ZERO";
762
   case EXCEPTION_INT_OVERFLOW:
763
      return "EXCEPTION_INT_OVERFLOW";
764
   case EXCEPTION_PRIV_INSTRUCTION:
765
      return "EXCEPTION_PRIV_INSTRUCTION";
766
   case EXCEPTION_STACK_OVERFLOW:
767
      return "EXCEPTION_STACK_OVERFLOW";
768
   }
769

770
   return "???";
771
}
772

773
WINAPI
774
static LONG win32_exception_handler(EXCEPTION_POINTERS *ExceptionInfo)
775
{
776
   DWORD code = ExceptionInfo->ExceptionRecord->ExceptionCode;
777
   PVOID addr = ExceptionInfo->ExceptionRecord->ExceptionAddress;
778

779
#if defined ARCH_ARM64
780
   DWORD64 ip = ExceptionInfo->ContextRecord->Pc;
781
#elif defined ARCH_X86_64
782
   DWORD64 ip = ExceptionInfo->ContextRecord->Rip;
783
#else
784
   DWORD ip = ExceptionInfo->ContextRecord->Eip;
785
#endif
786

787
   if (code == EXCEPTION_ACCESS_VIOLATION)
788
      addr = (PVOID)ExceptionInfo->ExceptionRecord->ExceptionInformation[1];
789

790
   color_fprintf(stderr, "\n$red$$bold$*** Caught exception %x (%s)",
791
                 (int)code, exception_name(code));
792

793
   switch (code) {
794
   case EXCEPTION_ACCESS_VIOLATION:
795
   case EXCEPTION_ILLEGAL_INSTRUCTION:
796
      fprintf(stderr, " [address=%p, ip=%p]", (void *)addr, (void*)ip);
797
      break;
798
   }
799

800
   color_fprintf(stderr, " ***$$\n\n");
801
   fflush(stderr);
802

803
#ifdef __WIN64
804
   if (code != EXCEPTION_STACK_OVERFLOW)
805
      show_stacktrace();
806
#endif
807

808
  return EXCEPTION_EXECUTE_HANDLER;
809
}
810

811
#else
812

813
#ifndef __SANITIZE_THREAD__
814
static const char *signame(int sig, siginfo_t *info)
×
815
{
816
   switch (sig) {
×
817
   case SIGSEGV:
×
818
#ifdef __linux__
819
      switch (info->si_code) {
×
820
      case SEGV_MAPERR: return "SEGV_MAPERR";
821
      case SEGV_ACCERR: return "SEGV_ACCERR";
×
822
      default: return "SIGSEGV";
×
823
      }
824
#else
825
      return "SIGSEGV";
826
#endif
827
   case SIGABRT: return "SIGABRT";
828
   case SIGILL: return "SIGILL";
×
829
   case SIGFPE: return "SIGFPE";
×
830
   case SIGUSR1: return "SIGUSR1";
×
831
   case SIGUSR2: return "SIGUSR2";
×
832
   case SIGBUS: return "SIGBUS";
×
833
   case SIGINT: return "SIGINT";
×
834
   case SIGTRAP: return "SIGTRAP";
×
835
   default: return "???";
×
836
   }
837
}
838

839
static void print_fatal_signal(int sig, siginfo_t *info, struct cpu_state *cpu)
×
840
{
841
   static volatile __thread sig_atomic_t recurse = 0;
×
842

843
   if (recurse++ > 1) {
×
844
      signal(SIGABRT, SIG_DFL);
×
845
      abort();
×
846
   }
847

848
   char buf[512], *p = buf, *s = buf, *end = buf + ARRAY_LEN(buf);
×
849
   p += checked_sprintf(p, end - p, "\n%s*** Caught signal %d (%s)%s",
×
850
                        want_color ? "\033[31m\033[1m" : "",
×
851
                        sig, signame(sig, info),
852
                        recurse > 1 ? " inside signal handler" : "");
×
853

854
   switch (sig) {
×
855
   case SIGSEGV:
×
856
   case SIGILL:
857
   case SIGFPE:
858
   case SIGBUS:
859
      p += checked_sprintf(p, end - p, " [address=%p, ip=%p]",
×
860
                           info->si_addr, (void*)cpu->pc);
×
861
      break;
×
862
   }
863

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

866
   for (int n; s < p && (n = write(STDERR_FILENO, s, p - s)) > 0; s += n);
×
867

868
   if (sig != SIGUSR1 && !atomic_cas(&crashing, SIG_ATOMIC_MAX, thread_id())) {
×
869
      sleep(60);
×
870
      _exit(EXIT_FAILURE);
×
871
   }
872
}
×
873
#endif  // !__SANITIZE_THREAD__
874

875
static __thread struct cpu_state *thread_regs = NULL;
876

877
static void signal_handler(int sig, siginfo_t *info, void *context)
×
878
{
879
   ucontext_t *uc = (ucontext_t*)context;
×
880
   struct cpu_state cpu;
×
881
   fill_cpu_state(&cpu, uc);
×
882

883
   struct cpu_state *req;
×
884
   if (sig == SIGUSR2 && (req = atomic_load(&thread_regs)) != NULL) {
×
885
      // Fill in registers for capture_registers
886
      *req = cpu;
×
887
      atomic_store(&thread_regs, NULL);
×
888
      return;
×
889
   }
890
   else if (sig == SIGINT) {
×
891
      void (*fn)(void *) = atomic_load(&ctrl_c_fn);
×
892
      if (fn != NULL) {
×
893
         (*fn)(ctrl_c_arg);
×
894
         return;
×
895
      }
896
   }
897

898
#ifdef __SANITIZE_THREAD__
899
   abort();
900
#else
901

902
   for (fault_handler_t *f = fault_handlers; f; f = f->next)
×
903
      (*f->fn)(sig, info->si_addr, &cpu, f->context);
×
904

905
   print_fatal_signal(sig, info, &cpu);
×
906

907
   show_stacktrace();
×
908

909
   show_bug_report();
×
910

911
   if (sig != SIGUSR1)
×
912
      _exit(2);
×
913
#endif  // !__SANITIZE_THREAD__
914
}
915
#endif  // ! __MINGW32__
916

917
void register_signal_handlers(void)
5,672✔
918
{
919
#ifdef __MINGW32__
920
   SetUnhandledExceptionFilter(win32_exception_handler);
921
#else
922

923
   struct sigaction sa = {
5,672✔
924
      .sa_sigaction = signal_handler,
925
      .sa_flags = SA_RESTART | SA_SIGINFO
926
   };
927
   sigemptyset(&sa.sa_mask);
5,672✔
928

929
#ifndef __SANITIZE_THREAD__
930
   sigaction(SIGSEGV, &sa, NULL);
5,672✔
931
   sigaction(SIGUSR1, &sa, NULL);
5,672✔
932
   sigaction(SIGFPE, &sa, NULL);
5,672✔
933
   sigaction(SIGBUS, &sa, NULL);
5,672✔
934
   sigaction(SIGILL, &sa, NULL);
5,672✔
935
   sigaction(SIGABRT, &sa, NULL);
5,672✔
936
   sigaction(SIGTRAP, &sa, NULL);
5,672✔
937
#endif  // !__SANITIZE_THREAD__
938
   sigaction(SIGUSR2, &sa, NULL);
5,672✔
939
#endif  // !__MINGW32__
940
}
5,672✔
941

942
#ifdef __MINGW32__
943
static BOOL win32_ctrl_c_handler(DWORD fdwCtrlType)
944
{
945
   switch (fdwCtrlType) {
946
   case CTRL_C_EVENT:
947
      {
948
         void (*fn)(void *) = atomic_load(&ctrl_c_fn);
949
         if (fn != NULL)
950
            (*fn)(ctrl_c_arg);
951
         return TRUE;
952
      }
953

954
   default:
955
      return FALSE;
956
   }
957
}
958
#endif
959

960
void set_ctrl_c_handler(void (*fn)(void *), void *arg)
7,426✔
961
{
962
   ctrl_c_arg = arg;
7,426✔
963
   atomic_store(&ctrl_c_fn, fn);
7,426✔
964

965
   if (fn != NULL) {
7,426✔
966
#ifndef __MINGW32__
967
      struct sigaction sa = {};
3,713✔
968
      sa.sa_sigaction = signal_handler;
3,713✔
969
      sigemptyset(&sa.sa_mask);
3,713✔
970
      sa.sa_flags = SA_RESTART | SA_SIGINFO;
3,713✔
971

972
      sigaction(SIGINT, &sa, NULL);
3,713✔
973
#else
974
      if (!SetConsoleCtrlHandler(win32_ctrl_c_handler, TRUE))
975
         fatal_trace("SetConsoleCtrlHandler");
976
#endif
977
   }
978
   else {
979
#ifndef __MINGW32__
980
      struct sigaction sa = {};
3,713✔
981
      sa.sa_handler = SIG_DFL;
3,713✔
982
      sigaction(SIGINT, &sa, NULL);
3,713✔
983
#else
984
      if (!SetConsoleCtrlHandler(win32_ctrl_c_handler, FALSE))
985
         fatal_trace("SetConsoleCtrlHandler");
986
#endif
987
   }
988
}
7,426✔
989

990
#ifdef __MINGW32__
991
static void restore_win32_codepage(void)
992
{
993
   assert(win32_codepage != 0);
994
   SetConsoleOutputCP(win32_codepage);
995
}
996
#endif
997

998
void term_init(void)
5,673✔
999
{
1000
   const char *nvc_colors = getenv("NVC_COLORS");
5,673✔
1001
   const char *term = getenv("TERM") ?: "";
5,673✔
1002

1003
   static const char *term_blacklist[] = {
5,673✔
1004
      "dumb"
1005
   };
1006

1007
   spin_wait();  // Dummy, to force linking thread.c
5,673✔
1008

1009
   bool is_tty = isatty(STDERR_FILENO);
5,673✔
1010

1011
#ifdef __MINGW32__
1012
   if (!is_tty) {
1013
      // Handle running under MinTty
1014
      HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
1015
      const size_t size = sizeof(FILE_NAME_INFO) + sizeof(WCHAR) * MAX_PATH;
1016
      FILE_NAME_INFO *nameinfo = malloc(size);
1017
      if (!GetFileInformationByHandleEx(hStdOut, FileNameInfo, nameinfo, size))
1018
         fatal_errno("GetFileInformationByHandle");
1019

1020
      if ((wcsncmp(nameinfo->FileName, L"\\msys-", 6) == 0
1021
           || wcsncmp(nameinfo->FileName, L"\\cygwin-", 8) == 0)
1022
          && wcsstr(nameinfo->FileName, L"pty") != NULL)
1023
         is_tty = true;
1024

1025
      free(nameinfo);
1026
   }
1027
#endif
1028

1029
   if (nvc_colors && strcmp(nvc_colors, "always") == 0)
5,673✔
1030
      want_color = true;
1✔
1031
   else if (nvc_colors && strcmp(nvc_colors, "never") == 0)
5,672✔
1032
      want_color = false;
×
1033
   else {
1034
      want_color = is_tty;
5,672✔
1035

1036
      if (want_color && (term != NULL)) {
5,672✔
1037
         for (size_t i = 0; i < ARRAY_LEN(term_blacklist); i++) {
×
1038
            if (strcmp(term, term_blacklist[i]) == 0) {
×
1039
               want_color = false;
×
1040
               break;
×
1041
            }
1042
         }
1043
      }
1044
   }
1045

1046
#ifdef __MINGW32__
1047
   HANDLE hConsole = GetStdHandle(STD_ERROR_HANDLE);
1048
   DWORD mode;
1049
   if (GetConsoleMode(hConsole, &mode)) {
1050
      mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT;
1051
      if (!SetConsoleMode(hConsole, mode))
1052
         want_color = false;
1053

1054
      CONSOLE_SCREEN_BUFFER_INFO info;
1055
      if (GetConsoleScreenBufferInfo(hConsole, &info))
1056
         term_width = info.dwSize.X;
1057
      else
1058
         term_width = 80;
1059
   }
1060
#else
1061
   if (is_tty) {
5,673✔
1062
      // Try to find the terminal size with tcgetwinsize or TIOCGWINSZ
1063
      term_width = 80;
×
1064
#if defined HAVE_TCGETWINSIZE
1065
      struct winsize ws;
1066
      if (tcgetwinsize(STDIN_FILENO, &ws) == 0)
1067
         term_width = ws.ws_col;
1068
#elif defined TIOCGWINSZ
1069
      struct winsize ws;
×
1070
      if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0)
×
1071
         term_width = ws.ws_col;
×
1072
#endif
1073
   }
1074
#endif
1075

1076
#ifndef __MINGW32__
1077
   // Only print link escape codes if this is really a terminal
1078
   want_links = want_color && is_tty;
5,673✔
1079
#else
1080
   want_links = false;    // Winpty doesn't recognise these
1081
#endif
1082

1083
#ifndef __MINGW32__
1084
   // Assume the terminal is expecting UTF-8 by default
1085
   want_utf8 = true;
5,673✔
1086

1087
   const char *lang = getenv("LANG");
5,673✔
1088
   if (lang != NULL && *lang != '\0' && strcasestr(lang, "utf-8") == NULL)
5,673✔
1089
      want_utf8 = false;
×
1090
#else
1091
   win32_codepage = GetConsoleOutputCP();
1092
   if (win32_codepage == 65001)
1093
      want_utf8 = true;
1094
   else if (win32_codepage != 28591) {
1095
      SetConsoleOutputCP(28591);
1096
      atexit(restore_win32_codepage);
1097
   }
1098
#endif
1099

1100
   // Diagnostics are printed to stderr and explicitly flushed
1101
   setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
5,673✔
1102
}
5,673✔
1103

1104
int terminal_width(void)
22,429✔
1105
{
1106
   return term_width;
22,429✔
1107
}
1108

1109
const char *ordinal_str(int n)
28✔
1110
{
1111
   switch (n) {
28✔
1112
   case 1: return "first";
1113
   case 2: return "second";
6✔
1114
   case 3: return "third";
×
1115
   default:
×
1116
      {
1117
         static char buf[16];
×
1118
         if (n > 20 && n % 10 == 1)
×
1119
            checked_sprintf(buf, sizeof(buf), "%dst", n);
×
1120
         else if (n > 20 && n % 10 == 2)
×
1121
            checked_sprintf(buf, sizeof(buf), "%dnd", n);
×
1122
         else if (n > 20 && n % 10 == 2)
×
1123
            checked_sprintf(buf, sizeof(buf), "%drd", n);
×
1124
         else
1125
            checked_sprintf(buf, sizeof(buf), "%dth", n);
×
1126
         return buf;
1127
      }
1128
   }
1129
}
1130

1131
char *null_terminate(const uint8_t *data, size_t len)
1,161✔
1132
{
1133
   char *cstr = xmalloc(len + 1);
1,161✔
1134
   if (data != NULL)
1,161✔
1135
      memcpy(cstr, data, len);
1,161✔
1136
   else
1137
      assert(len == 0);
×
1138
   cstr[len] = '\0';
1,161✔
1139
   return cstr;
1,161✔
1140
}
1141

1142
char toupper_iso88591(unsigned char ch)
7,966,603✔
1143
{
1144
   if (ch >= 'a' && ch <= 'z')
7,966,603✔
1145
      return ch - 'a' + 'A';
2,218,096✔
1146
   else if ((ch >= 0xe0 && ch <= 0xf6) || (ch >= 0xf8 && ch <= 0xfe))
5,748,507✔
1147
      return ch - 0x20;
39✔
1148
   else
1149
      return ch;
5,748,468✔
1150
}
1151

1152
char tolower_iso88591(unsigned char ch)
16,686,438✔
1153
{
1154
   if (ch >= 'A' && ch <= 'Z')
16,686,438✔
1155
      return ch + 'a' - 'A';
573,332✔
1156
   else if ((ch >= 0xc0 && ch <= 0xd6) || (ch >= 0xd8 && ch <= 0xde))
16,113,106✔
1157
      return ch + 0x20;
180✔
1158
   else
1159
      return ch;
16,112,926✔
1160
}
1161

1162
bool isprint_iso88591(unsigned char ch)
293,324✔
1163
{
1164
   return (ch >= 0x20 && ch <= 0x7e) || (ch >= 0xa0 && ch <= 0xff);
293,324✔
1165
}
1166

1167
bool isspace_iso88591(unsigned char ch)
83,981✔
1168
{
1169
   return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\v' || ch == '\f'
83,981✔
1170
      || ch == '\r' || ch == 0xa0;
83,981✔
1171
}
1172

1173
bool isdigit_iso88591(unsigned char ch)
19,678✔
1174
{
1175
   return ch >= '0' && ch <= '9';
19,678✔
1176
}
1177

1178
bool isupper_iso88591(unsigned char ch)
2,031✔
1179
{
1180
   return (ch >= 'A' && ch <= 'Z')
2,031✔
1181
      || (ch >= 0xc0 && ch <= 0xd6)
2,031✔
1182
      || (ch >= 0xd8 && ch <= 0xde);
2,031✔
1183
}
1184

1185
bool isalpha_iso88591(unsigned char ch)
12,007,406✔
1186
{
1187
   return (ch >= 'A' && ch <= 'Z')
12,007,406✔
1188
      || (ch >= 'a' && ch <= 'z')
12,007,406✔
1189
      || (ch >= 0xc0 && ch <= 0xd6)
1190
      || (ch >= 0xd8 && ch <= 0xde)
1191
      || (ch >= 0xe0 && ch <= 0xf6)
1192
      || (ch >= 0xf8 && ch <= 0xfe);
12,007,406✔
1193
}
1194

1195
bool isalnum_iso88591(unsigned char ch)
360,683✔
1196
{
1197
   return isalpha_iso88591(ch) || (ch >= '0' && ch <= '9');
360,683✔
1198
}
1199

1200
int next_power_of_2(int n)
987,585✔
1201
{
1202
   n--;
987,585✔
1203
   n |= n >> 1;
987,585✔
1204
   n |= n >> 2;
987,585✔
1205
   n |= n >> 4;
987,585✔
1206
   n |= n >> 8;
987,585✔
1207
   n |= n >> 16;
987,585✔
1208
   n++;
987,585✔
1209
   return n;
987,585✔
1210
}
1211

1212
int ilog2(int64_t n)
15,851✔
1213
{
1214
   if (n <= 1)
15,851✔
1215
      return 0;
1216
   else {
1217
      int r = 0;
1218
      int64_t c = 1;
1219
      while (c < n) {
69,118✔
1220
         r += 1;
53,294✔
1221
         c <<= 1;
53,294✔
1222
      }
1223
      return r;
15,824✔
1224
   }
1225
}
1226

1227
bool ipow_safe(int64_t x, int64_t y, int64_t *result)
308✔
1228
{
1229
   assert(y >= 0);
308✔
1230
   int overflow = 0, xo = 0;
1231
   int64_t r = 1;
1232
   while (y) {
1,168✔
1233
      if (y & 1)
860✔
1234
         overflow |= xo || __builtin_mul_overflow(r, x, &r);
499✔
1235
      y >>= 1;
860✔
1236
      xo |= __builtin_mul_overflow(x, x, &x);
860✔
1237
   }
1238
   *result = r;
308✔
1239
   return !overflow;
308✔
1240
}
1241

1242
int64_t ipow(int64_t x, int64_t y)
2✔
1243
{
1244
   int64_t result;
2✔
1245
   if (!ipow_safe(x, y, &result))
2✔
1246
      DEBUG_ONLY(fatal_trace("integer overflow in ipow"));
1247

1248
   return result;
2✔
1249
}
1250

1251
#if !ASAN_ENABLED
1252
static long nvc_page_size(void)
1253
{
1254
#ifdef __MINGW32__
1255
   SYSTEM_INFO si;
1256
   GetSystemInfo(&si);
1257
   return si.dwPageSize;
1258
#else
1259
   return sysconf(_SC_PAGESIZE);
1260
#endif
1261
}
1262
#endif
1263

1264
void nvc_munmap(void *ptr, size_t length)
15,780✔
1265
{
1266
#if ASAN_ENABLED
1267
   free(ptr);
15,780✔
1268
#elif !defined __MINGW32__
1269
   if (munmap(ptr, length) != 0)
1270
      fatal_errno("munmap");
1271
#else
1272
   if (!VirtualFree(ptr, length, MEM_DECOMMIT))
1273
      fatal_errno("VirtualFree");
1274
#endif
1275
}
15,780✔
1276

1277
void *nvc_memalign(size_t align, size_t sz)
52,311✔
1278
{
1279
#if ASAN_ENABLED
1280
   void *ptr;
52,311✔
1281
   if (posix_memalign(&ptr, align, sz) != 0)
104,622✔
1282
      fatal_errno("posix_memalign");
×
1283

1284
   memset(ptr, '\0', sz);
52,311✔
1285
   return ptr;
52,311✔
1286
#else
1287
   assert(is_power_of_2(align));
1288
   const size_t mapalign = MAX(align, nvc_page_size());
1289
   const size_t mapsz = ALIGN_UP(sz + mapalign - 1, mapalign);
1290

1291
#if defined __MINGW32__
1292
   void *ptr = VirtualAlloc(NULL, mapsz, MEM_COMMIT | MEM_RESERVE,
1293
                            PAGE_READWRITE);
1294
   if (ptr == NULL)
1295
      fatal_errno("VirtualAlloc");
1296
#else
1297
   void *ptr = mmap(NULL, mapsz, PROT_READ | PROT_WRITE,
1298
                    MAP_PRIVATE | MAP_ANON, -1, 0);
1299
   if (ptr == MAP_FAILED)
1300
      fatal_errno("mmap failed to allocate %zu bytes", sz);
1301
#endif
1302

1303
   void *aligned = ALIGN_UP(ptr, align);
1304
   void *limit = aligned + sz;
1305

1306
   if (align > nvc_page_size()) {
1307
      const size_t low_waste = aligned - ptr;
1308
      const size_t high_waste = ptr + mapsz - limit;
1309
      assert(low_waste + high_waste == align);
1310

1311
      if (low_waste > 0) nvc_munmap(ptr, low_waste);
1312
      if (high_waste > 0) nvc_munmap(limit, high_waste);
1313
   }
1314

1315
   return aligned;
1316
#endif
1317
}
1318

1319
void nvc_memprotect(void *ptr, size_t length, mem_access_t prot)
55,409✔
1320
{
1321
#if defined __MINGW32__
1322
   static const int map[] = {
1323
      PAGE_NOACCESS, PAGE_READONLY, PAGE_READWRITE, PAGE_EXECUTE_READ,
1324
      PAGE_EXECUTE_READWRITE
1325
   };
1326
   DWORD old_prot;
1327
   if (length > 0 && !VirtualProtect(ptr, length, map[prot], &old_prot))
1328
      fatal_errno("VirtualProtect");
1329
#else
1330
#if ASAN_ENABLED
1331
   // LeakSanitizer will not detect leaks in regions mapped read-only
1332
   if (prot == MEM_RO || prot == MEM_NONE)
55,409✔
1333
      return;
1334
#endif
1335
   static const int map[] = {
2,789✔
1336
      PROT_NONE, PROT_READ, PROT_READ | PROT_WRITE, PROT_READ | PROT_EXEC,
1337
      PROT_READ | PROT_WRITE | PROT_EXEC
1338
   };
1339
   if (mprotect(ptr, length, map[prot]) < 0)
2,789✔
1340
      fatal_errno("mprotect");
×
1341
#endif
1342
}
1343

1344
void nvc_decommit(void *ptr, size_t length)
16,755✔
1345
{
1346
#if defined __MINGW32__
1347
   if (length > 0 && !VirtualFree(ptr, length, MEM_DECOMMIT))
1348
      fatal_errno("VirtualFree");
1349
#elif defined __linux__
1350
   if (madvise(ptr, length, MADV_DONTNEED) != 0)
16,755✔
1351
      fatal_errno("madvise: MADV_DONTNEED");
×
1352
#endif
1353
}
16,755✔
1354

1355
void *map_huge_pages(size_t align, size_t sz)
15,809✔
1356
{
1357
#ifdef __linux__
1358
   if (sz >= HUGE_PAGE_SIZE) {
15,809✔
1359
      const size_t mapsz = ALIGN_UP(sz, HUGE_PAGE_SIZE);
15,270✔
1360
      void *mem = nvc_memalign(MAX(HUGE_PAGE_SIZE, align), mapsz);
15,270✔
1361

1362
      if (madvise(mem, mapsz, MADV_HUGEPAGE) < 0)
15,270✔
1363
         warnf("madvise: MADV_HUGEPAGE: %s", last_os_error());
×
1364

1365
      return mem;
15,270✔
1366
   }
1367
#endif
1368

1369
   return nvc_memalign(align, sz);
539✔
1370
}
1371

1372
void *map_jit_pages(size_t align, size_t sz)
2,789✔
1373
{
1374
#ifdef __APPLE__
1375
   void *ptr = mmap(NULL, sz, PROT_READ | PROT_WRITE | PROT_EXEC,
1376
                    MAP_PRIVATE | MAP_ANON | MAP_JIT, -1, 0);
1377
   if (ptr == MAP_FAILED)
1378
      fatal_errno("mmap failed to allocate %zu bytes for executable code", sz);
1379
#else
1380
   void *ptr = map_huge_pages(align, sz);
2,789✔
1381
   nvc_memprotect(ptr, sz, MEM_RWX);
2,789✔
1382
#endif
1383

1384
   return ptr;
2,789✔
1385
}
1386

1387
int checked_sprintf(char *buf, int len, const char *fmt, ...)
605,476✔
1388
{
1389
   assert(len > 0);
605,476✔
1390

1391
   va_list ap;
605,476✔
1392
   va_start(ap, fmt);
605,476✔
1393

1394
   const int nbytes = vsnprintf(buf, len, fmt, ap);
605,476✔
1395
   if (nbytes >= len)
605,476✔
1396
      fatal_trace("checked_sprintf requires %d bytes but have %d",
1397
                  nbytes + 1, len);
1398

1399
   va_end(ap);
605,476✔
1400

1401
   return nbytes;
605,476✔
1402
}
1403

1404
text_buf_t *tb_new(void)
364,255✔
1405
{
1406
   text_buf_t *tb = xmalloc(sizeof(text_buf_t));
364,255✔
1407
   tb->alloc = 256;
364,255✔
1408
   tb->len   = 0;
364,255✔
1409
   tb->buf   = xmalloc(tb->alloc);
364,255✔
1410

1411
   tb->buf[0] = '\0';
364,255✔
1412

1413
   return tb;
364,255✔
1414
}
1415

1416
void tb_free(text_buf_t *tb)
358,010✔
1417
{
1418
   free(tb->buf);
358,010✔
1419
   free(tb);
358,010✔
1420
}
358,010✔
1421

1422
void _tb_cleanup(text_buf_t **tb)
324,965✔
1423
{
1424
   if (*tb != NULL)
324,965✔
1425
      tb_free(*tb);
324,965✔
1426
}
324,965✔
1427

1428
void tb_vprintf(text_buf_t *tb, const char *fmt, va_list ap)
254,468✔
1429
{
1430
   int nchars, avail;
254,519✔
1431
   for (;;) {
51✔
1432
      va_list aq;
254,519✔
1433
      va_copy(aq, ap);
254,519✔
1434

1435
      avail  = tb->alloc - tb->len;
254,519✔
1436
      nchars = vsnprintf(tb->buf + tb->len, avail, fmt, aq);
254,519✔
1437

1438
      va_end(aq);
254,519✔
1439

1440
      if (nchars + 1 < avail)
254,519✔
1441
         break;
1442

1443
      tb->alloc *= 2;
51✔
1444
      tb->buf = xrealloc(tb->buf, tb->alloc);
51✔
1445
   }
1446

1447
   tb->len += nchars;
254,468✔
1448
}
254,468✔
1449

1450
void tb_printf(text_buf_t *tb, const char *fmt, ...)
222,096✔
1451
{
1452
   va_list ap;
222,096✔
1453
   va_start(ap, fmt);
222,096✔
1454
   tb_vprintf(tb, fmt, ap);
222,096✔
1455
   va_end(ap);
222,096✔
1456
}
222,096✔
1457

1458
void tb_istr(text_buf_t *tb, ident_t ident)
307,105✔
1459
{
1460
   // TODO: this function seems useless now
1461
   tb_cat(tb, istr(ident));
307,105✔
1462
}
307,105✔
1463

1464
void tb_append(text_buf_t *tb, char ch)
1,372,174✔
1465
{
1466
   if (tb->len + 2 >= tb->alloc) {
1,372,174✔
1467
      tb->alloc *= 2;
54✔
1468
      tb->buf = xrealloc(tb->buf, tb->alloc);
54✔
1469
   }
1470

1471
   tb->buf[(tb->len)++] = ch;
1,372,174✔
1472
   tb->buf[tb->len] = '\0';
1,372,174✔
1473
}
1,372,174✔
1474

1475
void tb_catn(text_buf_t *tb, const char *str, size_t nchars)
654,514✔
1476
{
1477
   if (tb->len + nchars + 1 >= tb->alloc) {
654,514✔
1478
      tb->alloc = next_power_of_2(tb->alloc + nchars);
2,112✔
1479
      tb->buf = xrealloc(tb->buf, tb->alloc);
2,112✔
1480
   }
1481

1482
   memcpy(tb->buf + tb->len, str, nchars);
654,514✔
1483
   tb->len += nchars;
654,514✔
1484
   tb->buf[tb->len] = '\0';
654,514✔
1485
}
654,514✔
1486

1487
void tb_cat(text_buf_t *tb, const char *str)
611,450✔
1488
{
1489
   tb_catn(tb, str, strlen(str));
611,450✔
1490
}
611,450✔
1491

1492
void tb_repeat(text_buf_t *tb, char ch, size_t count)
×
1493
{
1494
   if (tb->len + count + 1 >= tb->alloc) {
×
1495
      tb->alloc = next_power_of_2(tb->alloc + count + 1);
×
1496
      tb->buf = xrealloc(tb->buf, tb->alloc);
×
1497
   }
1498

1499
   memset(tb->buf + tb->len, ch, count);
×
1500
   tb->len += count;
×
1501
   tb->buf[tb->len] = '\0';
×
1502
}
×
1503

1504
char *tb_reserve(text_buf_t *tb, size_t size)
×
1505
{
1506
   if (tb->len + size + 1 >= tb->alloc) {
×
1507
      tb->alloc = next_power_of_2(tb->alloc + size + 1);
×
1508
      tb->buf = xrealloc(tb->buf, tb->alloc);
×
1509
   }
1510

1511
   char *start = tb->buf + tb->len;
×
1512

1513
   tb->len += size;
×
1514
   tb->buf[tb->len] = '\0';
×
1515

1516
   return start;
×
1517
}
1518

1519
size_t tb_len(text_buf_t *tb)
70,107✔
1520
{
1521
   return tb->len;
70,107✔
1522
}
1523

1524
char *tb_claim(text_buf_t *tb)
26,793✔
1525
{
1526
   char *buf = tb->buf;
26,793✔
1527
   tb->buf = NULL;
26,793✔
1528
   return buf;
26,793✔
1529
}
1530

1531
const char *tb_get(text_buf_t *tb)
387,160✔
1532
{
1533
   return tb->buf;
387,160✔
1534
}
1535

1536
void tb_rewind(text_buf_t *tb)
3,908✔
1537
{
1538
   tb->len = 0;
3,908✔
1539
   tb->buf[0] = '\0';
3,908✔
1540
}
3,908✔
1541

1542
void tb_trim(text_buf_t *tb, size_t newlen)
561✔
1543
{
1544
   assert(newlen <= tb->len);
561✔
1545
   tb->len = newlen;
561✔
1546
   tb->buf[tb->len] = '\0';
561✔
1547
}
561✔
1548

1549
void tb_strip(text_buf_t *tb)
1✔
1550
{
1551
   while (tb->len > 0 && isspace_iso88591(tb->buf[tb->len - 1]))
7✔
1552
      tb->buf[--(tb->len)] = '\0';
6✔
1553
}
1✔
1554

1555
void tb_downcase(text_buf_t *tb)
65,459✔
1556
{
1557
   for (size_t i = 0; i < tb->len; i++)
16,668,187✔
1558
      tb->buf[i] = tolower_iso88591(tb->buf[i]);
16,602,728✔
1559
}
65,459✔
1560

1561
void tb_upcase(text_buf_t *tb)
847✔
1562
{
1563
   for (size_t i = 0; i < tb->len; i++)
5,273✔
1564
      tb->buf[i] = toupper_iso88591(tb->buf[i]);
4,426✔
1565
}
847✔
1566

1567
void tb_replace(text_buf_t *tb, char old, char rep)
411✔
1568
{
1569
   for (size_t i = 0; i < tb->len; i++) {
6,475✔
1570
      if (tb->buf[i] == old)
6,064✔
1571
         tb->buf[i] = rep;
411✔
1572
   }
1573
}
411✔
1574

1575
void tb_strftime(text_buf_t *tb, const char *fmt, time_t time)
×
1576
{
1577
   struct tm tm;
×
1578
   switch (fmt[0]) {
×
1579
   case 'L':
×
1580
#ifdef __MINGW32__
1581
      localtime_s(&tm, &time);
1582
#else
1583
      localtime_r(&time, &tm);
×
1584
#endif
1585
      break;
×
1586
   case 'G':
×
1587
#ifdef __MINGW32__
1588
      gmtime_s(&tm, &time);
1589
#else
1590
      gmtime_r(&time, &tm);
×
1591
#endif
1592
      break;
×
1593
   default:
×
1594
      fatal_trace("invalid timezone specifier '%c'", fmt[0]);
1595
   }
1596

1597
   const size_t max = 64;
×
1598
   char *p = tb_reserve(tb, max);
×
1599
   if (strftime(p, max, fmt + 1, &tm) == 0)
×
1600
      fatal_trace("time format buffer too small");
1601
}
×
1602

1603
void _local_free(void *ptr)
820,269✔
1604
{
1605
   free(*(void **)ptr);
820,269✔
1606
}
820,269✔
1607

1608
void set_message_style(message_style_t style)
×
1609
{
1610
   message_style = style;
×
1611

1612
   if (style == MESSAGE_COMPACT)
×
1613
      want_color = false;
×
1614
}
×
1615

1616
message_style_t get_message_style(void)
21,997✔
1617
{
1618
   return message_style;
21,997✔
1619
}
1620

1621
#ifndef __MINGW32__
1622
static uint64_t timeval_us(struct timeval *tv)
7,510✔
1623
{
1624
   return (tv->tv_sec * UINT64_C(1000000)) + tv->tv_usec;
7,510✔
1625
}
1626
#endif
1627

1628
void nvc_rusage(nvc_rusage_t *ru)
3,755✔
1629
{
1630
#ifndef __MINGW32__
1631
   static uint64_t last_user, last_sys;
3,755✔
1632

1633
   struct rusage buf;
3,755✔
1634
   if (getrusage(RUSAGE_SELF, &buf) < 0)
3,755✔
1635
      fatal_errno("getrusage");
×
1636

1637
   const uint64_t user = timeval_us(&(buf.ru_utime));
3,755✔
1638
   const uint64_t sys = timeval_us(&(buf.ru_stime));
3,755✔
1639

1640
   ru->user = (user - last_user) / 1000;
3,755✔
1641
   ru->sys = (sys - last_sys) / 1000;
3,755✔
1642

1643
   last_sys = sys;
3,755✔
1644
   last_user = user;
3,755✔
1645

1646
#ifdef __APPLE__
1647
   const int rss_units = 1024;
1648
#else
1649
   const int rss_units = 1;
3,755✔
1650
#endif
1651

1652
   ru->rss = buf.ru_maxrss / rss_units;
3,755✔
1653
#else
1654
   static ULARGE_INTEGER last_kernel, last_user;
1655
   ULARGE_INTEGER lv_Tkernel, lv_Tuser;
1656
   HANDLE hProcess = GetCurrentProcess();
1657

1658
   FILETIME ftCreation, ftExit, ftKernel, ftUser;
1659
   if (!GetProcessTimes(hProcess, &ftCreation, &ftExit, &ftKernel, &ftUser))
1660
      fatal_errno("GetProcessTimes");
1661

1662
   lv_Tkernel.LowPart = ftKernel.dwLowDateTime;
1663
   lv_Tkernel.HighPart = ftKernel.dwHighDateTime;
1664
   lv_Tuser.LowPart = ftUser.dwLowDateTime;
1665
   lv_Tuser.HighPart = ftUser.dwHighDateTime;
1666

1667
   ru->user = (lv_Tuser.QuadPart - last_user.QuadPart) / 10000;
1668
   ru->sys = (lv_Tkernel.QuadPart - last_kernel.QuadPart) / 10000;
1669

1670
   last_user = lv_Tuser;
1671
   last_kernel = lv_Tkernel;
1672

1673
   PROCESS_MEMORY_COUNTERS counters;
1674
   if (!GetProcessMemoryInfo(GetCurrentProcess(), &counters, sizeof(counters)))
1675
      fatal_errno("GetProcessMemoryInfo");
1676

1677
   ru->rss = counters.PeakWorkingSetSize / 1024;
1678
#endif
1679

1680
   static uint64_t last_ts;
3,755✔
1681
   const uint64_t ts = get_timestamp_us();
3,755✔
1682
   ru->ms = last_ts == 0 ? ru->user + ru->sys : (ts - last_ts) / 1000;
3,755✔
1683
   last_ts = ts;
3,755✔
1684
}
3,755✔
1685

1686
#ifdef __MINGW32__
1687
static uint64_t file_time_to_nanos(LPFILETIME ft)
1688
{
1689
   uint64_t nanos = (uint64_t)ft->dwHighDateTime << 32;
1690
   nanos |= ft->dwLowDateTime;
1691

1692
   // Windows file timestamps are in units of 100 nanoseconds since
1693
   // 1601-01-01T00:00:00Z: convert that to nanoseconds since the Unix
1694
   // epoch 1970-01-01T00:00:00Z
1695
   nanos -= UINT64_C(116444736000000000);
1696
   nanos *= 100;
1697

1698
   return nanos;
1699
}
1700

1701
static bool fill_file_info(file_info_t *info, HANDLE handle)
1702
{
1703
   memset(info, '\0', sizeof(file_info_t));
1704

1705
   BY_HANDLE_FILE_INFORMATION hinfo;
1706
   if (!GetFileInformationByHandle(handle, &hinfo))
1707
      fatal_errno("GetFileInformationByHandle");
1708

1709
   info->size = (uint64_t)hinfo.nFileSizeHigh << 32;
1710
   info->size |= hinfo.nFileSizeLow;
1711

1712
   if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1713
      info->type = FILE_DIR;
1714
   else
1715
      info->type = FILE_REGULAR;
1716

1717
   info->mtime = file_time_to_nanos(&(hinfo.ftLastWriteTime));
1718

1719
   return true;
1720
}
1721
#else  // !__MINGW32__
1722
static void fill_file_info(file_info_t *info, const struct stat *st)
68,342✔
1723
{
1724
   memset(info, '\0', sizeof(file_info_t));
68,342✔
1725

1726
   info->size = st->st_size;
68,342✔
1727

1728
   if (S_ISDIR(st->st_mode))
68,342✔
1729
      info->type = FILE_DIR;
116✔
1730
   else if (!S_ISREG(st->st_mode))
68,226✔
1731
      info->type = FILE_FIFO;
51✔
1732
   else
1733
      info->type = FILE_REGULAR;
1734

1735
   info->mtime = st->st_mtime * UINT64_C(1000000000);
68,342✔
1736

1737
#if defined HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
1738
   info->mtime += st->st_mtimespec.tv_nsec;
1739
#elif defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
1740
   info->mtime += st->st_mtim.tv_nsec;
68,342✔
1741
#endif
1742
}
68,342✔
1743
#endif  // !__MINGW32__
1744

1745
bool get_file_info(const char *path, file_info_t *info)
32,198✔
1746
{
1747
#ifdef __MINGW32__
1748
   HANDLE handle = CreateFile(
1749
        path, FILE_READ_ATTRIBUTES,
1750
        FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1751
        NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1752

1753
   if (handle == INVALID_HANDLE_VALUE)
1754
      return false;
1755

1756
   fill_file_info(info, handle);
1757

1758
   if (!CloseHandle(handle))
1759
      fatal_errno("CloseHandle");
1760

1761
   return true;
1762
#else
1763
   struct stat st;
32,198✔
1764
   if (stat(path, &st) == 0) {
32,198✔
1765
      fill_file_info(info, &st);
6,973✔
1766
      return true;
6,973✔
1767
   }
1768
   else
1769
      return false;
1770
#endif
1771
}
1772

1773
bool get_handle_info(int fd, file_info_t *info)
61,369✔
1774
{
1775
#ifdef __MINGW32__
1776
   HANDLE handle = (HANDLE)_get_osfhandle(fd);
1777
   fill_file_info(info, handle);
1778
   return true;
1779
#else
1780
   struct stat st;
61,369✔
1781
   if (fstat(fd, &st) == 0) {
61,369✔
1782
      fill_file_info(info, &st);
61,369✔
1783
      return true;
61,369✔
1784
   }
1785
   else
1786
      return false;
1787
#endif
1788
}
1789

1790
void run_program(const char *const *args)
×
1791
{
1792
#if defined __CYGWIN__ || defined __MINGW32__
1793
   int status = spawnvp(_P_WAIT, args[0], (char *const *)args);
1794
#else  // __CYGWIN__
1795
   pid_t pid = fork();
×
1796
   int status = 0;
×
1797
   if (pid == 0) {
×
1798
      execvp(args[0], (char *const *)args);
×
1799
      fatal_errno("execv");
×
1800
   }
1801
   else if (pid > 0) {
×
1802
      if (waitpid(pid, &status, 0) != pid)
×
1803
         fatal_errno("waitpid");
×
1804

1805
      status = WEXITSTATUS(status);
×
1806
   }
1807
   else
1808
      fatal_errno("fork");
×
1809
#endif  // __CYGWIN__
1810

1811
   if (status != 0) {
×
1812
      LOCAL_TEXT_BUF tb = tb_new();
×
1813
      for (size_t i = 0; args[i] != NULL; i++)
×
1814
         tb_printf(tb, "%s%s", i > 0 ? " " : "", args[i]);
×
1815
      fatal("$bold$%s$$ failed with status %d", tb_get(tb), status);
×
1816
   }
1817
}
×
1818

1819
char *nvc_temp_file(void)
×
1820
{
1821
   static const char *try[] = { "TMPDIR", "TEMP", "TMP" };
×
1822
   const char *tmpdir = NULL;
×
1823
   for (int i = 0; tmpdir == NULL && i < ARRAY_LEN(try); i++)
×
1824
      tmpdir = getenv(try[i]);
×
1825

1826
#ifdef __MINGW32__
1827
   char *buf = xasprintf("%s\\nvc-XXXXXX", tmpdir ?: ".");
1828
   return _mktemp(buf);
1829
#else
1830
   char *buf = xasprintf("%s/nvc-XXXXXX", tmpdir ?: "/tmp");
×
1831
   int fd = mkstemp(buf);
×
1832
   if (fd < 0)
×
1833
      fatal_errno("mkstemp");
×
1834
   close(fd);
×
1835
   return buf;
×
1836
#endif
1837
}
1838

1839
void file_read_lock(int fd)
27,734✔
1840
{
1841
#ifdef __MINGW32__
1842
   HANDLE hf = (HANDLE)_get_osfhandle(fd);
1843

1844
   LARGE_INTEGER li;
1845
   li.QuadPart = _filelengthi64(fd);
1846

1847
   OVERLAPPED ovlp;
1848
   memset(&ovlp, 0, sizeof ovlp);
1849

1850
   if (!LockFileEx(hf, 0, 0, li.LowPart, li.HighPart, &ovlp))
1851
      fatal_errno("LockFileEx");
1852
#else
1853
   if (flock(fd, LOCK_SH) < 0)
27,734✔
1854
      fatal_errno("flock");
×
1855
#endif
1856
}
27,734✔
1857

1858
void file_write_lock(int fd)
8,239✔
1859
{
1860
#ifdef __MINGW32__
1861
   HANDLE hf = (HANDLE)_get_osfhandle(fd);
1862

1863
   LARGE_INTEGER li;
1864
   li.QuadPart = _filelengthi64(fd);
1865

1866
   OVERLAPPED ovlp;
1867
   memset(&ovlp, 0, sizeof ovlp);
1868

1869
   if (!LockFileEx(hf, LOCKFILE_EXCLUSIVE_LOCK, 0,
1870
                   li.LowPart, li.HighPart, &ovlp))
1871
      fatal_errno("LockFileEx");
1872
#else
1873
   if (flock(fd, LOCK_EX) < 0)
8,239✔
1874
      fatal_errno("flock");
×
1875
#endif
1876
}
8,239✔
1877

1878
void file_unlock(int fd)
35,964✔
1879
{
1880
#ifdef __MINGW32__
1881
   HANDLE hf = (HANDLE)_get_osfhandle(fd);
1882

1883
   LARGE_INTEGER li;
1884
   li.QuadPart = _filelengthi64 (fd);
1885

1886
   UnlockFile(hf, 0, 0, li.LowPart, li.HighPart);
1887
#else
1888
   if (flock(fd, LOCK_UN) < 0)
35,964✔
1889
      fatal_errno("flock");
×
1890
#endif
1891
}
35,964✔
1892

1893
void *map_file(int fd, size_t size)
33,601✔
1894
{
1895
#ifdef __MINGW32__
1896
   HANDLE handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), NULL,
1897
                                     PAGE_READONLY, 0, size, NULL);
1898
   if (!handle)
1899
      fatal_errno("CreateFileMapping");
1900

1901
   void *ptr = MapViewOfFileEx(handle, FILE_MAP_READ, 0,
1902
                               0, (SIZE_T) size, (LPVOID) NULL);
1903
   CloseHandle(handle);
1904
   if (ptr == NULL)
1905
      fatal_errno("MapViewOfFileEx");
1906
#else
1907
   void *ptr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
33,601✔
1908
   if (ptr == MAP_FAILED)
33,601✔
1909
      fatal_trace("mmap failed to map %zu byte file", size);
1910
#endif
1911
   return ptr;
33,601✔
1912
}
1913

1914
void unmap_file(void *ptr, size_t size)
28,427✔
1915
{
1916
#ifdef __MINGW32__
1917
   if (!UnmapViewOfFile((LPCVOID) ptr))
1918
      fatal_errno("UnmapViewOfFile");
1919
#else
1920
   munmap(ptr, size);
28,427✔
1921
#endif
1922
}
28,427✔
1923

1924
void make_dir(const char *fmt, ...)
4,280✔
1925
{
1926
   va_list ap;
4,280✔
1927
   va_start(ap, fmt);
4,280✔
1928
   char *path LOCAL = xvasprintf(fmt, ap);
8,560✔
1929
   va_end(ap);
4,280✔
1930

1931
#ifdef __MINGW32__
1932
   if (!CreateDirectory(path, NULL) && (GetLastError() != ERROR_ALREADY_EXISTS))
1933
      fatal_errno("mkdir: %s", path);
1934
#else
1935
   if (mkdir(path, 0777) != 0 && errno != EEXIST)
4,280✔
1936
      fatal_errno("mkdir: %s", path);
×
1937
#endif
1938
}
4,280✔
1939

1940
FILE *create_file(const char *fmt, ...)
930✔
1941
{
1942
   va_list ap;
930✔
1943
   va_start(ap, fmt);
930✔
1944
   char *path LOCAL = xvasprintf(fmt, ap);
930✔
1945
   va_end(ap);
930✔
1946

1947
   FILE *f = fopen(path, "w");
930✔
1948
   if (f == NULL)
930✔
1949
      fatal_errno("cannot create %s", path);
×
1950

1951
   return f;
930✔
1952
}
1953

1954
uint64_t get_timestamp_ns(void)
32,015,030✔
1955
{
1956
#if defined __MINGW32__
1957
   static volatile uint64_t freq;
1958
   if (load_acquire(&freq) == 0) {
1959
      LARGE_INTEGER tmp;
1960
      if (!QueryPerformanceFrequency(&tmp))
1961
         fatal_errno("QueryPerformanceFrequency");
1962
      store_release(&freq, tmp.QuadPart);
1963
   }
1964

1965
   LARGE_INTEGER ticks;
1966
   if (!QueryPerformanceCounter(&ticks))
1967
      fatal_errno("QueryPerformanceCounter");
1968
   return (double)ticks.QuadPart * (1e9 / (double)freq);
1969
#else
1970
   struct timespec ts;
32,015,030✔
1971
   if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
32,015,030✔
1972
      fatal_errno("clock_gettime");
×
1973
   return ts.tv_nsec + (ts.tv_sec * UINT64_C(1000000000));
32,015,030✔
1974
#endif
1975
}
1976

1977
uint64_t get_timestamp_us(void)
32,015,030✔
1978
{
1979
   return get_timestamp_ns() / 1000;
32,015,030✔
1980
}
1981

1982
timestamp_t get_real_time(void)
16,872✔
1983
{
1984
#if defined __MINGW32__
1985
   FILETIME ft;
1986
   GetSystemTimeAsFileTime(&ft);
1987

1988
   return file_time_to_nanos(&ft);
1989
#else
1990
   struct timespec ts;
16,872✔
1991
   if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
16,872✔
1992
      fatal_errno("clock_gettime");
×
1993

1994
   return (uint64_t)ts.tv_nsec + ((uint64_t)ts.tv_sec * UINT64_C(1000000000));
16,872✔
1995
#endif
1996
}
1997

1998
void open_pipe(int *rfd, int *wfd)
×
1999
{
2000
   int fds[2];
×
2001
#ifdef __MINGW32__
2002
   const int rc = _pipe(fds, 4096, _O_BINARY);
2003
#else
2004
   const int rc = pipe(fds) < 0;
×
2005
#endif
2006
   if (rc < 0)
×
2007
      fatal_errno("failed to create pipe");
2008

2009
   *rfd = fds[0];
×
2010
   *wfd = fds[1];
×
2011
}
×
2012

2013
#if defined _WIN32 || defined __CYGWIN__
2014
static struct {
2015
   char illegal;
2016
   const char *rep;
2017
} symbol_replacements[] = {
2018
   { '(', "_lp_"   },
2019
   { ')', "_rp_"   },
2020
   { '"', "_q_"    },
2021
   { '[', "_ls_"   },
2022
   { ']', "_rs_"   },
2023
   { '*', "_mult_" },
2024
   { '+', "_plus_" },
2025
   { '=', "_eq_"   },
2026
   { '\\', "_bs_"  },
2027
};
2028

2029
static text_buf_t *safe_symbol_win32(const char *text)
2030
{
2031
   text_buf_t *tb = tb_new();
2032

2033
   for (const char *p = text; *p != '\0'; p++) {
2034
      bool replaced = false;
2035
      for (size_t j = 0; j < ARRAY_LEN(symbol_replacements); j++) {
2036
         if (*p == symbol_replacements[j].illegal) {
2037
            tb_cat(tb, symbol_replacements[j].rep);
2038
            replaced = true;
2039
            break;
2040
         }
2041
      }
2042

2043
      if (!replaced)
2044
         tb_append(tb, *p);
2045
   }
2046

2047
   return tb;
2048
}
2049

2050
#endif
2051

2052
text_buf_t *safe_symbol(ident_t id)
×
2053
{
2054
   // Return a string that is safe to use as a symbol name on this platform
2055

2056
   text_buf_t *tb = tb_new();
×
2057
   tb_istr(tb, id);
×
2058

2059
#if defined _WIN32 || defined __CYGWIN__
2060
   if (strpbrk(tb_get(tb), "()\"[]*+=\\") == NULL)
2061
      return tb;
2062
   else {
2063
      text_buf_t *new = safe_symbol_win32(tb_get(tb));
2064
      tb_free(tb);
2065
      return new;
2066
   }
2067
#else
2068
   return tb;
×
2069
#endif
2070
}
2071

2072
void __cleanup_array(void *ptr)
123,880✔
2073
{
2074
   A(void *) *a = ptr;
123,880✔
2075
   ACLEAR(*a);
123,880✔
2076
}
123,880✔
2077

2078
void __array_resize_slow(void **ptr, uint32_t *limit, uint32_t count,
1,531,510✔
2079
                         size_t size)
2080
{
2081
   if (count == 0) {
1,531,510✔
2082
      free(*ptr);
×
2083
      *ptr = NULL;
×
2084
      *limit = 0;
×
2085
   }
2086
   else {
2087
      if (*limit == 0)
1,531,510✔
2088
         *limit = count;  // Setting the initial size of the array
1,334,930✔
2089
      else
2090
         *limit = next_power_of_2(count);
196,580✔
2091
      *ptr = xrealloc_array(*ptr, *limit, size);
1,531,510✔
2092
   }
2093
}
1,531,510✔
2094

2095
bool get_exe_path(text_buf_t *tb)
4,888✔
2096
{
2097
#if defined __linux__
2098
   char buf[PATH_MAX];
4,888✔
2099
   ssize_t nchars = readlink("/proc/self/exe", buf, sizeof(buf));
4,888✔
2100
   if (nchars > 0) {   // Does not append '\0'
4,888✔
2101
      tb_catn(tb, buf, nchars);
4,888✔
2102
      return true;
4,888✔
2103
   }
2104
#elif defined __APPLE__
2105
   char buf[PATH_MAX];
2106
   if (proc_pidpath(getpid(), buf, sizeof(buf)) > 0) {
2107
      tb_cat(tb, buf);
2108
      return true;
2109
   }
2110
#elif defined __FreeBSD__
2111
   char buf[PATH_MAX];
2112
   size_t size = sizeof(buf);
2113
   const int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
2114
   if (sysctl(name, ARRAY_LEN(name), buf, &size, NULL, 0) == 0) {
2115
      tb_catn(tb, buf, size);
2116
      return true;
2117
   }
2118
#elif defined __MINGW32__
2119
   HANDLE hProc = GetCurrentProcess();
2120
   char buf[PATH_MAX];
2121
   DWORD size = sizeof(buf);
2122
   if (QueryFullProcessImageNameA(hProc, 0, buf, &size)) {
2123
      tb_cat(tb, buf);
2124
      return true;
2125
   }
2126
#endif
2127
   return false;
2128
}
2129

2130
#if defined __MINGW32__
2131
static void get_relative_prefix(text_buf_t *tb)
2132
{
2133
   if (get_exe_path(tb)) {
2134
      int len = tb_len(tb);
2135
      const char *str = tb_get(tb);
2136
      for (int i = 0; i < 2; i++) {
2137
         do {
2138
            len--;
2139
         } while (str[len] != DIR_SEP[0]);
2140
      }
2141
      tb_trim(tb, len);
2142
   }
2143
   else
2144
      fatal("failed to read executable path");
2145
}
2146
#endif
2147

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

2158
void get_lib_dir(text_buf_t *tb)
5,235✔
2159
{
2160
#if defined __MINGW32__
2161
   get_relative_prefix(tb);
2162
   tb_cat(tb, DIR_SEP "lib" DIR_SEP "nvc");
2163
#else
2164
   tb_cat(tb, LIBDIR);
5,235✔
2165
#endif
2166
}
5,235✔
2167

2168
void get_data_dir(text_buf_t *tb)
23✔
2169
{
2170
#if defined __MINGW32__
2171
   get_relative_prefix(tb);
2172
   tb_cat(tb, DIR_SEP "share" DIR_SEP "nvc");
2173
#else
2174
   tb_cat(tb, DATADIR);
23✔
2175
#endif
2176
}
23✔
2177

2178
bool is_absolute_path(const char *path)
24✔
2179
{
2180
   if (path[0] == DIR_SEP[0] || path[0] == '/')
24✔
2181
      return true;
24✔
2182

2183
#ifdef __MINGW32__
2184
   if (isalpha((int)path[0]) && path[1] == ':')
2185
      return true;
2186
#endif
2187

2188
   return false;
2189
}
2190

2191
void get_relative_path(text_buf_t *tb, const char *from, const char *to)
234✔
2192
{
2193
#ifdef __MINGW32__
2194
    char abs_from[MAX_PATH], abs_to[MAX_PATH];
2195
    if (from == NULL || !_fullpath(abs_from, from, MAX_PATH))
2196
       goto fallback;
2197
    if (!_fullpath(abs_to, to, MAX_PATH))
2198
       goto fallback;
2199

2200
    char buffer[MAX_PATH];
2201
    if (!PathRelativePathToA(buffer, abs_from, FILE_ATTRIBUTE_DIRECTORY,
2202
                             abs_to, 0))
2203
       goto fallback;
2204

2205
    if (strncmp(buffer, ".\\", 2) == 0)
2206
       tb_cat(tb, buffer + 2);   // Strip leading .\ prefix
2207
    else
2208
       tb_cat(tb, buffer);
2209
#else
2210
   char abs_from[PATH_MAX], abs_to[PATH_MAX];
234✔
2211
   if (from == NULL || realpath(from, abs_from) == NULL)
438✔
2212
      goto fallback;
30✔
2213
   else if (realpath(to, abs_to) == NULL)
204✔
NEW
2214
      goto fallback;
×
2215

2216
   A(char *) from_parts = AINIT;
204✔
2217
   A(char *) to_parts = AINIT;
204✔
2218

2219
   for (char *tok = strtok(abs_from, "/"); tok; tok = strtok(NULL, "/"))
927✔
2220
      APUSH(from_parts, tok);
723✔
2221

2222
   for (char *tok = strtok(abs_to, "/"); tok; tok = strtok(NULL, "/"))
1,428✔
2223
      APUSH(to_parts, tok);
1,224✔
2224

2225
   int common = 0;
2226
   while (common < from_parts.count && common < to_parts.count
723✔
2227
          && strcmp(from_parts.items[common], to_parts.items[common]) == 0)
1,650✔
2228
      common++;
723✔
2229

2230
   for (int i = common; i < from_parts.count; i++) {
204✔
NEW
2231
      tb_cat(tb, "..");
×
NEW
2232
      if (i + 1 < from_parts.count || to_parts.count > common)
×
NEW
2233
         tb_cat(tb, "/");
×
2234
   }
2235

2236
   for (int i = common; i < to_parts.count; i++) {
705✔
2237
      tb_cat(tb, to_parts.items[i]);
501✔
2238
      if (i + 1 < to_parts.count)
501✔
2239
         tb_cat(tb, "/");
297✔
2240
   }
2241

2242
   ACLEAR(from_parts);
204✔
2243
   ACLEAR(to_parts);
204✔
2244
#endif
2245

2246
   if (tb_len(tb) == 0)
204✔
NEW
2247
      goto fallback;
×
2248

2249
   return;
204✔
2250

2251
 fallback:
30✔
2252
   tb_cat(tb, to);
30✔
2253
}
2254

2255
void progress(const char *fmt, ...)
12,904✔
2256
{
2257
   if (opt_get_int(OPT_VERBOSE)) {
12,904✔
2258
      va_list ap;
9✔
2259
      va_start(ap, fmt);
9✔
2260
      char *msg LOCAL = xvasprintf(fmt, ap);
9✔
2261
      va_end(ap);
9✔
2262

2263
      static nvc_rusage_t last_ru;
9✔
2264

2265
      nvc_rusage_t ru;
9✔
2266
      nvc_rusage(&ru);
9✔
2267

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

2270
      if (!isinf(conc) && conc > 1.1)
9✔
2271
         notef("%s [%ums %.1fx %+dkB]", msg, ru.ms, conc,
×
2272
               ru.rss - last_ru.rss);
×
2273
      else
2274
         notef("%s [%ums %+dkB]", msg, ru.ms, ru.rss - last_ru.rss);
9✔
2275

2276
      last_ru = ru;
9✔
2277
   }
2278
}
12,904✔
2279

2280
unsigned nvc_nprocs(void)
5,672✔
2281
{
2282
#if defined _WIN32
2283
   SYSTEM_INFO sysinfo;
2284
   GetSystemInfo(&sysinfo);
2285

2286
   return sysinfo.dwNumberOfProcessors;
2287
#elif defined _SC_NPROCESSORS_ONLN
2288
   long count = sysconf(_SC_NPROCESSORS_ONLN);
5,672✔
2289
   if (count == -1)
5,672✔
2290
      fatal_errno("sysconf(_SC_NPROCESSORS_ONLN)");
×
2291

2292
#if defined __linux__ && defined HAVE_GETTID
2293
   // Restrict to the number of CPUs we are allowed to run on
2294
   cpu_set_t s;
5,672✔
2295
   if (sched_getaffinity(gettid(), sizeof(cpu_set_t), &s) == 0)
5,672✔
2296
      return MAX(1, MIN(count, CPU_COUNT(&s)));
5,672✔
2297
#endif
2298

2299
   return count;
×
2300
#else
2301
#warning Cannot detect number of processors on this platform
2302
   return 1;
2303
#endif
2304
}
2305

2306
void capture_registers(struct cpu_state *cpu)
832✔
2307
{
2308
#if defined HAVE_GETCONTEXT
2309
   ucontext_t uc;
832✔
2310
   if (getcontext(&uc) != 0)
832✔
2311
      fatal_errno("getcontext");
×
2312

2313
   fill_cpu_state(cpu, &uc);
832✔
2314
#elif defined __MINGW32__
2315
   CONTEXT context;
2316
   RtlCaptureContext(&context);
2317
   fill_cpu_state(cpu, &context);
2318
#elif defined HAVE_PTHREAD
2319
   assert(atomic_load(&thread_regs) == NULL);
2320
   atomic_store(&thread_regs, cpu);
2321

2322
   if (pthread_kill(pthread_self(), SIGUSR2) != 0)
2323
      fatal_errno("pthread_kill");
2324

2325
   // Registers filled in by signal_handler
2326
   if (atomic_load(&thread_regs) != NULL)
2327
      fatal_trace("signal handler did not capture thread registers");
2328
#else
2329
#error cannot capture registers on this platform
2330
#endif
2331
}
832✔
2332

2333
void add_fault_handler(fault_fn_t fn, void *context)
8,093✔
2334
{
2335
   fault_handler_t *h = xmalloc(sizeof(fault_handler_t));
8,093✔
2336
   h->next    = fault_handlers;
8,093✔
2337
   h->fn      = fn;
8,093✔
2338
   h->context = context;
8,093✔
2339

2340
   fault_handlers = h;
8,093✔
2341
}
8,093✔
2342

2343
void remove_fault_handler(fault_fn_t fn, void *context)
2,783✔
2344
{
2345
   for (fault_handler_t **p = &fault_handlers; *p; p = &((*p)->next)) {
2,787✔
2346
      if ((*p)->fn == fn && (*p)->context == context) {
2,787✔
2347
         fault_handler_t *tmp = (*p)->next;
2,783✔
2348
         free(*p);
2,783✔
2349
         *p = tmp;
2,783✔
2350
         return;
2,783✔
2351
      }
2352
   }
2353

2354
   fatal_trace("no fault handler for %p with context %p", fn, context);
2355
}
2356

2357
void check_cpu_features(void)
4,600✔
2358
{
2359
#ifdef HAVE_POPCNT
2360
   if (!__builtin_cpu_supports("popcnt"))
4,600✔
2361
      fatal("CPU is missing support for POPCNT instruction");
×
2362
#endif
2363
}
4,600✔
2364

2365
#ifdef DEBUG
2366
void should_not_reach_here(void)
2367
{
2368
   fatal_trace("should not reach here");
2369
}
2370
#endif
2371

2372
mem_pool_t *pool_new(void)
24,982✔
2373
{
2374
   mem_pool_t *mp = xcalloc(sizeof(mem_pool_t));
24,982✔
2375
   mp->pageshift = POOL_PAGE_MIN;
24,982✔
2376

2377
   return mp;
24,982✔
2378
}
2379

2380
void pool_free(mem_pool_t *mp)
75,168✔
2381
{
2382
   if (mp == NULL)
75,168✔
2383
      return;
2384

2385
   for (pool_page_t *p = mp->pages, *tmp; p != NULL; p = tmp) {
42,641✔
2386
      tmp = p->next;
18,613✔
2387
      free(p);
18,613✔
2388
   }
2389

2390
   free(mp);
24,028✔
2391
}
2392

2393
static pool_page_t *pool_page_new(mem_pool_t *mp, size_t reqsz, size_t align)
19,280✔
2394
{
2395
   const size_t hdrsz = ALIGN_UP(sizeof(page_header_t), align);
19,280✔
2396
   const size_t minsz = next_power_of_2(reqsz + hdrsz);
19,280✔
2397
   const size_t allocsz = MAX(1 << mp->pageshift, minsz);
19,280✔
2398
   page_header_t *p = xmalloc(allocsz);
19,280✔
2399
   p->next  = mp->pages;
19,280✔
2400
   p->alloc = hdrsz;
19,280✔
2401
   p->size  = allocsz;
19,280✔
2402

2403
   ASAN_POISON(p + hdrsz, allocsz - hdrsz);
19,280✔
2404

2405
   if (mp->pageshift < POOL_PAGE_MAX)
19,280✔
2406
      mp->pageshift++;
19,280✔
2407

2408
   return (mp->pages = p);
19,280✔
2409
}
2410

2411
static void *pool_aligned_malloc(mem_pool_t *mp, size_t size, size_t align)
483,589✔
2412
{
2413
   assert(is_power_of_2(align));
483,589✔
2414

2415
   pool_page_t *page;
483,589✔
2416
   if (mp->pages == NULL)
483,589✔
2417
      page = pool_page_new(mp, size, align);
16,607✔
2418
   else {
2419
      const size_t base = ALIGN_UP(mp->pages->alloc + POOL_REDZONE, align);
466,982✔
2420
      if (base + size > mp->pages->size)
466,982✔
2421
         page = pool_page_new(mp, size, align);
2,673✔
2422
      else {
2423
         page = mp->pages;
464,309✔
2424
         page->alloc = base;
464,309✔
2425
      }
2426
   }
2427

2428
   assert((mp->pages->alloc & (align - 1)) == 0);
483,589✔
2429
   assert(mp->pages->alloc + size <= mp->pages->size);
483,589✔
2430
   assert(mp->pages->alloc >= sizeof(page_header_t));
483,589✔
2431

2432
   void *ptr = (void *)mp->pages + mp->pages->alloc;
483,589✔
2433
   mp->pages->alloc += size;
483,589✔
2434

2435
   ASAN_UNPOISON(ptr, size);
483,589✔
2436
   return ptr;
483,589✔
2437
}
2438

2439
void *pool_malloc(mem_pool_t *mp, size_t size)
419,817✔
2440
{
2441
   return pool_aligned_malloc(mp, size, POOL_MIN_ALIGN);
419,817✔
2442
}
2443

2444
void *pool_malloc_flex(mem_pool_t *mp, size_t fixed, size_t nelems,
392,563✔
2445
                       size_t size)
2446
{
2447
   size_t bytes;
392,563✔
2448
   if (__builtin_mul_overflow(nelems, size, &bytes))
392,563✔
2449
      fatal_trace("array size overflow: requested %zd * %zd bytes",
2450
                  nelems, size);
2451

2452
   return pool_malloc(mp, fixed + bytes);
392,563✔
2453
}
2454

2455
void *pool_malloc_array(mem_pool_t *mp, size_t nelems, size_t size)
2,572✔
2456
{
2457
   return pool_malloc_flex(mp, 0, nelems, size);
2,572✔
2458
}
2459

2460
void *pool_calloc(mem_pool_t *mp, size_t size)
63,772✔
2461
{
2462
   void *ptr = pool_aligned_malloc(mp, size, POOL_MIN_ALIGN);
63,772✔
2463
   memset(ptr, '\0', size);
63,772✔
2464
   return ptr;
63,772✔
2465
}
2466

2467
void pool_stats(mem_pool_t *mp, size_t *alloc, size_t *npages)
7,696✔
2468
{
2469
   *npages = 0;
7,696✔
2470
   *alloc = 0;
7,696✔
2471

2472
   for (pool_page_t *p = mp->pages; p != NULL; p = p->next) {
12,061✔
2473
      *npages += 1;
4,365✔
2474
      *alloc += p->alloc - sizeof(pool_page_t);
4,365✔
2475
   }
2476
}
7,696✔
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