codegen: introduce gen_mov and use it instead of explicit coding
[ajla.git] / error.c
blobdb6b5edab91d7c161aa3654eeb3f5e3c4394f2fa
1 /*
2 * Copyright (C) 2024 Mikulas Patocka
4 * This file is part of Ajla.
6 * Ajla is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
9 * version.
11 * Ajla is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along with
16 * Ajla. If not, see <https://www.gnu.org/licenses/>.
19 #include "ajla.h"
21 #include "thread.h"
22 #include "os.h"
24 #include <stdio.h>
25 #ifdef HAVE_SIGNAL_H
26 #include <signal.h>
27 #endif
28 #ifdef HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
32 #define ERROR_MSG_LENGTH 256
34 #ifdef NEED_EXPLICIT_ALIASING_BARRIER
35 volatile char alias_val;
36 volatile char * volatile alias_ptr = &alias_val;
37 #endif
39 #include "error.inc"
41 #ifdef DEBUG_TRACE
42 static FILE *trace_file = NULL;
43 #endif
45 struct error_tls {
46 char msg[ERROR_MSG_LENGTH];
47 tls_destructor_t destructor;
49 static struct error_tls thread1;
50 static tls_decl(struct error_tls *, error_tls);
51 bool error_threads_initialized;
53 static void error_per_thread_destructor(tls_destructor_t *destr)
55 struct error_tls *t = get_struct(destr, struct error_tls, destructor);
56 free(t);
59 static char *get_error_tls(void)
61 static struct error_tls *t;
62 if (unlikely(!error_threads_initialized))
63 return thread1.msg;
64 t = tls_get(struct error_tls *, error_tls);
65 if (likely(t != NULL))
66 return t->msg;
67 t = malloc(sizeof(struct error_tls));
68 if (unlikely(!t))
69 return thread1.msg;
70 tls_set(struct error_tls *, error_tls, t);
71 tls_destructor(&t->destructor, error_per_thread_destructor);
72 return t->msg;
75 const char attr_cold *error_decode(ajla_error_t error)
77 const char *e;
78 char *msg;
79 const char *const_msg;
80 const_msg = os_decode_error(error, get_error_tls);
81 if (unlikely(const_msg != NULL))
82 return const_msg;
83 switch (error.error_type) {
84 case AJLA_ERROR_SYSTEM: {
85 if (unlikely(error.error_aux < SYSTEM_ERROR_BASE) ||
86 unlikely(error.error_aux >= SYSTEM_ERROR_N))
87 break;
88 return system_error_codes[error.error_aux - SYSTEM_ERROR_BASE];
90 case AJLA_ERROR_ERRNO: {
91 #ifdef HAVE_STRERROR_R
92 uintptr_t r;
93 msg = get_error_tls();
94 r = (uintptr_t)strerror_r(error.error_aux, msg, ERROR_MSG_LENGTH);
95 if (r >= 4096) {
96 return num_to_ptr(r);
97 } else {
98 if (likely(!r))
99 return msg;
100 goto def;
102 #else
103 e = strerror(error.error_aux);
104 if (unlikely(!e))
105 e = "Unknown error";
106 return e;
107 #endif
109 case AJLA_ERROR_SUBPROCESS: {
110 msg = get_error_tls();
111 if (error.error_aux < 0x100)
112 sprintf(msg, "Subprocess returned an error: %d", error.error_aux);
113 else
114 sprintf(msg, "Subprocess terminated by a signal: %d", error.error_aux - 0x200);
115 return msg;
117 #ifdef HAVE_STRERROR_R
118 def:
119 #endif
120 default: {
121 e = error_ajla_decode(error.error_type);
122 if (unlikely(!e))
123 break;
124 if (!error.error_aux) {
125 return e;
126 } else {
127 msg = get_error_tls();
128 sprintf(msg, "%s: %d", e, error.error_aux);
129 return msg;
133 msg = get_error_tls();
134 sprintf(msg, "invalid error code: %d, %d, %d", error.error_class, error.error_type, error.error_aux);
135 return msg;
138 static char * attr_cold highlight(bool attr_unused on)
140 #if !(defined(OS_DOS) || defined(OS_OS2) || defined(OS_WIN32))
141 #ifdef HAVE_ISATTY
142 if (isatty(2))
143 return on ? "\033[1m" : "\033[0m";
144 #endif
145 #endif
146 return "";
149 static attr_noreturn attr_cold force_dump(void)
151 (void)fprintf(stderr, "\n%sForcing core dump%s\n", highlight(true), highlight(false));
152 (void)fflush(stdout);
153 (void)fflush(stderr);
154 #ifdef DEBUG_TRACE
155 if (trace_file)
156 fflush(trace_file);
157 #endif
158 #if defined(HAVE_SIGNAL_H) && defined(HAVE_RAISE) && !defined(__EMX__)
159 (void)raise(SIGSEGV);
160 #else
161 *(int *)BAD_POINTER_1 = 0;
162 *(int *)num_to_ptr((uintptr_t)-1) = 0;
163 #endif
164 exit(127);
167 static void attr_cold print_msg(FILE *f, const char *m, va_list l, const char *pfx, ...)
169 va_list pfx_l;
170 char attr_unused buffer[4096];
171 #ifdef OS_OS2
172 if (f == stderr) {
173 size_t i, j;
174 ULONG wrt;
175 va_start(pfx_l, pfx);
176 vsnprintf(buffer, sizeof buffer, pfx, pfx_l);
177 va_end(pfx_l);
178 DosWrite(2, buffer, strlen(buffer), &wrt);
179 vsnprintf(buffer, sizeof buffer, m, l);
180 for (i = 0; buffer[i]; i++) {
181 for (j = i; buffer[j] && buffer[j] != '\n'; j++);
182 DosWrite(2, buffer + i, j - i, &wrt);
183 DosWrite(2, "\r\n", 2, &wrt);
184 i = j;
185 if (!buffer[i])
186 break;
188 return;
190 #endif
191 #ifdef HAVE_VSNPRINTF
192 va_start(pfx_l, pfx);
193 vsnprintf(buffer, sizeof buffer, pfx, pfx_l);
194 va_end(pfx_l);
195 if (strlen(buffer) + strlen(m) + 2 <= sizeof(buffer)) {
196 strcat(buffer, m);
197 strcat(buffer, "\n");
198 (void)vfprintf(f, buffer, l);
199 return;
201 #endif
202 va_start(pfx_l, pfx);
203 (void)vfprintf(f, pfx, pfx_l);
204 va_end(pfx_l);
205 (void)vfprintf(f, m, l);
206 (void)fprintf(f, "\n");
209 #ifdef DEBUG_TRACE
210 void attr_cold trace_v(const char *m, va_list l)
212 if (!trace_file)
213 return;
214 print_msg(trace_file, m, l, "TRACE(%d): ", thread_get_id());
215 fflush(trace_file);
217 #endif
219 void attr_cold stderr_msg_v(const char *m, va_list l)
221 print_msg(stderr, m, l, "");
224 void attr_cold debug_v(const char *m, va_list l)
226 print_msg(stderr, m, l, "DEBUG MESSAGE: ");
229 void attr_cold warning_v(const char *m, va_list l)
231 print_msg(stderr, m, l, "WARNING: ");
234 attr_noreturn attr_cold fatal_v(const char *m, va_list l)
236 print_msg(stderr, m, l, "%sFATAL ERROR%s: ", highlight(true), highlight(false));
237 exit(127);
240 attr_noreturn attr_cold internal_v(const char *position, const char *m, va_list l)
242 print_msg(stderr, m, l, "%sINTERNAL ERROR%s at %s: ", highlight(true), highlight(false), position);
243 force_dump();
247 void error_init_multithreaded(void)
249 tls_init(struct error_tls *, error_tls);
250 tls_set(struct error_tls *, error_tls, &thread1);
251 error_threads_initialized = true;
254 void error_done_multithreaded(void)
256 error_threads_initialized = false;
257 tls_done(struct error_tls *, error_tls);
261 void error_init(void)
263 #ifdef SIGPIPE
265 void (*ret)(int);
266 EINTR_LOOP_VAL(ret, SIG_ERR, signal(SIGPIPE, SIG_IGN));
267 if (ret == SIG_ERR) {
268 int er = errno;
269 warning("signal(SIGPIPE, SIG_IGN) failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
272 #endif
273 #ifdef SIGXFSZ
275 void (*ret)(int);
276 EINTR_LOOP_VAL(ret, SIG_ERR, signal(SIGXFSZ, SIG_IGN));
277 if (ret == SIG_ERR) {
278 int er = errno;
279 warning("signal(SIGXFSZ, SIG_IGN) failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
282 #endif
283 if (unlikely(AJLA_ERROR_N - AJLA_ERROR_BASE != (int)n_array_elements(error_codes)))
284 internal(file_line, "error_init: error definitions do not match: %d != %d", AJLA_ERROR_N - AJLA_ERROR_BASE, (int)n_array_elements(error_codes));
285 if (unlikely(SYSTEM_ERROR_N - SYSTEM_ERROR_BASE != (int)n_array_elements(system_error_codes)))
286 internal(file_line, "error_init: system error definitions do not match: %d != %d", SYSTEM_ERROR_N - SYSTEM_ERROR_BASE, (int)n_array_elements(system_error_codes));
287 #if defined(HAVE_SETVBUF) && defined(__MINGW32__)
289 * When we spawn mingw binary from cygwin ssh session, stderr is
290 * bufferred even if it shouldn't be. We need to disable bufferring
291 * explicitly.
293 setvbuf(stderr, NULL, _IONBF, 0);
294 #endif
295 #if 0
296 ULONG resp;
297 HAB os2_hab;
298 HMQ os2_hmq;
299 os2_hab = WinInitialize(0);
300 os2_hmq = WinCreateMsgQueue(os2_hab, 0);
301 resp = WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, "text", "caption", 0, MB_OK | MB_ERROR | MB_APPLMODAL | MB_MOVEABLE);
302 debug("response: %lu", resp);
303 #endif
304 #ifdef DEBUG_TRACE
305 if (!getenv("AJLA_NO_TRACE"))
306 trace_file = fopen("ajla.tr", "w");
307 #if defined(HAVE_SETVBUF)
308 if (0 && trace_file)
309 setvbuf(trace_file, NULL, _IONBF, 0);
310 #endif
311 #endif
314 void error_done(void)
316 #ifdef DEBUG_TRACE
317 if (trace_file)
318 fclose(trace_file);
319 #endif