Add capability of creating a stack trace and/or a core dump on the fly.
This commit is contained in:
@ -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)
|
||||
|
||||
|
@ -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
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);
|
||||
}
|
4
stack_trace.h
Normal file
4
stack_trace.h
Normal file
@ -0,0 +1,4 @@
|
||||
#ifndef STACK_TRACE_H
|
||||
#define STACK_TRACE_H
|
||||
void stack_trace(int mode);
|
||||
#endif
|
Reference in New Issue
Block a user