1. Wez Furlong
  2. gimli

Commits

Wez Furlong  committed ad0db17 Merge

merge default -> lua

  • Participants
  • Parent commits a4251c5, 780bd5b
  • Branches lua

Comments (0)

Files changed (8)

File configure.ac

View file
         ;;
     esac
     LDFLAGS="-lrt"
-    CORONER_LDFLAGS="-lc_db"
+    CORONER_LDFLAGS="-lc_db -lrtld_db"
   ;;
 esac
 AC_DEFINE_UNQUOTED([MODULE_SUFFIX], "$MODULE_SUFFIX", [platform module suffix])

File dwarf-read.c

View file
       }
     }
   }
+
+  /* ideally would like to factor this out somewhere nicer...
+   * When we see a siginfo_t, replace our usual render with a more terse
+   * and readable version; siginfo_t is quite a big structure with a lot
+   * of union fields that are quite noisy to read */
+  if (!strcmp(datatype, "siginfo_t *")) {
+    char namebuf[1024];
+    siginfo_t si;
+
+    if (gimli_read_mem(varaddr, &si, sizeof(si)) == sizeof(si)) {
+      gimli_render_siginfo(&si, namebuf, sizeof(namebuf));
+      printf("  siginfo_t *%s = %p\n    %s\n",
+          varname, varaddr, namebuf);
+      return 1;
+    }
+  }
   return 0;
 }
 
       default:
         printf("Unhandled location form %llx\n", location->form);
     }
-  } else {
-    printf("no location attribute for parameter %s die->offset=%llx %s\n",
-        name ? (char*)name->ptr : "?", die->offset, m->objfile->objname);
+  } else if (name) {
+    /* no location defined, so assume the compiler optimized it away */
+    printf("  %s <optimized out>\n", name->ptr);
+    return 1;
   }
 
   //printf("param: die offset %llx @ %p\n", die->offset, (void*)(intptr_t)res);

File heartbeat.c

View file
  */
 static void request_trace(void)
 {
-#ifdef sun
-  /* on Solaris, it seems STOP'ing yourself somehow prevents one's
-   * parent from getting a SIGCHLD, so we trigger one explicitly
-   * first */
-  kill(getppid(), SIGCHLD);
-#endif
+  /* ask parent to trace us */
+  kill(getppid(), SIGUSR2);
   /* go to sleep; parent gets notified that we stopped and initiates
    * a trace; it will resume us once tracing is complete */
   kill(getpid(), SIGSTOP);

File impl.h

View file
 #endif
 #if defined(sun) || defined(__FreeBSD__)
 #include <proc_service.h>
+#include <rtld_db.h>
 #endif
 #if defined(sun) || defined(__linux__) || defined(__FreeBSD__)
 #include <thread_db.h>
 #if defined(__linux__)
   struct user_regs_struct regs;
 #elif defined(sun)
-  gregset_t regs;
+  prgregset_t regs;
   lwpstatus_t lwpst; 
 #elif defined(__FreeBSD__)
   gregset_t regs;

File main.c

View file
 #ifdef WIFCONTINUED
         if (WIFCONTINUED(status)) {
           gimli_set_proctitle("child pid %d continued", dead_pid);
+          p->exit_status = 0;
           break;
         }
 #endif
               kill(p->pid, SIGCONT);
               break;
           }
-        } else {
-          gimli_set_proctitle("child pid %d exited (status=%x)",
+        } else if (WIFSIGNALED(status)) {
+          gimli_set_proctitle("child pid %d killed by signal %d (status=0x%x)",
+              dead_pid, WTERMSIG(status), status);
+          p->running = 0;
+        } else if (WIFEXITED(status)) {
+          gimli_set_proctitle("child pid %d exited (status=0x%x)",
               dead_pid, status);
           p->running = 0;
+        } else {
+          gimli_set_proctitle("child pid %d has wait status=0x%x\n",
+              dead_pid, status);
         }
         break;
       }
   }
 }
 
