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

nickg / nvc / 20682794148

03 Jan 2026 07:14PM UTC coverage: 92.592% (-0.06%) from 92.65%
20682794148

push

github

nickg
Lazy lowering for processes

63 of 64 new or added lines in 4 files covered. (98.44%)

197 existing lines in 6 files now uncovered.

76009 of 82090 relevant lines covered (92.59%)

474891.2 hits per line

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

69.03
/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 "printf.h"
35
#include "thread.h"
36

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

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

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

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

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

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

95
#define HUGE_PAGE_SIZE  0x200000
96

97
#define POOL_MIN_ALIGN  sizeof(double)
98
#define POOL_PAGE_4K    12
99
#define POOL_PAGE_1M    20
100
#define POOL_PAGE_MIN   POOL_PAGE_4K
101
#define POOL_PAGE_MAX   POOL_PAGE_1M
102

103
#if ASAN_ENABLED
104
#define POOL_REDZONE 16
105
#else
106
#define POOL_REDZONE 0
107
#endif
108

109
static void show_bug_report(void);
110

111
typedef struct _fault_handler fault_handler_t;
112
typedef struct _pool_page pool_page_t;
113

114
struct text_buf {
115
   char  *buf;
116
   size_t alloc;
117
   size_t len;
118
};
119

120
struct _fault_handler {
121
   fault_handler_t *next;
122
   fault_fn_t       fn;
123
   void            *context;
124
};
125

126
typedef struct _pool_page {
127
   pool_page_t *next;
128
   size_t       alloc;
129
   size_t       size;
130
   uint8_t      data[0];
131
} page_header_t;
132

133
typedef struct _mem_pool {
134
   pool_page_t *pages;
135
   size_t       pageshift;
136
} mem_pool_t;
137

138
static bool             want_color = 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
void *xmalloc(size_t size)
10,720,844✔
153
{
154
   void *p = malloc(size);
10,720,844✔
155
   if (p == NULL)
10,720,844✔
156
      fatal("memory exhausted (malloc %lu)", (long unsigned)size);
×
157
   return p;
10,720,844✔
158
}
159

160
void *xmalloc_flex(size_t fixed, size_t nelems, size_t size)
8,135,969✔
161
{
162
   size_t bytes;
8,135,969✔
163
   if (__builtin_mul_overflow(nelems, size, &bytes))
8,135,969✔
164
      fatal_trace("array size overflow: requested %zd * %zd bytes",
165
                  nelems, size);
166

167
   return xmalloc(fixed + bytes);
8,135,969✔
168
}
169

170
void *xmalloc_array(size_t nelems, size_t size)
1,024,287✔
171
{
172
   return xmalloc_flex(0, nelems, size);
1,024,287✔
173
}
174

175
void *xcalloc(size_t size)
8,066,365✔
176
{
177
   void *p = calloc(1, size);
8,066,365✔
178
   if (p == NULL)
8,066,365✔
179
      fatal("memory exhausted (calloc %lu)", (long unsigned)size);
×
180
   return p;
8,066,365✔
181
}
182

183
void *xcalloc_flex(size_t fixed, size_t nelems, size_t size)
6,342,423✔
184
{
185
   size_t bytes;
6,342,423✔
186
   if (__builtin_mul_overflow(nelems, size, &bytes))
6,342,423✔
187
      fatal_trace("array size overflow: requested %zd * %zd bytes",
188
                  nelems, size);
189

190
   return xcalloc(fixed + bytes);
6,342,423✔
191
}
192

193
void *xcalloc_array(size_t nelems, size_t size)
1,159,763✔
194
{
195
   return xcalloc_flex(0, nelems, size);
1,159,763✔
196
}
197

198
void *xrealloc(void *ptr, size_t size)
3,161,410✔
199
{
200
   ptr = realloc(ptr, size);
3,161,410✔
201
   if (ptr == NULL)
3,161,410✔
202
      fatal("memory exhausted (realloc %lu)", (long unsigned)size);
×
203
   return ptr;
3,161,410✔
204
}
205

206
void *xrealloc_array(void *ptr, size_t nelems, size_t size)
2,550,465✔
207
{
208
   size_t bytes;
2,550,465✔
209
   if (__builtin_mul_overflow(nelems, size, &bytes))
2,550,465✔
210
      fatal_trace("array size overflow: requested %zd * %zd bytes",
211
                  nelems, size);
212

213
   return xrealloc(ptr, bytes);
2,550,465✔
214
}
215

216
void *xrealloc_flex(void *ptr, size_t fixed, size_t nelems, size_t size)
33,059✔
217
{
218
   size_t bytes;
33,059✔
219
   if (__builtin_mul_overflow(nelems, size, &bytes))
33,059✔
220
      fatal_trace("array size overflow: requested %zd * %zd bytes",
221
                  nelems, size);
222

223
   return xrealloc(ptr, bytes + fixed);
33,059✔
224
}
225

226
char *xstrdup(const char *str)
365,025✔
227
{
228
   char *copy = strdup(str);
365,025✔
229
   if (copy == NULL)
365,025✔
230
      fatal("memory exhausted (strdup)");
×
231
   return copy;
365,025✔
232
}
233

234
char *xstrndup(const char *str, size_t n)
42✔
235
{
236
   char *copy = strndup(str, n);
42✔
237
   if (copy == NULL)
42✔
238
      fatal("memory exhausted (strndup)");
×
239
   return copy;
42✔
240
}
241

242
char *xvasprintf(const char *fmt, va_list ap)
318,703✔
243
{
244
   char *strp = NULL;
318,703✔
245
   if (vasprintf(&strp, fmt, ap) < 0)
318,703✔
246
      fatal("memory exhausted (vasprintf)");
×
247
   return strp;
318,703✔
248
}
249

250
char *xasprintf(const char *fmt, ...)
146,193✔
251
{
252
   va_list ap;
146,193✔
253
   va_start(ap, fmt);
146,193✔
254
   char *strp = xvasprintf(fmt, ap);
146,193✔
255
   va_end(ap);
146,193✔
256
   return strp;
146,193✔
257
}
258

259
void errorf(const char *fmt, ...)
49✔
260
{
261
   diag_t *d = diag_new(DIAG_ERROR, NULL);
49✔
262
   va_list ap;
49✔
263
   va_start(ap, fmt);
49✔
264
   diag_vprintf(d, fmt, ap);
49✔
265
   va_end(ap);
49✔
266
   diag_emit(d);
49✔
267
}
49✔
268

269
void warnf(const char *fmt, ...)
32✔
270
{
271
   diag_t *d = diag_new(DIAG_WARN, NULL);
32✔
272
   va_list ap;
32✔
273
   va_start(ap, fmt);
32✔
274
   diag_vprintf(d, fmt, ap);
32✔
275
   va_end(ap);
32✔
276
   diag_emit(d);
32✔
277
}
32✔
278

279
void notef(const char *fmt, ...)
1,641✔
280
{
281
   diag_t *d = diag_new(DIAG_NOTE, NULL);
1,641✔
282
   va_list ap;
1,641✔
283
   va_start(ap, fmt);
1,641✔
284
   diag_vprintf(d, fmt, ap);
1,641✔
285
   va_end(ap);
1,641✔
286
   diag_emit(d);
1,641✔
287
}
1,641✔
288

289
void debugf(const char *fmt, ...)
2,010✔
290
{
291
   diag_t *d = diag_new(DIAG_DEBUG, NULL);
2,010✔
292
   va_list ap;
2,010✔
293
   va_start(ap, fmt);
2,010✔
294
   diag_vprintf(d, fmt, ap);
2,010✔
295
   va_end(ap);
2,010✔
296
   diag_emit(d);
2,010✔
297
}
2,010✔
298

299
bool color_terminal(void)
46,190✔
300
{
301
   return want_color;
46,190✔
302
}
303

304
bool utf8_terminal(void)
22,861✔
305
{
306
   return want_utf8;
22,861✔
307
}
308

309
void print_centred(const char *text)
3✔
310
{
311
   if (term_width == 0)
3✔
312
      fputs(text, stdout);
3✔
313
   else {
314
      const int pad = (term_width - strlen(text)) / 2;
×
315
      printf("%*s%s", pad, "", text);
×
316
   }
317
}
3✔
318

319
void fatal_exit(int status)
40✔
320
{
321
   async_barrier();
40✔
322

323
   if (atomic_load(&crashing) != SIG_ATOMIC_MAX)
40✔
324
      _exit(status);   // Exit during crash
×
325
   else if (!thread_attached() || thread_id() != 0)
40✔
326
      _exit(status);
×
327
   else
328
      exit(status);
40✔
329
}
330

331
void error_at(const loc_t *loc, const char *fmt, ...)
729✔
332
{
333
   diag_t *d = diag_new(DIAG_ERROR, loc);
729✔
334

335
   va_list ap;
729✔
336
   va_start(ap, fmt);
729✔
337
   diag_vprintf(d, fmt, ap);
729✔
338
   va_end(ap);
729✔
339

340
   diag_emit(d);
729✔
341
}
726✔
342

343
void warn_at(const loc_t *loc, const char *fmt, ...)
81✔
344
{
345
   diag_t *d = diag_new(DIAG_WARN, loc);
81✔
346

347
   va_list ap;
81✔
348
   va_start(ap, fmt);
81✔
349
   diag_vprintf(d, fmt, ap);
81✔
350
   va_end(ap);
81✔
351

352
   diag_emit(d);
81✔
353
}
81✔
354

355
void note_at(const loc_t *loc, const char *fmt, ...)
9,186✔
356
{
357
   diag_t *d = diag_new(DIAG_NOTE, loc);
9,186✔
358

359
   va_list ap;
9,186✔
360
   va_start(ap, fmt);
9,186✔
361
   diag_vprintf(d, fmt, ap);
9,186✔
362
   va_end(ap);
9,186✔
363

364
   diag_emit(d);
9,186✔
365
}
9,186✔
366

367
void fatal_at(const loc_t *loc, const char *fmt, ...)
×
368
{
369
   diag_t *d = diag_new(DIAG_FATAL, loc);
×
370
   diag_suppress(d, false);
×
371

372
   va_list ap;
×
373
   va_start(ap, fmt);
×
374
   diag_vprintf(d, fmt, ap);
×
375
   va_end(ap);
×
376

377
   diag_set_consumer(NULL, NULL);
×
378
   diag_emit(d);
×
379
   fatal_exit(EXIT_FAILURE);
×
380
}
381

382
void fatal(const char *fmt, ...)
33✔
383
{
384
   diag_t *d = diag_new(DIAG_FATAL, NULL);
33✔
385
   diag_suppress(d, false);
33✔
386

387
   va_list ap;
33✔
388
   va_start(ap, fmt);
33✔
389
   diag_vprintf(d, fmt, ap);
33✔
390
   va_end(ap);
33✔
391

392
   diag_set_consumer(NULL, NULL);
33✔
393
   diag_emit(d);
33✔
394
   fatal_exit(EXIT_FAILURE);
33✔
395
}
396

