From 539ea4d9dadee340be352dc24991da4d475f584e Mon Sep 17 00:00:00 2001 From: Douglas Clowes Date: Wed, 15 Oct 2014 12:18:38 +1100 Subject: [PATCH] Add capability of creating a stack trace and/or a core dump on the fly. --- site_ansto/make_gen_variables | 11 +- site_ansto/site_ansto.c | 7 +- stack_trace.c | 322 ++++++++++++++++++++++++++++++++++ stack_trace.h | 4 + 4 files changed, 337 insertions(+), 7 deletions(-) create mode 100644 stack_trace.c create mode 100644 stack_trace.h diff --git a/site_ansto/make_gen_variables b/site_ansto/make_gen_variables index 4fc334c4..7dcb970c 100644 --- a/site_ansto/make_gen_variables +++ b/site_ansto/make_gen_variables @@ -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) diff --git a/site_ansto/site_ansto.c b/site_ansto/site_ansto.c index f67feef4..6fb7ddf9 100644 --- a/site_ansto/site_ansto.c +++ b/site_ansto/site_ansto.c @@ -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. diff --git a/stack_trace.c b/stack_trace.c new file mode 100644 index 00000000..64b64f97 --- /dev/null +++ b/stack_trace.c @@ -0,0 +1,322 @@ +#define _GNU_SOURCE +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 +#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); +} diff --git a/stack_trace.h b/stack_trace.h new file mode 100644 index 00000000..cbc58f77 --- /dev/null +++ b/stack_trace.h @@ -0,0 +1,4 @@ +#ifndef STACK_TRACE_H +#define STACK_TRACE_H +void stack_trace(int mode); +#endif