Skip to content

Commit 77bb453

Browse files
committed
change backtrace gathering method for compilation time
1 parent dbe1a60 commit 77bb453

File tree

6 files changed

+94
-64
lines changed

6 files changed

+94
-64
lines changed

example/meson.build

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,9 @@ executable(
44
'example.c',
55
dependencies : c_minilib_error_dep,
66
c_args : [
7-
'-g', # Enable debug symbols (DWARF info)
8-
'-fno-pie', # Disable position-independent code generation
9-
'-fno-omit-frame-pointer', # Preserve frame pointer for accurate backtraces
107
'-O0' # Disable compiler optimizations (more accurate traces)
118
],
129
link_args : [
13-
'-no-pie', # Tell linker to produce a non-PIE executable
14-
'-O0' # Disable linker-level optimizations
10+
'-O0' # Disable linker-level optimizations (more accurate traces)
1511
],
1612
)

include/c_minilib_error.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@
99
#include <inttypes.h>
1010
#include <stdint.h>
1111

12-
// We are staticly restricting fields to create only one memory allocation for
13-
// whole error. Mallocs are pretty expensive and errors can be created and
14-
// destroyed very frequently.
1512
#ifdef CME_ENABLE_BACKTRACE
1613
#define CME_STACK_MAX 16
1714
#else
@@ -20,6 +17,9 @@
2017

2118
#define CME_STR_MAX 255
2219