+/* SIGUSR2 requests that we trace a child */
+static void catch_usr2(int sig_num)
+{
+  struct kid_proc *p;
+
+  for (p = procs; p; p = p->next) {
+    if (p->running && p->tracer_for == 0) {
+      if (p->should_trace == TRACE_NONE) {
+        logprint("caught signal SIGUSR2, tracing child pid=%d\n", p->pid);
+        p->should_trace = TRACE_ME;
+      } else if (p->should_trace == TRACE_DONE) {
+#ifdef sun
+        /* it was already traced by watchdog; then we sent it
+         * SIGABRT and it STOP'd itself, so we should wake it
+         * back up with a SIGCONT and let it call its shutdown
+         * function.
+         * This bit is Solaris specific since sending yourself
+         * SIGSTOP doesn't appear to raise SIGCHLD in the parent.
+         */
+        gimli_set_proctitle("child pid %d continuing", p->pid);
+        p->exit_status = 0;
+        kill(p->pid, SIGCONT);
+#endif
+      }
+    }
+  }
+
+  signal(SIGUSR2, catch_usr2);
+}
+
 static void catch_hup(int sig_num)
 {
   struct kid_proc *p;
   return 1;
 }
 
-static void mask_signal(int how, int signo)
+static void mask_signal(int how, int signo, int othersig)
 {
   sigset_t s;
 
   sigemptyset(&s);
   sigaddset(&s, signo);
+  sigaddset(&s, othersig);
 
   sigprocmask(how, &s, NULL);
 }
 
 static void link_child(struct kid_proc *p)
 {
-  mask_signal(SIG_BLOCK, SIGCHLD);
+  mask_signal(SIG_BLOCK, SIGCHLD, SIGUSR2);
 
   p->next = procs;
   if (procs) {
   }
   procs = p;
 
-  mask_signal(SIG_UNBLOCK, SIGCHLD);
+  mask_signal(SIG_UNBLOCK, SIGCHLD, SIGUSR2);
 }
 
 static void unlink_child(struct kid_proc *p)
 {
-  mask_signal(SIG_BLOCK, SIGCHLD);
+  mask_signal(SIG_BLOCK, SIGCHLD, SIGUSR2);
 
   if (procs == p) {
     procs = p->next;
     p->prev->next = p->next;
   }
 
-  mask_signal(SIG_UNBLOCK, SIGCHLD);
+  mask_signal(SIG_UNBLOCK, SIGCHLD, SIGUSR2);
 }
 
 static struct kid_proc *spawn_child(void)
   struct kid_proc *trc;
   int tracefd;
 
-//  sleep(300);
-
   if (p->watchdog) {
     gimli_set_proctitle("watchdog triggered: tracing %d", p->pid);
   } else {
           wait_for_exit(trc, 2);
         }
       }
+      if (debug) {
+        logprint("tracer pid=%d completed\n", trc->pid);
+      }
       p->should_trace = TRACE_DONE;
     }
 
   }
 
   if (p->watchdog) {
+    if (debug) {
+      logprint("watchdog detected in pid=%d, sending SIGABRT\n", p->pid);
+    }
     kill(p->pid, SIGABRT);
   } else {
     /* allow the child opportunity to clean up */
+    if (debug) {
+      logprint("fault detected in pid=%d, sending SIGCONT\n", p->pid);
+    }
+    if (p->running) {
+      p->exit_status = 0;
+    }
     kill(p->pid, SIGCONT);
   }
 }
   }
   signal(SIGCHLD, is_child ? SIG_DFL : catch_sigchld);
   signal(SIGUSR1, is_child ? SIG_DFL : catch_usr1);
+  signal(SIGUSR2, is_child ? SIG_DFL : catch_usr2);
 }
 
 static int did_hb_state_change(struct gimli_heartbeat *ref)
      * this event as a watchdog, we make setting the watchdog flag
      * contingent on us not being in the middle of a trace. */
     if (p->should_trace == TRACE_NONE) {
+      logprint("ticks = %d vs %d\n", hb.ticks, heartbeat->ticks);
       p->watchdog = 1;
       p->should_trace = TRACE_ME;
     }
        * <child stops self>
        * <monitor continues child>
        * <child runs shutdown handler>
-       * Without the sleep here, we can decide that the child is dead
-       * before it gets to its shutdown handler */
-      sleep(4);
+       */
     }
 
