324 lines
7.7 KiB
C
324 lines
7.7 KiB
C
#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') {
|
|
Log(DEBUG,"sys","%s",GetCharArray(s));
|
|
int this_size = atoi(GetCharArray(s));
|
|
total_size += this_size;
|
|
DynStringClear(s);
|
|
}
|
|
}
|
|
if (GetDynStringLength(s) > 0)
|
|
Log(DEBUG,"sys","%s",GetCharArray(s));
|
|
DeleteDynString(s);
|
|
close(pipefd[0]);
|
|
waitpid(child_pid, NULL, 0);
|
|
Log(DEBUG,"sys", "Total size of files \"%s\" is %ldK", glob, (long int)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);
|
|
}
|
|
Log(DEBUG,"sys","%s",GetCharArray(s));
|
|
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);
|
|
return 1;
|
|
}
|