#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') { 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; }