397
void fatal_trace(const char *fmt, ...)
398
{
399
   diag_t *d = diag_new(DIAG_FATAL, NULL);
×
400
   diag_suppress(d, false);
×
401

402
   va_list ap;
×
403
   va_start(ap, fmt);
×
404
   diag_vprintf(d, fmt, ap);
×
405
   va_end(ap);
×
406

407
   diag_set_consumer(NULL, NULL);
×
408
   diag_emit(d);
×
409

410
   show_stacktrace();
×
411

412
   show_bug_report();
×
413

414
   fatal_exit(EXIT_FAILURE);
×
415
}
416

417
void fatal_errno(const char *fmt, ...)
3✔
418
{
419
   diag_t *d = diag_new(DIAG_FATAL, NULL);
3✔
420
   diag_suppress(d, false);
3✔
421

422
   va_list ap;
3✔
423
   va_start(ap, fmt);
3✔
424
   diag_vprintf(d, fmt, ap);
3✔
425
   diag_printf(d, ": %s", last_os_error());
3✔
426
   va_end(ap);
3✔
427

428
   diag_set_consumer(NULL, NULL);
3✔
429
   diag_emit(d);
3✔
430
   fatal_exit(EXIT_FAILURE);
3✔
431
}
432

433
const char *last_os_error(void)
3✔
434
{
435
#ifdef __MINGW32__
436
   static __thread LPSTR mbuf = NULL;
437

438
   if (mbuf != NULL)
439
      LocalFree(mbuf);
440

441
   FormatMessage(
442
      FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
443
      | FORMAT_MESSAGE_IGNORE_INSERTS,
444
      NULL,
445
      GetLastError(),
446
      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
447
      (LPSTR)&mbuf, 0, NULL);
448

449
   return mbuf;
450
#else
451
   return strerror(errno);
3✔
452
#endif
453
}
454

455
static void trace_one_frame(uintptr_t pc, const char *module,
×
456
                            const char *srcfile, const char *symbol,
457
                            unsigned lineno, unsigned colno,
458
                            ptrdiff_t disp, frame_kind_t kind)
459
{
460
   ostream_t *os = nvc_stderr();
×
461

462
   nvc_fprintf(os, "[$green$%p$$] ", (void *)pc);
×
463
   if (kind == FRAME_LIB)
×
464
      nvc_fprintf(os, "($red$%s$$) ", module);
×
465
   if (srcfile != NULL)
×
466
      nvc_fprintf(os, "%s:%d ", srcfile, lineno);
×
467
   if (symbol != NULL) {
×
468
      nvc_fprintf(os, "$yellow$%s$$", symbol);
×
469
      if (srcfile == NULL && disp != 0)
×
470
         nvc_fprintf(os, "$yellow$+0x%"PRIxPTR"$$", disp);
×
471
   }
472
   if (kind == FRAME_VHDL)
×
473
      nvc_fprintf(os, " $magenta$[VHDL]$$");
×
474
   fprintf(stderr, "\n");
×
475

476
#ifndef __MINGW32__
477
   if (srcfile != NULL) {
×
478
      FILE *f = fopen(srcfile, "r");
×
479
      if (f != NULL) {
×
480
         char *line LOCAL = NULL;
×
481
         size_t linesz = 0;
×
482
         for (int i = 0, len; i < lineno + 1
×
483
                 && (len = getline(&line, &linesz, f)) != -1; i++) {
×
484
            if (i < lineno - 2)
×
485
               continue;
×
486

487
            if (len <= 1)
×
488
               continue;
×
489
            else if (line[len - 1] == '\n')
×
490
               line[len - 1] = '\0';
×
491

492
            if (i == lineno - 1)
×
493
               nvc_fprintf(os, "$cyan$$bold$-->$$ $cyan$%s$$\n", line);
×
494
            else
495
               nvc_fprintf(os, "    $cyan$%s$$\n", line);
×
496
         }
497
         fclose(f);
×
498
      }
499
   }
500
#endif
501
}
×
502

503
__attribute__((noinline))
504
void show_stacktrace(void)
×
505
{
506
   debug_info_t *di = debug_capture();
×
507

508
   const int nframes = debug_count_frames(di);
×
509
   for (int n = 1; n < nframes; n++) {
×
510
      const debug_frame_t *f = debug_get_frame(di, n);
×
511

512
      for (debug_inline_t *inl = f->inlined; inl != NULL; inl = inl->next)
×
513
         trace_one_frame(f->pc, f->module, inl->srcfile, inl->symbol,
×
514
                         inl->lineno, inl->colno, f->disp, f->kind);
×
515

516
      trace_one_frame(f->pc, f->module, f->srcfile, f->symbol, f->lineno,
×
517
                      f->colno, f->disp, f->kind);
×
518

519
   }
520

521
   debug_free(di);
×
522

523
#if defined __linux__ && !defined HAVE_LIBDW && !defined HAVE_LIBDWARF
524
   nvc_fprintf(nvc_stderr(), "\n$cyan$Hint: you can get better stack traces by "
525
               "installing the libdw-dev package and reconfiguring$$\n");
526
#endif
527

528
   fflush(stderr);
×
529
}
×
530

531
static void show_bug_report(void)
532
{
533
#ifndef DEBUG
534
   extern const char version_string[];
535
   nvc_fprintf(nvc_stderr(), "\n$!red$%s ["TARGET_SYSTEM"]\n\n"
536
               "Please report this bug at "PACKAGE_BUGREPORT"$$\n\n",
537
               version_string);
538
   fflush(stderr);
539
#endif
540
}
541

542
#ifdef __MINGW32__
543

544
static const char *exception_name(DWORD code)
545
{
546
   switch (code) {
547
   case EXCEPTION_ACCESS_VIOLATION:
548
      return "EXCEPTION_ACCESS_VIOLATION";
549
   case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
550
      return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
551
   case EXCEPTION_BREAKPOINT:
552
      return "EXCEPTION_BREAKPOINT";
553
   case EXCEPTION_DATATYPE_MISALIGNMENT:
554
      return "EXCEPTION_DATATYPE_MISALIGNMENT";
555
   case EXCEPTION_ILLEGAL_INSTRUCTION:
556
      return "EXCEPTION_ILLEGAL_INSTRUCTION";
557
   case EXCEPTION_IN_PAGE_ERROR:
558
      return "EXCEPTION_IN_PAGE_ERROR";
559
   case EXCEPTION_INT_DIVIDE_BY_ZERO:
560
      return "EXCEPTION_INT_DIVIDE_BY_ZERO";
561
   case EXCEPTION_INT_OVERFLOW:
562
      return "EXCEPTION_INT_OVERFLOW";
563
   case EXCEPTION_PRIV_INSTRUCTION:
564
      return "EXCEPTION_PRIV_INSTRUCTION";
565
   case EXCEPTION_STACK_OVERFLOW:
566
      return "EXCEPTION_STACK_OVERFLOW";
567
   }
568

569
   return "???";
570
}
571

572
WINAPI
573
static LONG win32_exception_handler(EXCEPTION_POINTERS *ExceptionInfo)
574
{
575
   DWORD code = ExceptionInfo->ExceptionRecord->ExceptionCode;
576
   PVOID addr = ExceptionInfo->ExceptionRecord->ExceptionAddress;
577

578
#if defined ARCH_ARM64
579
   DWORD64 ip = ExceptionInfo->ContextRecord->Pc;
580
#elif defined ARCH_X86_64
581
   DWORD64 ip = ExceptionInfo->ContextRecord->Rip;
582
#else
583
   DWORD ip = ExceptionInfo->ContextRecord->Eip;
584
#endif
585

586
   if (code == EXCEPTION_ACCESS_VIOLATION)
587
      addr = (PVOID)ExceptionInfo->ExceptionRecord->ExceptionInformation[1];
588

589
   fprintf(stderr, "\n%s*** Caught exception %x (%s)",
590
           want_color ? "\033[31m\033[1m" : "",
591
           (int)code, exception_name(code));
592

593
   switch (code) {
594
   case EXCEPTION_ACCESS_VIOLATION:
595
   case EXCEPTION_ILLEGAL_INSTRUCTION:
596
      fprintf(stderr, " [address=%p, ip=%p]", (void *)addr, (void*)ip);
597
      break;
598
   }
599

600
   fprintf(stderr, " ***%s\n\n", want_color ? "\033[0m" : "");
601
   fflush(stderr);
602

603
#ifdef __WIN64
604
   if (code != EXCEPTION_STACK_OVERFLOW)
605
      show_stacktrace();
606
#endif
607

608
  return EXCEPTION_EXECUTE_HANDLER;
609
}
610

611
#else
612

613
#ifndef __SANITIZE_THREAD__
614
static const char *signame(int sig, siginfo_t *info)
×
615
{
616
   switch (sig) {
×
617
   case SIGSEGV:
×
618
#ifdef __linux__
619
      switch (info->si_code) {
×
620
      case SEGV_MAPERR: return "SEGV_MAPERR";
621
      case SEGV_ACCERR: return "SEGV_ACCERR";
×
622
      default: return "SIGSEGV";
×
623
      }
624
#else
625
      return "SIGSEGV";
626
#endif
627
   case SIGABRT: return "SIGABRT";
628
   case SIGILL: return "SIGILL";
×
629
   case SIGFPE: return "SIGFPE";
×
630
   case SIGUSR1: return "SIGUSR1";
×
631
   case SIGUSR2: return "SIGUSR2";
×
632
   case SIGBUS: return "SIGBUS";
×
633
   case SIGINT: return "SIGINT";
×
634
   case SIGTRAP: return "SIGTRAP";
×
635
   default: return "???";
×
636
   }
637
}
638

639
static void print_fatal_signal(int sig, siginfo_t *info, struct cpu_state *cpu)
×
640
{
641
   static volatile __thread sig_atomic_t recurse = 0;
×
642

643
   if (recurse++ > 1) {
×
644
      signal(SIGABRT, SIG_DFL);
×
645
      abort();
×
646
   }
647

648
   char buf[512], *p = buf, *s = buf, *end = buf + ARRAY_LEN(buf);
×
649
   p += checked_sprintf(p, end - p, "\n%s*** Caught signal %d (%s)%s",
×
650
                        want_color ? "\033[31m\033[1m" : "",
×
651
                        sig, signame(sig, info),
652
                        recurse > 1 ? " inside signal handler" : "");
×
653

654
   switch (sig) {
×
655
   case SIGSEGV:
×
656
   case SIGILL:
657
   case SIGFPE:
658
   case SIGBUS:
659
      p += checked_sprintf(p, end - p, " [address=%p, ip=%p]",
×
660
                           info->si_addr, (void*)cpu->pc);
×
661
      break;
×
662
   }
663

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

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

668
   if (sig != SIGUSR1 && !atomic_cas(&crashing, SIG_ATOMIC_MAX, thread_id())) {
×
669
      sleep(60);
×
670
      _exit(EXIT_FAILURE);
×
671
   }
672
}
×
673
#endif  // !__SANITIZE_THREAD__
674

