Add capability of creating a stack trace and/or a core dump on the fly.

This commit is contained in:
Douglas Clowes
2014-10-15 12:18:38 +11:00
parent 61b1dcb4a5
commit 539ea4d9da
4 changed files with 337 additions and 7 deletions

View File

@ -1,12 +1,12 @@
# vim: ft=make ts=4 sw=4 noet cindent
COBJ = Sclient.o network.o ifile.o intcli.o $(FORTIFYOBJ)
COBJ = Sclient.o network.o ifile.o intcli.o $(FORTIFYOBJ)
SOBJ = alias.o anticollider.o arrayutil.o ascon.o asyncprotocol.o asyncqueue.o asynnet.o background.o callback.o \
cell.o chadapter.o choco.o circular.o commandlog.o cone.o confvirtualmot.o \
conman.o costa.o countersec.o danu.o definealias.o devexec.o devser.o diffscan.o d_mod.o \
drive.o d_sign.o dynstring.o emon.o errormsg.o evcontroller.o evdriver.o \
event.o exebuf.o exeman.o fitcenter.o fomerge.o $(FORTIFYOBJ) fourlib.o fourmess.o \
fourtable.o fupa.o genbinprot.o gpibcontroller.o hdbtable.o help.o hipadaba.o \
histdriv.o histmem.o histmemsec.o histregress.o histsim.o hklmot.o hkl.o \
histdriv.o histmem.o histmemsec.o histregress.o histsim.o hklmot.o hkl.o \
hmcontrol.o hmdata.o hmslave.o ifile.o initializer.o integrate.o interface.o \
intserv.o lin2ang.o lld_blob.o lld.o logger.o logreader.o logsetup.o lomax.o \
macro.o maximize.o mccontrol.o mcreader.o mcstascounter.o mcstashm.o \
@ -20,6 +20,7 @@ SOBJ = alias.o anticollider.o arrayutil.o ascon.o asyncprotocol.o asyncqueue.o a
selvar.o servlog.o sgclib.o sghkl.o sgio.o sicscron.o sicsdata.o sicsexit.o sicsglobal.o sicshdbadapter.o \
sicshdbfactory.o sicshipadaba.o sicslist.o SICSmain.o sicsobj.o sicspoll.o sicsutil.o \
sicvar.o sig_die.o simchop.o simev.o simidx.o simindex.o sinfox.o singlebi.o singlediff.o singlenb.o singlebinb.o singletas.o singlex.o splitter.o s_rnge.o statemon.o \
stack_trace.o \
statistics.o statusfile.o status.o stdscan.o stptok.o stringdict.o strlutil.o strrepl.o \
syncedprot.o synchronize.o tasdrive.o task.o tasscanub.o tasublib.o tasub.o tcldrivable.o \
tclev.o tclintimpl.o telnet.o testprot.o token.o trace.o trigd.o trim.o ubcalc.o \
@ -30,9 +31,9 @@ SOBJ = alias.o anticollider.o arrayutil.o ascon.o asyncprotocol.o asyncqueue.o a
# them as SECONDARY prevents make from removing them.
.SECONDARY.: ../sicspoll.c ../polldriv.c
MOTOROBJ = motor.o simdriv.o
COUNTEROBJ = countdriv.o simcter.o counter.o
VELOOBJ = velo.o velosim.o
MOTOROBJ = motor.o simdriv.o
COUNTEROBJ = countdriv.o simcter.o counter.o
VELOOBJ = velo.o velosim.o
COREOBJ = $(SOBJ) $(MOTOROBJ) $(COUNTEROBJ) $(VELOOBJ)

View File

@ -64,6 +64,7 @@
/*@observer@*//*@null@*/ pCounterDriver CreateMonCounter(/*@observer@*/SConnection *pCon, /*@observer@*/char *name, char *params);
extern void AddGalilProtocoll();
extern void AddModbusProtocoll();
extern void AddOxfordProtocoll();
@ -78,6 +79,7 @@ extern void AddTCPMBProtocol ();
extern void AddLFGenProtocol();
extern void AddSCAQAProtocol();
extern int ANSTO_MakeHistMemory(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]);
extern int Ansto_Stack_Trace(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]);
extern int testLogCmd(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]);
extern pCounterDriver CreateCam(SConnection *pCon, char *name, char *asynq);
@ -307,12 +309,12 @@ int testLogCmd(SConnection *pCon, SicsInterp *pInter, void *pData,
SICSLogWrite("Multiline:\nLine 1\r\nLine 2\r\nLine 3\r\n", eLog);
memset(lbuf, 0, sizeof(lbuf));
memset(sbuf, ' ', sizeof(sbuf));
SICSLogPrintf(eLog, "Hexlog %d all zero bytes", sizeof(lbuf));
SICSLogPrintf(eLog, "Hexlog %d all zero bytes", (int) sizeof(lbuf));
SICSLogWriteHex(lbuf, sizeof(lbuf), eLog);
for (i = 0; i <= 128; ++i)
sbuf[i] = sbuf[sizeof(sbuf) - 1 - i] = i;
sbuf[sizeof(sbuf) / 2] = '!';
SICSLogPrintf(eLog, "Hexlog %d mid space bytes", sizeof(sbuf));
SICSLogPrintf(eLog, "Hexlog %d mid space bytes", (int) sizeof(sbuf));
SICSLogWriteHex(sbuf, sizeof(sbuf), eLog);
for (i = 0; i < 1000; ++i)
sbuf[i] = ' ' + (i % 96);
@ -404,6 +406,7 @@ static void AddCommands(SicsInterp *pInter)
AddCommand(pInter, "FileEvalGlob", MacroFileEvalGlob, NULL, NULL);
AddCommand(pInter, "Valgrind", Ansto_Valgrind, NULL, NULL);
AddCommand(pInter, "anstocapture", Ansto_Capture, NULL, NULL);
AddCommand(pInter, "stack_trace", Ansto_Stack_Trace, NULL, NULL);
/*
* Tcl 8.5 has implemented the clock command in tcl rather then C.

322
stack_trace.c Normal file
View 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);
}

4
stack_trace.h Normal file
View File

@ -0,0 +1,4 @@
#ifndef STACK_TRACE_H
#define STACK_TRACE_H
void stack_trace(int mode);
#endif