2abacbaec7
PR go/52583 runtime: Stop backtrace at a few recognized functions. On x86_64 Solaris the makecontext function does not properly indicate that it is at the top of the stack. Attempting to unwind the stack past a call to makecontext tends to crash. This patch changes libgo to look for certain functions that are always found at the top of the stack, and to stop unwinding when it reaches one of those functions. There is never anything interesting past these functions--that is, there is never any code written by the user. From-SVN: r211640
177 lines
4.4 KiB
C
177 lines
4.4 KiB
C
/* 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;
|
|
|
|
/* There is no point to tracing past certain runtime functions.
|
|
Stopping the backtrace here can avoid problems on systems that
|
|
don't provide proper unwind information for makecontext, such as
|
|
Solaris (http://gcc.gnu.org/PR52583 comment #21). */
|
|
if (function != NULL)
|
|
{
|
|
if (__builtin_strcmp (function, "makecontext") == 0)
|
|
return 1;
|
|
if (filename != NULL)
|
|
{
|
|
const char *p;
|
|
|
|
p = strrchr (filename, '/');
|
|
if (p == NULL)
|
|
p = filename;
|
|
if (__builtin_strcmp (p, "/proc.c") == 0)
|
|
{
|
|
if (__builtin_strcmp (function, "kickoff") == 0
|
|
|| __builtin_strcmp (function, "runtime_mstart") == 0
|
|
|| __builtin_strcmp (function, "runtime_main") == 0)
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|