675
static __thread struct cpu_state *thread_regs = NULL;
676

677
static void signal_handler(int sig, siginfo_t *info, void *context)
×
678
{
679
   ucontext_t *uc = (ucontext_t*)context;
×
680
   struct cpu_state cpu;
×
681
   fill_cpu_state(&cpu, uc);
×
682

683
   struct cpu_state *req;
×
684
   if (sig == SIGUSR2 && (req = atomic_load(&thread_regs)) != NULL) {
×
685
      // Fill in registers for capture_registers
686
      *req = cpu;
×
687
      atomic_store(&thread_regs, NULL);
×
688
      return;
×
689
   }
690
   else if (sig == SIGINT) {
×
691
      void (*fn)(void *) = atomic_load(&ctrl_c_fn);
×
692
      if (fn != NULL) {
×
693
         (*fn)(ctrl_c_arg);
×
694
         return;
×
695
      }
696
   }
697

698
#ifdef __SANITIZE_THREAD__
699
   abort();
700
#else
701

702
   for (fault_handler_t *f = fault_handlers; f; f = f->next)
×
703
      (*f->fn)(sig, info->si_addr, &cpu, f->context);
×
704

705
   print_fatal_signal(sig, info, &cpu);
×
706

707
   show_stacktrace();
×
708

709
   show_bug_report();
×
710

711
   if (sig != SIGUSR1)
×
712
      _exit(2);
×
713
#endif  // !__SANITIZE_THREAD__
714
}
715
#endif  // ! __MINGW32__
716

717
void register_signal_handlers(void)
5,702✔
718
{
719
#ifdef __MINGW32__
720
   SetUnhandledExceptionFilter(win32_exception_handler);
721
#else
722

723
   struct sigaction sa = {
5,702✔
724
      .sa_sigaction = signal_handler,
725
      .sa_flags = SA_RESTART | SA_SIGINFO
726
   };
727
   sigemptyset(&sa.sa_mask);
5,702✔
728

729
#ifndef __SANITIZE_THREAD__
730
   sigaction(SIGSEGV, &sa, NULL);
5,702✔
731
   sigaction(SIGUSR1, &sa, NULL);
5,702✔
732
   sigaction(SIGFPE, &sa, NULL);
5,702✔
733
   sigaction(SIGBUS, &sa, NULL);
5,702✔
734
   sigaction(SIGILL, &sa, NULL);
5,702✔
735
   sigaction(SIGABRT, &sa, NULL);
5,702✔
736
   sigaction(SIGTRAP, &sa, NULL);
5,702✔
737
#endif  // !__SANITIZE_THREAD__
738
   sigaction(SIGUSR2, &sa, NULL);
5,702✔
739
#endif  // !__MINGW32__
740
}
5,702✔
741

742
#ifdef __MINGW32__
743
static BOOL win32_ctrl_c_handler(DWORD fdwCtrlType)
744
{
745
   switch (fdwCtrlType) {
746
   case CTRL_C_EVENT:
747
      {
748
         void (*fn)(void *) = atomic_load(&ctrl_c_fn);
749
         if (fn != NULL)
750
            (*fn)(ctrl_c_arg);
751
         return TRUE;
752
      }
753

754
   default:
755
      return FALSE;
756
   }
757
}
758
#endif
759

760
void set_ctrl_c_handler(void (*fn)(void *), void *arg)
7,464✔
761
{
762
   ctrl_c_arg = arg;
7,464✔
763
   atomic_store(&ctrl_c_fn, fn);
7,464✔
764

765
   if (fn != NULL) {
7,464✔
766
#ifndef __MINGW32__
767
      struct sigaction sa = {};
3,732✔
768
      sa.sa_sigaction = signal_handler;
3,732✔
769
      sigemptyset(&sa.sa_mask);
3,732✔
770
      sa.sa_flags = SA_RESTART | SA_SIGINFO;
3,732✔
771

772
      sigaction(SIGINT, &sa, NULL);
3,732✔
773
#else
774
      if (!SetConsoleCtrlHandler(win32_ctrl_c_handler, TRUE))
775
         fatal_trace("SetConsoleCtrlHandler");
776
#endif
777
   }
778
   else {
779
#ifndef __MINGW32__
780
      struct sigaction sa = {};
3,732✔
781
      sa.sa_handler = SIG_DFL;
3,732✔
782
      sigaction(SIGINT, &sa, NULL);
3,732✔
783
#else
784
      if (!SetConsoleCtrlHandler(win32_ctrl_c_handler, FALSE))
785
         fatal_trace("SetConsoleCtrlHandler");
786
#endif
787
   }
788
}
7,464✔
789

790
#ifdef __MINGW32__
791
static void restore_win32_codepage(void)
792
{
793
   assert(win32_codepage != 0);
794
   SetConsoleOutputCP(win32_codepage);
795
}
796
#endif
797

798
void term_init(void)
5,702✔
799
{
800
   const char *nvc_colors = getenv("NVC_COLORS");
5,702✔
801
   const char *term = getenv("TERM") ?: "";
5,702✔
802

803
   static const char *term_blacklist[] = {
5,702✔
804
      "dumb"
805
   };
806

807
   spin_wait();  // Dummy, to force linking thread.c
5,702✔
808

809
   bool is_tty = isatty(STDERR_FILENO);
5,702✔
810

811
#ifdef __MINGW32__
812
   if (!is_tty) {
813
      // Handle running under MinTty
814
      HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
815
      const size_t size = sizeof(FILE_NAME_INFO) + sizeof(WCHAR) * MAX_PATH;
816
      FILE_NAME_INFO *nameinfo = malloc(size);
817
      if (!GetFileInformationByHandleEx(hStdOut, FileNameInfo, nameinfo, size))
818
         fatal_errno("GetFileInformationByHandle");
819

820
      if ((wcsncmp(nameinfo->FileName, L"\\msys-", 6) == 0
821
           || wcsncmp(nameinfo->FileName, L"\\cygwin-", 8) == 0)
822
          && wcsstr(nameinfo->FileName, L"pty") != NULL)
823
         is_tty = true;
824

825
      free(nameinfo);
826
   }
827
#endif
828

829
   if (nvc_colors && strcmp(nvc_colors, "always") == 0)
5,702✔
830
      want_color = true;
×
831
   else if (nvc_colors && strcmp(nvc_colors, "never") == 0)
5,702✔
832
      want_color = false;
×
833
   else {
834
      want_color = is_tty;
5,702✔
835

836
      if (want_color && (term != NULL)) {
5,702✔
837
         for (size_t i = 0; i < ARRAY_LEN(term_blacklist); i++) {
×
838
            if (strcmp(term, term_blacklist[i]) == 0) {
×
839
               want_color = false;
×
840
               break;
×
841
            }
842
         }
843
      }
844
   }
845

846
#ifdef __MINGW32__
847
   HANDLE hConsole = GetStdHandle(STD_ERROR_HANDLE);
848
   DWORD mode;
849
   if (GetConsoleMode(hConsole, &mode)) {
850
      mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT;
851
      if (!SetConsoleMode(hConsole, mode))
852
         want_color = false;
853

854
      CONSOLE_SCREEN_BUFFER_INFO info;
855
      if (GetConsoleScreenBufferInfo(hConsole, &info))
856
         term_width = info.dwSize.X;
857
      else
858
         term_width = 80;
859
   }
860
#else
861
   if (is_tty) {
5,702✔
862
      // Try to find the terminal size with tcgetwinsize or TIOCGWINSZ
863
      term_width = 80;
×
864
#if defined HAVE_TCGETWINSIZE
865
      struct winsize ws;
866
      if (tcgetwinsize(STDIN_FILENO, &ws) == 0)
867
         term_width = ws.ws_col;
868
#elif defined TIOCGWINSZ
869
      struct winsize ws;
×
870
      if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0)
×
871
         term_width = ws.ws_col;
×
872
#endif
873
   }
874
#endif
875

876
#ifndef __MINGW32__
877
   // Assume the terminal is expecting UTF-8 by default
878
   want_utf8 = true;
5,702✔
879

880
   const char *lang = getenv("LANG");
5,702✔
881
   if (lang != NULL && *lang != '\0' && strcasestr(lang, "utf-8") == NULL)
5,702✔
882
      want_utf8 = false;
×
883
#else
884
   win32_codepage = GetConsoleOutputCP();
885
   if (win32_codepage == 65001)
886
      want_utf8 = true;
887
   else if (win32_codepage != 28591) {
888
      SetConsoleOutputCP(28591);
889
      atexit(restore_win32_codepage);
890
   }
891
#endif
892

893
   // Diagnostics are printed to stderr and explicitly flushed
894
   setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
5,702✔
895
}
5,702✔
896

897
int terminal_width(void)
22,432✔
898
{
899
   return term_width;
22,432✔
900
}
901

902
const char *ordinal_str(int n)
28✔
903
{
904
   switch (n) {
28✔
905
   case 1: return "first";
906
   case 2: return "second";
6✔
907
   case 3: return "third";
×
908
   default:
×
909
      {
910
         static char buf[16];
×
911
         if (n > 20 && n % 10 == 1)
×
912
            checked_sprintf(buf, sizeof(buf), "%dst", n);
×
913
         else if (n > 20 && n % 10 == 2)
×
914
            checked_sprintf(buf, sizeof(buf), "%dnd", n);
×
915
         else if (n > 20 && n % 10 == 2)
×
916
            checked_sprintf(buf, sizeof(buf), "%drd", n);
×
917
         else
918
            checked_sprintf(buf, sizeof(buf), "%dth", n);
×
919
         return buf;
920
      }
921
   }
922
}
923

924
char *null_terminate(const uint8_t *data, size_t len)
1,168✔
925
{
926
   char *cstr = xmalloc(len + 1);
1,168✔
927
   if (data != NULL)
1,168✔
928
      memcpy(cstr, data, len);
1,168✔
929
   else
930
      assert(len == 0);
×
931
   cstr[len] = '\0';
1,168✔
932
   return cstr;
1,168✔
933
}
934

935
char toupper_iso88591(unsigned char ch)
3,679,228✔
936
{
937
   if (ch >= 'a' && ch <= 'z')
3,679,228✔
938
      return ch - 'a' + 'A';
2,029,597✔
939
   else if ((ch >= 0xe0 && ch <= 0xf6) || (ch >= 0xf8 && ch <= 0xfe))
1,649,631✔
940
      return ch - 0x20;
39✔
941
   else
942
      return ch;
1,649,592✔
943
}
944