-    wait_for_exit(p, watchdog_stop_interval);
+    if (debug && p->running) {
+      logprint("waiting for child pid=%d to finish shutdown\n",
+          p->pid);
+    }
+
+    /* wait_for_exit can return early if the child heart-beats during
+     * its shutdown handler */
+    while (p->running) {
+      hb = *heartbeat;
+      wait_for_exit(p, watchdog_stop_interval);
+      if (use_heartbeat && did_hb_state_change(&hb)) {
+        continue;
+      }
+      break;
+    }
 
     while (p->running) {
+      if (debug) {
+        logprint("child pid %d still running after shutdown, sending SIGKILL\n",
+            p->pid);
+      }
       kill(p->pid, SIGKILL);
       wait_for_exit(p, 2);
     }

File solaris.c

View file
   int status_fd; /* handle on /proc/pid/status */
   pstatus_t status;
   struct ps_prochandle *next;
+  auxv_t *auxv;
+  int naux;
 };
 
 static struct ps_prochandle targetph = {
 static td_thragent_t *ta = NULL;
 static struct gimli_thread_state *cur_enum_thread = NULL;
 
+#define PR_OBJ_EVERY ((const char*)-1) /* search everything */
+
 ps_err_e ps_pcontinue(struct ps_prochandle *ph)
 {
   return PS_OK;
 ps_err_e ps_pglobal_lookup(struct ps_prochandle *ph,
       const char *object_name, const char *sym_name, psaddr_t *sym_addr)
 {
-  struct gimli_symbol *sym = gimli_sym_lookup(object_name, sym_name);
+  struct gimli_symbol *sym;
+
+  if (object_name == PS_OBJ_EXEC) {
+    object_name = gimli_files->objname;
+  } else if (object_name == PS_OBJ_LDSO) {
+    long base = 0;
+    auxv_t *av;
+    struct gimli_object_mapping *m;
+
+    object_name = NULL;
+    for (av = targetph.auxv; av->a_type != AT_NULL; av++) {
+      if (av->a_type == AT_BASE) {
+        base = av->a_un.a_val;
+        break;
+      }
+    }
+
+    for (m = gimli_mappings; m; m = m->next) {
+      if (m->base == base) {
+        object_name = m->objfile->objname;
+      }
+    }
+  } else if (object_name == PR_OBJ_EVERY) {
+    struct gimli_object_file *f;
+
+    for (f = gimli_files; f; f = f->next) {
+      sym = gimli_sym_lookup(f->objname, sym_name);
+      if (sym) {
+        *sym_addr = (psaddr_t)sym->addr;
+        return PS_OK;
+      }
+      return PS_NOSYM;
+    }
+  }
+
+  sym = gimli_sym_lookup(object_name, sym_name);
   if (sym) {
     *sym_addr = (psaddr_t)sym->addr;
     return PS_OK;
   return PS_ERR;
 }
 
-static void user_regs_to_thread(prgregset_t ur,
+static void show_regs(prgregset_t regs)
+{
+#if 0
+  int i;
+  for (i = 0; i < NPRGREG ; i++) {
+    printf("reg: %d: %p\n", i, regs[i]);
+  }
+#endif
+}
+
+static void user_regs_to_thread(prgregset_t *ur,
   struct gimli_thread_state *thr)
 {
   memcpy(&thr->regs, ur, sizeof(*ur));
-  thr->fp = (void*)ur[R_FP];
-  thr->pc = (void*)ur[R_PC];
-  thr->sp = (void*)ur[R_SP];
+
+  thr->fp = (void*)thr->regs[R_FP];
+  thr->pc = (void*)thr->regs[R_PC];
+  thr->sp = (void*)thr->regs[R_SP];
+
+  show_regs(thr->regs);
 }
 
 static int enum_threads(const td_thrhandle_t *thr, void *unused)
     return 0;
   }
 
-  user_regs_to_thread(ur, th);
+  user_regs_to_thread(&ur, th);
   get_lwp_status(targetph.pid, info.ti_lid, &cur_enum_thread->lwpst);
 
   cur_enum_thread->lwpid = info.ti_lid;
   return 0;
 }
 
+ps_err_e ps_pglobal_sym(struct ps_prochandle *h,
+	const char *object_name, const char *sym_name, ps_sym_t *sym)
+{
+  return PS_NOSYM;
+}
+
+ps_err_e ps_pread(struct ps_prochandle *h,
+			psaddr_t addr, void *buf, size_t size)
+{
+  return gimli_read_mem((void*)addr, buf, size) == size ? PS_OK : PS_BADADDR;
+}
+
+ps_err_e ps_pwrite(struct ps_prochandle *h,
+			psaddr_t addr, const void *buf, size_t size)
+{
+  if (targetph.as_fd >= 0) {
+    ssize_t ret = pwrite(targetph.as_fd, buf, size, (intptr_t)addr);
+    return ret == size ? PS_OK : PS_BADADDR;
+  }
+  return PS_ERR;
+}
+
+ps_err_e ps_pauxv(struct ps_prochandle *h, const auxv_t **auxv)
+{
+  *auxv = targetph.auxv;
+  return PS_OK;
+}
+
+void ps_plog(const char *fmt, ...)
+{
+  va_list ap;
+  va_start(ap, fmt);
+  vfprintf(stderr, fmt, ap);
+  va_end(ap);
+}
+
+static int collect_map(const rd_loadobj_t *obj, void *unused)
+{
+  char *name;
+
+  name = gimli_read_string((void*)obj->rl_nameaddr);
+  gimli_add_mapping(name, (void*)obj->rl_base, obj->rl_bend - obj->rl_base, 0);
+  free(name);
+
+  return 1;
+}
+
+ps_err_e ps_pdmodel(struct ps_prochandle *h, int *data_model)
+{
+  *data_model = targetph.status.pr_dmodel;
+  return PS_OK;
+}
+
+static int read_auxv(void)
+{
+  int fd, n;
+  char path[1024];
+  struct stat st;
+
+  snprintf(path, sizeof(path), "/proc/%d/auxv", targetph.pid);
+  fd = open(path, O_RDONLY);
+  if (fd == -1) {
+    return 0;
+  }
+  if (fstat(fd, &st) == 0 && st.st_size >= sizeof(auxv_t)) {
+    targetph.auxv = malloc(st.st_size + sizeof(auxv_t));
+
+    n = read(fd, targetph.auxv, st.st_size);
+    n /= sizeof(auxv_t);
+    if (n >= 1) {
+      targetph.auxv[n].a_type = AT_NULL;
+      targetph.auxv[n].a_un.a_val = 0L;
+      targetph.naux = n;
+    }
+  }
+  close(fd);
+}
+
+static void read_rtld_maps(void)
+{
+  rd_agent_t *agt;
+
+  if (!read_auxv()) {
+    return;
+  }
+
+  agt = rd_new(&targetph);
+  rd_loadobj_iter(agt, collect_map, NULL);
+  rd_reset(agt);
+}
+
 static void read_maps(void)
 {
   char filename[1024];
       gimli_add_mapping(target, (void*)m->pr_vaddr, m->pr_size, m->pr_offset);
     }
   }
-  
+
   free(maps);
 }
 
     fprintf(stderr, "open(%s): %s\n", path, strerror(errno));
     goto err;
   }
