ATLAS Offline Software
Loading...
Searching...
No Matches
FPEAudit_linux.icc
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
3*/
4
5#ifndef ATHENASERVICES_FPEAUDIT_LINUX_ICC
6#define ATHENASERVICES_FPEAUDIT_LINUX_ICC 1
7
8// C includes
9#include <fenv.h>
10#include <signal.h>
11#include <execinfo.h>
12#include <iostream>
13#include <string.h>
14#include "GaudiKernel/MsgStream.h"
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <dlfcn.h>
19#include <execinfo.h>
20#include <signal.h>
21#include <bfd.h>
22#include <unistd.h>
23#include <cxxabi.h> // for demangling
24#include <link.h> // for following code in shared libraries
25
26// signal handler needs C linkage
27extern "C"
28{
29 namespace FPEAudit
30 {
31 void fpe_sig_action( int /*sig*/, siginfo_t *si, void *puc )
32 {
33#if 0
34 // Pre-MT code had this. If the handler had been disabled,
35 // this would fall back to the previous behavior on a signal,
36 // which would usually mean a crash. However, this would not really
37 // be expected to happen, since exceptions are masked when the
38 // handler is disabled. However, with MT, it would be possible
39 // for a signal to be generated if it happens in a different
40 // thread than the one in which the handler was disabled.
41 // As we probably never really want to go to the fallback
42 // behavior (since that would well mean a crash), just disable
43 // this behavior.
44
45 // call old handlers if too many exceptions occured in this job
46 if ( s_handlerDisabled )
47 {
48 if (s_oldactHandler.sa_flags & SA_SIGINFO) {
49 if (s_oldactHandler.sa_handler) s_oldactHandler.sa_sigaction(sig,si,puc);
50 } else {
51 if (s_oldactHandler.sa_handler) s_oldactHandler.sa_handler(sig);
52 }
53 }
54#endif
55
56 // modify mcontext_t struct to reset FPE exception mask
57 ucontext_t *uc = (ucontext_t *)puc;
58
59 // not all FPEs classified correctly
60 std::cerr << "\n Caught FPE " << si->si_code << " (";
61 switch (si->si_code)
62 {
63 case FPE_INTDIV: std::cerr << "integer divide by zero"; break;
64 case FPE_INTOVF: std::cerr << "integer overflow"; break;
65 case FPE_FLTDIV: std::cerr << "floating point divide by zero"; break;
66 case FPE_FLTOVF: std::cerr << "floating point overflow"; break;
67 case FPE_FLTUND: std::cerr << "floating point underflow"; break;
68 case FPE_FLTRES: std::cerr << "floating point inexact result"; break;
69 case FPE_FLTINV: std::cerr << "floating point invalid operation"; break;
70 case FPE_FLTSUB: std::cerr << "subscript out of range"; break;
71 }
72
73 std::cerr << ") at address " << si->si_addr << ")\n";
74 int nptrs;
75
76 // stack trace is later printed in FPEAuditor::report_fpe()
77 if ( si->si_code == FPE_FLTDIV )
78 {
79 nptrs = backtrace(s_tlsdata.s_array_D, 100);
80 std::cerr << " backtrace() returned " << nptrs << " addresses\n\n";
81 if ( nptrs<100 )
82 s_tlsdata.s_array_D[nptrs]=NULL;
83 }
84 else if ( si->si_code == FPE_FLTOVF )
85 {
86 nptrs = backtrace(s_tlsdata.s_array_O, 100);
87 std::cerr << " backtrace() returned " << nptrs << " addresses\n\n";
88 if ( nptrs<100 )
89 s_tlsdata.s_array_O[nptrs]=NULL;
90 }
91 else
92 {
93 nptrs = backtrace(s_tlsdata.s_array_I, 100);
94 std::cerr << " backtrace() returned " << nptrs << " addresses\n\n";
95 if ( nptrs<100 )
96 s_tlsdata.s_array_I[nptrs]=NULL;
97 }
98
99 // reset FPE mask of the context where FPE occured
100#if defined(__linux__) && defined(__i386__)
101#if !defined(X86_FXSR_MAGIC)
102#define X86_FXSR_MAGIC 0x0000
103#endif
104#endif
105
106#if defined(__linux__)
107#if defined(__x86_64__)
108 mcontext_t *mc = &uc->uc_mcontext;
109 fpregset_t fpstate = mc->fpregs;
110 fpstate->mxcsr = 0x1F80;
111 fpstate->swd &= ~0xFF;
112#elif defined(__i386__)
113 mcontext_t *mc = &uc->uc_mcontext;
114 fpregset_t fpstate = mc->fpregs;
115 if ((fpstate->status >> 16) == X86_FXSR_MAGIC)
116 ((struct _fpstate*)fpstate)->mxcsr = 0x1F80;
117 fpstate->sw &= ~0xFF;
118#endif
119#endif
120
121#if defined(__amd64__) && defined(__x86_64__) || defined(__i386__)
122#define FPU_EXCEPTION_MASK 0x3f
123
124 /*
125 * x86 FPU Status Word:
126 *
127 * 0..5 -> Exception flags (see x86 FPU Control Word)
128 * 6 -> SF Stack Fault
129 * 7 -> ES Error Summary Status
130 */
131#define FPU_STATUS_FLAGS 0xff
132
133 /*
134 * MXCSR Control and Status Register:
135 *
136 * 0..5 -> Exception flags (see x86 FPU Control Word)
137 * 6 -> DAZ Denormals Are Zero
138 * 7..12 -> Exception mask (see x86 FPU Control Word)
139 */
140#define SSE_STATUS_FLAGS FPU_EXCEPTION_MASK
141#define SSE_EXCEPTION_MASK (FPU_EXCEPTION_MASK << 7)
142
143#if defined(__i386__)
144 /*
145 * It seems that we have no access to mxcsr on Linux. libc
146 * seems to be translating cw/sw to mxcsr.
147 */
148 unsigned long int *cw = &uc->uc_mcontext.fpregs->cw;
149 *cw |= FPU_EXCEPTION_MASK;
150
151 unsigned long int *sw = &uc->uc_mcontext.fpregs->sw;
152 *sw &= ~FPU_STATUS_FLAGS;
153#endif
154#if defined(__amd64__) && defined(__x86_64__) && !defined(__APPLE__)
155 __uint16_t *cw = &uc->uc_mcontext.fpregs->cwd;
156 *cw |= FPU_EXCEPTION_MASK;
157
158 __uint16_t *sw = &uc->uc_mcontext.fpregs->swd;
159 *sw &= ~FPU_STATUS_FLAGS;
160
161 __uint32_t *mxcsr = &uc->uc_mcontext.fpregs->mxcsr;
162 *mxcsr |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */
163 *mxcsr &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */
164 // fprintf(stderr, "in 64 bit code\n");
165#endif
166#endif
167 }
168
169 /*
170 * Implement unmask_fpe() and check_fpe() based on CPU/OS combination
171 */
172
173#if (defined(__i386__) || defined(__x86_64__)) && defined(__GNUC__) && !defined(__CYGWIN__)
174
175 void mask_x87(void)
176 {
177 unsigned short cw;
178 __asm__ __volatile__("fclex");
179 __asm__ __volatile__("fstcw %0" : "=m"(cw));
180 // std::cout << "mask fpe: " << std::hex << cw << std::dec << "\n";
181 cw |= (0x01|0x04|0x08); /* mask IM, ZM, OM */
182 __asm__ __volatile__("fldcw %0" : : "m"(cw));
183 // std::cout << "mask fpe: " << std::hex << cw << std::dec << "\n";
184 }
185
186 void mask_sse2(void)
187 {
188 unsigned int mxcsr;
189 __asm__ __volatile__("stmxcsr %0" : "=m"(mxcsr));
190 mxcsr |= 0x0680; /* clear exn flags, unmask OM, ZM, IM (not PM, UM, DM) */
191 __asm__ __volatile__("ldmxcsr %0" : : "m"(mxcsr));
192 }
193
194 void unmask_x87(void)
195 {
196 unsigned short cw;
197 __asm__ __volatile__("fstcw %0" : "=m"(cw));
198 // std::cout << "unmask fpe: " << std::hex << cw << std::dec << "\n";
199 cw &= ~(0x01|0x04|0x08); /* unmask IM, ZM, OM */
200 __asm__ __volatile__("fldcw %0" : : "m"(cw));
201 // std::cout << "unmask fpe: " << std::hex << cw << std::dec << "\n";
202 }
203
204 void unmask_sse2(void)
205 {
206 unsigned int mxcsr;
207 __asm__ __volatile__("stmxcsr %0" : "=m"(mxcsr));
208 mxcsr &= ~(0x003F|0x0680); /* clear exn flags, unmask OM, ZM, IM (not PM, UM, DM) */
209 __asm__ __volatile__("ldmxcsr %0" : : "m"(mxcsr));
210 }
211
212#if defined(__x86_64__)
213
214 inline int cpu_has_sse2(void) { return 1; }
215
216#else /* !__x86_64__ */
217
218 /*
219 * Check if an x86-32 processor has SSE2.
220 */
221 static unsigned int xor_eflags(unsigned int mask)
222 {
223 unsigned int eax, edx;
224
225 eax = mask; /* eax = mask */
226 __asm__("pushfl\n\t"
227 "popl %0\n\t" /* edx = original EFLAGS */
228 "xorl %0, %1\n\t" /* eax = mask ^ EFLAGS */
229 "pushl %1\n\t"
230 "popfl\n\t" /* new EFLAGS = mask ^ original EFLAGS */
231 "pushfl\n\t"
232 "popl %1\n\t" /* eax = new EFLAGS */
233 "xorl %0, %1\n\t" /* eax = new EFLAGS ^ old EFLAGS */
234 "pushl %0\n\t"
235 "popfl" /* restore original EFLAGS */
236 : "=d"(edx), "=a"(eax)
237 : "1"(eax));
238 return eax;
239 }
240
241 static __inline__ unsigned int cpuid_eax(unsigned int op)
242 {
243 unsigned int eax, save_ebx;
244
245 /* In PIC mode i386 reserves EBX. So we must save
246 and restore it ourselves to not upset gcc. */
247 __asm__(
248 "movl %%ebx, %1\n\t"
249 "cpuid\n\t"
250 "movl %1, %%ebx"
251 : "=a"(eax), "=m"(save_ebx)
252 : "0"(op)
253 : "cx", "dx");
254 return eax;
255 }
256
257 static __inline__ unsigned int cpuid_edx(unsigned int op)
258 {
259 unsigned int eax, edx, save_ebx;
260
261 /* In PIC mode i386 reserves EBX. So we must save
262 and restore it ourselves to not upset gcc. */
263 __asm__(
264 "movl %%ebx, %2\n\t"
265 "cpuid\n\t"
266 "movl %2, %%ebx"
267 : "=a"(eax), "=d"(edx), "=m"(save_ebx)
268 : "0"(op)
269 : "cx");
270 return edx;
271 }
272
273 /* The AC bit, bit #18, is a new bit introduced in the EFLAGS
274 * register on the Intel486 processor to generate alignment
275 * faults. This bit cannot be set on the Intel386 processor.
276 */
277 static __inline__ int is_386(void)
278 {
279 return ((xor_eflags(1<<18) >> 18) & 1) == 0;
280 }
281
282 /* Newer x86 processors have a CPUID instruction, as indicated by
283 * the ID bit (#21) in EFLAGS being modifiable.
284 */
285 static __inline__ int has_CPUID(void)
286 {
287 return (xor_eflags(1<<21) >> 21) & 1;
288 }
289
290 int cpu_has_sse2(void)
291 {
292 unsigned int maxlev, features;
293 static int has_sse2 = -1;
294
295 if (has_sse2 >= 0)
296 return has_sse2;
297 has_sse2 = 0;
298
299 if (is_386())
300 return 0;
301 if (!has_CPUID())
302 return 0;
303 maxlev = cpuid_eax(0);
304 /* Intel A-step Pentium had a preliminary version of CPUID.
305 It also didn't have SSE2. */
306 if ((maxlev & 0xFFFFFF00) == 0x0500)
307 return 0;
308 /* If max level is zero then CPUID cannot report any features. */
309 if (maxlev == 0)
310 return 0;
311 features = cpuid_edx(1);
312 has_sse2 = (features & (1 << 26)) != 0;
313
314 return has_sse2;
315 }
316#endif /* !__x86_64__ */
317
318#endif
319
320 void mask_fpe(void)
321 {
322 mask_x87();
323 if (cpu_has_sse2())
324 mask_sse2();
325 }
326
327 void unmask_fpe(void)
328 {
329 unmask_x87();
330 if (cpu_has_sse2())
331 unmask_sse2();
332 }
333
334 // printing of stacktrace including inlined functions. needs debug symbols
335 // uses libbdf and libiberty from gdb, which currently seemed to have a
336 // small memory leak (gdb 7.4.1)
337 void resolve(void *address, MsgStream& msg, bool print=false)
338 {
339 bfd *ibfd;
340 asymbol **symtab;
341 long nsize;
342 char **matching;
343 asection *text;
344
345 Dl_info info;
346 if (dladdr (address, &info) && info.dli_fname && info.dli_fname[0])
347 {
348 bfd_init();
349 ibfd = bfd_openr(info.dli_fname, NULL);
350
351 if (ibfd == NULL)
352 {
353 fprintf(stderr,"bfd_openr error\n");
354 return;
355 }
356
357 if (!bfd_check_format_matches(ibfd, bfd_object, &matching))
358 {
359 fprintf(stderr,"format_matches\n");
360 return;
361 }
362
363 nsize = bfd_get_symtab_upper_bound (ibfd);
364 symtab = (asymbol **)malloc(nsize);
365 /*nsyms =*/ bfd_canonicalize_symtab(ibfd, symtab);
366
367 text = bfd_get_section_by_name(ibfd, ".text");
368
369 long offset(0);
370 if(text)
371 offset = ((long)address) - text->vma;
372
373 if (strstr (info.dli_fname, ".so") != 0)
374 {
375 unsigned long libaddr = (unsigned long) info.dli_fbase;
376 unsigned long addr = (unsigned long)address;
377 if (text)
378 offset = addr - libaddr - text->vma;
379 }
380
381 if (offset > 0)
382 {
383 const char *file;
384 const char *func;
385 unsigned line;
386
387 bool first=true;
388 char *realname(0);
389 int status;
390
391 bool found = bfd_find_nearest_line(ibfd, text, symtab, offset, &file, &func, &line);
392
393 // dli_sname can be null. If we try to write that
394 // to a MsgStream, the stream will misbehave (all subsequent
395 // messages will be blank).
396 const char* dli_sname = info.dli_sname;
397 if (!dli_sname) {
398 dli_sname = "(null)";
399 }
400 if ( found && file)
401 {
402 do
403 {
404 // from http://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_demangling.html
405 realname = abi::__cxa_demangle(func ? func : info.dli_sname, 0, 0, &status);
406 if ( realname )
407 {
408 if (print)
409 fprintf(stderr,"%s : %s (%s,%u)\n",first ? " in function" : " included from", realname, file, line);
410 else
411 msg << ( first ? " in function" : " included from" ) << " : " << realname << " (" << file << ":" << line << ")\n";
412 }
413 else
414 {
415 if (print)
416 fprintf(stderr,"%s : %s (%s,%u)\n", first ? " in function" : " included from", func ? func : dli_sname, file, line);
417 else {
418 msg << ( first ? " in function" : " included from" )
419 << " : "
420 << ( func ? func : dli_sname )
421 << " (" << file << ":" << line << ")\n";
422 }
423 }
424 free(realname);
425
426 found = bfd_find_inliner_info (ibfd, &file, &func, &line);
427 first=false;
428 }
429 while(found);
430 }
431 }
432 if (print)
433 fprintf(stderr," in library : %s",info.dli_fname);
434 else
435 msg << " in library : " << info.dli_fname;
436 bfd_close(ibfd);
437 }
438 }
439 }
440}
441
442#endif /* !ATHENASERVICES_FPEAUDIT_LINUX_ICC*/