945
char tolower_iso88591(unsigned char ch)
16,687,018✔
946
{
947
   if (ch >= 'A' && ch <= 'Z')
16,687,018✔
948
      return ch + 'a' - 'A';
573,500✔
949
   else if ((ch >= 0xc0 && ch <= 0xd6) || (ch >= 0xd8 && ch <= 0xde))
16,113,518✔
950
      return ch + 0x20;
180✔
951
   else
952
      return ch;
16,113,338✔
953
}
954

955
bool isprint_iso88591(unsigned char ch)
293,468✔
956
{
957
   return (ch >= 0x20 && ch <= 0x7e) || (ch >= 0xa0 && ch <= 0xff);
293,468✔
958
}
959

960
bool isspace_iso88591(unsigned char ch)
83,902✔
961
{
962
   return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\v' || ch == '\f'
83,902✔
963
      || ch == '\r' || ch == 0xa0;
83,902✔
964
}
965

966
bool isdigit_iso88591(unsigned char ch)
20,597✔
967
{
968
   return ch >= '0' && ch <= '9';
20,597✔
969
}
970

971
bool isupper_iso88591(unsigned char ch)
2,207✔
972
{
973
   return (ch >= 'A' && ch <= 'Z')
2,207✔
974
      || (ch >= 0xc0 && ch <= 0xd6)
2,207✔
975
      || (ch >= 0xd8 && ch <= 0xde);
2,207✔
976
}
977

978
bool isalpha_iso88591(unsigned char ch)
1,560,976✔
979
{
980
   return (ch >= 'A' && ch <= 'Z')
1,560,976✔
981
      || (ch >= 'a' && ch <= 'z')
1,560,976✔
982
      || (ch >= 0xc0 && ch <= 0xd6)
983
      || (ch >= 0xd8 && ch <= 0xde)
984
      || (ch >= 0xe0 && ch <= 0xf6)
985
      || (ch >= 0xf8 && ch <= 0xfe);
1,560,976✔
986
}
987

988
bool isalnum_iso88591(unsigned char ch)
362,582✔
989
{
990
   return isalpha_iso88591(ch) || (ch >= '0' && ch <= '9');
362,582✔
991
}
992

993
int next_power_of_2(int n)
1,089,337✔
994
{
995
   n--;
1,089,337✔
996
   n |= n >> 1;
1,089,337✔
997
   n |= n >> 2;
1,089,337✔
998
   n |= n >> 4;
1,089,337✔
999
   n |= n >> 8;
1,089,337✔
1000
   n |= n >> 16;
1,089,337✔
1001
   n++;
1,089,337✔
1002
   return n;
1,089,337✔
1003
}
1004

1005
int ilog2(int64_t n)
15,581✔
1006
{
1007
   if (n <= 1)
15,581✔
1008
      return 0;
1009
   else {
1010
      int r = 0;
1011
      int64_t c = 1;
1012
      while (c < n) {
67,981✔
1013
         r += 1;
52,427✔
1014
         c <<= 1;
52,427✔
1015
      }
1016
      return r;
15,554✔
1017
   }
1018
}
1019

1020
bool ipow_safe(int64_t x, int64_t y, int64_t *result)
311✔
1021
{
1022
   assert(y >= 0);
311✔
1023
   int overflow = 0, xo = 0;
1024
   int64_t r = 1;
1025
   while (y) {
1,189✔
1026
      if (y & 1)
878✔
1027
         overflow |= xo || __builtin_mul_overflow(r, x, &r);
502✔
1028
      y >>= 1;
878✔
1029
      xo |= __builtin_mul_overflow(x, x, &x);
878✔
1030
   }
1031
   *result = r;
311✔
1032
   return !overflow;
311✔
1033
}
1034

1035
int64_t ipow(int64_t x, int64_t y)
2✔
1036
{
1037
   int64_t result;
2✔
1038
   if (!ipow_safe(x, y, &result))
2✔
1039
      DEBUG_ONLY(fatal_trace("integer overflow in ipow"));
1040

1041
   return result;
2✔
1042
}
1043

1044
#if !ASAN_ENABLED
1045
static long nvc_page_size(void)
1046
{
1047
#ifdef __MINGW32__
1048
   SYSTEM_INFO si;
1049
   GetSystemInfo(&si);
1050
   return si.dwPageSize;
1051
#else
1052
   return sysconf(_SC_PAGESIZE);
1053
#endif
1054
}
1055
#endif
1056

1057
void nvc_munmap(void *ptr, size_t length)
15,869✔
1058
{
1059
#if ASAN_ENABLED
1060
   free(ptr);
15,869✔
1061
#elif !defined __MINGW32__
1062
   if (munmap(ptr, length) != 0)
1063
      fatal_errno("munmap");
1064
#else
1065
   if (!VirtualFree(ptr, length, MEM_DECOMMIT))
1066
      fatal_errno("VirtualFree");
1067
#endif
1068
}
15,869✔
1069

1070
void *nvc_memalign(size_t align, size_t sz)
52,187✔
1071
{
1072
#if ASAN_ENABLED
1073
   void *ptr;
52,187✔
1074
   if (posix_memalign(&ptr, align, sz) != 0)
104,374✔
1075
      fatal_errno("posix_memalign");
×
1076

1077
   memset(ptr, '\0', sz);
52,187✔
1078
   return ptr;
52,187✔
1079
#else
1080
   assert(is_power_of_2(align));
1081
   const size_t mapalign = MAX(align, nvc_page_size());
1082
   const size_t mapsz = ALIGN_UP(sz + mapalign - 1, mapalign);
1083

1084
#if defined __MINGW32__
1085
   void *ptr = VirtualAlloc(NULL, mapsz, MEM_COMMIT | MEM_RESERVE,
1086
                            PAGE_READWRITE);
1087
   if (ptr == NULL)
1088
      fatal_errno("VirtualAlloc");
1089
#else
1090
   void *ptr = mmap(NULL, mapsz, PROT_READ | PROT_WRITE,
1091
                    MAP_PRIVATE | MAP_ANON, -1, 0);
1092
   if (ptr == MAP_FAILED)
1093
      fatal_errno("mmap failed to allocate %zu bytes", sz);
1094
#endif
1095

1096
   void *aligned = ALIGN_UP(ptr, align);
1097
   void *limit = aligned + sz;
1098

1099
   if (align > nvc_page_size()) {
1100
      const size_t low_waste = aligned - ptr;
1101
      const size_t high_waste = ptr + mapsz - limit;
1102
      assert(low_waste + high_waste == align);
1103

1104
      if (low_waste > 0) nvc_munmap(ptr, low_waste);
1105
      if (high_waste > 0) nvc_munmap(limit, high_waste);
1106
   }
1107

1108
   return aligned;
1109
#endif
1110
}
1111

1112
void nvc_memprotect(void *ptr, size_t length, mem_access_t prot)
55,283✔
1113
{
1114
#if defined __MINGW32__
1115
   static const int map[] = {
1116
      PAGE_NOACCESS, PAGE_READONLY, PAGE_READWRITE, PAGE_EXECUTE_READ,
1117
      PAGE_EXECUTE_READWRITE
1118
   };
1119
   DWORD old_prot;
1120
   if (length > 0 && !VirtualProtect(ptr, length, map[prot], &old_prot))
1121
      fatal_errno("VirtualProtect");
1122
#else
1123
#if ASAN_ENABLED
1124
   // LeakSanitizer will not detect leaks in regions mapped read-only
1125
   if (prot == MEM_RO || prot == MEM_NONE)
55,283✔
1126
      return;
1127
#endif
1128
   static const int map[] = {
2,805✔
1129
      PROT_NONE, PROT_READ, PROT_READ | PROT_WRITE, PROT_READ | PROT_EXEC,
1130
      PROT_READ | PROT_WRITE | PROT_EXEC
1131
   };
1132
   if (mprotect(ptr, length, map[prot]) < 0)
2,805✔
1133
      fatal_errno("mprotect");
×
1134
#endif
1135
}
1136

1137
void nvc_decommit(void *ptr, size_t length)
16,829✔
1138
{
1139
#if defined __MINGW32__
1140
   if (length > 0 && !VirtualFree(ptr, length, MEM_DECOMMIT))
1141
      fatal_errno("VirtualFree");
1142
#elif defined __linux__
1143
   if (madvise(ptr, length, MADV_DONTNEED) != 0)
16,829✔
1144
      fatal_errno("madvise: MADV_DONTNEED");
×
1145
#endif
1146
}
16,829✔
1147

1148
void *map_huge_pages(size_t align, size_t sz)
15,898✔
1149
{
1150
#ifdef __linux__
1151
   if (sz >= HUGE_PAGE_SIZE) {
15,898✔
1152
      const size_t mapsz = ALIGN_UP(sz, HUGE_PAGE_SIZE);
15,355✔
1153
      void *mem = nvc_memalign(MAX(HUGE_PAGE_SIZE, align), mapsz);
15,355✔
1154

1155
      if (madvise(mem, mapsz, MADV_HUGEPAGE) < 0)
15,355✔
1156
         warnf("madvise: MADV_HUGEPAGE: %s", last_os_error());
×
1157

1158
      return mem;
15,355✔
1159
   }
1160
#endif
1161

1162
   return nvc_memalign(align, sz);
543✔
1163
}
1164

1165
void *map_jit_pages(size_t align, size_t sz)
2,805✔
1166
{
1167
#ifdef __APPLE__
1168
   void *ptr = mmap(NULL, sz, PROT_READ | PROT_WRITE | PROT_EXEC,
1169
                    MAP_PRIVATE | MAP_ANON | MAP_JIT, -1, 0);
1170
   if (ptr == MAP_FAILED)
1171
      fatal_errno("mmap failed to allocate %zu bytes for executable code", sz);
1172
#else
1173
   void *ptr = map_huge_pages(align, sz);
2,805✔
1174
   nvc_memprotect(ptr, sz, MEM_RWX);
2,805✔
1175
#endif
1176

1177
   return ptr;
2,805✔
1178
}
1179

1180
int checked_sprintf(char *buf, int len, const char *fmt, ...)
857,320✔
1181
{
1182
   assert(len > 0);
857,320✔
1183

1184
   va_list ap;
857,320✔
1185
   va_start(ap, fmt);
857,320✔
1186

1187
   const int nbytes = vsnprintf(buf, len, fmt, ap);
857,320✔
1188
   if (nbytes >= len)
857,320✔
1189
      fatal_trace("checked_sprintf requires %d bytes but have %d",
1190
                  nbytes + 1, len);
1191

1192
   va_end(ap);
857,320✔
1193

1194
   return nbytes;
857,320✔
1195
}
1196

