Skip to content

Commit 5836553

Browse files
committed
Add support for large line numbers in stack traces (up to 2147483647)
Signed-off-by: Paul Guyot <pguyot@kallisys.net>
1 parent bc21d17 commit 5836553

File tree

5 files changed

+2109
-62
lines changed

5 files changed

+2109
-62
lines changed

src/libAtomVM/module.c

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,6 @@
4343
#define LITT_UNCOMPRESSED_SIZE_OFFSET 8
4444
#define LITT_HEADER_SIZE 12
4545

46-
// TODO Constants similar to these are defined in opcodesswitch.h and should
47-
// be refactored so they can be used here, as well.
48-
#define TAG_COMPACT_INT 0x01
49-
#define TAG_COMPACT_ATOM 0x02
50-
#define TAG_EXTENDED_INT 0x09
51-
#define TAG_EXTENDED_ATOM 0x0A
52-
5346
#define CHECK_FREE_SPACE(space, error) \
5447
if ((size_t) ((pos + space) - data) > len) { \
5548
fprintf(stderr, error); \
@@ -449,22 +442,39 @@ static bool module_check_line_refs(Module *mod, const uint8_t **data, size_t len
449442
}
450443
uint8_t tag = *pos;
451444
switch (tag & 0x0F) {
452-
case TAG_COMPACT_INT: {
445+
case COMPACT_INTEGER: {
453446
++i;
454447
++pos;
455448
break;
456449
}
457-
case TAG_EXTENDED_INT: {
450+
case COMPACT_LARGE_INTEGER: {
458451
++pos;
452+
switch (tag & COMPACT_LARGE_IMM_MASK) {
453+
case COMPACT_11BITS_VALUE: {
454+
++pos;
455+
break;
456+
}
457+
case COMPACT_NBITS_VALUE: {
458+
int sz = (tag >> 5) + 2;
459+
if (UNLIKELY(sz > 4)) {
460+
fprintf(stderr, "Invalid line_ref: expected extended int with sz <= 4 (line number <= 2^31)");
461+
return false;
462+
}
463+
pos += sz;
464+
break;
465+
}
466+
default:
467+
fprintf(stderr, "Invalid line_ref: expected extended int -- tag = %u", (unsigned int) tag);
468+
return false;
469+
}
459470
if ((size_t) (pos - *data) > len) {
460471
fprintf(stderr, "Invalid line_ref: expected extended int.\n");
461472
return false;
462473
}
463474
++i;
464-
++pos;
465475
break;
466476
}
467-
case TAG_COMPACT_ATOM: {
477+
case COMPACT_ATOM: {
468478
uint16_t location_ix = ((tag & 0xF0) >> 4);
469479
if (location_ix > mod->locations_count) {
470480
fprintf(stderr, "Invalid line_ref: location_ix = %d is greater than locations_count = %d.\n", (int) location_ix, (int) mod->locations_count);
@@ -473,11 +483,16 @@ static bool module_check_line_refs(Module *mod, const uint8_t **data, size_t len
473483
++pos;
474484
break;
475485
}
476-
case TAG_EXTENDED_ATOM: {
486+
case COMPACT_LARGE_ATOM: {
487+
// We don't support more than 11bits (2048) locations.
488+
if (UNLIKELY((tag & COMPACT_LARGE_IMM_MASK) != COMPACT_11BITS_VALUE)) {
489+
fprintf(stderr, "Invalid line_ref: location_ix is larger than 2048.\n");
490+
return false;
491+
}
477492
uint16_t high_order_3_bits = (tag & 0xE0);
478493
++pos;
479494
if ((size_t) (pos - *data) > len) {
480-
fprintf(stderr, "Invalid line_ref: expected extended int.\n");
495+
fprintf(stderr, "Invalid line_ref: expected extended atom.\n");
481496
return false;
482497
}
483498
uint8_t next_byte = *pos;
@@ -490,7 +505,6 @@ static bool module_check_line_refs(Module *mod, const uint8_t **data, size_t len
490505
break;
491506
}
492507
default:
493-
// TODO handle integer compact encodings > 2048
494508
fprintf(stderr, "Unsupported line_ref tag: %u\n", tag);
495509
return false;
496510
}
@@ -520,7 +534,7 @@ static bool module_check_locations(Module *mod, const uint8_t *data, size_t len)
520534
return true;
521535
}
522536

523-
static bool module_get_line_ref(Module *mod, uint16_t line_ref, uint16_t *out_line, uint16_t *out_location)
537+
static bool module_get_line_ref(Module *mod, uint16_t line_ref, uint32_t *out_line, uint16_t *out_location)
524538
{
525539
// First is undefined
526540
if (line_ref == 0) {
@@ -535,9 +549,9 @@ static bool module_get_line_ref(Module *mod, uint16_t line_ref, uint16_t *out_li
535549
while (i <= mod->line_refs_count) {
536550
uint8_t tag = *pos;
537551
switch (tag & 0x0F) {
538-
case TAG_COMPACT_INT: {
552+
case COMPACT_INTEGER: {
539553
if (i == line_ref) {
540-
uint16_t line_idx = ((tag & 0xF0) >> 4);
554+
uint32_t line_idx = ((tag & 0xF0) >> 4);
541555
*out_line = line_idx;
542556
*out_location = location_ix;
543557
return true;
@@ -546,32 +560,49 @@ static bool module_get_line_ref(Module *mod, uint16_t line_ref, uint16_t *out_li
546560
++pos;
547561
break;
548562
}
549-
case TAG_EXTENDED_INT: {
563+
case COMPACT_LARGE_INTEGER: {
564+
uint32_t line_idx;
565+
switch (tag & COMPACT_LARGE_IMM_MASK) {
566+
case COMPACT_11BITS_VALUE: {
567+
uint16_t high_order_3_bits = (tag & 0xE0);
568+
line_idx = ((high_order_3_bits << 3) | pos[1]);
569+
pos += 2;
570+
break;
571+
}
572+
case COMPACT_NBITS_VALUE: {
573+
pos++;
574+
int sz = (tag >> 5) + 2;
575+
line_idx = 0;
576+
for (int i = 0; i < sz; i++) {
577+
line_idx = line_idx * 256 + pos[i];
578+
}
579+
pos += sz;
580+
break;
581+
}
582+
default:
583+
UNREACHABLE();
584+
}
550585
if (i == line_ref) {
551-
uint16_t high_order_3_bits = (tag & 0xE0);
552-
uint16_t line_idx = ((high_order_3_bits << 3) | pos[1]);
553586
*out_line = line_idx;
554587
*out_location = location_ix;
555588
return true;
556589
}
557-
pos += 2;
558590
++i;
559591
break;
560592
}
561-
case TAG_COMPACT_ATOM: {
593+
case COMPACT_ATOM: {
562594
location_ix = ((tag & 0xF0) >> 4);
563595
++pos;
564596
break;
565597
}
566-
case TAG_EXTENDED_ATOM: {
598+
case COMPACT_LARGE_ATOM: {
567599
uint16_t high_order_3_bits = (tag & 0xE0);
568600
location_ix = ((high_order_3_bits << 3) | pos[1]);
569601
pos += 2;
570602
break;
571603
}
572604
default:
573-
// TODO handle integer compact encodings > 2048
574-
return false;
605+
UNREACHABLE();
575606
}
576607
}
577608

@@ -680,7 +711,7 @@ void module_insert_line_ref_offset(Module *mod, int line_ref, int offset)
680711
list_append(&mod->line_ref_offsets, &ref_offset->head);
681712
}
682713

683-
static bool module_find_line_ref(Module *mod, uint16_t line_ref, uint16_t *line, size_t *filename_len, const uint8_t **filename)
714+
static bool module_find_line_ref(Module *mod, uint16_t line_ref, uint32_t *line, size_t *filename_len, const uint8_t **filename)
684715
{
685716
uint16_t location_ix;
686717
if (UNLIKELY(!module_get_line_ref(mod, line_ref, line, &location_ix))) {
@@ -689,7 +720,7 @@ static bool module_find_line_ref(Module *mod, uint16_t line_ref, uint16_t *line,
689720
return module_get_location(mod, location_ix, filename_len, filename);
690721
}
691722

692-
bool module_find_line(Module *mod, unsigned int offset, uint16_t *line, size_t *filename_len, const uint8_t **filename)
723+
bool module_find_line(Module *mod, unsigned int offset, uint32_t *line, size_t *filename_len, const uint8_t **filename)
693724
{
694725
int i = 0;
695726
struct LineRefOffset *head = GET_LIST_ENTRY(&mod->line_ref_offsets, struct LineRefOffset, head);

src/libAtomVM/module.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ void module_insert_line_ref_offset(Module *mod, int line_ref, int offset);
410410
* @param filename on output the filename or NULL if it's module.erl. Can be NULL.
411411
* @return \c true if the line was found
412412
*/
413-
bool module_find_line(Module *mod, unsigned int offset, uint16_t *line, size_t *filename_len, const uint8_t **filename);
413+
bool module_find_line(Module *mod, unsigned int offset, uint32_t *line, size_t *filename_len, const uint8_t **filename);
414414

415415
/**
416416
* @return true if the module has line information, false, otherwise.

src/libAtomVM/stacktrace.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ term stacktrace_create_raw(Context *ctx, Module *mod, int current_offset, term e
139139
prev_mod = cp_mod;
140140
prev_mod_offset = mod_offset;
141141
if (module_has_line_chunk(cp_mod)) {
142-
uint16_t line;
142+
uint32_t line;
143143
const uint8_t *filename;
144144
size_t filename_len;
145145
if (LIKELY(module_find_line(cp_mod, (unsigned int) mod_offset, &line, &filename_len, &filename))) {
@@ -163,7 +163,7 @@ term stacktrace_create_raw(Context *ctx, Module *mod, int current_offset, term e
163163
prev_mod = cl_mod;
164164
prev_mod_offset = mod_offset;
165165
if (module_has_line_chunk(cl_mod)) {
166-
uint16_t line;
166+
uint32_t line;
167167
const uint8_t *filename;
168168
size_t filename_len;
169169
if (LIKELY(module_find_line(cl_mod, (unsigned int) mod_offset, &line, &filename_len, &filename))) {
@@ -180,7 +180,7 @@ term stacktrace_create_raw(Context *ctx, Module *mod, int current_offset, term e
180180

181181
num_frames++;
182182
if (module_has_line_chunk(mod)) {
183-
uint16_t line;
183+
uint32_t line;
184184
const uint8_t *filename;
185185
size_t filename_len;
186186
if (LIKELY(module_find_line(mod, (unsigned int) current_offset, &line, &filename_len, &filename))) {
@@ -342,7 +342,7 @@ term stacktrace_build(Context *ctx, term *stack_info, uint32_t live)
342342

343343
term aux_data = term_nil();
344344
if (module_has_line_chunk(cp_mod)) {
345-
uint16_t line;
345+
uint32_t line;
346346
const uint8_t *filename;
347347
size_t filename_len;
348348
if (LIKELY(module_find_line(cp_mod, (unsigned int) mod_offset, &line, &filename_len, &filename))) {

tests/erlang_tests/test_stacktrace.erl

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
-module(test_stacktrace).
2121

2222
-export([start/0, maybe_crash/1]).
23+
-export([
24+
throw_with_file_and_line/0,
25+
throw_with_other_file_and_line/0,
26+
throw_with_other_file_and_line_large_value/0
27+
]).
2328

2429
-include("test_stacktrace.hrl").
2530

@@ -36,6 +41,7 @@ start() ->
3641
ok = test_catch(),
3742
ok = maybe_test_filelineno(),
3843
ok = maybe_test_filelineno_other_file(),
44+
ok = maybe_test_filelineno_large(),
3945
0.
4046

4147
test_local_throw() ->
@@ -233,15 +239,15 @@ test_catch() ->
233239
do_some_stuff(Result),
234240
Result.
235241

236-
maybe_test_filelineno() ->
242+
maybe_test_filelineno(Fun) ->
237243
ok =
238244
try
239-
throw_with_file_and_line(),
245+
?MODULE:Fun(),
240246
fail
241247
catch
242248
throw:{File, Line}:Stacktrace ->
243249
[Frame | _] = Stacktrace,
244-
{?MODULE, throw_with_file_and_line, 0, AuxData} = Frame,
250+
{?MODULE, Fun, 0, AuxData} = Frame,
245251
case {get_value(file, AuxData), get_value(line, AuxData)} of
246252
{undefined, undefined} ->
247253
ok;
@@ -262,34 +268,16 @@ maybe_test_filelineno() ->
262268
end
263269
end.
264270

271+
maybe_test_filelineno() ->
272+
maybe_test_filelineno(throw_with_file_and_line).
273+
265274
maybe_test_filelineno_other_file() ->
266-
ok =
267-
try
268-
throw_with_other_file_and_line(),
269-
fail
270-
catch
271-
throw:{File, Line}:Stacktrace ->
272-
[Frame | _] = Stacktrace,
273-
{?MODULE, throw_with_other_file_and_line, 0, AuxData} = Frame,
274-
case {get_value(file, AuxData), get_value(line, AuxData)} of
275-
{undefined, undefined} ->
276-
ok;
277-
{F, L} ->
278-
Ef =
279-
case is_binary(F) of
280-
true ->
281-
erlang:binary_to_list(F);
282-
_ ->
283-
F
284-
end,
285-
case File == Ef andalso Line == L of
286-
true ->
287-
ok;
288-
_ ->
289-
{unexpected_file_line, F, L}
290-
end
291-
end
292-
end.
275+
maybe_test_filelineno(throw_with_other_file_and_line).
276+
277+
% This test actually succeeds even if large line numbers are not supported
278+
% because all line numbers are then disabled.
279+
maybe_test_filelineno_large() ->
280+
maybe_test_filelineno(throw_with_other_file_and_line_large_value).
293281

294282
get_value(_Key, []) ->
295283
undefined;

0 commit comments

Comments
 (0)