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
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/>.
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
;
42 static FILE *trace_file
= NULL
;
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
);
59 static char *get_error_tls(void)
61 static struct error_tls
*t
;
62 if (unlikely(!error_threads_initialized
))
64 t
= tls_get(struct error_tls
*, error_tls
);
65 if (likely(t
!= NULL
))
67 t
= malloc(sizeof(struct error_tls
));
70 tls_set(struct error_tls
*, error_tls
, t
);
71 tls_destructor(&t
->destructor
, error_per_thread_destructor
);
75 const char attr_cold
*error_decode(ajla_error_t error
)
79 const char *const_msg
;
80 const_msg
= os_decode_error(error
, get_error_tls
);
81 if (unlikely(const_msg
!= NULL
))
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
))
88 return system_error_codes
[error
.error_aux
- SYSTEM_ERROR_BASE
];
90 case AJLA_ERROR_ERRNO
: {
91 #ifdef HAVE_STRERROR_R
93 msg
= get_error_tls();
94 r
= (uintptr_t)strerror_r(error
.error_aux
, msg
, ERROR_MSG_LENGTH
);
103 e
= strerror(error
.error_aux
);
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
);
114 sprintf(msg
, "Subprocess terminated by a signal: %d", error
.error_aux
- 0x200);
117 #ifdef HAVE_STRERROR_R
121 e
= error_ajla_decode(error
.error_type
);
124 if (!error
.error_aux
) {
127 msg
= get_error_tls();
128 sprintf(msg
, "%s: %d", e
, error
.error_aux
);
133 msg
= get_error_tls();
134 sprintf(msg
, "invalid error code: %d, %d, %d", error
.error_class
, error
.error_type
, error
.error_aux
);
138 static char * attr_cold
highlight(bool attr_unused on
)
140 #if !(defined(OS_DOS) || defined(OS_OS2) || defined(OS_WIN32))
143 return on
? "\033[1m" : "\033[0m";
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
);
158 #if defined(HAVE_SIGNAL_H) && defined(HAVE_RAISE) && !defined(__EMX__)
159 (void)raise(SIGSEGV
);
161 *(int *)BAD_POINTER_1
= 0;
162 *(int *)num_to_ptr((uintptr_t)-1) = 0;
167 static void attr_cold
print_msg(FILE *f
, const char *m
, va_list l
, const char *pfx
, ...)
170 char attr_unused buffer
[4096];
175 va_start(pfx_l
, pfx
);
176 vsnprintf(buffer
, sizeof buffer
, pfx
, 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
);
191 #ifdef HAVE_VSNPRINTF
192 va_start(pfx_l
, pfx
);
193 vsnprintf(buffer
, sizeof buffer
, pfx
, pfx_l
);
195 if (strlen(buffer
) + strlen(m
) + 2 <= sizeof(buffer
)) {
197 strcat(buffer
, "\n");
198 (void)vfprintf(f
, buffer
, l
);
202 va_start(pfx_l
, pfx
);
203 (void)vfprintf(f
, pfx
, pfx_l
);
205 (void)vfprintf(f
, m
, l
);
206 (void)fprintf(f
, "\n");
210 void attr_cold
trace_v(const char *m
, va_list l
)
214 print_msg(trace_file
, m
, l
, "TRACE(%d): ", thread_get_id());
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));
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
);
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)
266 EINTR_LOOP_VAL(ret
, SIG_ERR
, signal(SIGPIPE
, SIG_IGN
));
267 if (ret
== SIG_ERR
) {
269 warning("signal(SIGPIPE, SIG_IGN) failed: %d, %s", er
, error_decode(error_from_errno(EC_SYSCALL
, er
)));
276 EINTR_LOOP_VAL(ret
, SIG_ERR
, signal(SIGXFSZ
, SIG_IGN
));
277 if (ret
== SIG_ERR
) {
279 warning("signal(SIGXFSZ, SIG_IGN) failed: %d, %s", er
, error_decode(error_from_errno(EC_SYSCALL
, er
)));
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
293 setvbuf(stderr
, NULL
, _IONBF
, 0);
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
);
305 if (!getenv("AJLA_NO_TRACE"))
306 trace_file
= fopen("ajla.tr", "w");
307 #if defined(HAVE_SETVBUF)
309 setvbuf(trace_file
, NULL
, _IONBF
, 0);
314 void error_done(void)