1197
text_buf_t *tb_new(void)
291,853✔
1198
{
1199
   text_buf_t *tb = xmalloc(sizeof(text_buf_t));
291,853✔
1200
   tb->alloc = 256;
291,853✔
1201
   tb->len   = 0;
291,853✔
1202
   tb->buf   = xmalloc(tb->alloc);
291,853✔
1203

1204
   tb->buf[0] = '\0';
291,853✔
1205

1206
   return tb;
291,853✔
1207
}
1208

1209
void tb_free(text_buf_t *tb)
290,505✔
1210
{
1211
   if (tb != NULL) {
290,505✔
1212
      free(tb->buf);
290,505✔
1213
      free(tb);
290,505✔
1214
   }
1215
}
290,505✔
1216

1217
void _tb_cleanup(text_buf_t **tb)
392,455✔
1218
{
1219
   if (*tb != NULL)
392,455✔
1220
      tb_free(*tb);
256,919✔
1221
}
392,455✔
1222

1223
void tb_vprintf(text_buf_t *tb, const char *fmt, va_list ap)
233,021✔
1224
{
1225
   ostream_t os = { tb_ostream_write, tb, CHARSET_ISO88591, false };
233,021✔
1226
   nvc_vfprintf(&os, fmt, ap);
233,021✔
1227
}
233,021✔
1228

1229
void tb_printf(text_buf_t *tb, const char *fmt, ...)
220,295✔
1230
{
1231
   va_list ap;
220,295✔
1232
   va_start(ap, fmt);
220,295✔
1233
   tb_vprintf(tb, fmt, ap);
220,295✔
1234
   va_end(ap);
220,295✔
1235
}
220,295✔
1236

1237
void tb_istr(text_buf_t *tb, ident_t ident)
307,030✔
1238
{
1239
   // TODO: this function seems useless now
1240
   tb_cat(tb, istr(ident));
307,030✔
1241
}
307,030✔
1242

1243
void tb_append(text_buf_t *tb, char ch)
919,079✔
1244
{
1245
   if (tb->len + 2 >= tb->alloc) {
919,079✔
1246
      tb->alloc *= 2;
66✔
1247
      tb->buf = xrealloc(tb->buf, tb->alloc);
66✔
1248
   }
1249

1250
   tb->buf[(tb->len)++] = ch;
919,079✔
1251
   tb->buf[tb->len] = '\0';
919,079✔
1252
}
919,079✔
1253

1254
void tb_catn(text_buf_t *tb, const char *str, size_t nchars)
1,321,028✔
1255
{
1256
   if (tb->len + nchars + 1 >= tb->alloc) {
1,321,028✔
1257
      tb->alloc = next_power_of_2(tb->alloc + nchars);
2,457✔
1258
      tb->buf = xrealloc(tb->buf, tb->alloc);
2,457✔
1259
   }
1260

1261
   memcpy(tb->buf + tb->len, str, nchars);
1,321,028✔
1262
   tb->len += nchars;
1,321,028✔
1263
   tb->buf[tb->len] = '\0';
1,321,028✔
1264
}
1,321,028✔
1265

1266
void tb_cat(text_buf_t *tb, const char *str)
610,213✔
1267
{
1268
   tb_catn(tb, str, strlen(str));
610,213✔
1269
}
610,213✔
1270

1271
void tb_repeat(text_buf_t *tb, char ch, size_t count)
×
1272
{
1273
   if (tb->len + count + 1 >= tb->alloc) {
×
1274
      tb->alloc = next_power_of_2(tb->alloc + count + 1);
×
1275
      tb->buf = xrealloc(tb->buf, tb->alloc);
×
1276
   }
1277

1278
   memset(tb->buf + tb->len, ch, count);
×
1279
   tb->len += count;
×
1280
   tb->buf[tb->len] = '\0';
×
1281
}
×
1282

1283
char *tb_reserve(text_buf_t *tb, size_t size)
×
1284
{
1285
   if (tb->len + size + 1 >= tb->alloc) {
×
1286
      tb->alloc = next_power_of_2(tb->alloc + size + 1);
×
1287
      tb->buf = xrealloc(tb->buf, tb->alloc);
×
1288
   }
1289

1290
   char *start = tb->buf + tb->len;
×
1291

1292
   tb->len += size;
×
1293
   tb->buf[tb->len] = '\0';
×
1294

1295
   return start;
×
1296
}
1297

1298
size_t tb_len(text_buf_t *tb)
92,183✔
1299
{
1300
   return tb->len;
92,183✔
1301
}
1302

1303
char *tb_claim(text_buf_t *tb)
36,219✔
1304
{
1305
   char *buf = tb->buf;
36,219✔
1306
   tb->buf = NULL;
36,219✔
1307
   return buf;
36,219✔
1308
}
1309

1310
const char *tb_get(text_buf_t *tb)
304,418✔
1311
{
1312
   return tb->buf;
304,418✔
1313
}
1314

1315
void tb_rewind(text_buf_t *tb)
3,926✔
1316
{
1317
   tb->len = 0;
3,926✔
1318
   tb->buf[0] = '\0';
3,926✔
1319
}
3,926✔
1320

1321
void tb_trim(text_buf_t *tb, size_t newlen)
561✔
1322
{
1323
   assert(newlen <= tb->len);
561✔
1324
   tb->len = newlen;
561✔
1325
   tb->buf[tb->len] = '\0';
561✔
1326
}
561✔
1327

1328
void tb_strip(text_buf_t *tb)
1✔
1329
{
1330
   while (tb->len > 0 && isspace_iso88591(tb->buf[tb->len - 1]))
7✔
1331
      tb->buf[--(tb->len)] = '\0';
6✔
1332
}
1✔
1333

1334
void tb_downcase(text_buf_t *tb)
65,485✔
1335
{
1336
   for (size_t i = 0; i < tb->len; i++)
16,668,544✔
1337
      tb->buf[i] = tolower_iso88591(tb->buf[i]);
16,603,059✔
1338
}
65,485✔
1339

1340
void tb_upcase(text_buf_t *tb)
863✔
1341
{
1342
   for (size_t i = 0; i < tb->len; i++)
5,367✔
1343
      tb->buf[i] = toupper_iso88591(tb->buf[i]);
4,504✔
1344
}
863✔
1345

1346
void tb_replace(text_buf_t *tb, char old, char rep)
409✔
1347
{
1348
   for (size_t i = 0; i < tb->len; i++) {
6,451✔
1349
      if (tb->buf[i] == old)
6,042✔
1350
         tb->buf[i] = rep;
409✔
1351
   }
1352
}
409✔
1353

1354
void tb_strftime(text_buf_t *tb, const char *fmt, time_t time)
×
1355
{
1356
   struct tm tm;
×
1357
   switch (fmt[0]) {
×
1358
   case 'L':
×
1359
#ifdef __MINGW32__
1360
      localtime_s(&tm, &time);
1361
#else
1362
      localtime_r(&time, &tm);
×
1363
#endif
1364
      break;
×
1365
   case 'G':
×
1366
#ifdef __MINGW32__
1367
      gmtime_s(&tm, &time);
1368
#else
1369
      gmtime_r(&time, &tm);
×
1370
#endif
1371
      break;
×
1372
   default:
×
1373
      fatal_trace("invalid timezone specifier '%c'", fmt[0]);
1374
   }
1375

1376
   const size_t max = 64;
×
1377
   char *p = tb_reserve(tb, max);
×
1378
   if (strftime(p, max, fmt + 1, &tm) == 0)
×
1379
      fatal_trace("time format buffer too small");
1380
}
×
1381

1382
void tb_ostream_write(const char *buf, size_t len, void *ctx)
684,288✔
1383
{
1384
   text_buf_t *tb = ctx;
684,288✔
1385
   tb_catn(tb, buf, len);
684,288✔
1386
}
684,288✔
1387

1388
void _local_free(void *ptr)
949,583✔
1389
{
1390
   free(*(void **)ptr);
949,583✔
1391
}
949,583✔
1392

1393
void set_message_style(message_style_t style)
×
1394
{
1395
   message_style = style;
×
1396

1397
   if (style == MESSAGE_COMPACT)
×
1398
      want_color = false;
×
1399
}
×
1400

1401
message_style_t get_message_style(void)
21,997✔
1402
{
1403
   return message_style;
21,997✔
1404
}
1405

1406
#ifndef __MINGW32__
1407
static uint64_t timeval_us(struct timeval *tv)
7,530✔
1408
{
1409
   return (tv->tv_sec * UINT64_C(1000000)) + tv->tv_usec;
7,530✔
1410
}
1411
#endif
1412

1413
void nvc_rusage(nvc_rusage_t *ru)
3,765✔
1414
{
1415
#ifndef __MINGW32__
1416
   static uint64_t last_user, last_sys;
3,765✔
1417

1418
   struct rusage buf;
3,765✔
1419
   if (getrusage(RUSAGE_SELF, &buf) < 0)
3,765✔
1420
      fatal_errno("getrusage");
×
1421

1422
   const uint64_t user = timeval_us(&(buf.ru_utime));
3,765✔
1423
   const uint64_t sys = timeval_us(&(buf.ru_stime));
3,765✔
1424

1425
   ru->user = (user - last_user) / 1000;
3,765✔
1426
   ru->sys = (sys - last_sys) / 1000;
3,765✔
1427

1428
   last_sys = sys;
3,765✔
1429
   last_user = user;
3,765✔
1430

1431
#ifdef __APPLE__
1432
   const int rss_units = 1024;
1433
#else
1434
   const int rss_units = 1;
3,765✔
1435
#endif
1436

1437
   ru->rss = buf.ru_maxrss / rss_units;
3,765✔
1438
#else
1439
   static ULARGE_INTEGER last_kernel, last_user;
1440
   ULARGE_INTEGER lv_Tkernel, lv_Tuser;
1441
   HANDLE hProcess = GetCurrentProcess();
1442

1443
   FILETIME ftCreation, ftExit, ftKernel, ftUser;
1444
   if (!GetProcessTimes(hProcess, &ftCreation, &ftExit, &ftKernel, &ftUser))
1445
      fatal_errno("GetProcessTimes");
1446

1447
   lv_Tkernel.LowPart = ftKernel.dwLowDateTime;
1448
   lv_Tkernel.HighPart = ftKernel.dwHighDateTime;
1449
   lv_Tuser.LowPart = ftUser.dwLowDateTime;
1450
   lv_Tuser.HighPart = ftUser.dwHighDateTime;
1451

1452
   ru->user = (lv_Tuser.QuadPart - last_user.QuadPart) / 10000;
1453
   ru->sys = (lv_Tkernel.QuadPart - last_kernel.QuadPart) / 10000;
1454

1455
   last_user = lv_Tuser;
1456
   last_kernel = lv_Tkernel;
1457

1458
   PROCESS_MEMORY_COUNTERS counters;
1459
   if (!GetProcessMemoryInfo(GetCurrentProcess(), &counters, sizeof(counters)))
1460
      fatal_errno("GetProcessMemoryInfo");
1461

1462
   ru->rss = counters.PeakWorkingSetSize / 1024;
1463
#endif
1464

1465
   static uint64_t last_ts;
3,765✔
1466
   const uint64_t ts = get_timestamp_us();
3,765✔
1467
   ru->ms = last_ts == 0 ? ru->user + ru->sys : (ts - last_ts) / 1000;
3,765✔
1468
   last_ts = ts;
3,765✔
1469
}
3,765✔
1470

