SURFEX v8.1
General documentation of Surfex
linuxtrbk.c
Go to the documentation of this file.
1 
2 /* linuxtrbk.c : Print traceback on linux */
3 
4 /*
5  Author: Sami Saarinen, ECMWF, 28-Apr-2006
6  The code "nicked" from ifsaux/support/drhook.c
7 
8 */
9 
10 #if defined(__GNUC__)
11 #define _GNU_SOURCE
12 #endif
13 
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <sys/types.h>
18 #include <unistd.h>
19 #include <signal.h>
20 #include <alloca.h>
21 #include "cargs.h"
22 
23 #define _XOPEN_SOURCE
24 
25 #ifdef CRAYXT
26 /* Cray XT3/XT4 with catamount microkernel */
27 #define system(cmd) (-1)
28 #endif
29 
30 #define PRETOSTR(x) #x
31 #define TOSTR(x) PRETOSTR(x)
32 
33 #if defined(SUN4) && !defined(PSTACKTRACE)
34 #define PSTACKTRACE /bin/pstack
35 #endif
36 
37 #define strequ(s1,s2) ((void *)s1 && (void *)s2 && strcmp(s1,s2) == 0)
38 #define strnequ(s1,s2,n) ((void *)s1 && (void *)s2 && memcmp(s1,s2,n) == 0)
39 
40 typedef struct {
41  const char *func;
42  const char *file;
43  unsigned int lineno;
44 } BFD_t;
45 
46 static int ResolveViaBFD(void *address, BFD_t *b, const char *str);
47 static void InitBFD();
48 
49 #if (defined(LINUX) || defined(SUN4)) && !defined(XT3) && !defined(XD1)
50 
51 #ifndef LINELEN
52 #define LINELEN 1024
53 #endif
54 
55 #define FFL __FUNCTION__,__FILE__,__LINE__
56 
57 #if defined(__GNUC__)
58 
59 #define _GNU_SOURCE
60 #include <pthread.h>
61 #include <sys/time.h>
62 #include <sys/resource.h>
63 
64 #if defined(LINUX) && !defined(CYGWIN) && !defined(DARWIN) && !defined(CRAYXT)
65 #include <execinfo.h>
66 #elif defined(DARWIN)
67 #include <errno.h>
68 #include <limits.h>
69 #undef HANDLE
70 #define HANDLE pthread_t *
71 #endif
72 
73 //#define __USE_GNU
74 //#define _XOPEN_SOURCE
75 
76 #if !defined(CYGWIN)
77 #include <ucontext.h>
78 #endif
79 
80 #endif /* defined(__GNUC__) */
81 
82 #if !defined(ADDR2LINE)
83 #define ADDR2LINE /usr/bin/addr2line
84 #endif
85 
86 #define GNUC_BTRACE 128
87 #define len_addr2linecmd (sizeof(TOSTR(ADDR2LINE)) + 80 + 4096 + GNUC_BTRACE * 30)
88 static char prealloc_addr2linecmd[len_addr2linecmd] = "";
89 
90 #ifdef BFDLIB
91 #include <bfd.h>
92 static const int has_bfd = 1;
93 #else
94 static const int has_bfd = 0;
95 #endif
96 
97 #if 0
98 /*
99  This code section is disabled for now (by Sami Saarinen, ECMWF, 05-Oct-2016
100  Does not (yet) work with GUARD region size changes done to ../support/env.c @ pthread_attr_init() override
101 */
102 #if defined(__GNUC__)
103 #include <sys/syscall.h>
104 #include <limits.h>
105 
106 #ifndef SYS_gettid
107 #define SYS_gettid __NR_gettid
108 #endif
109 
110 static pid_t gettid() {
111  pid_t tid = syscall(SYS_gettid);
112  return tid;
113 }
114 
116 
118 {
119  size_t StackSize = 0;
120  int StackMult = 1; /* Multiply slave stacksize by this number to get master stacksize; the default = 4 */
121  long long int value = 0;
122  char *env_ignore = getenv("IGNORE_STACKSIZE");
123  int ignore = 0;
124  int display = 0;
125  char *envstksz = getenv("STACKSIZE"); /* Master thread's stacksize -- according to Intel */
126  char *envompstksz = getenv("OMP_STACKSIZE"); /* slaves' stacksize, not master's. Master gets its default value from ulimit -s */
127  char *envmult = getenv("STACKMULT"); /* Becomes new StackMult, if defined and >= 1. Not used in case "envstksz" is defined. */
128 
129  /* Trying to figure out MPI task id since are *before* MPI_Init*() */
130  int me = -1; /* MPI task id >= 0 && <= NPES - 1 */
131  char *env_procid = getenv("EC_FARM_ID");
132  if (!env_procid) env_procid = getenv("ALPS_APP_PE");
133  if (!env_procid) env_procid = getenv("PMI_RANK");
134  if (!env_procid) env_procid = getenv("OMPI_COMM_WORLD_RANK");
135 
136  if (env_procid) {
137  me = atoi(env_procid);
138  if (me == 0) display = 1; /* To avoid lots of output */
139  }
140 
141  if (env_ignore) ignore = atoi(env_ignore);
142  if (!envstksz && !envompstksz) ignore = 1;
143 
144  if (envstksz || envompstksz) {
145  struct rlimit rlim;
146  char *s = envstksz ? strdup(envstksz) : strdup(envompstksz);
147  int slen = strlen(s);
148  char *last = s + slen - 1;
149  long long int mult = 1024;
150  if (*last == 'b' || *last == 'B') {
151  mult = 1;
152  *last = 0;
153  }
154  else if (*last == 'k' || *last == 'K') {
155  mult = 1024;
156  *last = 0;
157  }
158  else if (*last == 'm' || *last == 'M') {
159  mult = 1048576;
160  *last = 0;
161  }
162  else if (*last == 'g' || *last == 'G') {
163  mult = 1073741824;
164  *last = 0;
165  }
166  value = atoll(s) * mult;
167  free(s);
168  if (envmult) {
169  StackMult = atoi(envmult);
170  if (StackMult < 1) StackMult = 1;
171  }
172  if (envstksz) StackMult = 1;
173  if (getrlimit(RLIMIT_STACK, &rlim) == 0) {
174  rlim.rlim_cur = value * StackMult;
175  setrlimit(RLIMIT_STACK, &rlim);
176  }
177  }
178 
179  if (display) {
180  pthread_attr_t Attributes;
181  void *StackAddress;
182  pid_t pid = getpid();
183  pid_t tid = gettid();
184  char hostname[HOST_NAME_MAX];
185  char prefix[HOST_NAME_MAX + 256];
186  if (gethostname(hostname,sizeof(hostname)) != 0) strcpy(hostname,"unknown");
187 
188  snprintf(prefix,sizeof(prefix),"%s:%d:%lld:%lld",hostname,me,(long long int)pid,(long long int)tid);
189 
190  /* Get the pthread attributes using pthread-routines */
191  memset(&Attributes, 0x0, sizeof (Attributes)); /* An overkill */
192  pthread_getattr_np(pthread_self(), &Attributes);
193  pthread_attr_getstack(&Attributes, &StackAddress, &StackSize);
194  pthread_attr_destroy(&Attributes);
195 
196  if (ignore) {
197  fprintf(stderr,
198  "[%s] [%s@%s:%d] : Master thread's stack size = %llu bytes [setrlimit() was not called]\n",
199  prefix,FFL,
200  (unsigned long long int)StackSize);
201  }
202  else if (envstksz) {
203  fprintf(stderr,
204  "[%s] [%s@%s:%d] : Master thread's stack size = %llu bytes, (export STACKSIZE=%lld bytes)\n",
205  prefix,FFL,
206  (unsigned long long int)StackSize,
207  value);
208  }
209  else if (envompstksz) {
210  fprintf(stderr,
211  "[%s] [%s@%s:%d] : Master thread's stack size = %llu bytes, (export OMP_STACKSIZE=%lld bytes for slave thread, STACKMULT = %d)\n",
212  prefix,FFL,
213  (unsigned long long int)StackSize,
214  value,
215  StackMult);
216  }
217  }
218 }
219 #endif /* defined(__GNUC__) */
220 /* End of disabled code section */
221 #endif
222 
223 void
224 LinuxTraceBack(const char *prefix, const char *timestr, void *sigcontextptr)
225 {
226  const char *pfx = prefix ? prefix : "";
227  const char *ts = timestr ? timestr : "";
228  int sigcontextptr_given = sigcontextptr ? 1 : 0;
229  extern void gdb_trbk_();
230  extern void dbx_trbk_();
231 #if defined(__GNUC__) && defined(LINUX) && !defined(CYGWIN)
232  ucontext_t ctx;
233 #endif
234  static int recur = 0;
235  const char *a_out = ec_GetArgs(0);
236 #if defined(__GNUC__) && defined(LINUX) && !defined(CYGWIN) && !defined(DARWIN)
237  if (!sigcontextptr) {
238  sigcontextptr = (getcontext(&ctx) == 0) ? &ctx : NULL;
239  }
240 #endif
241  fprintf(stderr,"%s %s [%s@%s:%d] Backtrace(s) for program '%s' : sigcontextptr=%p\n",
242  pfx,ts,FFL,a_out ? a_out : "/dev/null", sigcontextptr);
243 
244  if (++recur > 1) {
245  fprintf(stderr,
246  "%s %s [%s@%s:%d] I don't handle recursive calls very well (recursion level = %d)\n",
247  pfx,ts,FFL,recur);
248  if (recur > 10) {
249  fprintf(stderr,"%s %s [%s@%s:%d] Recursion too deep. Exiting immediately with _exit(%d)\n",
250  pfx,ts,FFL,recur);
251  fflush(NULL);
252  _exit(recur); /* Exit immediately */
253  }
254  }
255 
256 #if defined(__GNUC__) && defined(LINUX) && !defined(CYGWIN) && !defined(DARWIN)
257  //fflush(NULL);
258 
259  if (sigcontextptr) {
260  /* To have a desired effect,
261  compile with -g (and maybe -O1 or greater to get some optimization)
262  and link with -g -Wl,-export-dynamic */
263  void *trace[GNUC_BTRACE];
264  ucontext_t *uc = (ucontext_t *)sigcontextptr;
265  int fd = fileno(stderr);
266  int trace_size = backtrace(trace, GNUC_BTRACE);
267  char *addr2linecmd = (has_bfd || (access(TOSTR(ADDR2LINE),X_OK) != 0)) ? NULL : prealloc_addr2linecmd;
268  char **strings = NULL;
269  if (trace_size > 0) {
270  /* overwrite sigaction with caller's address */
271 #if defined(REG_RIP)
272  trace[1] = (void *) uc->uc_mcontext.gregs[REG_RIP]; /* REG_RIP only available in 64-bit mode */
273 #elif defined(REG_EIP)
274  trace[1] = (void *) uc->uc_mcontext.gregs[REG_EIP]; /* REG_EIP only available in 32-bit mode */
275 #endif
276  }
277  strings = backtrace_symbols(trace, trace_size);
278  fprintf(stderr,"%s %s [%s@%s:%d] Backtrace (size = %d) with %s\n",
279  pfx,ts,FFL,trace_size,
280  addr2linecmd ? "addr2line-cmd" : has_bfd ? "BFD-method" : "plain hex-dump");
281  if (strings && trace_size > 0) {
282  int i;
283  FILE *fp = NULL;
284  if (addr2linecmd) {
285  /* Use ADDR2LINE to obtain source file & line numbers for each trace-address */
286  //snprintf(addr2linecmd, len_addr2linecmd, "%s -fs -e '%s'", TOSTR(ADDR2LINE), a_out);
287  strcpy(addr2linecmd,TOSTR(ADDR2LINE));
288  strcat(addr2linecmd," -fs -e '");
289  strcat(addr2linecmd,a_out);
290  strcat(addr2linecmd,"'");
291  for (i = 0; i < trace_size; i++) {
292  char s[30];
293  if (trace[i])
294  snprintf(s,sizeof(s)," %p",trace[i]);
295  else
296  snprintf(s,sizeof(s)," 0x0");
297  strcat(addr2linecmd,s);
298  }
299  if (getenv("LD_PRELOAD")) {
300  static char ld_preload[] = "LD_PRELOAD=";
301  putenv(ld_preload);
302  }
303  fprintf(stderr,"%s %s [%s@%s:%d] %s\n",
304  pfx,ts,FFL,addr2linecmd);
305  fp = popen(addr2linecmd,"r");
306  /* free(addr2linecmd); */
307  }
308  if (fp) {
309  for (i = 0; i < trace_size; i++) {
310  int ok = 0;
311  char func[LINELEN];
312  if (!feof(fp) && fgets(func, LINELEN, fp)) {
313  char line[LINELEN];
314  if (!feof(fp) && fgets(line, LINELEN, fp)) {
315  char *nl = strchr(func,'\n');
316  const char *last_slash = strrchr(strings[i],'/');
317  if (last_slash) last_slash++; else last_slash = strings[i];
318  if (nl) *nl = '\0';
319  nl = strchr(line,'\n');
320  if (nl) *nl = '\0';
321  fprintf(stderr, "%s %s [%s@%s:%d] [%d]: %s : %s() at %s\n", pfx, ts, FFL,i, last_slash, func, line);
322  ok = 1;
323  }
324  }
325  if (!ok) fprintf(stderr, "%s %s [%s@%s:%d] [%d]: %s\n", pfx, ts, FFL,i, strings[i]);
326  } /* for (i = 0; i < trace_size; i++) */
327  fflush(stderr);
328  pclose(fp);
329  } /* if (fp) */
330  else {
331  int rc;
332  InitBFD();
333  for (i = 0 ; i < trace_size; ++i) {
334  BFD_t b;
335  const char *last_slash = strrchr(strings[i],'/');
336  if (last_slash) last_slash++; else last_slash = strings[i];
337  rc = ResolveViaBFD(trace[i], &b, last_slash);
338  if (rc == 0) {
339  fprintf(stderr,"%s %s [%s@%s:%d] [%d]: %s : %s() at %s:%u\n",pfx,ts,FFL,i,last_slash,b.func,b.file,b.lineno);
340  }
341  else {
342  fprintf(stderr,"%s %s [%s@%s:%d] [%d]: %s : %p\n",pfx,ts,FFL,i,last_slash,trace[i]);
343  }
344  }
345  }
346  }
347  else {
348  /* Print traceback directly to fd=2 (stderr) */
349  backtrace_symbols_fd(trace, trace_size, fd);
350  } /* if (addr2linecmd) else ... */
351  if (strings) free(strings); /* Could we live without this free() ? */
352  }
353 #endif /* __GNUC__ */
354 
355  if (!sigcontextptr_given) goto finish;
356 
357 #if defined(PSTACKTRACE)
358  /* This is normally available on Sun/Solaris ("SUN4") platforms */
359  if (access(TOSTR(PSTACKTRACE),X_OK) == 0) {
360  char cmd[sizeof(TOSTR(PSTACKTRACE)) + 20];
361  snprintf(cmd,sizeof(cmd),"%s %d", TOSTR(PSTACKTRACE), pid);
362  fflush(NULL);
363  system(cmd);
364  fflush(NULL);
365  }
366 #endif /* defined(PSTACKTRACE) */
367 
368  gdb_trbk_();
369  dbx_trbk_();
370 
371  finish:
372  fprintf(stderr,"%s %s [%s@%s:%d] End of backtrace(s)\n",pfx,ts,FFL);
373  recur--;
374 }
375 
376 void linux_trbk_(void)
377 {
378  LinuxTraceBack(NULL,NULL,NULL);
379 }
380 
381 #else
382 
383 /* Non-Linux: A dummy call which does nothing */
384 
385 void LinuxTraceBack(const char *prefix, const char *timestr, void *sigcontextptr) { }
386 
387 void linux_trbk_(void) { }
388 
389 #endif
390 
391 void linux_trbk(void)
392 {
393  linux_trbk_();
394 }
395 
396 /* GNU-debugger traceback */
397 
398 #if !defined(GNUDEBUGGER)
399 #define GNUDEBUGGER /usr/bin/gdb
400 #endif
401 
402 void gdb_trbk_()
403 {
404  char *gdb = getenv("GNUDEBUGGER");
405  if (gdb &&
406  (access(TOSTR(GNUDEBUGGER),X_OK) == 0) && /* GNUDEBUGGER was set */
407  (strequ(gdb,"1") ||
408  strequ(gdb,"true") ||
409  strequ(gdb,"TRUE"))) {
410  char gdbcmd[65536];
411  pid_t pid = getpid();
412  const char *a_out = ec_GetArgs(0);
413  fprintf(stderr,
414  "[gdb_trbk] : Invoking %s ...\n",
415  TOSTR(GNUDEBUGGER));
416  snprintf(gdbcmd,sizeof(gdbcmd),
417  "set +e; /bin/echo '"
418  "set watchdog 1\n"
419  "set confirm off\n"
420  "set pagination off\n"
421  "set print elements 16\n"
422  "set print repeats 3\n"
423  "set print sevenbit-strings on\n"
424  "where\n"
425  "quit\n' > ./gdb_drhook.%d ; "
426  "%s -x ./gdb_drhook.%d -q -n -f -batch %s %d < /dev/null ; "
427  "/bin/rm -f ./gdb_drhook.%d"
428  , pid
429  , TOSTR(GNUDEBUGGER), pid, a_out, pid
430  , pid);
431 
432  /* fprintf(stderr,"%s\n",gdbcmd); */
433  fflush(NULL);
434  system(gdbcmd);
435  fflush(NULL);
436  }
437 }
438 
439 void gdb_trbk() { gdb_trbk_(); }
440 
441 
442 /* DBX-debugger traceback */
443 
444 #if !defined(DBXDEBUGGER)
445 #define DBXDEBUGGER /usr/bin/dbx
446 #endif
447 
448 void dbx_trbk_()
449 {
450  char *dbx = getenv("DBXDEBUGGER");
451  if (dbx &&
452  (access(TOSTR(DBXDEBUGGER),X_OK) == 0) && /* DBXDEBUGGER was set */
453  (strequ(dbx,"1") ||
454  strequ(dbx,"true") ||
455  strequ(dbx,"TRUE"))) {
456  pid_t pid = getpid();
457  const char *a_out = ec_GetArgs(0);
458  char dbxcmd[65536];
459 #if defined(SUN4)
460  const char *qopt = " -q";
461 #else
462  const char *qopt = "";
463 #endif
464  fprintf(stderr,
465  "[dbx_trbk] : Invoking %s ...\n",
466  TOSTR(DBXDEBUGGER));
467  if (a_out && (access(a_out,X_OK|R_OK) == 0)) {
468  snprintf(dbxcmd,sizeof(dbxcmd),
469  "set +e; /bin/echo 'where; quit; '"
470  " | %s%s %s %d ",
471  TOSTR(DBXDEBUGGER), qopt, a_out, pid);
472  }
473  else {
474  snprintf(dbxcmd,sizeof(dbxcmd),
475  "set +e; /bin/echo 'where; quit; '"
476  " | %s%s - %d ",
477  TOSTR(DBXDEBUGGER), qopt, pid);
478  }
479 
480  /* fprintf(stderr,"%s\n",dbxcmd); */
481  fflush(NULL);
482  system(dbxcmd);
483  fflush(NULL);
484  }
485 }
486 
487 void dbx_trbk() { dbx_trbk_(); }
488 
489 #ifdef BFDLIB
490 static bfd *abfd = 0;
491 static asymbol **syms = 0;
492 static asection *text = 0;
493 #endif
494 
495 static void InitBFD()
496 {
497 #ifdef BFDLIB
498  if (!abfd) {
499  const char *a_out = ec_GetArgs(0);
500 
501  bfd_init();
502 
503  abfd = bfd_openr(a_out, 0);
504  if (!abfd) {
505  perror("bfd_openr failed: ");
506  return;
507  }
508 
509  bfd_check_format(abfd,bfd_object);
510 
511  unsigned int storage_needed = bfd_get_symtab_upper_bound(abfd);
512  syms = (asymbol **) malloc(storage_needed);
513  unsigned int cSymbols = bfd_canonicalize_symtab(abfd, syms);
514 
515  text = bfd_get_section_by_name(abfd, ".text");
516  }
517 #endif
518 }
519 
520 static int ResolveViaBFD(void *address, BFD_t *b, const char *str)
521 {
522  int rc = -1;
523 #ifdef BFDLIB
524  if (!abfd) InitBFD();
525  if (b) {
526  long offset = (long)(address - text->vma);
527  if (offset > 0) {
528  memset(b,0x0,sizeof(*b));
529  if (bfd_find_nearest_line(abfd, text, syms, offset, &b->file, &b->func, &b->lineno) && b->file && b->func) {
530  const char *last_slash = strrchr(b->file,'/');
531  if (last_slash) b->file = last_slash + 1;
532  rc = 0;
533  }
534  else if (str) {
535  static const char qmarks[] = "??";
536  if (!b->func) {
537  static char *s = NULL; // Not thread safe
538  char *loc_lbr;
539  if (s) free(s);
540  s = strdup(str);
541  loc_lbr = strchr(s,'(');
542  if (loc_lbr) {
543  char *loc_add = NULL;
544  *loc_lbr++ = 0;
545  loc_add = strchr(loc_lbr,'+');
546  if (loc_add) *loc_add = 0;
547  b->func = loc_lbr;
548  }
549  else {
550  b->func = qmarks;
551  }
552  }
553  if (!b->file) b->file = qmarks;
554  rc = 0;
555  } /* if (bfd_find_nearest_line(...)) else if (str) ... */
556  }
557  }
558 #endif
559  return rc;
560 }
561 
const char * ec_GetArgs(int argno)
Definition: cargs.c:138
void LinuxTraceBack(const char *prefix, const char *timestr, void *sigcontextptr)
Definition: linuxtrbk.c:224
void dbx_trbk()
Definition: linuxtrbk.c:487
static pid_t gettid()
Definition: linuxtrbk.c:110
static bfd * abfd
Definition: linuxtrbk.c:490
void linux_trbk_(void)
Definition: linuxtrbk.c:376
FILE * fp
Definition: opfla_perfmon.c:24
static const int has_bfd
Definition: linuxtrbk.c:92
void linux_trbk(void)
Definition: linuxtrbk.c:391
static void SetMasterThreadsStackSizeBeforeMain()
Definition: linuxtrbk.c:115
static int ResolveViaBFD(void *address, BFD_t *b, const char *str)
Definition: linuxtrbk.c:520
static asection * text
Definition: linuxtrbk.c:492
void gdb_trbk()
Definition: linuxtrbk.c:439
int snprintf(char *str, size_t size, const char *format,...)
Definition: endian.c:108
void __attribute__((constructor))
Definition: memory_hook.c:50
static asymbol ** syms
Definition: linuxtrbk.c:491
static char prealloc_addr2linecmd[len_addr2linecmd]
Definition: linuxtrbk.c:88
static char * a_out
Definition: cargs.c:18
pid_t pid
Definition: opfla_perfmon.c:22
void dbx_trbk_()
Definition: linuxtrbk.c:448
void gdb_trbk_()
Definition: linuxtrbk.c:402
static void InitBFD()
Definition: linuxtrbk.c:495