2012-03-27 23:13:14 +00:00
|
|
|
/* go-caller.c -- runtime.Caller and runtime.FuncForPC for Go.
|
|
|
|
|
|
|
|
Copyright 2009 The Go Authors. All rights reserved.
|
|
|
|
Use of this source code is governed by a BSD-style
|
|
|
|
license that can be found in the LICENSE file. */
|
|
|
|
|
|
|
|
/* Implement runtime.Caller. */
|
|
|
|
|
|
|
|
#include <stdint.h>
|
2015-08-28 15:33:40 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
2012-03-27 23:13:14 +00:00
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
#include "backtrace.h"
|
|
|
|
|
2012-03-27 23:13:14 +00:00
|
|
|
#include "runtime.h"
|
|
|
|
|
|
|
|
/* Get the function name, file name, and line number for a PC value.
|
2014-09-21 17:33:12 +00:00
|
|
|
We use the backtrace library to get this. */
|
|
|
|
|
|
|
|
/* Data structure to gather file/line information. */
|
2012-03-27 23:13:14 +00:00
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
struct caller
|
|
|
|
{
|
|
|
|
String fn;
|
|
|
|
String file;
|
|
|
|
intgo line;
|
|
|
|
};
|
2012-03-27 23:13:14 +00:00
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
/* Collect file/line information for a PC value. If this is called
|
|
|
|
more than once, due to inlined functions, we use the last call, as
|
|
|
|
that is usually the most useful one. */
|
2012-03-27 23:13:14 +00:00
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
static int
|
|
|
|
callback (void *data, uintptr_t pc __attribute__ ((unused)),
|
|
|
|
const char *filename, int lineno, const char *function)
|
|
|
|
{
|
|
|
|
struct caller *c = (struct caller *) data;
|
|
|
|
|
2015-08-28 15:33:40 +00:00
|
|
|
/* The libbacktrace library says that these strings might disappear,
|
|
|
|
but with the current implementation they won't. We can't easily
|
|
|
|
allocate memory here, so for now assume that we can save a
|
|
|
|
pointer to the strings. */
|
|
|
|
c->fn = runtime_gostringnocopy ((const byte *) function);
|
|
|
|
c->file = runtime_gostringnocopy ((const byte *) filename);
|
2014-09-21 17:33:12 +00:00
|
|
|
c->line = lineno;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2012-03-27 23:13:14 +00:00
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
/* The error callback for backtrace_pcinfo and backtrace_syminfo. */
|
2012-03-27 23:13:14 +00:00
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
static void
|
|
|
|
error_callback (void *data __attribute__ ((unused)),
|
|
|
|
const char *msg, int errnum)
|
|
|
|
{
|
|
|
|
if (errnum == -1)
|
|
|
|
return;
|
|
|
|
if (errnum > 0)
|
|
|
|
runtime_printf ("%s errno %d\n", msg, errnum);
|
|
|
|
runtime_throw (msg);
|
|
|
|
}
|
2012-03-27 23:13:14 +00:00
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
/* The backtrace library state. */
|
2012-03-27 23:13:14 +00:00
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
static void *back_state;
|
|
|
|
|
|
|
|
/* A lock to control creating back_state. */
|
|
|
|
|
|
|
|
static Lock back_state_lock;
|
|
|
|
|
|
|
|
/* Fetch back_state, creating it if necessary. */
|
|
|
|
|
|
|
|
struct backtrace_state *
|
|
|
|
__go_get_backtrace_state ()
|
2012-03-27 23:13:14 +00:00
|
|
|
{
|
2014-09-21 17:33:12 +00:00
|
|
|
runtime_lock (&back_state_lock);
|
|
|
|
if (back_state == NULL)
|
|
|
|
{
|
|
|
|
const char *filename;
|
2015-08-28 15:33:40 +00:00
|
|
|
struct stat s;
|
2014-09-21 17:33:12 +00:00
|
|
|
|
|
|
|
filename = (const char *) runtime_progname ();
|
|
|
|
|
|
|
|
/* If there is no '/' in FILENAME, it was found on PATH, and
|
|
|
|
might not be the same as the file with the same name in the
|
|
|
|
current directory. */
|
|
|
|
if (__builtin_strchr (filename, '/') == NULL)
|
|
|
|
filename = NULL;
|
|
|
|
|
2015-08-28 15:33:40 +00:00
|
|
|
/* If the file is small, then it's not the real executable.
|
|
|
|
This is specifically to deal with Docker, which uses a bogus
|
|
|
|
argv[0] (http://gcc.gnu.org/PR61895). It would be nice to
|
|
|
|
have a better check for whether this file is the real
|
|
|
|
executable. */
|
|
|
|
if (stat (filename, &s) < 0 || s.st_size < 1024)
|
|
|
|
filename = NULL;
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
back_state = backtrace_create_state (filename, 1, error_callback, NULL);
|
|
|
|
}
|
|
|
|
runtime_unlock (&back_state_lock);
|
|
|
|
return back_state;
|
2012-03-27 23:13:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Return function/file/line information for PC. */
|
|
|
|
|
|
|
|
_Bool
|
2014-09-21 17:33:12 +00:00
|
|
|
__go_file_line (uintptr pc, String *fn, String *file, intgo *line)
|
2012-03-27 23:13:14 +00:00
|
|
|
{
|
2014-09-21 17:33:12 +00:00
|
|
|
struct caller c;
|
|
|
|
|
|
|
|
runtime_memclr (&c, sizeof c);
|
|
|
|
backtrace_pcinfo (__go_get_backtrace_state (), pc, callback,
|
|
|
|
error_callback, &c);
|
|
|
|
*fn = c.fn;
|
|
|
|
*file = c.file;
|
|
|
|
*line = c.line;
|
|
|
|
return c.file.len > 0;
|
2012-03-27 23:13:14 +00:00
|
|
|
}
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
/* Collect symbol information. */
|
2012-03-27 23:13:14 +00:00
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
static void
|
|
|
|
syminfo_callback (void *data, uintptr_t pc __attribute__ ((unused)),
|
|
|
|
const char *symname __attribute__ ((unused)),
|
|
|
|
uintptr_t address, uintptr_t size __attribute__ ((unused)))
|
2012-03-27 23:13:14 +00:00
|
|
|
{
|
2014-09-21 17:33:12 +00:00
|
|
|
uintptr_t *pval = (uintptr_t *) data;
|
|
|
|
|
|
|
|
*pval = address;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set *VAL to the value of the symbol for PC. */
|
|
|
|
|
|
|
|
static _Bool
|
|
|
|
__go_symbol_value (uintptr_t pc, uintptr_t *val)
|
|
|
|
{
|
|
|
|
*val = 0;
|
|
|
|
backtrace_syminfo (__go_get_backtrace_state (), pc, syminfo_callback,
|
|
|
|
error_callback, val);
|
|
|
|
return *val != 0;
|
2012-03-27 23:13:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* The values returned by runtime.Caller. */
|
|
|
|
|
|
|
|
struct caller_ret
|
|
|
|
{
|
|
|
|
uintptr_t pc;
|
2014-09-21 17:33:12 +00:00
|
|
|
String file;
|
|
|
|
intgo line;
|
2012-03-27 23:13:14 +00:00
|
|
|
_Bool ok;
|
|
|
|
};
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
struct caller_ret Caller (int n) __asm__ (GOSYM_PREFIX "runtime.Caller");
|
2012-03-27 23:13:14 +00:00
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
Func *FuncForPC (uintptr_t) __asm__ (GOSYM_PREFIX "runtime.FuncForPC");
|
2012-03-27 23:13:14 +00:00
|
|
|
|
|
|
|
/* Implement runtime.Caller. */
|
|
|
|
|
|
|
|
struct caller_ret
|
|
|
|
Caller (int skip)
|
|
|
|
{
|
|
|
|
struct caller_ret ret;
|
2014-09-21 17:33:12 +00:00
|
|
|
Location loc;
|
2012-03-27 23:13:14 +00:00
|
|
|
int32 n;
|
|
|
|
|
|
|
|
runtime_memclr (&ret, sizeof ret);
|
2015-08-28 15:33:40 +00:00
|
|
|
n = runtime_callers (skip + 1, &loc, 1, false);
|
|
|
|
if (n < 1 || loc.pc == 0)
|
2012-03-27 23:13:14 +00:00
|
|
|
return ret;
|
2014-09-21 17:33:12 +00:00
|
|
|
ret.pc = loc.pc;
|
|
|
|
ret.file = loc.filename;
|
|
|
|
ret.line = loc.lineno;
|
|
|
|
ret.ok = 1;
|
2012-03-27 23:13:14 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Implement runtime.FuncForPC. */
|
|
|
|
|
|
|
|
Func *
|
|
|
|
FuncForPC (uintptr_t pc)
|
|
|
|
{
|
|
|
|
Func *ret;
|
2014-09-21 17:33:12 +00:00
|
|
|
String fn;
|
|
|
|
String file;
|
|
|
|
intgo line;
|
2012-03-27 23:13:14 +00:00
|
|
|
uintptr_t val;
|
|
|
|
|
|
|
|
if (!__go_file_line (pc, &fn, &file, &line))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
ret = (Func *) runtime_malloc (sizeof (*ret));
|
|
|
|
ret->name = fn;
|
2014-09-21 17:33:12 +00:00
|
|
|
|
|
|
|
if (__go_symbol_value (pc, &val))
|
|
|
|
ret->entry = val;
|
|
|
|
else
|
|
|
|
ret->entry = 0;
|
|
|
|
|
2012-03-27 23:13:14 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Look up the file and line information for a PC within a
|
|
|
|
function. */
|
|
|
|
|
|
|
|
struct funcline_go_return
|
|
|
|
{
|
2014-09-21 17:33:12 +00:00
|
|
|
String retfile;
|
|
|
|
intgo retline;
|
2012-03-27 23:13:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct funcline_go_return
|
|
|
|
runtime_funcline_go (Func *f, uintptr targetpc)
|
2014-09-21 17:33:12 +00:00
|
|
|
__asm__ (GOSYM_PREFIX "runtime.funcline_go");
|
2012-03-27 23:13:14 +00:00
|
|
|
|
|
|
|
struct funcline_go_return
|
|
|
|
runtime_funcline_go (Func *f __attribute__((unused)), uintptr targetpc)
|
|
|
|
{
|
|
|
|
struct funcline_go_return ret;
|
2014-09-21 17:33:12 +00:00
|
|
|
String fn;
|
2012-03-27 23:13:14 +00:00
|
|
|
|
|
|
|
if (!__go_file_line (targetpc, &fn, &ret.retfile, &ret.retline))
|
|
|
|
runtime_memclr (&ret, sizeof ret);
|
|
|
|
return ret;
|
|
|
|
}
|
2014-09-21 17:33:12 +00:00
|
|
|
|
|
|
|
/* Return the name of a function. */
|
|
|
|
String runtime_funcname_go (Func *f)
|
|
|
|
__asm__ (GOSYM_PREFIX "runtime.funcname_go");
|
|
|
|
|
|
|
|
String
|
|
|
|
runtime_funcname_go (Func *f)
|
|
|
|
{
|
2015-08-28 15:33:40 +00:00
|
|
|
if (f == NULL)
|
|
|
|
return runtime_gostringnocopy ((const byte *) "");
|
2014-09-21 17:33:12 +00:00
|
|
|
return f->name;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the entry point of a function. */
|
|
|
|
uintptr runtime_funcentry_go(Func *f)
|
|
|
|
__asm__ (GOSYM_PREFIX "runtime.funcentry_go");
|
|
|
|
|
|
|
|
uintptr
|
|
|
|
runtime_funcentry_go (Func *f)
|
|
|
|
{
|
|
|
|
return f->entry;
|
|
|
|
}
|