1471
#ifdef __MINGW32__
1472
static uint64_t file_time_to_nanos(LPFILETIME ft)
1473
{
1474
   uint64_t nanos = (uint64_t)ft->dwHighDateTime << 32;
1475
   nanos |= ft->dwLowDateTime;
1476

1477
   // Windows file timestamps are in units of 100 nanoseconds since
1478
   // 1601-01-01T00:00:00Z: convert that to nanoseconds since the Unix
1479
   // epoch 1970-01-01T00:00:00Z
1480
   nanos -= UINT64_C(116444736000000000);
1481
   nanos *= 100;
1482

1483
   return nanos;
1484
}
1485

1486
static bool fill_file_info(file_info_t *info, HANDLE handle)
1487
{
1488
   memset(info, '\0', sizeof(file_info_t));
1489

1490
   BY_HANDLE_FILE_INFORMATION hinfo;
1491
   if (!GetFileInformationByHandle(handle, &hinfo))
1492
      fatal_errno("GetFileInformationByHandle");
1493

1494
   info->size = (uint64_t)hinfo.nFileSizeHigh << 32;
1495
   info->size |= hinfo.nFileSizeLow;
1496

1497
   if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1498
      info->type = FILE_DIR;
1499
   else
1500
      info->type = FILE_REGULAR;
1501

1502
   info->mtime = file_time_to_nanos(&(hinfo.ftLastWriteTime));
1503

1504
   return true;
1505
}
1506
#else  // !__MINGW32__
1507
static void fill_file_info(file_info_t *info, const struct stat *st)
67,113✔
1508
{
1509
   memset(info, '\0', sizeof(file_info_t));
67,113✔
1510

1511
   info->size = st->st_size;
67,113✔
1512

1513
   if (S_ISDIR(st->st_mode))
67,113✔
1514
      info->type = FILE_DIR;
117✔
1515
   else if (!S_ISREG(st->st_mode))
66,996✔
1516
      info->type = FILE_FIFO;
54✔
1517
   else
1518
      info->type = FILE_REGULAR;
1519

1520
   info->mtime = st->st_mtime * UINT64_C(1000000000);
67,113✔
1521

1522
#if defined HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
1523
   info->mtime += st->st_mtimespec.tv_nsec;
1524
#elif defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
1525
   info->mtime += st->st_mtim.tv_nsec;
67,113✔
1526
#endif
1527
}
67,113✔
1528
#endif  // !__MINGW32__
1529

1530
bool get_file_info(const char *path, file_info_t *info)
31,975✔
1531
{
1532
#ifdef __MINGW32__
1533
   HANDLE handle = CreateFile(
1534
        path, FILE_READ_ATTRIBUTES,
1535
        FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1536
        NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1537

1538
   if (handle == INVALID_HANDLE_VALUE)
1539
      return false;
1540

1541
   fill_file_info(info, handle);
1542

1543
   if (!CloseHandle(handle))
1544
      fatal_errno("CloseHandle");
1545

1546
   return true;
1547
#else
1548
   struct stat st;
31,975✔
1549
   if (stat(path, &st) == 0) {
31,975✔
1550
      fill_file_info(info, &st);
7,000✔
1551
      return true;
7,000✔
1552
   }
1553
   else
1554
      return false;
1555
#endif
1556
}
1557

1558
bool get_handle_info(int fd, file_info_t *info)
60,113✔
1559
{
1560
#ifdef __MINGW32__
1561
   HANDLE handle = (HANDLE)_get_osfhandle(fd);
1562
   fill_file_info(info, handle);
1563
   return true;
1564
#else
1565
   struct stat st;
60,113✔
1566
   if (fstat(fd, &st) == 0) {
60,113✔
1567
      fill_file_info(info, &st);
60,113✔
1568
      return true;
60,113✔
1569
   }
1570
   else
1571
      return false;
1572
#endif
1573
}
1574

1575
void run_program(const char *const *args)
×
1576
{
1577
#if defined __CYGWIN__ || defined __MINGW32__
1578
   int status = spawnvp(_P_WAIT, args[0], (char *const *)args);
1579
#else  // __CYGWIN__
1580
   pid_t pid = fork();
×
1581
   int status = 0;
×
1582
   if (pid == 0) {
×
1583
      execvp(args[0], (char *const *)args);
×
1584
      fatal_errno("execv");
×
1585
   }
1586
   else if (pid > 0) {
×
1587
      if (waitpid(pid, &status, 0) != pid)
×
1588
         fatal_errno("waitpid");
×
1589

1590
      status = WEXITSTATUS(status);
×
1591
   }
1592
   else
1593
      fatal_errno("fork");
×
1594
#endif  // __CYGWIN__
1595

1596
   if (status != 0) {
×
1597
      LOCAL_TEXT_BUF tb = tb_new();
×
1598
      for (size_t i = 0; args[i] != NULL; i++)
×
1599
         tb_printf(tb, "%s%s", i > 0 ? " " : "", args[i]);
×
1600
      fatal("$bold$%s$$ failed with status %d", tb_get(tb), status);
×
1601
   }
1602
}
×
1603

1604
char *nvc_temp_file(void)
×
1605
{
1606
   static const char *try[] = { "TMPDIR", "TEMP", "TMP" };
×
1607
   const char *tmpdir = NULL;
×
1608
   for (int i = 0; tmpdir == NULL && i < ARRAY_LEN(try); i++)
×
1609
      tmpdir = getenv(try[i]);
×
1610

1611
#ifdef __MINGW32__
1612
   char *buf = xasprintf("%s\\nvc-XXXXXX", tmpdir ?: ".");
1613
   return _mktemp(buf);
1614
#else
1615
   char *buf = xasprintf("%s/nvc-XXXXXX", tmpdir ?: "/tmp");
×
1616
   int fd = mkstemp(buf);
×
1617
   if (fd < 0)
×
1618
      fatal_errno("mkstemp");
×
1619
   close(fd);
×
1620
   return buf;
×
1621
#endif
1622
}
1623

1624
void file_read_lock(int fd)
27,087✔
1625
{
1626
#ifdef __MINGW32__
1627
   HANDLE hf = (HANDLE)_get_osfhandle(fd);
1628

1629
   LARGE_INTEGER li;
1630
   li.QuadPart = _filelengthi64(fd);
1631

1632
   OVERLAPPED ovlp;
1633
   memset(&ovlp, 0, sizeof ovlp);
1634

1635
   if (!LockFileEx(hf, 0, 0, li.LowPart, li.HighPart, &ovlp))
1636
      fatal_errno("LockFileEx");
1637
#else
1638
   if (flock(fd, LOCK_SH) < 0)
27,087✔
1639
      fatal_errno("flock");
×
1640
#endif
1641
}
27,087✔
1642

1643
void file_write_lock(int fd)
8,280✔
1644
{
1645
#ifdef __MINGW32__
1646
   HANDLE hf = (HANDLE)_get_osfhandle(fd);
1647

1648
   LARGE_INTEGER li;
1649
   li.QuadPart = _filelengthi64(fd);
1650

1651
   OVERLAPPED ovlp;
1652
   memset(&ovlp, 0, sizeof ovlp);
1653

1654
   if (!LockFileEx(hf, LOCKFILE_EXCLUSIVE_LOCK, 0,
1655
                   li.LowPart, li.HighPart, &ovlp))
1656
      fatal_errno("LockFileEx");
1657
#else
1658
   if (flock(fd, LOCK_EX) < 0)
8,280✔
1659
      fatal_errno("flock");
×
1660
#endif
1661
}
8,280✔
1662

1663
void file_unlock(int fd)
35,358✔
1664
{
1665
#ifdef __MINGW32__
1666
   HANDLE hf = (HANDLE)_get_osfhandle(fd);
1667

1668
   LARGE_INTEGER li;
1669
   li.QuadPart = _filelengthi64 (fd);
1670

1671
   UnlockFile(hf, 0, 0, li.LowPart, li.HighPart);
1672
#else
1673
   if (flock(fd, LOCK_UN) < 0)
35,358✔
1674
      fatal_errno("flock");
×
1675
#endif
1676
}
35,358✔
1677

1678
void *map_file(int fd, size_t size)
32,987✔
1679
{
1680
#ifdef __MINGW32__
1681
   HANDLE handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), NULL,
1682
                                     PAGE_READONLY, 0, size, NULL);
1683
   if (!handle)
1684
      fatal_errno("CreateFileMapping");
1685

1686
   void *ptr = MapViewOfFileEx(handle, FILE_MAP_READ, 0,
1687
                               0, (SIZE_T) size, (LPVOID) NULL);
1688
   CloseHandle(handle);
1689
   if (ptr == NULL)
1690
      fatal_errno("MapViewOfFileEx");
1691
#else
1692
   void *ptr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
32,987✔
1693
   if (ptr == MAP_FAILED)
32,987✔
1694
      fatal_trace("mmap failed to map %zu byte file", size);
1695
#endif
1696
   return ptr;
32,987✔
1697
}
1698

1699
void unmap_file(void *ptr, size_t size)
27,785✔
1700
{
1701
#ifdef __MINGW32__
1702
   if (!UnmapViewOfFile((LPCVOID) ptr))
1703
      fatal_errno("UnmapViewOfFile");
1704
#else
1705
   munmap(ptr, size);
27,785✔
1706
#endif
1707
}
27,785✔
1708

1709
void make_dir(const char *fmt, ...)
4,302✔
1710
{
1711
   va_list ap;
4,302✔
1712
   va_start(ap, fmt);
4,302✔
1713
   char *path LOCAL = xvasprintf(fmt, ap);
8,604✔
1714
   va_end(ap);
4,302✔
1715

1716
#ifdef __MINGW32__
1717
   if (!CreateDirectory(path, NULL) && (GetLastError() != ERROR_ALREADY_EXISTS))
1718
      fatal_errno("mkdir: %s", path);
1719
#else
1720
   if (mkdir(path, 0777) != 0 && errno != EEXIST)
4,302✔
1721
      fatal_errno("mkdir: %s", path);
×
1722
#endif
1723
}
4,302✔
1724

