Commit | Line | Data |
---|---|---|
970ed795 | 1 | /////////////////////////////////////////////////////////////////////////////// |
3abe9331 | 2 | // Copyright (c) 2000-2015 Ericsson Telecom AB |
970ed795 EL |
3 | // All rights reserved. This program and the accompanying materials |
4 | // are made available under the terms of the Eclipse Public License v1.0 | |
5 | // which accompanies this distribution, and is available at | |
6 | // http://www.eclipse.org/legal/epl-v10.html | |
7 | /////////////////////////////////////////////////////////////////////////////// | |
8 | #include "Error.hh" | |
9 | #include "TitanLoggerApi.hh" | |
10 | ||
11 | #include <stdarg.h> | |
3abe9331 | 12 | #include <stdint.h> |
970ed795 EL |
13 | |
14 | #include "../common/memory.h" | |
15 | #include "Logger.hh" | |
16 | #include "Runtime.hh" | |
17 | #include "Charstring.hh" | |
18 | ||
19 | #ifdef LINUX | |
20 | #include <ucontext.h> | |
21 | #include <dlfcn.h> | |
22 | #include <execinfo.h> | |
23 | ||
24 | #ifndef NO_CPP_DEMANGLE | |
25 | #include <cxxabi.h> | |
26 | using namespace __cxxabiv1; | |
27 | #endif | |
28 | ||
29 | #endif | |
30 | ||
31 | #ifdef LINUX | |
32 | ||
33 | const size_t MAX_DEPTH=100; | |
34 | ||
35 | void stacktrace(const ucontext_t& ctx) | |
36 | { | |
37 | TTCN_Logger::log_event_str("\nStack trace:\n"); | |
38 | ||
39 | (void)ctx; | |
40 | void *array[MAX_DEPTH]; | |
41 | size_t size; | |
42 | char **strings; | |
43 | size_t i; | |
44 | ||
45 | size = backtrace (array, MAX_DEPTH); | |
46 | strings = backtrace_symbols (array, size); | |
47 | ||
48 | //printf ("Obtained %lu stack frames.\n", (unsigned long)size); | |
49 | ||
50 | for (i = 0; i < size; i++) { | |
51 | const char * symname = strings[i]; | |
52 | ||
53 | const char *begin = 0, *end = 0; | |
54 | // The format is: /path/to/exe(_Z11mangledname+0xBAAD) [0x...] | |
55 | // Mangled name starts here ---^ ends here--^ | |
56 | for (const char *j = symname; *j; ++j) { | |
57 | if (*j == '(') | |
58 | begin = j+1; | |
59 | else if (*j == '+') | |
60 | end = j; | |
61 | } | |
62 | ||
63 | char *dem = 0; | |
64 | if (begin && end) { | |
65 | size_t len = end - begin; | |
66 | char *mangled = (char*)malloc(len + 1); | |
67 | memcpy(mangled, begin, len); | |
68 | mangled[len] = '\0'; | |
69 | ||
70 | int status; | |
71 | dem = __cxa_demangle(mangled, NULL, 0, &status); | |
72 | ||
73 | if(status == 0 && dem) | |
74 | symname = dem; | |
75 | } | |
76 | ||
77 | if (TTCN_Logger::is_logger_up()) { | |
78 | TTCN_Logger::log_event ("%2lu: %s%s\n", (unsigned long)i, symname, (end ? end : "")); | |
79 | } | |
80 | else { | |
81 | fprintf (stderr, "%2lu: %s%s\n", (unsigned long)i, symname, (end ? end : "")); | |
82 | } | |
83 | ||
84 | if (dem) | |
85 | free(dem); | |
86 | } | |
87 | ||
88 | free (strings); | |
89 | } | |
90 | ||
91 | void where_am_i(TTCN_Logger::Severity sev = TTCN_Logger::USER_UNQUALIFIED) | |
92 | { | |
93 | ucontext_t uc; | |
94 | int er = getcontext(&uc); | |
95 | if (!er) { | |
96 | TTCN_Logger::begin_event(sev); | |
97 | stacktrace(uc); | |
98 | TTCN_Logger::end_event(); | |
99 | } | |
100 | else { | |
101 | perror("getcontext"); | |
102 | } | |
103 | } | |
104 | ||
105 | ||
106 | static void signal_segv(int signum, siginfo_t* info, void*ptr) { | |
107 | static const char *si_codes[3] = {"", "SEGV_MAPERR", "SEGV_ACCERR"}; | |
108 | ucontext_t *ucontext = (ucontext_t*)ptr; | |
109 | fprintf(stderr, "\n\n!!! Segmentation Fault !!!\n\n"); | |
110 | fprintf(stderr, "info.si_signo = %d\n", signum); | |
111 | fprintf(stderr, "info.si_errno = %d\n", info->si_errno); | |
112 | fprintf(stderr, "info.si_code = %d (%s)\n", info->si_code, si_codes[info->si_code]); | |
113 | fprintf(stderr, "info.si_addr = %p\n", info->si_addr); | |
114 | ||
115 | TTCN_Logger::begin_event(TTCN_Logger::ERROR_UNQUALIFIED); | |
116 | stacktrace(*ucontext); | |
117 | TTCN_Logger::end_event(); | |
118 | ||
119 | fprintf(stderr, "\nGoodbye, cruel world!\n"); | |
120 | exit(-1); | |
121 | } | |
122 | ||
123 | int setup_sigsegv() { | |
124 | struct sigaction action; | |
125 | memset(&action, 0, sizeof(action)); | |
126 | action.sa_sigaction = signal_segv; | |
127 | action.sa_flags = SA_SIGINFO; | |
128 | if(sigaction(SIGSEGV, &action, NULL) < 0) { | |
129 | perror("sigaction"); | |
130 | return 0; | |
131 | } | |
132 | return 1; | |
133 | } | |
134 | ||
135 | static void __attribute((constructor)) init(void) { | |
136 | setup_sigsegv(); | |
137 | } | |
138 | ||
139 | #elif defined(SOLARIS) || defined(SOLARIS8) | |
140 | ||
141 | /* | |
142 | walks up call stack, printing library:routine+offset for each routine | |
143 | */ | |
144 | ||
145 | #include <dlfcn.h> | |
146 | #include <setjmp.h> | |
147 | #include<sys/types.h> | |
148 | #include <sys/reg.h> | |
149 | #include <sys/frame.h> | |
150 | ||
151 | #if defined(sparc) || defined(__sparc) | |
152 | #define FLUSHWIN() asm("ta 3"); | |
153 | #define FRAME_PTR_INDEX 1 | |
154 | #define SKIP_FRAMES 0 | |
155 | #endif | |
156 | ||
157 | #if defined(i386) || defined(__i386) | |
158 | #define FLUSHWIN() | |
159 | #define FRAME_PTR_INDEX 3 | |
160 | #define SKIP_FRAMES 1 | |
161 | #endif | |
162 | ||
163 | #if defined(ppc) || defined(__ppc) | |
164 | #define FLUSHWIN() | |
165 | #define FRAME_PTR_INDEX 0 | |
166 | #define SKIP_FRAMES 2 | |
167 | #endif | |
168 | ||
169 | #include <cxxabi.h> | |
170 | ||
171 | ||
172 | /* | |
173 | this function walks up call stack, calling user-supplied | |
174 | function once for each stack frame, passing the pc and the user-supplied | |
175 | usrarg as the argument. | |
176 | */ | |
177 | ||
178 | int cs_operate(int (*func)(void *, void *), void * usrarg) | |
179 | { | |
180 | struct frame *sp; | |
181 | jmp_buf env; | |
182 | int i; | |
183 | int * iptr; | |
184 | ||
185 | FLUSHWIN(); | |
186 | ||
187 | setjmp(env); | |
188 | iptr = (int*) env; | |
189 | ||
190 | sp = (struct frame *) iptr[FRAME_PTR_INDEX]; | |
191 | ||
192 | for(i=0;i<SKIP_FRAMES && sp;i++) | |
193 | sp = (struct frame*) sp->fr_savfp; | |
194 | ||
195 | i = 0; | |
196 | ||
197 | while(sp && sp->fr_savpc && ++i && (*func)((void*)sp->fr_savpc, usrarg)) { | |
198 | sp = (struct frame*) sp->fr_savfp; | |
199 | } | |
200 | ||
201 | return(i); | |
202 | } | |
203 | ||
204 | void print_my_stack() | |
205 | { | |
206 | int print_address(void *, void *); | |
207 | cs_operate(print_address, NULL); | |
208 | } | |
209 | ||
210 | int print_address(void *pc, void * usrarg) | |
211 | { | |
212 | Dl_info info; | |
213 | const char * func; | |
214 | const char * lib; | |
215 | ||
216 | if(dladdr(pc, & info) == 0) { | |
217 | func = "??"; | |
218 | lib = "??"; | |
219 | } | |
220 | else { | |
221 | lib = info.dli_fname; | |
222 | func = info.dli_sname; | |
223 | } | |
224 | ||
225 | size_t len = strlen(lib); | |
226 | for (--len; len > 0; --len) { | |
227 | if (lib[len] == '/') break; | |
228 | } | |
229 | if (len > 0) lib += ++len; | |
230 | ||
231 | int status = 42; | |
232 | char *demangled = abi::__cxa_demangle(func, NULL, 0, &status); | |
233 | if (status == 0) func = demangled; | |
234 | ||
235 | if (TTCN_Logger::is_logger_up()) { | |
3abe9331 | 236 | TTCN_Logger::log_event("%s:%s+%p\n", |
970ed795 EL |
237 | lib, |
238 | func, | |
3abe9331 | 239 | (void *)((uintptr_t)pc - (uintptr_t)info.dli_saddr) |
240 | ); | |
970ed795 EL |
241 | } |
242 | else { | |
3abe9331 | 243 | fprintf(stderr, "%s:%s+%p\n", |
970ed795 EL |
244 | lib, |
245 | func, | |
3abe9331 | 246 | (void *)((uintptr_t)pc - (uintptr_t)info.dli_saddr) |
247 | ); | |
970ed795 EL |
248 | } |
249 | ||
250 | if (status == 0) free(demangled); | |
251 | return(1); | |
252 | } | |
253 | ||
254 | void where_am_i(TTCN_Logger::Severity sev = TTCN_Logger::USER_UNQUALIFIED) | |
255 | { | |
256 | TTCN_Logger::begin_event(sev); | |
257 | print_my_stack(); | |
258 | TTCN_Logger::end_event(); | |
259 | } | |
260 | ||
261 | #else | |
262 | ||
263 | void where_am_i(TTCN_Logger::Severity /*sev*/ = TTCN_Logger::USER_UNQUALIFIED) | |
264 | { | |
265 | // You are on your own | |
266 | } | |
267 | #endif // LINUX | |
268 | ||
269 | ||
270 | TTCN_Error::~TTCN_Error() | |
271 | { | |
272 | Free(error_msg); | |
273 | } | |
274 | ||
275 | ||
276 | void TTCN_error(const char *err_msg, ...) | |
277 | { | |
278 | if (TTCN_Runtime::is_in_ttcn_try_block()) { | |
279 | // Add location data as if it were logged | |
280 | char* error_str = TTCN_Location::print_location( | |
281 | TTCN_Logger::SINFO_STACK == TTCN_Logger::get_source_info_format(), | |
282 | TTCN_Logger::SINFO_NONE != TTCN_Logger::get_source_info_format(), | |
283 | TTCN_Logger::get_log_entity_name()); | |
284 | if (error_str) { | |
285 | error_str = mputstr(error_str, " "); | |
286 | } | |
287 | error_str = mputstr(error_str, "Dynamic test case error: "); | |
288 | va_list p_var; | |
289 | va_start(p_var, err_msg); | |
290 | error_str = mputprintf_va_list(error_str, err_msg, p_var); | |
291 | va_end(p_var); | |
292 | throw TTCN_Error(error_str); | |
293 | } else { | |
294 | TTCN_Logger::begin_event(TTCN_Logger::ERROR_UNQUALIFIED); | |
295 | if (TTCN_Logger::SINFO_NONE == TTCN_Logger::get_source_info_format()) | |
296 | { | |
297 | //Always print some location info in case of dynamic testcase error | |
298 | char * loc = TTCN_Location::print_location(false, true, false); | |
299 | if (loc) { | |
300 | TTCN_Logger::log_event_str(loc); | |
301 | TTCN_Logger::log_event_str(": "); | |
302 | Free(loc); | |
303 | } | |
304 | } | |
305 | TTCN_Logger::log_event_str("Dynamic test case error: "); | |
306 | va_list p_var; | |
307 | va_start(p_var, err_msg); | |
308 | TTCN_Logger::log_event_va_list(err_msg, p_var); | |
309 | va_end(p_var); | |
310 | TTCN_Logger::OS_error(); | |
311 | TTCN_Logger::end_event(); | |
312 | #ifndef NDEBUG | |
313 | // the current implementation of the stack trace printout is misleading and cause more | |
314 | // trouble and misunderstanding than it solves | |
315 | // So it disabled in production compilation | |
316 | // It remains active in debug compilation | |
317 | where_am_i(TTCN_Logger::ERROR_UNQUALIFIED); | |
318 | #endif | |
319 | TTCN_Runtime::set_error_verdict(); | |
320 | TTCN_Logger::log_executor_runtime( | |
321 | TitanLoggerApi::ExecutorRuntime_reason::performing__error__recovery); | |
322 | throw TC_Error(); | |
323 | } | |
324 | } | |
325 | ||
326 | void TTCN_error_begin(const char *err_msg, ...) | |
327 | { | |
328 | if (TTCN_Runtime::is_in_ttcn_try_block()) { | |
329 | TTCN_Logger::begin_event_log2str(); | |
330 | // Add location data as if it were logged | |
331 | char* loc = TTCN_Location::print_location( | |
332 | TTCN_Logger::SINFO_STACK == TTCN_Logger::get_source_info_format(), | |
333 | TTCN_Logger::SINFO_NONE != TTCN_Logger::get_source_info_format(), | |
334 | TTCN_Logger::get_log_entity_name()); | |
335 | if (loc) { | |
336 | TTCN_Logger::log_event_str(loc); | |
337 | TTCN_Logger::log_event_str(" "); | |
338 | Free(loc); | |
339 | } | |
340 | TTCN_Logger::log_event_str("Dynamic test case error: "); | |
341 | va_list p_var; | |
342 | va_start(p_var, err_msg); | |
343 | TTCN_Logger::log_event_va_list(err_msg, p_var); | |
344 | va_end(p_var); | |
345 | } else { | |
346 | TTCN_Logger::begin_event(TTCN_Logger::ERROR_UNQUALIFIED); | |
347 | TTCN_Logger::log_event_str("Dynamic test case error: "); | |
348 | va_list p_var; | |
349 | va_start(p_var, err_msg); | |
350 | TTCN_Logger::log_event_va_list(err_msg, p_var); | |
351 | va_end(p_var); | |
352 | } | |
353 | } | |
354 | ||
355 | void TTCN_error_end() | |
356 | { | |
357 | if (TTCN_Runtime::is_in_ttcn_try_block()) { | |
358 | CHARSTRING error_str = TTCN_Logger::end_event_log2str(); | |
359 | throw TTCN_Error(mcopystr((const char*)error_str)); | |
360 | } else { | |
361 | TTCN_Logger::OS_error(); | |
362 | TTCN_Logger::end_event(); | |
363 | TTCN_Runtime::set_error_verdict(); | |
364 | TTCN_Logger::log_executor_runtime( | |
365 | TitanLoggerApi::ExecutorRuntime_reason::performing__error__recovery); | |
366 | throw TC_Error(); | |
367 | } | |
368 | } | |
369 | ||
370 | void TTCN_warning(const char *warning_msg, ...) | |
371 | { | |
372 | TTCN_Logger::begin_event(TTCN_Logger::WARNING_UNQUALIFIED); | |
373 | TTCN_Logger::log_event_str("Warning: "); | |
374 | va_list p_var; | |
375 | va_start(p_var, warning_msg); | |
376 | TTCN_Logger::log_event_va_list(warning_msg, p_var); | |
377 | va_end(p_var); | |
378 | TTCN_Logger::end_event(); | |
379 | } | |
380 | ||
381 | void TTCN_warning_begin(const char *warning_msg, ...) | |
382 | { | |
383 | TTCN_Logger::begin_event(TTCN_Logger::WARNING_UNQUALIFIED); | |
384 | TTCN_Logger::log_event_str("Warning: "); | |
385 | va_list p_var; | |
386 | va_start(p_var, warning_msg); | |
387 | TTCN_Logger::log_event_va_list(warning_msg, p_var); | |
388 | va_end(p_var); | |
389 | } | |
390 | ||
391 | void TTCN_warning_end() | |
392 | { | |
393 | TTCN_Logger::end_event(); | |
394 | } | |
395 | ||
396 | void TTCN_pattern_error(const char *error_msg, ...) | |
397 | { | |
398 | va_list p_var; | |
399 | va_start(p_var, error_msg); | |
400 | char *error_str = mprintf_va_list(error_msg, p_var); | |
401 | va_end(p_var); | |
402 | try { | |
403 | TTCN_error("Charstring pattern: %s", error_str); | |
404 | } catch (...) { | |
405 | Free(error_str); | |
406 | throw; | |
407 | } | |
408 | } | |
409 | ||
410 | void TTCN_pattern_warning(const char *warning_msg, ...) | |
411 | { | |
412 | va_list p_var; | |
413 | va_start(p_var, warning_msg); | |
414 | char *warning_str = mprintf_va_list(warning_msg, p_var); | |
415 | va_end(p_var); | |
416 | TTCN_warning("Charstring pattern: %s", warning_str); | |
417 | Free(warning_str); | |
418 | } |