-  
+
   snprintf(path, sizeof(path)-1, "/proc/%d/status", pid);
   targetph.status_fd = open(path, O_RDONLY);
   if (targetph.status_fd == -1) {
     fprintf(stderr, "open(%s): %s\n", path, strerror(errno));
     goto err;
   }
- 
+
   /* now ask the process the stop */
   ctl[0] = PCDSTOP;
   ctl[1] = PCTWSTOP;
   }
 #endif
 
-  read_maps();
-
   te = td_init();
   if (te != TD_OK) {
     fprintf(stderr, "td_init failed: %d\n", te);
     }
   } else {
     gimli_threads = calloc(1, sizeof(*gimli_threads));
-    user_regs_to_thread(targetph.status.pr_lwp.pr_reg, &gimli_threads[0]);
+    user_regs_to_thread(&targetph.status.pr_lwp.pr_reg, &gimli_threads[0]);
     gimli_threads->lwpst = targetph.status.pr_lwp;
     gimli_nthreads = 1;
     gimli_threads->lwpid = pid;
   }
 
+  read_maps();
+  read_rtld_maps();
+
   return 1;
 
 err:
 
     write(targetph.ctl_fd, ctl, sizeof(ctl));
   }
-  
+
   if (targetph.as_fd >= 0) {
     close(targetph.as_fd);
     targetph.as_fd = -1;
   return 1;
 }
 