1725
FILE *create_file(const char *fmt, ...)
936✔
1726
{
1727
   va_list ap;
936✔
1728
   va_start(ap, fmt);
936✔
1729
   char *path LOCAL = xvasprintf(fmt, ap);
936✔
1730
   va_end(ap);
936✔
1731

1732
   FILE *f = fopen(path, "w");
936✔
1733
   if (f == NULL)
936✔
1734
      fatal_errno("cannot create %s", path);
×
1735

1736
   return f;
936✔
1737
}
1738

1739
uint64_t get_timestamp_ns(void)
33,765,793✔
1740
{
1741
#if defined __MINGW32__
1742
   static volatile uint64_t freq;
1743
   if (load_acquire(&freq) == 0) {
1744
      LARGE_INTEGER tmp;
1745
      if (!QueryPerformanceFrequency(&tmp))
1746
         fatal_errno("QueryPerformanceFrequency");
1747
      store_release(&freq, tmp.QuadPart);
1748
   }
1749

1750
   LARGE_INTEGER ticks;
1751
   if (!QueryPerformanceCounter(&ticks))
1752
      fatal_errno("QueryPerformanceCounter");
1753
   return (double)ticks.QuadPart * (1e9 / (double)freq);
1754
#else
1755
   struct timespec ts;
33,765,793✔
1756
   if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
33,765,793✔
1757
      fatal_errno("clock_gettime");
×
1758
   return ts.tv_nsec + (ts.tv_sec * UINT64_C(1000000000));
33,765,793✔
1759
#endif
1760
}
1761

1762
uint64_t get_timestamp_us(void)
33,765,793✔
1763
{
1764
   return get_timestamp_ns() / 1000;
33,765,793✔
1765
}
1766

1767
timestamp_t get_real_time(void)
16,946✔
1768
{
1769
#if defined __MINGW32__
1770
   FILETIME ft;
1771
   GetSystemTimeAsFileTime(&ft);
1772

1773
   return file_time_to_nanos(&ft);
1774
#else
1775
   struct timespec ts;
16,946✔
1776
   if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
16,946✔
1777
      fatal_errno("clock_gettime");
×
1778

1779
   return (uint64_t)ts.tv_nsec + ((uint64_t)ts.tv_sec * UINT64_C(1000000000));
16,946✔
1780
#endif
1781
}
1782

1783
void open_pipe(int *rfd, int *wfd)
×
1784
{
1785
   int fds[2];
×
1786
#ifdef __MINGW32__
1787
   const int rc = _pipe(fds, 4096, _O_BINARY);
1788
#else
1789
   const int rc = pipe(fds) < 0;
×
1790
#endif
1791
   if (rc < 0)
×
1792
      fatal_errno("failed to create pipe");
1793

1794
   *rfd = fds[0];
×
1795
   *wfd = fds[1];
×
1796
}
×
1797

1798
#if defined _WIN32 || defined __CYGWIN__
1799
static struct {
1800
   char illegal;
1801
   const char *rep;
1802
} symbol_replacements[] = {
1803
   { '(', "_lp_"   },
1804
   { ')', "_rp_"   },
1805
   { '"', "_q_"    },
1806
   { '[', "_ls_"   },
1807
   { ']', "_rs_"   },
1808
   { '*', "_mult_" },
1809
   { '+', "_plus_" },
1810
   { '=', "_eq_"   },
1811
   { '\\', "_bs_"  },
1812
};
1813

1814
static text_buf_t *safe_symbol_win32(const char *text)
1815
{
1816
   text_buf_t *tb = tb_new();
1817

1818
   for (const char *p = text; *p != '\0'; p++) {
1819
      bool replaced = false;
1820
      for (size_t j = 0; j < ARRAY_LEN(symbol_replacements); j++) {
1821
         if (*p == symbol_replacements[j].illegal) {
1822
            tb_cat(tb, symbol_replacements[j].rep);
1823
            replaced = true;
1824
            break;
1825
         }
1826
      }
1827

1828
      if (!replaced)
1829
         tb_append(tb, *p);
1830
   }
1831

1832
   return tb;
1833
}
1834

1835
#endif
1836

1837
text_buf_t *safe_symbol(ident_t id)
×
1838
{
1839
   // Return a string that is safe to use as a symbol name on this platform
1840

1841
   text_buf_t *tb = tb_new();
×
1842
   tb_istr(tb, id);
×
1843

1844
#if defined _WIN32 || defined __CYGWIN__
1845
   if (strpbrk(tb_get(tb), "()\"[]*+=\\") == NULL)
1846
      return tb;
1847
   else {
1848
      text_buf_t *new = safe_symbol_win32(tb_get(tb));
1849
      tb_free(tb);
1850
      return new;
1851
   }
1852
#else
1853
   return tb;
×
1854
#endif
1855
}
1856

1857
void __cleanup_array(void *ptr)
177,697✔
1858
{
1859
   A(void *) *a = ptr;
177,697✔
1860
   ACLEAR(*a);
177,697✔
1861
}
177,697✔
1862

1863
void __array_resize_slow(void **ptr, uint32_t *limit, uint32_t count,
1,626,987✔
1864
                         size_t size)
1865
{
1866
   if (count == 0) {
1,626,987✔
1867
      free(*ptr);
×
1868
      *ptr = NULL;
×
1869
      *limit = 0;
×
1870
   }
1871
   else {
1872
      if (*limit == 0)
1,626,987✔
1873
         *limit = count;  // Setting the initial size of the array
1,424,209✔
1874
      else
1875
         *limit = next_power_of_2(count);
202,778✔
1876
      *ptr = xrealloc_array(*ptr, *limit, size);
1,626,987✔
1877
   }
1878
}
1,626,987✔
1879

1880
bool get_exe_path(text_buf_t *tb)
4,917✔
1881
{
1882
#if defined __linux__
1883
   char buf[PATH_MAX];
4,917✔
1884
   ssize_t nchars = readlink("/proc/self/exe", buf, sizeof(buf));
4,917✔
1885
   if (nchars > 0) {   // Does not append '\0'
4,917✔
1886
      tb_catn(tb, buf, nchars);
4,917✔
1887
      return true;
4,917✔
1888
   }
1889
#elif defined __APPLE__
1890
   char buf[PATH_MAX];
1891
   if (proc_pidpath(getpid(), buf, sizeof(buf)) > 0) {
1892
      tb_cat(tb, buf);
1893
      return true;
1894
   }
1895
#elif defined __FreeBSD__
1896
   char buf[PATH_MAX];
1897
   size_t size = sizeof(buf);
1898
   const int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
1899
   if (sysctl(name, ARRAY_LEN(name), buf, &size, NULL, 0) == 0) {
1900
      tb_catn(tb, buf, size);
1901
      return true;
1902
   }
1903
#elif defined __MINGW32__
1904
   HANDLE hProc = GetCurrentProcess();
1905
   char buf[PATH_MAX];
1906
   DWORD size = sizeof(buf);
1907
   if (QueryFullProcessImageNameA(hProc, 0, buf, &size)) {
1908
      tb_cat(tb, buf);
1909
      return true;
1910
   }
1911
#endif
1912
   return false;
1913
}
1914

1915
#if defined __MINGW32__
1916
static void get_relative_prefix(text_buf_t *tb)
1917
{
1918
   if (get_exe_path(tb)) {
1919
      int len = tb_len(tb);
1920
      const char *str = tb_get(tb);
1921
      for (int i = 0; i < 2; i++) {
1922
         do {
1923
            len--;
1924
         } while (str[len] != DIR_SEP[0]);
1925
      }
1926
      tb_trim(tb, len);
1927
   }
1928
   else
1929
      fatal("failed to read executable path");
1930
}
1931
#endif
1932

1933
void get_libexec_dir(text_buf_t *tb)
×
1934
{
1935
#if defined __MINGW32__
1936
   get_relative_prefix(tb);
1937
   tb_cat(tb, DIR_SEP "libexec" DIR_SEP "nvc");
1938
#else
1939
   tb_cat(tb, LIBEXECDIR);
×
1940
#endif
1941
}
×
1942

1943
void get_lib_dir(text_buf_t *tb)
5,125✔
1944
{
1945
#if defined __MINGW32__
1946
   get_relative_prefix(tb);
1947
   tb_cat(tb, DIR_SEP "lib" DIR_SEP "nvc");
1948
#else
1949
   tb_cat(tb, LIBDIR);
5,125✔
1950
#endif
1951
}
5,125✔
1952

1953
void get_data_dir(text_buf_t *tb)
23✔
1954
{
1955
#if defined __MINGW32__
1956
   get_relative_prefix(tb);
1957
   tb_cat(tb, DIR_SEP "share" DIR_SEP "nvc");
1958
#else
1959
   tb_cat(tb, DATADIR);
23✔
1960
#endif
1961
}
23✔
1962

1963
bool is_absolute_path(const char *path)
24✔
1964
{
1965
   if (path[0] == DIR_SEP[0] || path[0] == '/')
24✔
1966
      return true;
24✔
1967

1968
#ifdef __MINGW32__
1969
   if (isalpha((int)path[0]) && path[1] == ':')
1970
      return true;
1971
#endif
1972

1973
   return false;
1974
}
1975

1976
void get_relative_path(text_buf_t *tb, const char *from, const char *to)
240✔
1977
{
1978
#ifdef __MINGW32__
1979
    char abs_from[MAX_PATH], abs_to[MAX_PATH];
1980
    if (from == NULL || !_fullpath(abs_from, from, MAX_PATH))
1981
       goto fallback;
1982
    if (!_fullpath(abs_to, to, MAX_PATH))
1983
       goto fallback;
1984

1985
    char buffer[MAX_PATH];
1986
    if (!PathRelativePathToA(buffer, abs_from, FILE_ATTRIBUTE_DIRECTORY,
1987
                             abs_to, 0))
1988
       goto fallback;
1989

1990
    if (strncmp(buffer, ".\\", 2) == 0)
1991
       tb_cat(tb, buffer + 2);   // Strip leading .\ prefix
1992
    else
1993
       tb_cat(tb, buffer);
1994
#else
1995
   char abs_from[PATH_MAX], abs_to[PATH_MAX];
240✔
1996
   if (from == NULL || realpath(from, abs_from) == NULL)
450✔
1997
      goto fallback;
30✔
1998
   else if (realpath(to, abs_to) == NULL)
210✔
1999
      goto fallback;
×
2000

2001
   A(char *) from_parts = AINIT;
210✔
2002
   A(char *) to_parts = AINIT;
210✔
2003

2004
   for (char *tok = strtok(abs_from, "/"); tok; tok = strtok(NULL, "/"))
963✔
2005
      APUSH(from_parts, tok);
753✔
2006

2007
   for (char *tok = strtok(abs_to, "/"); tok; tok = strtok(NULL, "/"))
1,470✔
2008
      APUSH(to_parts, tok);
1,260✔
2009

2010
   int common = 0;
2011
   while (common < from_parts.count && common < to_parts.count
753✔
2012
          && strcmp(from_parts.items[common], to_parts.items[common]) == 0)
1,716✔
2013
      common++;
753✔
2014

2015
   for (int i = common; i < from_parts.count; i++) {
210✔
2016
      tb_cat(tb, "..");
×
2017
      if (i + 1 < from_parts.count || to_parts.count > common)
×
2018
         tb_cat(tb, "/");
×
2019
   }
2020

2021
   for (int i = common; i < to_parts.count; i++) {
717✔
2022
      tb_cat(tb, to_parts.items[i]);
507✔
2023
      if (i + 1 < to_parts.count)
507✔
2024
         tb_cat(tb, "/");
297✔
2025
   }
2026

2027
   ACLEAR(from_parts);
210✔
2028
   ACLEAR(to_parts);
210✔
2029
#endif
2030

2031
   if (tb_len(tb) == 0)
210✔
2032
      goto fallback;
×
2033

2034
   return;
210✔
2035

2036
 fallback:
30✔
2037
   tb_cat(tb, to);
30✔
2038
}
2039