20+
// Resolving symbols is quite cumbersome and require a lot of compilation flags
21+
// like -fno-omit-pointer, -fno-pie etc. Resolving symbols on compilation
22+
// step simplify process much.
2323
struct cme_StackSymbol {
2424
const char *source_file;
2525
const char *source_func;
@@ -30,6 +30,9 @@ struct __attribute__((aligned(8))) cme_Error {
3030
uint32_t code;
3131
char msg[CME_STR_MAX];
3232
uint32_t stack_length;
33+
// We are staticly restricting fields to create only one memory allocation for
34+
// whole error. Mallocs are pretty expensive and errors can be created and
35+
// destroyed very frequently.
3336
struct cme_StackSymbol stack_symbols[CME_STACK_MAX];
3437
};
3538

src/c_minilib_error.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,9 @@ int cme_error_dump_to_str(cme_error_t err, uint32_t n, char *buffer) {
8080

8181
written =
8282
cme_sprintf(buffer + offset, n - offset, "------------------------\n");
83-
if (written < 0)
83+
if (written < 0) {
8484
return ENOBUFS;
85+
}
8586
offset += (size_t)written;
8687

8788
for (uint32_t i = 0; i < err->stack_length; ++i) {
@@ -90,8 +91,9 @@ int cme_error_dump_to_str(cme_error_t err, uint32_t n, char *buffer) {
9091
sym->source_func ? sym->source_func : "??",
9192
sym->source_file ? sym->source_file : "??",
9293
sym->source_line);
93-
if (written < 0)
94+
if (written < 0) {
9495
return ENOBUFS;
96+
}
9597
offset += (size_t)written;
9698
}
9799

@@ -120,10 +122,12 @@ cme_error_t cme_error_push_symbol(cme_error_t err, const char *file,
120122
(void)line;
121123
return err;
122124
#else
123-
if (!err)
125+
if (!err) {
124126
return err;
125-
if (err->stack_length >= CME_STACK_MAX)
127+
}
128+
if (err->stack_length >= CME_STACK_MAX) {
126129
return err;
130+
}
127131

128132
struct cme_StackSymbol *f = &err->stack_symbols[err->stack_length++];
129133
f->source_file = file;

test/test_c_minilib_error.d/common.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
#include <string.h>
77
#include <unistd.h>
88

9-
inline static char *create_temp_file_path(void) __attribute__((unused));
109
inline static char *create_temp_file_path(void) {
1110
char *template = strdup("/tmp/cme_error_dump_XXXXXX");
1211
if (!template) {

test/test_c_minilib_error.d/test_c_minilib_error.c

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
1+
// test/test_c_minilib_error.d/test_c_minilib_error.c
12
#include <errno.h>
23
#include <stdio.h>
34
#include <stdlib.h>
45
#include <string.h>
56
#include <unistd.h>
67

7-
#include <unity.h>
8-
98
#include "c_minilib_error.h"
109
#include "common.h"
10+
#include <unity.h>
1111

1212
void test_create_error_success(void) {
1313
struct cme_Error *err = cme_error_create(42, "test.c", "test_func", 123,
1414
"Failed with code %d", 42);
1515

1616
TEST_ASSERT_NOT_NULL(err);
1717
TEST_ASSERT_EQUAL(42, err->code);
18-
TEST_ASSERT_EQUAL_STRING("test.c", err->source_file);
19-
TEST_ASSERT_EQUAL_STRING("test_func", err->source_func);
20-
TEST_ASSERT_EQUAL(123, err->source_line);
18+
TEST_ASSERT_EQUAL_STRING("test.c", err->stack_symbols[0].source_file);
19+
TEST_ASSERT_EQUAL_STRING("test_func", err->stack_symbols[0].source_func);
20+
TEST_ASSERT_EQUAL(123, err->stack_symbols[0].source_line);
2121
TEST_ASSERT_EQUAL_STRING("Failed with code 42", err->msg);
2222

2323
cme_error_destroy(err);
@@ -43,9 +43,7 @@ void test_create_error_invalid_format(void) {
4343
cme_error_destroy(err);
4444
}
4545

46-
void test_destroy_null(void) {
47-
cme_error_destroy(NULL); // Should not crash
48-
}
46+
void test_destroy_null(void) { cme_error_destroy(NULL); }
4947

5048
void test_create_error_multiple_format_args(void) {
5149
struct cme_Error *err =
@@ -54,9 +52,9 @@ void test_create_error_multiple_format_args(void) {
5452

5553
TEST_ASSERT_NOT_NULL(err);
5654
TEST_ASSERT_EQUAL(7, err->code);
57-
TEST_ASSERT_EQUAL_STRING("multi.c", err->source_file);
58-
TEST_ASSERT_EQUAL_STRING("multi_func", err->source_func);
59-
TEST_ASSERT_EQUAL(77, err->source_line);
55+
TEST_ASSERT_EQUAL_STRING("multi.c", err->stack_symbols[0].source_file);
56+
TEST_ASSERT_EQUAL_STRING("multi_func", err->stack_symbols[0].source_func);
57+
TEST_ASSERT_EQUAL(77, err->stack_symbols[0].source_line);
6058
TEST_ASSERT_EQUAL_STRING("Error code 7 at multi.c:77", err->msg);
6159

6260
cme_error_destroy(err);
@@ -68,28 +66,22 @@ void test_create_error_no_format_string(void) {
6866

6967
TEST_ASSERT_NOT_NULL(err);
7068
TEST_ASSERT_EQUAL(88, err->code);
71-
TEST_ASSERT_EQUAL_STRING("plain.c", err->source_file);
72-
TEST_ASSERT_EQUAL_STRING("plain_func", err->source_func);
73-
TEST_ASSERT_EQUAL(88, err->source_line);
69+
TEST_ASSERT_EQUAL_STRING("plain.c", err->stack_symbols[0].source_file);
70+
TEST_ASSERT_EQUAL_STRING("plain_func", err->stack_symbols[0].source_func);
71+
TEST_ASSERT_EQUAL(88, err->stack_symbols[0].source_line);
7472
TEST_ASSERT_EQUAL_STRING("Static message", err->msg);
7573

7674
cme_error_destroy(err);
7775
}
7876

7977
void test_cme_error_dump_tmp(void) {
80-
struct cme_Error err = {
81-
.code = 42,
82-
.source_line = 100,
83-
.stack_length = 0,
84-
#ifdef CME_ENABLE_BACKTRACE
85-
.stack_symbols = {0},
86-
#else
87-
.stack_symbols = NULL,
88-
#endif
89-
};
78+
struct cme_Error err;
79+
err.code = 42;
80+
err.stack_length = 1;
9081
strncpy(err.msg, "Temporary error message", CME_STR_MAX);
91-
strncpy(err.source_file, "test_tmp.c", CME_STR_MAX);
92-
strncpy(err.source_func, "test_tmp_func", CME_STR_MAX);
82+
err.stack_symbols[0].source_file = "test_tmp.c";
83+
err.stack_symbols[0].source_func = "test_tmp_func";
84+
err.stack_symbols[0].source_line = 100;
9385

9486
char *temp_file = create_temp_file_path();
9587
TEST_ASSERT_NOT_NULL(temp_file);
@@ -108,9 +100,7 @@ void test_cme_error_dump_tmp(void) {
108100
TEST_ASSERT_NOT_NULL(strstr(buf, "====== ERROR DUMP ======"));
109101
TEST_ASSERT_NOT_NULL(strstr(buf, "Error code: 42"));
110102
TEST_ASSERT_NOT_NULL(strstr(buf, err.msg));
111-
TEST_ASSERT_NOT_NULL(strstr(buf, err.source_file));
112-
TEST_ASSERT_NOT_NULL(strstr(buf, "Src line: 100"));
113-
TEST_ASSERT_NOT_NULL(strstr(buf, err.source_func));
103+
TEST_ASSERT_NOT_NULL(strstr(buf, "0:test_tmp_func:test_tmp.c:100"));
114104

115105
remove(temp_file);
116106
free(temp_file);
Lines changed: 60 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,45 @@
1+
// test/test_c_minilib_error.d/test_c_minilib_error_with_backtrace.c
12
#include <errno.h>
23
#include <stdio.h>
34
#include <stdlib.h>
45
#include <string.h>
56
#include <unistd.h>
67

7-
#include <unity.h>
8-
98
#include "c_minilib_error.h"
109
#include "common.h"
10+
#include <unity.h>
1111

12-
void test_create_error_with_backtrace(void) {
13-
struct cme_Error *err = cme_errorf(101, "Backtrace test");
14-
12+
// Verifies code, message, and the first backtrace symbol fields
13+
void test_create_error_with_backtrace_fields(void) {
14+
struct cme_Error *err = cme_errorf(300, "Field test");
1515
TEST_ASSERT_NOT_NULL(err);
16-
TEST_ASSERT_EQUAL(101, err->code);
17-
TEST_ASSERT_NOT_NULL(err->msg);
18-
TEST_ASSERT_TRUE(err->stack_length == 0);
16+
17+
TEST_ASSERT_EQUAL(300, err->code);
18+
TEST_ASSERT_EQUAL_STRING("Field test", err->msg);
19+
TEST_ASSERT_EQUAL(1, err->stack_length);
20+
21+
const struct cme_StackSymbol *sym = &err->stack_symbols[0];
22+
TEST_ASSERT_NOT_NULL(sym->source_file);
23+
24+
TEST_ASSERT_TRUE(
25+
strstr(sym->source_file, "test_c_minilib_error_with_backtrace.c"));
26+
27+
TEST_ASSERT_EQUAL_STRING("test_create_error_with_backtrace_fields",
28+
sym->source_func);
29+
30+
TEST_ASSERT_TRUE(sym->source_line > 0);
1931

2032
cme_error_destroy(err);
2133
}
2234

2335
void test_cme_error_dump_with_backtrace(void) {
24-
struct cme_Error err = {
25-
.code = 42,
26-
.source_line = 100,
27-
.stack_length = 1,
28-
};
36+
struct cme_Error err;
37+
err.code = 42;
38+
err.stack_length = 1;
2939
strncpy(err.msg, "Temporary error message", CME_STR_MAX);
30-
strncpy(err.source_file, "test_tmp.c", CME_STR_MAX);
31-
strncpy(err.source_func, "test_tmp_func", CME_STR_MAX);
32-
33-
void *dummy_symbols[] = {(void *)0x1234abcd};
34-
memcpy(err.stack_symbols, dummy_symbols, sizeof(dummy_symbols));
40+
err.stack_symbols[0].source_file = "test_tmp.c";
41+
err.stack_symbols[0].source_func = "test_tmp_func";
42+
err.stack_symbols[0].source_line = 100;
3543

3644
char *temp_file = create_temp_file_path();
3745
TEST_ASSERT_NOT_NULL(temp_file);
@@ -50,11 +58,41 @@ void test_cme_error_dump_with_backtrace(void) {
5058
TEST_ASSERT_NOT_NULL(strstr(buf, "====== ERROR DUMP ======"));
5159
TEST_ASSERT_NOT_NULL(strstr(buf, "Error code: 42"));
5260
TEST_ASSERT_NOT_NULL(strstr(buf, err.msg));
53-
TEST_ASSERT_NOT_NULL(strstr(buf, err.source_file));
54-
TEST_ASSERT_NOT_NULL(strstr(buf, "Src line: 100"));
55-
TEST_ASSERT_NOT_NULL(strstr(buf, err.source_func));
56-
TEST_ASSERT_NOT_NULL(strstr(buf, "0x"));
61+
TEST_ASSERT_NOT_NULL(strstr(buf, "0:test_tmp_func:test_tmp.c:100"));
5762

5863
remove(temp_file);
5964
free(temp_file);
6065
}
66+
67+
// Helper wrappers for testing cme_return
68+
static cme_error_t return_level1(void) {
69+
return cme_errorf(200, "Level1 error");
70+
}
71+
72+
static cme_error_t return_level2(void) { return cme_return(return_level1()); }
73+
74+
void test_cme_return_macro(void) {
75+
cme_error_t err = return_level2();
76+
TEST_ASSERT_NOT_NULL(err);
77+
TEST_ASSERT_EQUAL(200, err->code);
78+
79+
// Expect two frames: level1 then level2
80+
TEST_ASSERT_EQUAL(2, err->stack_length);
81+
82+
const struct cme_StackSymbol *sym0 = &err->stack_symbols[0];
83+
const struct cme_StackSymbol *sym1 = &err->stack_symbols[1];
84+
85+
// First symbol should come from return_level1()
86+
TEST_ASSERT_TRUE(
87+
strstr(sym0->source_file, "test_c_minilib_error_with_backtrace.c"));
88+
TEST_ASSERT_EQUAL_STRING("return_level1", sym0->source_func);
89+
TEST_ASSERT_TRUE(sym0->source_line > 0);
90+
91+
// Second symbol should come from return_level2()
92+
TEST_ASSERT_TRUE(
93+
strstr(sym1->source_file, "test_c_minilib_error_with_backtrace.c"));
94+
TEST_ASSERT_EQUAL_STRING("return_level2", sym1->source_func);
95+
TEST_ASSERT_TRUE(sym1->source_line > 0);
96+
97+
cme_error_destroy(err);
98+
}

0 commit comments

Comments
 (0)