+#ifdef __sparc__
+static int read_gwindow(struct gimli_unwind_cursor *cur)
+{
+  char path[1024];
+  int fd, n, rv = 0;
+  struct stat64 st;
+  gwindows_t gwin;
+
+  snprintf(path, sizeof(path), "/proc/%d/lwp/%d/gwindows",
+    targetph.pid, cur->st.lwpid);
+
+  if (stat64(path, &st) == -1 || st.st_size == 0) {
+    return 0;
+  }
+
+  fd = open(path, O_RDONLY);
+  if (fd == -1) {
+    return 0;
+  }
+
+  /* zero out gwin, as we may get a partial read */
+  memset(&gwin, 0, sizeof(gwin));
+  n = read(fd, &gwin, sizeof(gwin));
+  if (n > 0) {
+    int i;
+
+    for (i = 0; i < gwin.wbcnt; i++) {
+      if (gwin.spbuf[i] == cur->st.fp) {
+        /* we found the frame we wanted */
+        memcpy(&cur->st.regs[R_L0], &gwin.wbuf[i], sizeof(struct rwindow));
+        rv = 1;
+        break;
+      }
+    }
+  }
+  close(fd);
+
+  return rv;
+}
+#endif
+
 int gimli_unwind_next(struct gimli_unwind_cursor *cur)
 {
   struct frame frame;
   struct gimli_unwind_cursor c;
-  
+
   if (gimli_is_signal_frame(cur)) {
     ucontext_t uc;
 
   }
 
   if (c.st.fp) {
+    *cur = c;
+
+#ifndef __sparc__
     if (gimli_read_mem(c.st.fp, &frame, sizeof(frame)) != sizeof(frame)) {
       memset(&frame, 0, sizeof(frame));
     }
 
-    if (c.st.fp == frame.fr_savfp) {
+    if (c.st.fp == (void*)frame.fr_savfp) {
       return 0;
     }
     cur->st.fp = (void*)frame.fr_savfp;
       cur->st.pc--;
     }
     cur->st.regs[R_FP] = (intptr_t)cur->st.fp;
-#ifdef __sparc__
+#else
     cur->st.regs[R_PC] = cur->st.regs[R_I7];
     cur->st.regs[R_nPC] = cur->st.regs[R_PC] + 4;
     memcpy(&cur->st.regs[R_O0], &cur->st.regs[R_I0], 8 * sizeof(prgreg_t));
-    cur->st.sp = (void*)cur->st.regs[R_FP] + STACK_BIAS;
+    show_regs(cur->st.regs);
+    if (cur->st.regs[R_FP] == 0) {
+      return 0;
+    }
 
-    if (cur->st.sp && gimli_read_mem(cur->st.sp, &cur->st.regs[R_L0],
+    if (gimli_read_mem((void*)(cur->st.regs[R_FP] + STACK_BIAS),
+        &cur->st.regs[R_L0],
         sizeof(struct rwindow)) != sizeof(struct rwindow)) {
-      /* we should try to fill this data in via gwindow information */
-      fprintf(stderr, "unable to read rwindow @ %p\n", cur->st.sp);
+      /* try to fill this data in via gwindow information */
+      if (!read_gwindow(cur)) {
+        fprintf(stderr, "unable to read rwindow @ %p, and no gwindow\n",
+          cur->st.regs[R_FP]);
+      }
     }
+    cur->st.fp = (void*)cur->st.regs[R_FP];
+    cur->st.pc = (void*)cur->st.regs[R_PC];
+    cur->st.sp = (void*)cur->st.regs[R_SP];
+    cur->dwarffail = 0;
 #endif
 
     if (cur->st.pc == 0 && cur->st.lwpst.pr_oldcontext) {

File trace.c

View file
   return "";
 }
 
+int gimli_render_siginfo(siginfo_t *si, char *buf, size_t bufsize)
+{
+  char *source = "";
+  int use_fault_addr = 0;
+  int use_pid = 0;
+  char *signame;
+  char pidbuf[64];
+  char namebuf[1024];
+  char addrbuf[1024];
+
+  signame = strsignal(si->si_signo);
+  if (!signame) signame = "Unknown signal";
+
+  if (si->si_code > 0) {
+    /* kernel generated; si_code has additional information */
+    switch (si->si_signo) {
+      case SIGILL:
+        use_fault_addr = 1;
+        switch (si->si_code) {
+          case ILL_ILLOPC: source = "illegal opcode"; break;
+          case ILL_ILLOPN: source = "illegal operand"; break;
+          case ILL_ILLADR: source = "illegal addressing mode"; break;
+          case ILL_ILLTRP: source = "illegal trap"; break;
+          case ILL_PRVOPC: source = "privileged opcode"; break;
+          case ILL_PRVREG: source = "privileged register"; break;
+          case ILL_COPROC: source = "co-processor error"; break;
+          case ILL_BADSTK: source = "internal stack error"; break;
+        }
+        break;
+      case SIGFPE:
+        use_fault_addr = 1;
+        switch (si->si_code) {
+          case FPE_INTDIV: source = "integer divide by zero"; break;
+          case FPE_INTOVF: source = "integer overflow"; break;
+          case FPE_FLTDIV: source = "floating point divide by zero"; break;
+          case FPE_FLTOVF: source = "floating point overflow"; break;
+          case FPE_FLTUND: source = "floating point underflow"; break;
+          case FPE_FLTRES: source = "floating point inexact result"; break;
+          case FPE_FLTINV: source = "floating point invalid operation"; break;
+          case FPE_FLTSUB: source = "subscript out of range"; break;
+        }
+        break;
+      case SIGSEGV:
+        use_fault_addr = 1;
+        switch (si->si_code) {
+          case SEGV_MAPERR: source = "address not mapped to object"; break;
+          case SEGV_ACCERR: source = "invalid permissions for mapped object"; break;
+        }
+        break;
+      case SIGBUS:
+        use_fault_addr = 1;
+        switch (si->si_code) {
+          case BUS_ADRALN: source = "invalid address alignment"; break;
+          case BUS_ADRERR: source = "non-existent physical address"; break;
+          case BUS_OBJERR: source = "object specific hardware error"; break;
+        }
+        break;
+      case SIGTRAP:
+        switch (si->si_code) {
+          case TRAP_BRKPT: source = "process breakpoint"; break;
+          case TRAP_TRACE: source = "process trace trap"; break;
+        }
+        break;
+      case SIGCHLD:
+        use_pid = 1;
+        switch (si->si_code) {
+          case CLD_EXITED: source = "child has exited"; break;
+          case CLD_KILLED: source = "child was killed"; break;
+          case CLD_DUMPED: source = "child terminated abnormally"; break;
+          case CLD_TRAPPED: source = "traced child has trapped"; break;
+          case CLD_STOPPED: source = "child has stopped"; break;
+          case CLD_CONTINUED: source = "stopped child has continued"; break;
+        }
+        break;
+#ifdef SIGPOLL
+      case SIGPOLL:
+        switch (si->si_code) {
+          case POLL_IN: source = "data input available"; break;
+          case POLL_OUT: source = "output buffers available"; break;
+          case POLL_MSG: source = "input message available"; break;
+          case POLL_ERR: source = "I/O error"; break;
+          case POLL_PRI: source = "high priority input available"; break;
+          case POLL_HUP: source = "device disconnected"; break;
+        }
+        break;
+#endif
+
+    }
+  } else {
+    use_pid = 1;
+    switch (si->si_code) {
+#ifdef SI_NOINFO
+      case SI_NOINFO:
+        /* explicitly have no info */
+        use_pid = 0;
+        break;
+#endif
+      case SI_USER:    source = "userspace"; break;
+#ifdef SI_LWP
+      case SI_LWP:     source = "_lwp_kill"; break;
+#endif
+      case SI_QUEUE:   source = "sigqueue"; break;
+      case SI_TIMER:   source = "timer";   break;
+      case SI_ASYNCIO: source = "asyncio"; break;
+      case SI_MESGQ:   source = "mesgq"; break;
+#ifdef SI_KERNEL
+      case SI_KERNEL:  source = "kernel"; break;
+#endif
+#ifdef SI_SIGIO
+      case SI_SIGIO:   source = "sigio"; break;
+#endif
+#ifdef SI_TKILL
+      case SI_TKILL:   source = "tkill"; break;
+#endif
+#ifdef SI_RCTL
+      case SI_RCTL: source = "resource-control"; break;
+#endif
+    }
+  }
+
+  pidbuf[0] = '\0';
+  addrbuf[0] = '\0';
+  if (use_pid) {
+    snprintf(pidbuf, sizeof(pidbuf), " pid=%d", si->si_pid);
+  }
+  if (use_fault_addr) {
+    const char *name;
+
+    name = gimli_pc_sym_name(si->si_addr, namebuf, sizeof(namebuf));
+    if (name && strlen(name)) {
+      snprintf(addrbuf, sizeof(addrbuf), " (%s)", name);
+    } else {
+      snprintf(addrbuf, sizeof(addrbuf), " (" PTRFMT ")", si->si_addr);
+    }
+  }
+
+  return snprintf(buf, bufsize, "Signal %d: %s. %s%s%s",
+      si->si_signo, signame, source,
+      pidbuf, addrbuf);
+}
+
 void gimli_render_frame(int tid, int nframe, struct gimli_unwind_cursor *frame)
 {
   const char *name;
 
   if (gimli_is_signal_frame(&cur)) {
     if (cur.si.si_signo) {
-      char *source = "";
-      int use_fault_addr = 0;
-      int use_pid = 0;
-      char *signame;
-
-      signame = strsignal(cur.si.si_signo);
-      if (!signame) signame = "Unknown signal";
-
-      if (cur.si.si_code > 0) {
-        /* kernel generated; si_code has additional information */
-        switch (cur.si.si_signo) {
-          case SIGILL:
-            use_fault_addr = 1;
-            switch (cur.si.si_code) {
-              case ILL_ILLOPC: source = "illegal opcode"; break;
-              case ILL_ILLOPN: source = "illegal operand"; break;
-              case ILL_ILLADR: source = "illegal addressing mode"; break;
-              case ILL_ILLTRP: source = "illegal trap"; break;
-              case ILL_PRVOPC: source = "privileged opcode"; break;
-              case ILL_PRVREG: source = "privileged register"; break;
-              case ILL_COPROC: source = "co-processor error"; break;
-              case ILL_BADSTK: source = "internal stack error"; break;
-            }
-            break;
-          case SIGFPE:
-            use_fault_addr = 1;
-            switch (cur.si.si_code) {
-              case FPE_INTDIV: source = "integer divide by zero"; break;
-              case FPE_INTOVF: source = "integer overflow"; break;
-              case FPE_FLTDIV: source = "floating point divide by zero"; break;
-              case FPE_FLTOVF: source = "floating point overflow"; break;
-              case FPE_FLTUND: source = "floating point underflow"; break;
-              case FPE_FLTRES: source = "floating point inexact result"; break;
-              case FPE_FLTINV: source = "floating point invalid operation"; break;
-              case FPE_FLTSUB: source = "subscript out of range"; break;
-            }
-            break;
-          case SIGSEGV:
-            use_fault_addr = 1;
-            switch (cur.si.si_code) {
-              case SEGV_MAPERR: source = "address not mapped to object"; break;
-              case SEGV_ACCERR: source = "invalid permissions for mapped object"; break;
-            }
-            break;
-          case SIGBUS:
-            use_fault_addr = 1;
-            switch (cur.si.si_code) {
-              case BUS_ADRALN: source = "invalid address alignment"; break;
-              case BUS_ADRERR: source = "non-existent physical address"; break;
-              case BUS_OBJERR: source = "object specific hardware error"; break;
-            }
-            break;
-          case SIGTRAP:
-            switch (cur.si.si_code) {
-              case TRAP_BRKPT: source = "process breakpoint"; break;
-              case TRAP_TRACE: source = "process trace trap"; break;
-            }
-            break;
-          case SIGCHLD:
-            use_pid = 1;
-            switch (cur.si.si_code) {
-              case CLD_EXITED: source = "child has exited"; break;
-              case CLD_KILLED: source = "child was killed"; break;
-              case CLD_DUMPED: source = "child terminated abnormally"; break;
-              case CLD_TRAPPED: source = "traced child has trapped"; break;
-              case CLD_STOPPED: source = "child has stopped"; break;
-              case CLD_CONTINUED: source = "stopped child has continued"; break;
-            }
-            break;
-#ifdef SIGPOLL
-          case SIGPOLL:
-            switch (cur.si.si_code) {
-              case POLL_IN: source = "data input available"; break;
-              case POLL_OUT: source = "output buffers available"; break;
-              case POLL_MSG: source = "input message available"; break;
-              case POLL_ERR: source = "I/O error"; break;
-              case POLL_PRI: source = "high priority input available"; break;
-              case POLL_HUP: source = "device disconnected"; break;
-            }
-            break;
-#endif
-
-        }
-      } else {
-        use_pid = 1;
-        switch (cur.si.si_code) {
-#ifdef SI_NOINFO
-          case SI_NOINFO:
-            /* explicitly have no info */
-            use_pid = 0;
-            break;
-#endif
-          case SI_USER:    source = "userspace"; break;
-#ifdef SI_LWP
-          case SI_LWP:     source = "_lwp_kill"; break;
-#endif
-          case SI_QUEUE:   source = "sigqueue"; break;
-          case SI_TIMER:   source = "timer";   break;
-          case SI_ASYNCIO: source = "asyncio"; break;
-          case SI_MESGQ:   source = "mesgq"; break;
-#ifdef SI_KERNEL
-          case SI_KERNEL:  source = "kernel"; break;
-#endif
-#ifdef SI_SIGIO
-          case SI_SIGIO:   source = "sigio"; break;
-#endif
-#ifdef SI_TKILL
-          case SI_TKILL:   source = "tkill"; break;
-#endif
-#ifdef SI_RCTL
-          case SI_RCTL: source = "resource-control"; break;
-#endif
-        }
-      }
-      printf("#%-2d Signal %d: %s. %s",
-          nframe, cur.si.si_signo, signame, source);
-
-      if (use_pid) {
-        printf(" pid=%d", cur.si.si_pid);
-      }
-
-      if (use_fault_addr) {
-        name = gimli_pc_sym_name(cur.si.si_addr, namebuf, sizeof(namebuf));
-        if (name && strlen(name)) {
-          printf(" (%s)", name);
-        } else {
-          printf(" (" PTRFMT ")", (PTRFMT_T)cur.si.si_addr);
-        }
-      }
-      printf("\n");
-
+      gimli_render_siginfo(&cur.si, namebuf, sizeof(namebuf));
+      printf("#%-2d %s\n", nframe, namebuf);
     } else {
       printf("#%-2d signal handler\n", nframe);
     }

File wedgie.c

View file
 static void mr_wedge(struct wedgie_data *data, int port)
 {
   fprintf(stderr, "taking a nap in func %p\n", mr_wedge);
+  fflush(stderr);
   sleep(2);
   *(long*)42 = 42;
   sleep(10);
 }
 
 static void func_one(struct wedgie_data *data, wedgie_t w_t, const char *string,
-  enum wedgie_enum w_e, struct wedgie_data data_not_pointer, union wedgie_union u)
+  enum wedgie_enum w_e, struct wedgie_data data_not_pointer
+#ifndef __sparc__
+  /* gcc emits code that causes %sp to end up NULL on call if we include
+   * this union parameter on the stack */
+  , union wedgie_union u
+#endif
+)
 {
+  printf("calling mr_wedge\n"); fflush(stdout);
   mr_wedge(data, 8080);
   printf("done wedging\n");
 }
 
 static void func_two(void)
 {
+  union wedgie_union u;
   struct wedgie_data d = { 42, "forty-two" };
-  union wedgie_union u;
+
+  printf("initialize some data\n"); fflush(stdout);
   u.one = 1;
   u.s.inner2 = 2;
   d.bit1 = 1;
   d.bit2 = 0;
   d.moo = 13;
 
-  func_one(&d, 32, "hello", wee_two, d, u);
+  printf("call func_one\n"); fflush(stdout);
+  func_one(&d, 32, "hello", wee_two, d
+#ifndef __sparc__
+    , u
+#endif
+  );
   printf("func_one called\n");
 }
 
 #endif
 //    signal(SIGSEGV, handler);
   }
+  fprintf(stderr, "calling func_two\n");
+  fflush(stderr);
   func_two();
   fprintf(stderr, "wedgie is done\n");
   return 0;