2040
void progress(const char *fmt, ...)
12,967✔
2041
{
2042
   if (opt_get_int(OPT_VERBOSE)) {
12,967✔
UNCOV
2043
      va_list ap;
×
UNCOV
2044
      va_start(ap, fmt);
×
UNCOV
2045
      char *msg LOCAL = xvasprintf(fmt, ap);
×
UNCOV
2046
      va_end(ap);
×
2047

UNCOV
2048
      static nvc_rusage_t last_ru;
×
2049

UNCOV
2050
      nvc_rusage_t ru;
×
UNCOV
2051
      nvc_rusage(&ru);
×
2052

UNCOV
2053
      const double conc = (double)(ru.user + ru.sys) / ru.ms;
×
2054

UNCOV
2055
      if (!isinf(conc) && conc > 1.1)
×
2056
         notef("%s [%ums %.1fx %+dkB]", msg, ru.ms, conc,
×
2057
               ru.rss - last_ru.rss);
×
2058
      else
UNCOV
2059
         notef("%s [%ums %+dkB]", msg, ru.ms, ru.rss - last_ru.rss);
×
2060

UNCOV
2061
      last_ru = ru;
×
2062
   }
2063
}
12,967✔
2064

2065
unsigned nvc_nprocs(void)
5,702✔
2066
{
2067
#if defined _WIN32
2068
   SYSTEM_INFO sysinfo;
2069
   GetSystemInfo(&sysinfo);
2070

2071
   return sysinfo.dwNumberOfProcessors;
2072
#elif defined _SC_NPROCESSORS_ONLN
2073
   long count = sysconf(_SC_NPROCESSORS_ONLN);
5,702✔
2074
   if (count == -1)
5,702✔
2075
      fatal_errno("sysconf(_SC_NPROCESSORS_ONLN)");
×
2076

2077
#if defined __linux__ && defined HAVE_GETTID
2078
   // Restrict to the number of CPUs we are allowed to run on
2079
   cpu_set_t s;
5,702✔
2080
   if (sched_getaffinity(gettid(), sizeof(cpu_set_t), &s) == 0)
5,702✔
2081
      return MAX(1, MIN(count, CPU_COUNT(&s)));
5,702✔
2082
#endif
2083

2084
   return count;
×
2085
#else
2086
#warning Cannot detect number of processors on this platform
2087
   return 1;
2088
#endif
2089
}
2090

2091
void capture_registers(struct cpu_state *cpu)
834✔
2092
{
2093
#if defined HAVE_GETCONTEXT
2094
   ucontext_t uc;
834✔
2095
   if (getcontext(&uc) != 0)
834✔
2096
      fatal_errno("getcontext");
×
2097

2098
   fill_cpu_state(cpu, &uc);
834✔
2099
#elif defined __MINGW32__
2100
   CONTEXT context;
2101
   RtlCaptureContext(&context);
2102
   fill_cpu_state(cpu, &context);
2103
#elif defined HAVE_PTHREAD
2104
   assert(atomic_load(&thread_regs) == NULL);
2105
   atomic_store(&thread_regs, cpu);
2106

2107
   if (pthread_kill(pthread_self(), SIGUSR2) != 0)
2108
      fatal_errno("pthread_kill");
2109

2110
   // Registers filled in by signal_handler
2111
   if (atomic_load(&thread_regs) != NULL)
2112
      fatal_trace("signal handler did not capture thread registers");
2113
#else
2114
#error cannot capture registers on this platform
2115
#endif
2116
}
834✔
2117

2118
void add_fault_handler(fault_fn_t fn, void *context)
8,134✔
2119
{
2120
   fault_handler_t *h = xmalloc(sizeof(fault_handler_t));
8,134✔
2121
   h->next    = fault_handlers;
8,134✔
2122
   h->fn      = fn;
8,134✔
2123
   h->context = context;
8,134✔
2124

2125
   fault_handlers = h;
8,134✔
2126
}
8,134✔
2127

2128
void remove_fault_handler(fault_fn_t fn, void *context)
2,799✔
2129
{
2130
   for (fault_handler_t **p = &fault_handlers; *p; p = &((*p)->next)) {
2,803✔
2131
      if ((*p)->fn == fn && (*p)->context == context) {
2,803✔
2132
         fault_handler_t *tmp = (*p)->next;
2,799✔
2133
         free(*p);
2,799✔
2134
         *p = tmp;
2,799✔
2135
         return;
2,799✔
2136
      }
2137
   }
2138

2139
   fatal_trace("no fault handler for %p with context %p", fn, context);
2140
}
2141

2142
void check_cpu_features(void)
4,625✔
2143
{
2144
#ifdef HAVE_POPCNT
2145
   if (!__builtin_cpu_supports("popcnt"))
4,625✔
2146
      fatal("CPU is missing support for POPCNT instruction");
×
2147
#endif
2148
}
4,625✔
2149

2150
#ifdef DEBUG
2151
void should_not_reach_here(void)
2152
{
2153
   fatal_trace("should not reach here");
2154
}
2155
#endif
2156

2157
mem_pool_t *pool_new(void)
25,141✔
2158
{
2159
   mem_pool_t *mp = xcalloc(sizeof(mem_pool_t));
25,141✔
2160
   mp->pageshift = POOL_PAGE_MIN;
25,141✔
2161

2162
   return mp;
25,141✔
2163
}
2164

2165
void pool_free(mem_pool_t *mp)
75,021✔
2166
{
2167
   if (mp == NULL)
75,021✔
2168
      return;
2169

2170
   for (pool_page_t *p = mp->pages, *tmp; p != NULL; p = tmp) {
42,901✔
2171
      tmp = p->next;
18,731✔
2172
      free(p);
18,731✔
2173
   }
2174

2175
   free(mp);
24,170✔
2176
}
2177

2178
static pool_page_t *pool_page_new(mem_pool_t *mp, size_t reqsz, size_t align)
19,414✔
2179
{
2180
   const size_t hdrsz = ALIGN_UP(sizeof(page_header_t), align);
19,414✔
2181
   const size_t minsz = next_power_of_2(reqsz + hdrsz);
19,414✔
2182
   const size_t allocsz = MAX(1 << mp->pageshift, minsz);
19,414✔
2183
   page_header_t *p = xmalloc(allocsz);
19,414✔
2184
   p->next  = mp->pages;
19,414✔
2185
   p->alloc = hdrsz;
19,414✔
2186
   p->size  = allocsz;
19,414✔
2187

2188
   ASAN_POISON(p + hdrsz, allocsz - hdrsz);
19,414✔
2189

2190
   if (mp->pageshift < POOL_PAGE_MAX)
19,414✔
2191
      mp->pageshift++;
19,414✔
2192

2193
   return (mp->pages = p);
19,414✔
2194
}
2195

2196
static void *pool_aligned_malloc(mem_pool_t *mp, size_t size, size_t align)
490,117✔
2197
{
2198
   assert(is_power_of_2(align));
490,117✔
2199

2200
   pool_page_t *page;
490,117✔
2201
   if (mp->pages == NULL)
490,117✔
2202
      page = pool_page_new(mp, size, align);
16,718✔
2203
   else {
2204
      const size_t base = ALIGN_UP(mp->pages->alloc + POOL_REDZONE, align);
473,399✔
2205
      if (base + size > mp->pages->size)
473,399✔
2206
         page = pool_page_new(mp, size, align);
2,696✔
2207
      else {
2208
         page = mp->pages;
470,703✔
2209
         page->alloc = base;
470,703✔
2210
      }
2211
   }
2212

2213
   assert((mp->pages->alloc & (align - 1)) == 0);
490,117✔
2214
   assert(mp->pages->alloc + size <= mp->pages->size);
490,117✔
2215
   assert(mp->pages->alloc >= sizeof(page_header_t));
490,117✔
2216

2217
   void *ptr = (void *)mp->pages + mp->pages->alloc;
490,117✔
2218
   mp->pages->alloc += size;
490,117✔
2219

2220
   ASAN_UNPOISON(ptr, size);
490,117✔
2221
   return ptr;
490,117✔
2222
}
2223

2224
void *pool_malloc(mem_pool_t *mp, size_t size)
422,176✔
2225
{
2226
   return pool_aligned_malloc(mp, size, POOL_MIN_ALIGN);
422,176✔
2227
}
2228

2229
void *pool_malloc_flex(mem_pool_t *mp, size_t fixed, size_t nelems,
394,865✔
2230
                       size_t size)
2231
{
2232
   size_t bytes;
394,865✔
2233
   if (__builtin_mul_overflow(nelems, size, &bytes))
394,865✔
2234
      fatal_trace("array size overflow: requested %zd * %zd bytes",
2235
                  nelems, size);
2236

2237
   return pool_malloc(mp, fixed + bytes);
394,865✔
2238
}
2239

2240
void *pool_malloc_array(mem_pool_t *mp, size_t nelems, size_t size)
2,593✔
2241
{
2242
   return pool_malloc_flex(mp, 0, nelems, size);
2,593✔
2243
}
2244

2245
void *pool_calloc(mem_pool_t *mp, size_t size)
67,941✔
2246
{
2247
   void *ptr = pool_aligned_malloc(mp, size, POOL_MIN_ALIGN);
67,941✔
2248
   memset(ptr, '\0', size);
67,941✔
2249
   return ptr;
67,941✔
2250
}
2251

2252
void pool_stats(mem_pool_t *mp, size_t *alloc, size_t *npages)
7,734✔
2253
{
2254
   *npages = 0;
7,734✔
2255
   *alloc = 0;
7,734✔
2256

2257
   for (pool_page_t *p = mp->pages; p != NULL; p = p->next) {
12,118✔
2258
      *npages += 1;
4,384✔
2259
      *alloc += p->alloc - sizeof(pool_page_t);
4,384✔
2260
   }
2261
}
7,734✔
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