Add capability of creating a stack trace and/or a core dump on the fly.
This commit is contained in:
322
stack_trace.c
Normal file
322
stack_trace.c
Normal file
@ -0,0 +1,322 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <execinfo.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "dynstring.h"
|
||||
#include "sics.h"
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static const char* timestamp() {
|
||||
static char ts[80];
|
||||
int year, month, day;
|
||||
int hour, min, sec, usec;
|
||||
struct timeval tv;
|
||||
struct tm *time;
|
||||
gettimeofday(&tv, NULL);
|
||||
time = localtime(&tv.tv_sec);
|
||||
year = 1900 + time->tm_year;
|
||||
month = time->tm_mon + 1;
|
||||
day = time->tm_mday;
|
||||
hour = time->tm_hour;
|
||||
min = time->tm_min;
|
||||
sec = time->tm_sec;
|
||||
usec = (int) tv.tv_usec;
|
||||
snprintf(ts, 80, "%04d%02d%02d%02d%02d%02d",
|
||||
year, month, day, hour, min, sec);
|
||||
return ts;
|
||||
}
|
||||
|
||||
#ifdef SICS_HAS_BACKTRACE
|
||||
void stack_trace_backtrace() {
|
||||
void *array[STRACE_SIZE];
|
||||
size_t stack_size;
|
||||
size_t i;
|
||||
char **strings;
|
||||
char buffer[1024];
|
||||
pDynString s = CreateDynString(1000, 100);
|
||||
|
||||
stack_size = backtrace(array, STRACE_SIZE);
|
||||
strings = backtrace_symbols(array, stack_size);
|
||||
for (i = 0; i < stack_size; ++i) {
|
||||
snprintf(buffer, 1024, "%p: %s\n", array[i], strings[i]);
|
||||
DynStringConcat(s, buffer);
|
||||
}
|
||||
free(strings);
|
||||
|
||||
SICSLogWrite(GetCharArray(s), eLogError);
|
||||
DeleteDynString(s);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SICS_HAS_BFD
|
||||
#include <bfd.h>
|
||||
#define STRACE_SIZE 64
|
||||
|
||||
/* globals retained across calls to resolve. */
|
||||
static bfd* abfd = 0;
|
||||
static asymbol **syms = 0;
|
||||
static asection *text = 0;
|
||||
static void resolve(void *address, char *buffer, int buf_len) {
|
||||
if (!abfd) {
|
||||
char ename[1024];
|
||||
int l = readlink("/proc/self/exe",ename,sizeof(ename));
|
||||
if (l == -1) {
|
||||
perror("failed to find executable\n");
|
||||
return;
|
||||
}
|
||||
ename[l] = 0;
|
||||
|
||||
bfd_init();
|
||||
|
||||
abfd = bfd_openr(ename, 0);
|
||||
if (!abfd) {
|
||||
perror("bfd_openr failed: ");
|
||||
return;
|
||||
}
|
||||
|
||||
/* oddly, this is required for it to work... */
|
||||
bfd_check_format(abfd,bfd_object);
|
||||
|
||||
unsigned storage_needed = bfd_get_symtab_upper_bound(abfd);
|
||||
syms = (asymbol **) malloc(storage_needed);
|
||||
unsigned cSymbols = bfd_canonicalize_symtab(abfd, syms);
|
||||
|
||||
text = bfd_get_section_by_name(abfd, ".text");
|
||||
}
|
||||
|
||||
long offset = ((long)address) - text->vma;
|
||||
if (offset > 0) {
|
||||
const char *file;
|
||||
const char *func;
|
||||
unsigned line;
|
||||
if (bfd_find_nearest_line(abfd, text, syms, offset, &file, &func, &line) && file)
|
||||
snprintf(buffer, buf_len, "file: %s, line: %u, func %s\n",file,line,func);
|
||||
}
|
||||
}
|
||||
|
||||
void stack_trace_bfd() {
|
||||
void *array[STRACE_SIZE];
|
||||
size_t stack_size;
|
||||
size_t i;
|
||||
char buffer[1024];
|
||||
pDynString s = CreateDynString(1000, 100);
|
||||
|
||||
stack_size = backtrace(array, STRACE_SIZE);
|
||||
for (i = 0; i < stack_size; ++i) {
|
||||
resolve(array[i], buffer, 1024);
|
||||
DynStringConcat(s, buffer);
|
||||
}
|
||||
SICSLogWrite(GetCharArray(s), eLogError);
|
||||
DeleteDynString(s);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SICS_HAS_GDB
|
||||
void stack_trace_gdb() {
|
||||
int pipefd[2];
|
||||
char pid_buf[30];
|
||||
sprintf(pid_buf, "%d", getpid());
|
||||
char name_buf[512];
|
||||
name_buf[readlink("/proc/self/exe", name_buf, 511)]=0;
|
||||
if (pipe(pipefd) == -1) {
|
||||
perror("pipe");
|
||||
abort();
|
||||
}
|
||||
int child_pid = fork();
|
||||
if (child_pid == -1) {
|
||||
perror("fork");
|
||||
abort();
|
||||
}
|
||||
if (!child_pid) {
|
||||
close(pipefd[0]);
|
||||
dup2(pipefd[1], 1); /* redirect stdout to pipe */
|
||||
dup2(pipefd[1], 2); /* redirect stderr to pipe */
|
||||
fprintf(stdout,"stack trace for %s pid=%s\n",name_buf,pid_buf);
|
||||
fflush(stdout);
|
||||
execlp("gdb", "gdb", "--batch", "-n", "-ex", "thread", "-ex", "bt", name_buf, pid_buf, NULL);
|
||||
abort(); /* If gdb failed to start */
|
||||
} else {
|
||||
char buff;
|
||||
pDynString s = CreateDynString(100, 100);
|
||||
close(pipefd[1]);
|
||||
while (read(pipefd[0], &buff, 1) > 0) {
|
||||
DynStringConcatChar(s, buff);
|
||||
}
|
||||
SICSLogWrite(GetCharArray(s), eLogError);
|
||||
DeleteDynString(s);
|
||||
close(pipefd[0]);
|
||||
waitpid(child_pid,NULL,0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SICS_HAS_ABORT
|
||||
void stack_trace_abort() {
|
||||
int child_pid = fork();
|
||||
if (child_pid == -1) {
|
||||
perror("fork");
|
||||
abort();
|
||||
}
|
||||
if (!child_pid) {
|
||||
/* Dump core */
|
||||
abort();
|
||||
} else {
|
||||
waitpid(child_pid,NULL,0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t get_size_of_files(char *glob) {
|
||||
int pipefd[2];
|
||||
if (pipe(pipefd) == -1) {
|
||||
perror("pipe");
|
||||
abort();
|
||||
}
|
||||
int child_pid = fork();
|
||||
if (child_pid == -1) {
|
||||
perror("fork");
|
||||
abort();
|
||||
}
|
||||
if (!child_pid) {
|
||||
char cmd[120];
|
||||
close(pipefd[0]);
|
||||
dup2(pipefd[1], 1); /* redirect stdout to pipe */
|
||||
dup2(pipefd[1], 2); /* redirect stderr to pipe */
|
||||
snprintf(cmd, sizeof(cmd)-1, "du -s %s", glob);
|
||||
execlp("bash", "bash", "-c", cmd, NULL);
|
||||
exit(0); /* If gdb failed to start */
|
||||
} else {
|
||||
size_t total_size = 0;
|
||||
char buff;
|
||||
pDynString s = CreateDynString(100, 100);
|
||||
close(pipefd[1]);
|
||||
while (read(pipefd[0], &buff, 1) > 0) {
|
||||
DynStringConcatChar(s, buff);
|
||||
if (buff == '\n') {
|
||||
SICSLogWrite(GetCharArray(s), eLog);
|
||||
int this_size = atoi(GetCharArray(s));
|
||||
total_size += this_size;
|
||||
DynStringClear(s);
|
||||
}
|
||||
}
|
||||
if (GetDynStringLength(s) > 0)
|
||||
SICSLogWrite(GetCharArray(s), eLogError);
|
||||
DeleteDynString(s);
|
||||
close(pipefd[0]);
|
||||
waitpid(child_pid, NULL, 0);
|
||||
SICSLogPrintf(eLog, "Total size of files \"%s\" is %ldK", glob, total_size);
|
||||
return total_size;
|
||||
}
|
||||
}
|
||||
|
||||
void generate_stack_trace(int full, int dump) {
|
||||
int pipefd[2];
|
||||
char pid_buf[30];
|
||||
sprintf(pid_buf, "%d", getpid());
|
||||
char name_buf[512];
|
||||
name_buf[readlink("/proc/self/exe", name_buf, 511)]=0;
|
||||
if (pipe(pipefd) == -1) {
|
||||
perror("pipe");
|
||||
abort();
|
||||
}
|
||||
int child_pid = fork();
|
||||
if (child_pid == -1) {
|
||||
perror("fork");
|
||||
abort();
|
||||
}
|
||||
if (!child_pid) {
|
||||
char *bt;
|
||||
close(pipefd[0]);
|
||||
dup2(pipefd[1], 1); /* redirect stdout to pipe */
|
||||
dup2(pipefd[1], 2); /* redirect stderr to pipe */
|
||||
fprintf(stdout, "stack trace for %s pid=%s\n", name_buf, pid_buf);
|
||||
fflush(stdout);
|
||||
if (full)
|
||||
bt = "bt full";
|
||||
else
|
||||
bt = "bt";
|
||||
if (dump) {
|
||||
char gen_buf[128];
|
||||
snprintf(gen_buf, sizeof(gen_buf)-1, "generate-core-file /tmp/core.%s.%s", timestamp(), pid_buf);
|
||||
execlp("gdb", "gdb", "--batch", "-n", "-ex", "thread", "-ex", bt, "-ex", gen_buf, name_buf, pid_buf, NULL);
|
||||
} else {
|
||||
execlp("gdb", "gdb", "--batch", "-n", "-ex", "thread", "-ex", bt, name_buf, pid_buf, NULL);
|
||||
}
|
||||
fprintf(stdout, "gdb failed to start for %s pid=%d\n", name_buf, getpid());
|
||||
fflush(stdout);
|
||||
abort(); /* If gdb failed to start */
|
||||
} else {
|
||||
char buff;
|
||||
pDynString s = CreateDynString(100, 100);
|
||||
close(pipefd[1]);
|
||||
while (read(pipefd[0], &buff, 1) > 0) {
|
||||
DynStringConcatChar(s, buff);
|
||||
}
|
||||
SICSLogWrite(GetCharArray(s), eLogError);
|
||||
DeleteDynString(s);
|
||||
close(pipefd[0]);
|
||||
waitpid(child_pid, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void stack_trace(int mode) {
|
||||
switch (mode) {
|
||||
case 0:
|
||||
default:
|
||||
#ifdef SICS_HAS_BACKTRACE
|
||||
stack_trace_0();
|
||||
#endif
|
||||
break;
|
||||
case 1:
|
||||
#ifdef SICS_HAS_BFD
|
||||
stack_trace_bfd();
|
||||
#endif
|
||||
break;
|
||||
case 2:
|
||||
#ifdef SICS_HAS_GDB
|
||||
stack_trace_gdb();
|
||||
#endif
|
||||
break;
|
||||
case 3:
|
||||
#ifdef SICS_HAS_ABORT
|
||||
stack_trace_abort();
|
||||
#endif
|
||||
break;
|
||||
case 4:
|
||||
generate_stack_trace(0, 0);
|
||||
break;
|
||||
case 5:
|
||||
generate_stack_trace(0, 1);
|
||||
break;
|
||||
case 6:
|
||||
generate_stack_trace(1, 0);
|
||||
break;
|
||||
case 7:
|
||||
generate_stack_trace(1, 1);
|
||||
break;
|
||||
case 99:
|
||||
get_size_of_files("/tmp/core.*");
|
||||
break;
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
/**
|
||||
* \brief Tests the stack_trace facility
|
||||
*/
|
||||
int Ansto_Stack_Trace(SConnection *pCon, SicsInterp * pSics, void *pData, int argc, char *argv[]) {
|
||||
int mode;
|
||||
void stack_trace(int mode);
|
||||
if (argc > 1)
|
||||
mode = atoi(argv[1]);
|
||||
else
|
||||
mode = 1;
|
||||
stack_trace(mode);
|
||||
}
|
Reference in New Issue
Block a user