master
c 141 lines 5.15 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "libnetdata/libnetdata.h"
4 #include "stacktrace.h"
5
6 // Structure to hold all test data
7 typedef struct {
8 BUFFER *direct_trace; // Buffer for direct stack trace
9 BUFFER *indirect_trace; // Buffer for indirect stack trace
10 BUFFER *direct_root_cause; // Root cause function from direct capture
11 BUFFER *indirect_root_cause; // Root cause function from indirect capture
12 const char *never_inline_fn; // Name of the never-inline function
13 const char *always_inline_fn; // Name of the always-inline function
14 } stacktrace_test_data_t;
15
16 // Function to analyze a stack trace
17 static bool analyze_stack_trace(
18 const char *stack_trace,
19 const char *never_inline_fn,
20 const char *always_inline_fn,
21 const char *unittest_fn,
22 const char *root_cause)
23 {
24 fprintf(stderr, "--------------------------------------------------------------------------------\n");
25 fprintf(stderr, "%s\n", stack_trace);
26 fprintf(stderr, "--------------------------------------------------------------------------------\n");
27
28 if (!stack_trace || !*stack_trace) {
29 fprintf(stderr, " - empty stack trace\n");
30 return false;
31 }
32
33 // Report presence of each function
34 bool never_inline_found = strstr(stack_trace, never_inline_fn) != NULL;
35 bool always_inline_found = strstr(stack_trace, always_inline_fn) != NULL;
36 bool unittest_found = strstr(stack_trace, unittest_fn) != NULL;
37
38 fprintf(stderr, " - %50.50s: %s\n",
39 never_inline_fn, never_inline_found ? "FOUND" : "NOT FOUND");
40 fprintf(stderr, " - %50.50s: %s\n",
41 always_inline_fn, always_inline_found ? "FOUND" : "NOT FOUND");
42 fprintf(stderr, " - %50.50s: %s\n",
43 unittest_fn, unittest_found ? "FOUND" : "NOT FOUND");
44 fprintf(stderr, " - %50.50s: %s\n",
45 "root cause function",
46 root_cause && *root_cause ? root_cause : "NOT FOUND");
47
48 // We only require the unittest function to be present for the test to pass
49 return unittest_found;
50 }
51
52 // This function will never be inlined
53 NEVER_INLINE
54 static void never_inline_function_to_capture_stack_trace(stacktrace_test_data_t *test_data) {
55 test_data->never_inline_fn = __FUNCTION__;
56
57 BUFFER *wb = buffer_create(4096, NULL);
58
59 // Test 1: Direct capture
60 stacktrace_capture(wb);
61 buffer_strcat(test_data->direct_trace, buffer_tostring(wb));
62
63 // Get root cause (first time)
64 buffer_flush(test_data->direct_root_cause);
65 buffer_strcat(test_data->direct_root_cause, stacktrace_root_cause_function());
66
67 // Test 2: Indirect capture (get + to_buffer)
68 buffer_flush(wb);
69 STACKTRACE trace = stacktrace_get(0);
70 if (trace) {
71 stacktrace_to_buffer(trace, wb);
72 buffer_strcat(test_data->indirect_trace, buffer_tostring(wb));
73
74 // Get root cause (second time)
75 buffer_flush(test_data->indirect_root_cause);
76 buffer_strcat(test_data->indirect_root_cause, stacktrace_root_cause_function());
77 }
78
79 buffer_free(wb);
80 }
81
82 // This function will be inlined in the caller
83 ALWAYS_INLINE
84 static void inline_function_to_capture_stack_trace(stacktrace_test_data_t *test_data) {
85 test_data->always_inline_fn = __FUNCTION__;
86
87 // Call the non-inlined function
88 never_inline_function_to_capture_stack_trace(test_data);
89 }
90
91 // Run the stacktrace unittest
92 int stacktrace_unittest(void) {
93 // Initialize stacktrace subsystem
94 stacktrace_init();
95
96 // Setup test data structure
97 stacktrace_test_data_t test_data = {
98 .direct_trace = buffer_create(4096, NULL),
99 .indirect_trace = buffer_create(4096, NULL),
100 .direct_root_cause = buffer_create(4096, NULL),
101 .indirect_root_cause = buffer_create(4096, NULL),
102 .never_inline_fn = NULL,
103 .always_inline_fn = NULL,
104 };
105
106 // Run the test function to gather stack traces
107 inline_function_to_capture_stack_trace(&test_data);
108
109 // Print basic test information
110 fprintf(stderr, "\nSTACKTRACE TEST: Backend: %s\n", stacktrace_backend());
111
112 // Analyze both stack traces
113 fprintf(stderr, "\nDIRECT STACK TRACE\n");
114 bool direct_analysis = analyze_stack_trace(
115 buffer_tostring(test_data.direct_trace),
116 test_data.never_inline_fn,
117 test_data.always_inline_fn,
118 "stacktrace_unittest",
119 buffer_tostring(test_data.direct_root_cause));
120
121 fprintf(stderr, "\nINDIRECT STACK TRACE\n");
122 bool indirect_analysis = analyze_stack_trace(
123 buffer_tostring(test_data.indirect_trace),
124 test_data.never_inline_fn,
125 test_data.always_inline_fn,
126 "stacktrace_unittest",
127 buffer_tostring(test_data.indirect_root_cause));
128
129 // Free resources
130 buffer_free(test_data.direct_trace);
131 buffer_free(test_data.indirect_trace);
132 buffer_free(test_data.direct_root_cause);
133 buffer_free(test_data.indirect_root_cause);
134
135 // Report overall test status - success if both analyses succeed
136 bool test_success = direct_analysis && indirect_analysis;
137 fprintf(stderr, "\nSTACKTRACE TEST: Overall result: %s\n",
138 test_success ? "SUCCESS" : "FAILURE");
139
140 return test_success ? 0 : 1;
141 }