Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
Loading...
Searching...
No Matches
errors.c
Go to the documentation of this file.
1/* -*- C -*-
2 * Serene programming language
3 * Copyright (C) 2019-2026 Sameer Rahmani <lxsameer@lxsameer.com>
4 *
5 * This library is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19#include "serene/rt/errors.h"
20
21#if defined(__unix__) || defined(__APPLE__)
22# include "errno.h"
23#endif
24#include <stdio.h>
25
26#include "serene/rt/context.h"
27#include "serene/utils.h"
29
30#ifdef SRN_WITH_SERENE
31# include "serene/rt/core.h"
32#endif
33
34// TODO(lxsameer): Move this function to an auto generated file alongside
35// the actual srn_error_tag_t, something like tablegen or something.
36static const char *srn_error_tag_name(srn_error_tag_t tag) {
37 switch (tag) {
38 case ABSURD:
39 return "ABSURD";
40 case NOT_IMPLEMENTED:
41 return "NOT_IMPLEMENTED";
43 return "SEQ_LIMIT_REACHED";
45 return "AL_LIMIT_REACHED";
47 return "SM_BUFFER_ID_EXHAUSTED";
49 return "INDEX_OUT_OF_BOUND";
50 case CORRUPTED_SEQ:
51 return "CORRUPTED_SEQ";
53 return "CORRUPTED_ARRAY_LIST";
55 return "FAILED_TO_ADD_MODULE";
57 return "JIT_IR_PARSE_FAILED";
59 return "JIT_SYMBOL_NOT_FOUND";
61 return "JIT_SYMBOL_ALREADY_DEFINED";
63 return "EMPTY_NAMESPACE_NAME";
65 return "EMPTY_SYMBOL_NAME";
67 return "STRING_LENGTH_LIMIT_EXCEEDED";
68 case WRONG_RANGE:
69 return "WRONG_RANGE";
71 return "EPOLL_INIT_FAILED";
73 return "EPOLL_EVENT_FD_FAILED";
75 return "EPOLL_FAILED_TO_ADD_FD";
77 return "CONNECTION_REFUSED";
79 return "CONNECTION_RESET";
81 return "CONNECTION_ABORTED";
82 case BROKEN_PIPE:
83 return "BROKEN_PIPE";
84 case BAD_DESCRIPTOR:
85 return "BAD_DESCRIPTOR";
86 case TIMED_OUT:
87 return "TIMED_OUT";
88 case CANCELED:
89 return "CANCELED";
90 case NOT_CONNECTED:
91 return "NOT_CONNECTED";
92 case ADDRESS_IN_USE:
93 return "ADDRESS_IN_USE";
95 return "HOST_UNREACHABLE";
97 return "NETWORK_UNREACHABLE";
99 return "MESSAGE_TOO_LONG";
101 return "TOO_MANY_OPEN_FILES";
103 return "PERMISSION_DENIED";
104 case OUT_OF_MEMORY:
105 return "OUT_OF_MEMORY";
106 case NOT_FOUND:
107 return "NOT_FOUND";
108 case ALREADY_EXISTS:
109 return "ALREADY_EXISTS";
110 case IS_A_DIRECTORY:
111 return "IS_A_DIRECTORY";
112 case NOT_A_DIRECTORY:
113 return "NOT_A_DIRECTORY";
114 case NO_SPACE:
115 return "NO_SPACE";
116 case INVALID_ARGUMENT:
117 return "INVALID_ARGUMENT";
118 case CHANNEL_BUSY:
119 return "CHANNEL_BUSY";
121 return "MEMORY_MAP_FAILED";
122 case IR_PARSE_ERROR:
123 return "IR_PARSE_ERROR";
124 case UNKNOWN_IO_ERROR:
125 return "UNKNOWN_IO_ERROR";
126 }
127 return "UNKNOWN";
128}
129
130// TODO(lxsameer): This is pretty basic, we need to replace it by a better implementation
131// TODO(lxsameer): use LLVMSymbolizer (llvm::symbolize::LLVMSymbolizer) to resolve frame addresses
132// to file and line, not just symbol names.
133char *srn_error_to_string(const srn_context_t *ctx, const srn_error_t *err) {
134 PANIC_IF_NULL(err);
135 const char *tag = srn_error_tag_name(err->tag);
136 const char *msg = err->msg != nullptr ? err->msg : "<null>";
137
138 int header_len = snprintf(nullptr, 0, "[%s]: %s", tag, msg);
139 PANIC_IF(header_len < 0, "srn_error_to_string: formatting failed");
140
141 // Measure the trace lines before allocating so a single allocation covers
142 // the whole string.
143 int trace_len = 0;
144 char sym[256];
145 if (err->layer == SRN_ERR_RUNTIME_LAYER && err->as.frames != nullptr) {
146 for (uint16_t i = 0; i < err->as.frames->count; i++) {
147 void *addr = err->as.frames->return_addrs[i];
148 const char *name = srn_dbg_sym_name(addr, sym, sizeof(sym));
149 if (name != nullptr) {
150 trace_len += snprintf(nullptr, 0, "\n #%u %p %s", i, addr, name);
151 } else {
152 trace_len += snprintf(nullptr, 0, "\n #%u %p", i, addr);
153 }
154 }
155 }
156
157 size_t total = (size_t)header_len + (size_t)trace_len + 1;
158 char *buf = srn_allocate(ctx, total, alignof(char));
159 PANIC_IF_NULL(buf);
160
161 int written = snprintf(buf, total, "[%s]: %s", tag, msg);
162 if (err->layer == SRN_ERR_RUNTIME_LAYER && err->as.frames != nullptr) {
163 for (uint16_t i = 0; i < err->as.frames->count; i++) {
164 void *addr = err->as.frames->return_addrs[i];
165 const char *name = srn_dbg_sym_name(addr, sym, sizeof(sym));
166 if (name != nullptr) {
167 written += snprintf(buf + written, total - (size_t)written, "\n #%u %p %s", i, addr, name);
168 } else {
169 written += snprintf(buf + written, total - (size_t)written, "\n #%u %p", i, addr);
170 }
171 }
172 }
173
174 return buf;
175}
176
177void srn_error_trace_push(const srn_context_t *ctx, srn_error_t *err, void *addr) {
178 if (err->layer != SRN_ERR_RUNTIME_LAYER) {
179 return;
180 }
181
182 if (err->as.frames == nullptr) {
183 err->as.frames = ALLOC(ctx, srn_trace_frames_t);
184 PANIC_IF_NULL(err->as.frames);
185 err->as.frames->count = 0;
186 }
187
188 if (err->as.frames->count >= TRACE_FRAME_DEPTH) {
189 return;
190 }
191
192 err->as.frames->return_addrs[err->as.frames->count++] = addr;
193}
194
196srn_errors_make(const srn_context_t *ctx, srn_error_tag_t tag, const char *msg, void *caller) {
197 srn_error_t *err = ALLOC(ctx, srn_error_t);
198 PANIC_IF_NULL(err);
200 err->tag = tag;
201 err->msg = (char *)msg;
202 err->as.frames = nullptr;
203 srn_error_trace_push(ctx, err, caller);
204 return err;
205}
206
207srn_error_t *srn_errors_from(const srn_context_t *ctx, const srn_error_t *src, void *caller) {
208 PANIC_IF_NULL(src);
210 err->layer = src->layer;
211 err->tag = src->tag;
212 err->msg = src->msg;
213 err->as = src->as;
214
215 if (src->layer == SRN_ERR_RUNTIME_LAYER && src->as.frames != nullptr) {
217 *err->as.frames = *src->as.frames;
218 }
219
220 srn_error_trace_push(ctx, err, caller);
221 return err;
222}
223
225 const srn_context_t *ctx, size_t size, size_t alignment, srn_error_tag_t tag, const char *msg,
226 void *caller
227) {
228 // The ERROR_HEADER convention guarantees `srn_error_t *maybe_error` is the
229 // first member of any fallible T, so offset 0 of the value is that pointer.
230 // Allocate the value and set it to a fresh error; the caller checks it with
231 // HAS_ERROR and never reads the other fields of an errored value.
232 void *value = srn_allocate(ctx, size, alignment);
233 PANIC_IF_NULL(value);
234 *(srn_error_t **)value = srn_errors_make(ctx, tag, msg, caller);
235 return value;
236}
237
238#if defined(__unix__) || defined(__APPLE__)
239srn_error_tag_t srn_errno_to_tag(int errno_value, srn_error_tag_t default_tag) {
240 switch (errno_value) {
241 case ECONNREFUSED:
242 return CONNECTION_REFUSED;
243 case ECONNRESET:
244 return CONNECTION_RESET;
245 case ECONNABORTED:
246 return CONNECTION_ABORTED;
247 case EPIPE:
248 return BROKEN_PIPE;
249 case EBADF:
250 return BAD_DESCRIPTOR;
251 case ETIMEDOUT:
252 return TIMED_OUT;
253 case ECANCELED:
254 return CANCELED;
255 case ENOTCONN:
256 return NOT_CONNECTED;
257 case EADDRINUSE:
258 return ADDRESS_IN_USE;
259 case EHOSTUNREACH:
260 return HOST_UNREACHABLE;
261 case ENETUNREACH:
262 return NETWORK_UNREACHABLE;
263 case EMSGSIZE:
264 return MESSAGE_TOO_LONG;
265 case EMFILE:
266 case ENFILE:
267 return TOO_MANY_OPEN_FILES;
268 case EACCES:
269 case EPERM:
270 return PERMISSION_DENIED;
271 case ENOMEM:
272 case ENOBUFS:
273 return OUT_OF_MEMORY;
274 case ENOENT:
275 return NOT_FOUND;
276 case EEXIST:
277 return ALREADY_EXISTS;
278 case EISDIR:
279 return IS_A_DIRECTORY;
280 case ENOTDIR:
281 return NOT_A_DIRECTORY;
282 case ENOSPC:
283 return NO_SPACE;
284 default:
285 return default_tag;
286 }
287}
288#else
289# error "srn_errno_to_tag has no errno mapping for this target; provide a native mapping."
290#endif
291
292#ifdef SRN_WITH_SERENE
293srn_value_t *srn_errors_make_error(
294 srn_context_t *ctx, srn_metadata_t *metadata, srn_error_tag_t tag, const char *msg
295) {
296 PANIC_IF_NULL(metadata);
297 srn_error_t *err = ALLOC(ctx, srn_error_t);
298 PANIC_IF_NULL(err);
299 err->layer = SRN_ERR_PARSE_LAYER;
300 err->tag = tag;
301 err->msg = (char *)msg;
302 err->as.metadata = metadata;
303 return srn_value_make(ctx, VError, metadata, (void *)err);
304}
305
307srn_errors_err_to_value(srn_context_t *ctx, srn_metadata_t *metadata, srn_error_t *err) {
308 PANIC_IF_NULL(err);
309 PANIC_IF_NULL(ctx);
310 return srn_value_make(ctx, VError, metadata, (void *)err);
311}
312#endif
static atomic_int total
Definition 05_parallel.c:40
void * srn_allocate(const srn_context_t *ctx, size_t size, size_t alignment)
Allocate size bytes aligned to alignment from the context's memory block.
Definition context.c:73
#define ALLOC(ctx, T)
Definition context.h:95
#define ALLOC_OR_PANIC(ctx, T)
Definition context.h:98
srn_value_t * srn_value_make(srn_context_t *ctx, srn_value_tag_t tag, srn_metadata_t *metadata, void *payload)
Creates a new serene value.
Definition core.c:36
@ VError
VError should be last.
Definition core.h:106
srn_error_t * srn_errors_make(const srn_context_t *ctx, srn_error_tag_t tag, const char *msg, void *caller)
Build an error in ctx's memory, tagged tag and carrying msg.
Definition errors.c:196
static const char * srn_error_tag_name(srn_error_tag_t tag)
Definition errors.c:36
void srn_error_trace_push(const srn_context_t *ctx, srn_error_t *err, void *addr)
Append a return address to the trace of a runtime-layer error.
Definition errors.c:177
void * srn_errors_fail(const srn_context_t *ctx, size_t size, size_t alignment, srn_error_tag_t tag, const char *msg, void *caller)
Allocate a size/alignment value in ctx, set its ERROR_HEADER (the first member, srn_error_t *maybe_er...
Definition errors.c:224
srn_error_t * srn_errors_from(const srn_context_t *ctx, const srn_error_t *src, void *caller)
Build an error in ctx's memory that carries the layer, tag, message and payload of src,...
Definition errors.c:207
char * srn_error_to_string(const srn_context_t *ctx, const srn_error_t *err)
Format err as a newly allocated string of the form [TAG]: msg, allocated in ctx.
Definition errors.c:133
Error handling for the runtime.
srn_error_tag_t
The kind of a failure.
Definition errors.h:92
@ AL_LIMIT_REACHED
Definition errors.h:96
@ EPOLL_EVENT_FD_FAILED
Definition errors.h:113
@ NOT_IMPLEMENTED
Definition errors.h:94
@ BROKEN_PIPE
Definition errors.h:121
@ SM_BUFFER_ID_EXHAUSTED
Definition errors.h:97
@ HOST_UNREACHABLE
Definition errors.h:127
@ CORRUPTED_SEQ
Definition errors.h:99
@ CORRUPTED_ARRAY_LIST
Definition errors.h:100
@ EMPTY_NAMESPACE_NAME
Definition errors.h:108
@ JIT_IR_PARSE_FAILED
The JIT could not parse a module's LLVM IR or bitcode.
Definition errors.h:103
@ NETWORK_UNREACHABLE
Definition errors.h:128
@ STRING_LENGTH_LIMIT_EXCEEDED
Definition errors.h:110
@ IS_A_DIRECTORY
Definition errors.h:135
@ INVALID_ARGUMENT
The request itself is malformed (an empty POLL interest mask, for example), so retrying it unchanged ...
Definition errors.h:140
@ CHANNEL_BUSY
The channel already has a full ring's worth of operations in flight.
Definition errors.h:144
@ ABSURD
Definition errors.h:93
@ WRONG_RANGE
Definition errors.h:111
@ JIT_SYMBOL_ALREADY_DEFINED
A JIT symbol definition collided with a name already defined.
Definition errors.h:107
@ PERMISSION_DENIED
Definition errors.h:131
@ CONNECTION_RESET
Definition errors.h:119
@ UNKNOWN_IO_ERROR
Definition errors.h:147
@ TOO_MANY_OPEN_FILES
Definition errors.h:130
@ JIT_SYMBOL_NOT_FOUND
A JIT symbol lookup found no symbol of that name.
Definition errors.h:105
@ NOT_CONNECTED
Definition errors.h:125
@ CONNECTION_REFUSED
Definition errors.h:118
@ FAILED_TO_ADD_MODULE
Definition errors.h:101
@ INDEX_OUT_OF_BOUND
Definition errors.h:98
@ SEQ_LIMIT_REACHED
Definition errors.h:95
@ CONNECTION_ABORTED
Definition errors.h:120
@ ADDRESS_IN_USE
Definition errors.h:126
@ EPOLL_FAILED_TO_ADD_FD
Definition errors.h:114
@ EPOLL_INIT_FAILED
Definition errors.h:112
@ BAD_DESCRIPTOR
Definition errors.h:122
@ OUT_OF_MEMORY
Definition errors.h:132
@ NOT_FOUND
Definition errors.h:133
@ CANCELED
Definition errors.h:124
@ NOT_A_DIRECTORY
Definition errors.h:136
@ TIMED_OUT
Definition errors.h:123
@ IR_PARSE_ERROR
Definition errors.h:146
@ EMPTY_SYMBOL_NAME
Definition errors.h:109
@ MEMORY_MAP_FAILED
Definition errors.h:145
@ MESSAGE_TOO_LONG
Definition errors.h:129
@ ALREADY_EXISTS
Definition errors.h:134
@ NO_SPACE
Definition errors.h:137
srn_error_tag_t srn_errno_to_tag(int errno_value, srn_error_tag_t default_tag)
Map a POSIX errno to error tag.
#define TRACE_FRAME_DEPTH
Definition errors.h:63
@ SRN_ERR_RUNTIME_LAYER
Definition errors.h:151
Portable symbol resolution for error trace printing.
const char * srn_dbg_sym_name(void *addr, char *buf, size_t buflen)
Resolve addr to a symbol name and write it into buf (null-terminated, at most buflen bytes including ...
Definition srn_dbg_sym.c:26
A runtime error, a tag classifying the failure and a human-readable message.
Definition errors.h:173
union srn_error_t::@110141362101221075206211275212023153042233307220 as
srn_error_layer_t layer
Definition errors.h:174
char * msg
Definition errors.h:176
srn_error_tag_t tag
Definition errors.h:175
srn_trace_frames_t * frames
Definition errors.h:181
This is how we create a traceback for the C level code in the runtime lib.
Definition errors.h:164
uint16_t count
Definition errors.h:166
void * return_addrs[TRACE_FRAME_DEPTH]
Definition errors.h:165
#define PANIC_IF_NULL(ptr)
Definition utils.h:66
#define PANIC_IF(cond, msg)
Definition utils.h:59