diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/callbacks_test.c | 25 | ||||
-rw-r--r-- | test/cbor_serialize_test.c | 32 | ||||
-rw-r--r-- | test/cbor_stream_decode_test.c | 8 | ||||
-rw-r--r-- | test/float_ctrl_test.c | 6 | ||||
-rw-r--r-- | test/pretty_printer_test.c | 197 | ||||
-rw-r--r-- | test/string_test.c | 67 | ||||
-rw-r--r-- | test/tag_test.c | 23 | ||||
-rw-r--r-- | test/test_allocator.c | 2 |
8 files changed, 338 insertions, 22 deletions
diff --git a/test/callbacks_test.c b/test/callbacks_test.c index d9e90195c819..65c5d37f4399 100644 --- a/test/callbacks_test.c +++ b/test/callbacks_test.c @@ -371,6 +371,30 @@ static void test_invalid_indef_break(void** _CBOR_UNUSED(_state)) { assert_true(res.error.code == CBOR_ERR_SYNTAXERROR); } +static void test_invalid_state_indef_break(void** _CBOR_UNUSED(_state)) { + struct _cbor_stack stack = _cbor_stack_init(); + assert_non_null(_cbor_stack_push(&stack, cbor_new_int8(), /*subitems=*/0)); + struct _cbor_decoder_context context = { + .creation_failed = false, + .syntax_error = false, + .root = NULL, + .stack = &stack, + }; + + cbor_builder_indef_break_callback(&context); + + assert_false(context.creation_failed); + assert_true(context.syntax_error); + assert_size_equal(context.stack->size, 1); + // The stack remains unchanged + cbor_item_t* small_int = stack.top->item; + assert_size_equal(cbor_refcount(small_int), 1); + assert_true(cbor_isa_uint(small_int)); + + cbor_decref(&small_int); + _cbor_stack_pop(&stack); +} + int main(void) { const struct CMUnitTest tests[] = { cmocka_unit_test(test_default_callbacks), @@ -388,6 +412,7 @@ int main(void) { cmocka_unit_test(test_append_array_failure), cmocka_unit_test(test_append_map_failure), cmocka_unit_test(test_invalid_indef_break), + cmocka_unit_test(test_invalid_state_indef_break), }; cmocka_run_group_tests(tests, NULL, NULL); diff --git a/test/cbor_serialize_test.c b/test/cbor_serialize_test.c index d549c37217cc..a2907b149345 100644 --- a/test/cbor_serialize_test.c +++ b/test/cbor_serialize_test.c @@ -218,6 +218,34 @@ static void test_serialize_definite_string(void **_CBOR_UNUSED(_state)) { cbor_decref(&item); } +static void test_serialize_definite_string_4b_header( + void **_CBOR_UNUSED(_state)) { +#if SIZE_MAX > UINT16_MAX + cbor_item_t *item = cbor_new_definite_string(); + const size_t size = (size_t)UINT16_MAX + 1; + unsigned char *data = malloc(size); + memset(data, 0, size); + cbor_string_set_handle(item, data, size); + assert_size_equal(cbor_serialized_size(item), 1 + 4 + size); + cbor_decref(&item); +#endif +} + +static void test_serialize_definite_string_8b_header( + void **_CBOR_UNUSED(_state)) { +#if SIZE_MAX > UINT32_MAX + cbor_item_t *item = cbor_new_definite_string(); + const size_t size = (size_t)UINT32_MAX + 1; + unsigned char *data = malloc(1); + data[0] = '\0'; + cbor_string_set_handle(item, data, 1); + // Pretend that we have a big item to avoid the huge malloc + item->metadata.string_metadata.length = size; + assert_size_equal(cbor_serialized_size(item), 1 + 8 + size); + cbor_decref(&item); +#endif +} + static void test_serialize_indefinite_string(void **_CBOR_UNUSED(_state)) { cbor_item_t *item = cbor_new_indefinite_string(); cbor_item_t *chunk = cbor_new_definite_string(); @@ -242,6 +270,7 @@ static void test_serialize_indefinite_string(void **_CBOR_UNUSED(_state)) { static void test_serialize_string_no_space(void **_CBOR_UNUSED(_state)) { cbor_item_t *item = cbor_new_definite_string(); unsigned char *data = malloc(12); + memset(data, 0, 12); cbor_string_set_handle(item, data, 12); assert_size_equal(cbor_serialize(item, buffer, 1), 0); @@ -254,6 +283,7 @@ static void test_serialize_indefinite_string_no_space( cbor_item_t *item = cbor_new_indefinite_string(); cbor_item_t *chunk = cbor_new_definite_string(); unsigned char *data = malloc(256); + memset(data, 0, 256); cbor_string_set_handle(chunk, data, 256); assert_true(cbor_string_add_chunk(item, cbor_move(chunk))); @@ -638,6 +668,8 @@ int main(void) { cmocka_unit_test(test_serialize_bytestring_no_space), cmocka_unit_test(test_serialize_indefinite_bytestring_no_space), cmocka_unit_test(test_serialize_definite_string), + cmocka_unit_test(test_serialize_definite_string_4b_header), + cmocka_unit_test(test_serialize_definite_string_8b_header), cmocka_unit_test(test_serialize_indefinite_string), cmocka_unit_test(test_serialize_string_no_space), cmocka_unit_test(test_serialize_indefinite_string_no_space), diff --git a/test/cbor_stream_decode_test.c b/test/cbor_stream_decode_test.c index 1b8caf47765d..5288016c2fc6 100644 --- a/test/cbor_stream_decode_test.c +++ b/test/cbor_stream_decode_test.c @@ -613,9 +613,9 @@ static void test_int64_tag_decoding(void **_CBOR_UNUSED(_state)) { assert_minimum_input_size(9, int64_tag_data); } -unsigned char bad_tag_data[] = {0xC6}; -static void test_bad_tag_decoding(void **_CBOR_UNUSED(_state)) { - assert_decoder_result(0, CBOR_DECODER_ERROR, decode(bad_tag_data, 1)); +unsigned char reserved_byte_data[] = {0xDC}; +static void test_reserved_byte_decoding(void **_CBOR_UNUSED(_state)) { + assert_decoder_result(0, CBOR_DECODER_ERROR, decode(reserved_byte_data, 1)); } unsigned char float2_data[] = {0xF9, 0x7B, 0xFF}; @@ -729,7 +729,7 @@ int main(void) { stream_test(test_int16_tag_decoding), stream_test(test_int32_tag_decoding), stream_test(test_int64_tag_decoding), - stream_test(test_bad_tag_decoding), + stream_test(test_reserved_byte_decoding), stream_test(test_float2_decoding), stream_test(test_float4_decoding), diff --git a/test/float_ctrl_test.c b/test/float_ctrl_test.c index 1bf8b7b7ba1d..c939486877e7 100644 --- a/test/float_ctrl_test.c +++ b/test/float_ctrl_test.c @@ -30,7 +30,7 @@ static void test_float2(void **_CBOR_UNUSED(_state)) { assert_true(cbor_is_float(float_ctrl)); assert_true(cbor_float_get_width(float_ctrl) == CBOR_FLOAT_16); assert_true(cbor_float_get_float2(float_ctrl) == 65504.0F); - assert_true(fabs(cbor_float_get_float(float_ctrl) - 65504.0F) < eps); + assert_float_equal(cbor_float_get_float(float_ctrl), 65504.0F, eps); cbor_decref(&float_ctrl); assert_null(float_ctrl); } @@ -43,7 +43,7 @@ static void test_float4(void **_CBOR_UNUSED(_state)) { assert_true(cbor_is_float(float_ctrl)); assert_true(cbor_float_get_width(float_ctrl) == CBOR_FLOAT_32); assert_true(cbor_float_get_float4(float_ctrl) == 100000.0F); - assert_true(fabs(cbor_float_get_float(float_ctrl) - 100000.0F) < eps); + assert_float_equal(cbor_float_get_float(float_ctrl), 100000.0F, eps); cbor_decref(&float_ctrl); assert_null(float_ctrl); } @@ -58,6 +58,8 @@ static void test_float8(void **_CBOR_UNUSED(_state)) { assert_true(cbor_float_get_width(float_ctrl) == CBOR_FLOAT_64); // XXX: the cast prevents promotion to 80-bit floats on 32-bit x86 assert_true(cbor_float_get_float8(float_ctrl) == (double)1.0e+300); + // Not using `assert_double_equal` since CI has an old version of cmocka + assert_true(fabs(cbor_float_get_float(float_ctrl) - (double)1.0e+300) < eps); cbor_decref(&float_ctrl); assert_null(float_ctrl); } diff --git a/test/pretty_printer_test.c b/test/pretty_printer_test.c index 4a6249c412d8..6ea40c0e7ce2 100644 --- a/test/pretty_printer_test.c +++ b/test/pretty_printer_test.c @@ -6,33 +6,200 @@ */ #include <stdio.h> +#include <string.h> + #include "assertions.h" #include "cbor.h" -unsigned char data[] = {0x8B, 0x01, 0x20, 0x5F, 0x41, 0x01, 0x41, 0x02, - 0xFF, 0x7F, 0x61, 0x61, 0x61, 0x62, 0xFF, 0x9F, - 0xFF, 0xA1, 0x61, 0x61, 0x61, 0x62, 0xC0, 0xBF, - 0xFF, 0xFB, 0x40, 0x09, 0x1E, 0xB8, 0x51, 0xEB, - 0x85, 0x1F, 0xF6, 0xF7, 0xF5}; - -static void test_pretty_printer(void **_CBOR_UNUSED(_state)) { +void assert_describe_result(cbor_item_t *item, char *expected_result) { #if CBOR_PRETTY_PRINTER + // We know the expected size based on `expected_result`, but read everything + // in order to get the full actual output in a useful error message. + const size_t buffer_size = 512; FILE *outfile = tmpfile(); - struct cbor_load_result res; - cbor_item_t *item = cbor_load(data, 37, &res); cbor_describe(item, outfile); + rewind(outfile); + // Treat string as null-terminated since cmocka doesn't have asserts + // for explicit length strings. + char *output = malloc(buffer_size); + assert_non_null(output); + size_t output_size = fread(output, sizeof(char), buffer_size, outfile); + output[output_size] = '\0'; + assert_string_equal(output, expected_result); + assert_true(feof(outfile)); + free(output); + fclose(outfile); +#endif +} + +static void test_uint(void **_CBOR_UNUSED(_state)) { + cbor_item_t *item = cbor_build_uint8(42); + assert_describe_result(item, "[CBOR_TYPE_UINT] Width: 1B, Value: 42\n"); cbor_decref(&item); +} - item = cbor_new_ctrl(); - cbor_set_ctrl(item, 1); - cbor_describe(item, outfile); +static void test_negint(void **_CBOR_UNUSED(_state)) { + cbor_item_t *item = cbor_build_negint16(40); + assert_describe_result(item, + "[CBOR_TYPE_NEGINT] Width: 2B, Value: -40 - 1\n"); cbor_decref(&item); +} - fclose(outfile); -#endif +static void test_definite_bytestring(void **_CBOR_UNUSED(_state)) { + unsigned char data[] = {0x01, 0x02, 0x03}; + cbor_item_t *item = cbor_build_bytestring(data, 3); + assert_describe_result(item, + "[CBOR_TYPE_BYTESTRING] Definite, Length: 3B, Data:\n" + " 010203\n"); + cbor_decref(&item); +} + +static void test_indefinite_bytestring(void **_CBOR_UNUSED(_state)) { + unsigned char data[] = {0x01, 0x02, 0x03}; + cbor_item_t *item = cbor_new_indefinite_bytestring(); + assert_true(cbor_bytestring_add_chunk( + item, cbor_move(cbor_build_bytestring(data, 3)))); + assert_true(cbor_bytestring_add_chunk( + item, cbor_move(cbor_build_bytestring(data, 2)))); + assert_describe_result( + item, + "[CBOR_TYPE_BYTESTRING] Indefinite, Chunks: 2, Chunk data:\n" + " [CBOR_TYPE_BYTESTRING] Definite, Length: 3B, Data:\n" + " 010203\n" + " [CBOR_TYPE_BYTESTRING] Definite, Length: 2B, Data:\n" + " 0102\n"); + cbor_decref(&item); +} + +static void test_definite_string(void **_CBOR_UNUSED(_state)) { + char *string = "Hello!"; + cbor_item_t *item = cbor_build_string(string); + assert_describe_result( + item, + "[CBOR_TYPE_STRING] Definite, Length: 6B, Codepoints: 6, Data:\n" + " Hello!\n"); + cbor_decref(&item); +} + +static void test_indefinite_string(void **_CBOR_UNUSED(_state)) { + char *string = "Hello!"; + cbor_item_t *item = cbor_new_indefinite_string(); + assert_true( + cbor_string_add_chunk(item, cbor_move(cbor_build_string(string)))); + assert_true( + cbor_string_add_chunk(item, cbor_move(cbor_build_string(string)))); + assert_describe_result( + item, + "[CBOR_TYPE_STRING] Indefinite, Chunks: 2, Chunk data:\n" + " [CBOR_TYPE_STRING] Definite, Length: 6B, Codepoints: 6, Data:\n" + " Hello!\n" + " [CBOR_TYPE_STRING] Definite, Length: 6B, Codepoints: 6, Data:\n" + " Hello!\n"); + cbor_decref(&item); +} + +static void test_multibyte_string(void **_CBOR_UNUSED(_state)) { + // "Štěstíčko" in UTF-8 + char *string = "\xc5\xa0t\xc4\x9bst\xc3\xad\xc4\x8dko"; + cbor_item_t *item = cbor_build_string(string); + assert_describe_result( + item, + "[CBOR_TYPE_STRING] Definite, Length: 13B, Codepoints: 9, Data:\n" + " \xc5\xa0t\xc4\x9bst\xc3\xad\xc4\x8dko\n"); + cbor_decref(&item); +} + +static void test_definite_array(void **_CBOR_UNUSED(_state)) { + cbor_item_t *item = cbor_new_definite_array(2); + assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(1)))); + assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(2)))); + assert_describe_result(item, + "[CBOR_TYPE_ARRAY] Definite, Size: 2, Contents:\n" + " [CBOR_TYPE_UINT] Width: 1B, Value: 1\n" + " [CBOR_TYPE_UINT] Width: 1B, Value: 2\n"); + cbor_decref(&item); +} + +static void test_indefinite_array(void **_CBOR_UNUSED(_state)) { + cbor_item_t *item = cbor_new_indefinite_array(); + assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(1)))); + assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(2)))); + assert_describe_result(item, + "[CBOR_TYPE_ARRAY] Indefinite, Size: 2, Contents:\n" + " [CBOR_TYPE_UINT] Width: 1B, Value: 1\n" + " [CBOR_TYPE_UINT] Width: 1B, Value: 2\n"); + cbor_decref(&item); +} + +static void test_definite_map(void **_CBOR_UNUSED(_state)) { + cbor_item_t *item = cbor_new_definite_map(1); + assert_true(cbor_map_add( + item, (struct cbor_pair){.key = cbor_move(cbor_build_uint8(1)), + .value = cbor_move(cbor_build_uint8(2))})); + assert_describe_result(item, + "[CBOR_TYPE_MAP] Definite, Size: 1, Contents:\n" + " Map entry 0\n" + " [CBOR_TYPE_UINT] Width: 1B, Value: 1\n" + " [CBOR_TYPE_UINT] Width: 1B, Value: 2\n"); + cbor_decref(&item); +} + +static void test_indefinite_map(void **_CBOR_UNUSED(_state)) { + cbor_item_t *item = cbor_new_indefinite_map(); + assert_true(cbor_map_add( + item, (struct cbor_pair){.key = cbor_move(cbor_build_uint8(1)), + .value = cbor_move(cbor_build_uint8(2))})); + assert_describe_result(item, + "[CBOR_TYPE_MAP] Indefinite, Size: 1, Contents:\n" + " Map entry 0\n" + " [CBOR_TYPE_UINT] Width: 1B, Value: 1\n" + " [CBOR_TYPE_UINT] Width: 1B, Value: 2\n"); + cbor_decref(&item); +} + +static void test_tag(void **_CBOR_UNUSED(_state)) { + cbor_item_t *item = cbor_build_tag(42, cbor_move(cbor_build_uint8(1))); + assert_describe_result(item, + "[CBOR_TYPE_TAG] Value: 42\n" + " [CBOR_TYPE_UINT] Width: 1B, Value: 1\n"); + cbor_decref(&item); +} + +static void test_floats(void **_CBOR_UNUSED(_state)) { + cbor_item_t *item = cbor_new_indefinite_array(); + assert_true(cbor_array_push(item, cbor_move(cbor_build_bool(true)))); + assert_true( + cbor_array_push(item, cbor_move(cbor_build_ctrl(CBOR_CTRL_UNDEF)))); + assert_true( + cbor_array_push(item, cbor_move(cbor_build_ctrl(CBOR_CTRL_NULL)))); + assert_true(cbor_array_push(item, cbor_move(cbor_build_ctrl(24)))); + assert_true(cbor_array_push(item, cbor_move(cbor_build_float4(3.14f)))); + assert_describe_result( + item, + "[CBOR_TYPE_ARRAY] Indefinite, Size: 5, Contents:\n" + " [CBOR_TYPE_FLOAT_CTRL] Bool: true\n" + " [CBOR_TYPE_FLOAT_CTRL] Undefined\n" + " [CBOR_TYPE_FLOAT_CTRL] Null\n" + " [CBOR_TYPE_FLOAT_CTRL] Simple value: 24\n" + " [CBOR_TYPE_FLOAT_CTRL] Width: 4B, Value: 3.140000\n"); + cbor_decref(&item); } int main(void) { - const struct CMUnitTest tests[] = {cmocka_unit_test(test_pretty_printer)}; + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_uint), + cmocka_unit_test(test_negint), + cmocka_unit_test(test_definite_bytestring), + cmocka_unit_test(test_indefinite_bytestring), + cmocka_unit_test(test_definite_string), + cmocka_unit_test(test_indefinite_string), + cmocka_unit_test(test_multibyte_string), + cmocka_unit_test(test_definite_array), + cmocka_unit_test(test_indefinite_array), + cmocka_unit_test(test_definite_map), + cmocka_unit_test(test_indefinite_map), + cmocka_unit_test(test_tag), + cmocka_unit_test(test_floats), + }; return cmocka_run_group_tests(tests, NULL, NULL); } diff --git a/test/string_test.c b/test/string_test.c index 991aa578a26e..c3079b449838 100644 --- a/test/string_test.c +++ b/test/string_test.c @@ -217,6 +217,22 @@ static void test_short_indef_string(void **_CBOR_UNUSED(_state)) { assert_null(string); } +static void test_invalid_utf(void **_CBOR_UNUSED(_state)) { + /* 0x60 + 1 | 0xC5 (invalid unfinished 2B codepoint) */ + unsigned char string_data[] = {0x61, 0xC5}; + string = cbor_load(string_data, 2, &res); + + assert_non_null(string); + assert_true(cbor_typeof(string) == CBOR_TYPE_STRING); + assert_true(cbor_isa_string(string)); + assert_size_equal(cbor_string_length(string), 1); + assert_size_equal(cbor_string_codepoint_count(string), 0); + assert_true(cbor_string_is_definite(string)); + assert_true(res.read == 2); + + cbor_decref(&string); +} + static void test_inline_creation(void **_CBOR_UNUSED(_state)) { string = cbor_build_string("Hello!"); assert_memory_equal(cbor_string_handle(string), "Hello!", strlen("Hello!")); @@ -275,6 +291,53 @@ static void test_add_chunk_reallocation_overflow(void **_CBOR_UNUSED(_state)) { cbor_decref(&string); } +static void test_set_handle(void **_CBOR_UNUSED(_state)) { + string = cbor_new_definite_string(); + char *test_string = "Hello"; + unsigned char *string_data = malloc(strlen(test_string)); + memcpy(string_data, test_string, strlen(test_string)); + assert_ptr_not_equal(string_data, NULL); + cbor_string_set_handle(string, string_data, strlen(test_string)); + + assert_ptr_equal(cbor_string_handle(string), string_data); + assert_size_equal(cbor_string_length(string), 5); + assert_size_equal(cbor_string_codepoint_count(string), 5); + + cbor_decref(&string); +} + +static void test_set_handle_multibyte_codepoint(void **_CBOR_UNUSED(_state)) { + string = cbor_new_definite_string(); + // "Štěstíčko" in UTF-8 + char *test_string = "\xc5\xa0t\xc4\x9bst\xc3\xad\xc4\x8dko"; + unsigned char *string_data = malloc(strlen(test_string)); + memcpy(string_data, test_string, strlen(test_string)); + assert_ptr_not_equal(string_data, NULL); + cbor_string_set_handle(string, string_data, strlen(test_string)); + + assert_ptr_equal(cbor_string_handle(string), string_data); + assert_size_equal(cbor_string_length(string), 13); + assert_size_equal(cbor_string_codepoint_count(string), 9); + + cbor_decref(&string); +} + +static void test_set_handle_invalid_utf(void **_CBOR_UNUSED(_state)) { + string = cbor_new_definite_string(); + // Invalid multi-byte character (missing the second byte). + char *test_string = "Test: \xc5"; + unsigned char *string_data = malloc(strlen(test_string)); + memcpy(string_data, test_string, strlen(test_string)); + assert_ptr_not_equal(string_data, NULL); + cbor_string_set_handle(string, string_data, strlen(test_string)); + + assert_ptr_equal(cbor_string_handle(string), string_data); + assert_size_equal(cbor_string_length(string), 7); + assert_size_equal(cbor_string_codepoint_count(string), 0); + + cbor_decref(&string); +} + int main(void) { const struct CMUnitTest tests[] = { cmocka_unit_test(test_empty_string), @@ -285,10 +348,14 @@ int main(void) { cmocka_unit_test(test_int32_string), cmocka_unit_test(test_int64_string), cmocka_unit_test(test_short_indef_string), + cmocka_unit_test(test_invalid_utf), cmocka_unit_test(test_inline_creation), cmocka_unit_test(test_string_creation), cmocka_unit_test(test_string_add_chunk), cmocka_unit_test(test_add_chunk_reallocation_overflow), + cmocka_unit_test(test_set_handle), + cmocka_unit_test(test_set_handle_multibyte_codepoint), + cmocka_unit_test(test_set_handle_invalid_utf), }; return cmocka_run_group_tests(tests, NULL, NULL); } diff --git a/test/tag_test.c b/test/tag_test.c index 4bf6c718f0b9..4bce10589803 100644 --- a/test/tag_test.c +++ b/test/tag_test.c @@ -102,6 +102,28 @@ static void test_nested_tag(void **_CBOR_UNUSED(_state)) { assert_null(nested_tag); } +static void test_all_tag_values_supported(void **_CBOR_UNUSED(_state)) { + /* Test all items in the protected range of + * https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml */ + for (int64_t tag_value = 0; tag_value <= 32767; tag_value++) { + cbor_item_t *tag_item = + cbor_build_tag(tag_value, cbor_move(cbor_build_uint8(42))); + unsigned char *serialized_tag; + size_t serialized_tag_size = + cbor_serialize_alloc(tag_item, &serialized_tag, NULL); + assert_true(serialized_tag_size > 0); + tag = cbor_load(serialized_tag, serialized_tag_size, &res); + assert_true(res.read == serialized_tag_size); + assert_true(cbor_typeof(tag) == CBOR_TYPE_TAG); + assert_true(cbor_tag_value(tag) == tag_value); + cbor_decref(&tag); + assert_null(tag); + cbor_decref(&tag_item); + assert_null(tag_item); + free(serialized_tag); + } +} + static void test_build_tag(void **_CBOR_UNUSED(_state)) { tag = cbor_build_tag(1, cbor_move(cbor_build_uint8(42))); @@ -134,6 +156,7 @@ int main(void) { cmocka_unit_test(test_int32_tag), cmocka_unit_test(test_int64_tag), cmocka_unit_test(test_nested_tag), + cmocka_unit_test(test_all_tag_values_supported), cmocka_unit_test(test_build_tag), cmocka_unit_test(test_build_tag_failure), cmocka_unit_test(test_tag_creation), diff --git a/test/test_allocator.c b/test/test_allocator.c index 72ccf6591d25..a2f98efa22f7 100644 --- a/test/test_allocator.c +++ b/test/test_allocator.c @@ -29,7 +29,7 @@ void finalize_mock_malloc(void) { free(expectations); } -void print_backtrace() { +void print_backtrace(void) { #if HAS_EXECINFO void *buffer[128]; int frames = backtrace(buffer, 128); |