ATLAS Offline Software
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
27 extern "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*/