File stack_dump.c¶
File List > base > stack_dump.c
Go to the documentation of this file
/*
* Copyright 2008 Search Solution Corporation
* Copyright 2016 CUBRID Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/*
* stack_dump.c - call stack dump
*/
#ident "$Id$"
#include "printer.hpp"
#if defined(x86_SOLARIS)
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <ucontext.h>
#include <sys/stack.h>
#include <sys/frame.h>
#include <sys/elf.h>
#include "stack_dump.h"
#elif defined(LINUX)
#if __WORDSIZE == 32
#include <stdio.h>
#include <string.h>
#include <ucontext.h>
#include <dlfcn.h>
#include "error_code.h"
#include "memory_hash.h"
#include "stack_dump.h"
#else // __WORDSIZE == 32
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <execinfo.h>
#include "error_code.h"
#include "memory_hash.h"
#include "stack_dump.h"
#endif // __WORDSIZE == 32
#else // LINUX
#include <stdio.h>
#include "stack_dump.h"
#endif // x86_SOLARIS, LINUX
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"
#if defined(x86_SOLARIS)
#define FRAME_PTR_REGISTER EBP
#define TR_ARG_MAX 6
#define PGRAB_RDONLY 0x04
#define MIN(a,b) ((a) < (b) ? (a) : (b))
typedef Elf64_Sym GElf_Sym;
extern struct ps_prochandle *Pgrab (pid_t, int, int *);
extern void Pfree (struct ps_prochandle *);
extern int Plookup_by_addr (struct ps_prochandle *, uintptr_t, char *, size_t, GElf_Sym *);
static ulong_t argcount (uintptr_t eip);
static int read_safe (int fd, struct frame *fp, struct frame **savefp, uintptr_t * savepc);
/*
* argcount -
* return:
* eip(in):
*/
static ulong_t
argcount (uintptr_t eip)
{
const uint8_t *ins = (const uint8_t *) eip;
ulong_t n;
enum
{
M_MODRM_ESP = 0xc4, /* Mod/RM byte indicates %esp */
M_ADD_IMM32 = 0x81, /* ADD imm32 to r/m32 */
M_ADD_IMM8 = 0x83 /* ADD imm8 to r/m32 */
};
switch (ins[0])
{
case M_ADD_IMM32:
n = ins[2] + (ins[3] << 8) + (ins[4] << 16) + (ins[5] << 24);
break;
case M_ADD_IMM8:
n = ins[2];
break;
default:
return (TR_ARG_MAX);
}
n /= sizeof (long);
return (MIN (n, TR_ARG_MAX));
}
/*
* read_safe -
* return:
* fd(in):
* fp(in):
* savefp(in):
* savepc(in):
*/
static int
read_safe (int fd, struct frame *fp, struct frame **savefp, uintptr_t * savepc)
{
uintptr_t newfp;
if ((uintptr_t) fp & (sizeof (void *) - 1))
{
return (-1); /* misaligned */
}
if ((pread (fd, (void *) &newfp, sizeof (fp->fr_savfp), (off_t) & fp->fr_savfp) != sizeof (fp->fr_savfp))
|| pread (fd, (void *) savepc, sizeof (fp->fr_savpc), (off_t) & fp->fr_savpc) != sizeof (fp->fr_savpc))
{
return (-1);
}
if (newfp != 0)
{
newfp += STACK_BIAS;
}
*savefp = (struct frame *) newfp;
return (0);
}
/*
* log_stack_info -
* return:
* output(in/out):
* pc(in):
* argc(in):
* argv(in):
* Pr(in):
*/
static int
log_stack_info (print_output & output, uintptr_t pc, ulong_t argc, long *argv, struct ps_prochandle *Pr)
{
char buff[255];
GElf_Sym sym;
uintptr_t start;
int i;
sprintf (buff, "%.*lx", 8, (long) pc);
strcpy (buff + 8, " ????????");
if (Plookup_by_addr (Pr, pc, buff + 1 + 8, sizeof (buff) - 1 - 8, &sym) == 0)
{
start = sym.st_value;
}
else
{
start = pc;
}
output ("%-17s(", buff);
for (i = 0; i < argc; i++)
{
output ((i + 1 == argc) ? "%lx" : "%lx, ", argv[i]);
}
output ((start != pc) ? ") + %lx\n" : ")\n", (long) (pc - start));
output.flush ();
return (0);
}
/*
* er_dump_call_stack_internal - dump call stack
* return: none
* output(in/put):
*/
void
er_dump_call_stack_internal (print_output & output)
{
ucontext_t ucp;
struct frame *fp, *savefp;
ulong_t argc;
long *argv;
struct ps_prochandle *Pr;
int err, fd;
uintptr_t savepc;
Pr = Pgrab (getpid (), PGRAB_RDONLY, &err);
if (Pr == NULL)
{
return;
}
if (getcontext (&ucp) < 0)
{
Pfree (Pr);
return;
}
fp = (struct frame *) ((caddr_t) ucp.uc_mcontext.gregs[FRAME_PTR_REGISTER] + STACK_BIAS);
fd = open ("/proc/self/as", O_RDONLY);
if (fd < 0)
{
Pfree (Pr);
return;
}
while (fp != NULL)
{
if (read_safe (fd, fp, &savefp, &savepc) != 0)
{
close (fd);
Pfree (Pr);
return;
}
/* break when reaches the bottom of stack */
if (savefp == NULL)
{
break;
}
if (savefp->fr_savfp == 0)
{
break;
}
argc = argcount (savepc);
argv = (long *) ((char *) savefp + sizeof (struct frame));
log_stack_info (output, savepc, argc, argv, Pr);
fp = savefp;
}
close (fd);
Pfree (Pr);
}
#elif defined(LINUX)
static int er_resolve_function_name (const void *address, const char *lib_file_name, char *buffer, int buffer_size);
#if __WORDSIZE == 32
#define PEEK_DATA(addr) (*(size_t *)(addr))
#define MAXARGS 6
#define BUFFER_SIZE 1024
/*
* er_dump_call_stack_internal - dump call stack
* return:
* output(in/out):
*/
void
er_dump_call_stack_internal (print_output & output)
{
ucontext_t ucp;
size_t frame_pointer_addr, next_frame_pointer_addr;
size_t return_addr, arg;
int i, nargs;
Dl_info dl_info;
const char *func_name_p;
const void *func_addr_p = NULL;
char buffer[BUFFER_SIZE];
if (getcontext (&ucp) < 0)
{
return;
}
return_addr = ucp.uc_mcontext.gregs[REG_EIP];
frame_pointer_addr = ucp.uc_mcontext.gregs[REG_EBP];
while (frame_pointer_addr)
{
if (dladdr ((size_t *) return_addr, &dl_info) == 0)
{
break;
}
if (dl_info.dli_fbase >= (const void *) 0x40000000)
{
func_addr_p = (void *) ((size_t) ((const char *) return_addr) - (size_t) dl_info.dli_fbase);
}
else
{
func_addr_p = (void *) return_addr;
}
if (dl_info.dli_sname)
{
func_name_p = dl_info.dli_sname;
}
else
{
if (er_resolve_function_name (func_addr_p, dl_info.dli_fname, buffer, sizeof (buffer)) == NO_ERROR)
{
func_name_p = buffer;
}
else
{
func_name_p = "???";
}
}
output ("%s(%p): %s", dl_info.dli_fname, func_addr_p, func_name_p);
next_frame_pointer_addr = PEEK_DATA (frame_pointer_addr);
nargs = (next_frame_pointer_addr - frame_pointer_addr - 8) / 4;
if (nargs > MAXARGS)
{
nargs = MAXARGS;
}
output (" (");
if (nargs > 0)
{
for (i = 1; i <= nargs; i++)
{
arg = PEEK_DATA (frame_pointer_addr + 4 * (i + 1));
output ("%x", arg);
if (i < nargs)
{
output (", ");
}
}
}
output (")\n");
if (next_frame_pointer_addr == 0)
{
break;
}
return_addr = PEEK_DATA (frame_pointer_addr + 4);
frame_pointer_addr = next_frame_pointer_addr;
}
output.flush ();
}
#else /* __WORDSIZE == 32 */
#define MAX_TRACE 64
#define BUFFER_SIZE 1024
/*
* er_dump_call_stack_internal - dump call stack
* return:
* output(in/out):
*/
void
er_dump_call_stack_internal (print_output & output)
{
void *return_addr[MAX_TRACE];
int i, trace_count;
Dl_info dl_info;
const char *func_name_p;
const void *func_addr_p;
char buffer[BUFFER_SIZE];
trace_count = backtrace (return_addr, MAX_TRACE);
for (i = 0; i < trace_count; i++)
{
if (dladdr (return_addr[i], &dl_info) == 0)
{
output ("[%.02d] %.016p: unknown function at unknown file\n", trace_count - i, return_addr[i]);
continue;
}
if (dl_info.dli_fbase >= (const void *) 0x40000000)
{
func_addr_p = (void *) ((size_t) ((const char *) return_addr[i]) - (size_t) dl_info.dli_fbase);
}
else
{
func_addr_p = return_addr[i];
}
if (er_resolve_function_name (func_addr_p, dl_info.dli_fname, buffer, sizeof (buffer)) == NO_ERROR)
{
func_name_p = buffer;
}
else
{
func_name_p = NULL;
}
if (func_name_p && strstr (func_name_p, " at "))
{
output ("[%.02d] %s\n", trace_count - i, func_name_p);
}
else
{
output ("[%.02d] %.016p: %s at %s\n", trace_count - i, return_addr[i],
(dl_info.dli_sname == NULL ? "unknown function" : dl_info.dli_sname),
(dl_info.dli_fname == NULL ? "unknown file" : dl_info.dli_fname));
}
}
output.flush ();
}
#endif /* __WORDSIZE == 32 */
MHT_TABLE *fname_table;
static int
er_resolve_function_name (const void *address, const char *lib_file_name_p, char *buffer, int buffer_size)
{
FILE *output;
char cmd_line[BUFFER_SIZE];
char *func_name_p, *pos;
char buf[BUFFER_SIZE], *key, *data;
snprintf (buf, BUFFER_SIZE, "%p%s", address, lib_file_name_p);
data = (char *) mht_get (fname_table, buf);
if (data != NULL)
{
snprintf (buffer, buffer_size, data);
return NO_ERROR;
}
if (strchr (lib_file_name_p, '/') == NULL)
{
snprintf (cmd_line, sizeof (cmd_line), "addr2line -a -f -C -p -e %s/bin/%s %p 2>/dev/null", getenv ("CUBRID"),
lib_file_name_p, address);
}
else
{
snprintf (cmd_line, sizeof (cmd_line), "addr2line -a -f -C -p -e %s %p 2>/dev/null", lib_file_name_p, address);
}
output = popen (cmd_line, "r");
if (!output)
{
return ER_FAILED;
}
func_name_p = fgets (buffer, buffer_size - 1, output);
if (!func_name_p || !func_name_p[0])
{
pclose (output);
return ER_FAILED;
}
pos = strchr (func_name_p, '\n');
if (pos)
{
pos[0] = '\0';
}
pclose (output);
key = strdup (buf);
if (key == NULL)
{
return ER_FAILED;
}
data = strdup (func_name_p);
if (data == NULL)
{
free (key);
return ER_FAILED;
}
if (mht_put (fname_table, key, data) == NULL)
{
free (key);
free (data);
return ER_FAILED;
}
return NO_ERROR;
}
#else /* LINUX */
/*
* er_dump_call_stack_internal - dump call stack
* return:
* output(in/out):
*/
void
er_dump_call_stack_internal (print_output & output)
{
output ("call stack dump: NOT available in this platform\n");
}
#endif /* X86_SOLARIS, LINUX */
void
er_dump_call_stack (FILE * outfp)
{
file_print_output output (outfp);
er_dump_call_stack_internal (output);
}
char *
er_dump_call_stack_to_string (void)
{
string_print_output output;
er_dump_call_stack_internal (output);
char *ptr = strdup (output.get_buffer ());
output.clear ();
return ptr;
}