Add capability of creating a stack trace and/or a core dump on the fly.
This commit is contained in:
@ -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 \
|
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 \
|
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 \
|
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 \
|
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 \
|
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 \
|
tclev.o tclintimpl.o telnet.o testprot.o token.o trace.o trigd.o trim.o ubcalc.o \
|
||||||
|
@ -64,6 +64,7 @@
|
|||||||
|
|
||||||
/*@observer@*//*@null@*/ pCounterDriver CreateMonCounter(/*@observer@*/SConnection *pCon, /*@observer@*/char *name, char *params);
|
/*@observer@*//*@null@*/ pCounterDriver CreateMonCounter(/*@observer@*/SConnection *pCon, /*@observer@*/char *name, char *params);
|
||||||
|
|
||||||
|
|
||||||
extern void AddGalilProtocoll();
|
extern void AddGalilProtocoll();
|
||||||
extern void AddModbusProtocoll();
|
extern void AddModbusProtocoll();
|
||||||
extern void AddOxfordProtocoll();
|
extern void AddOxfordProtocoll();
|
||||||
@ -78,6 +79,7 @@ extern void AddTCPMBProtocol ();
|
|||||||
extern void AddLFGenProtocol();
|
extern void AddLFGenProtocol();
|
||||||
extern void AddSCAQAProtocol();
|
extern void AddSCAQAProtocol();
|
||||||
extern int ANSTO_MakeHistMemory(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]);
|
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 int testLogCmd(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]);
|
||||||
extern pCounterDriver CreateCam(SConnection *pCon, char *name, char *asynq);
|
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);
|
SICSLogWrite("Multiline:\nLine 1\r\nLine 2\r\nLine 3\r\n", eLog);
|
||||||
memset(lbuf, 0, sizeof(lbuf));
|
memset(lbuf, 0, sizeof(lbuf));
|
||||||
memset(sbuf, ' ', sizeof(sbuf));
|
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);
|
SICSLogWriteHex(lbuf, sizeof(lbuf), eLog);
|
||||||
for (i = 0; i <= 128; ++i)
|
for (i = 0; i <= 128; ++i)
|
||||||
sbuf[i] = sbuf[sizeof(sbuf) - 1 - i] = i;
|
sbuf[i] = sbuf[sizeof(sbuf) - 1 - i] = i;
|
||||||
sbuf[sizeof(sbuf) / 2] = '!';
|
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);
|
SICSLogWriteHex(sbuf, sizeof(sbuf), eLog);
|
||||||
for (i = 0; i < 1000; ++i)
|
for (i = 0; i < 1000; ++i)
|
||||||
sbuf[i] = ' ' + (i % 96);
|
sbuf[i] = ' ' + (i % 96);
|
||||||
@ -404,6 +406,7 @@ static void AddCommands(SicsInterp *pInter)
|
|||||||
AddCommand(pInter, "FileEvalGlob", MacroFileEvalGlob, NULL, NULL);
|
AddCommand(pInter, "FileEvalGlob", MacroFileEvalGlob, NULL, NULL);
|
||||||
AddCommand(pInter, "Valgrind", Ansto_Valgrind, NULL, NULL);
|
AddCommand(pInter, "Valgrind", Ansto_Valgrind, NULL, NULL);
|
||||||
AddCommand(pInter, "anstocapture", Ansto_Capture, 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.
|
* 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