blob: ae411d9c83a7c1db74fd5b7e1d6dcfeb60848985 [file] [log] [blame]
/* go-callers.c -- get callers for Go.
Copyright 2012 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. */
#include "config.h"
#include "backtrace.h"
#include "runtime.h"
#include "array.h"
/* This is set to non-zero when calling backtrace_full. This is used
to avoid getting hanging on a recursive lock in dl_iterate_phdr on
older versions of glibc when a SIGPROF signal arrives while
collecting a backtrace. */
uint32 runtime_in_callers;
/* Argument passed to callback function. */
struct callers_data
{
Location *locbuf;
int skip;
int index;
int max;
};
/* Callback function for backtrace_full. Just collect the locations.
Return zero to continue, non-zero to stop. */
static int
callback (void *data, uintptr_t pc, const char *filename, int lineno,
const char *function)
{
struct callers_data *arg = (struct callers_data *) data;
Location *loc;
/* Skip split stack functions. */
if (function != NULL)
{
const char *p;
p = function;
if (__builtin_strncmp (p, "___", 3) == 0)
++p;
if (__builtin_strncmp (p, "__morestack_", 12) == 0)
return 0;
}
else if (filename != NULL)
{
const char *p;
p = strrchr (filename, '/');
if (p == NULL)
p = filename;
if (__builtin_strncmp (p, "/morestack.S", 12) == 0)
return 0;
}
/* Skip thunks and recover functions. There is no equivalent to
these functions in the gc toolchain, so returning them here means
significantly different results for runtime.Caller(N). */
if (function != NULL)
{
const char *p;
p = __builtin_strchr (function, '.');
if (p != NULL && __builtin_strncmp (p + 1, "$thunk", 6) == 0)
return 0;
p = __builtin_strrchr (function, '$');
if (p != NULL && __builtin_strcmp(p, "$recover") == 0)
return 0;
}
if (arg->skip > 0)
{
--arg->skip;
return 0;
}
loc = &arg->locbuf[arg->index];
loc->pc = pc;
/* 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. */
loc->filename = runtime_gostringnocopy ((const byte *) filename);
loc->function = runtime_gostringnocopy ((const byte *) function);
loc->lineno = lineno;
++arg->index;
return arg->index >= arg->max;
}
/* Error callback. */
static void
error_callback (void *data __attribute__ ((unused)),
const char *msg, int errnum)
{
if (errnum != 0)
runtime_printf ("%s errno %d\n", msg, errnum);
runtime_throw (msg);
}
/* Gather caller PC's. */
int32
runtime_callers (int32 skip, Location *locbuf, int32 m)
{
struct callers_data data;
data.locbuf = locbuf;
data.skip = skip + 1;
data.index = 0;
data.max = m;
runtime_xadd (&runtime_in_callers, 1);
backtrace_full (__go_get_backtrace_state (), 0, callback, error_callback,
&data);
runtime_xadd (&runtime_in_callers, -1);
return data.index;
}
int Callers (int, struct __go_open_array)
__asm__ (GOSYM_PREFIX "runtime.Callers");
int
Callers (int skip, struct __go_open_array pc)
{
Location *locbuf;
int ret;
int i;
locbuf = (Location *) runtime_mal (pc.__count * sizeof (Location));
/* In the Go 1 release runtime.Callers has an off-by-one error,
which we can not correct because it would break backward
compatibility. Normally we would add 1 to SKIP here, but we
don't so that we are compatible. */
ret = runtime_callers (skip, locbuf, pc.__count);
for (i = 0; i < ret; i++)
((uintptr *) pc.__values)[i] = locbuf[i].pc;
return ret;
}