1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
|
/* 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>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "backtrace.h"
#include "runtime.h"
/* Get the function name, file name, and line number for a PC value.
We use the backtrace library to get this. */
/* Data structure to gather file/line information. */
struct caller
{
String fn;
String file;
intgo line;
};
/* 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. */
static int
callback (void *data, uintptr_t pc __attribute__ ((unused)),
const char *filename, int lineno, const char *function)
{
struct caller *c = (struct caller *) data;
/* 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);
c->line = lineno;
return 0;
}
/* The error callback for backtrace_pcinfo and backtrace_syminfo. */
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);
}
/* The backtrace library state. */
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 ()
{
runtime_lock (&back_state_lock);
if (back_state == NULL)
{
const char *filename;
struct stat s;
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;
/* 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;
back_state = backtrace_create_state (filename, 1, error_callback, NULL);
}
runtime_unlock (&back_state_lock);
return back_state;
}
/* Return function/file/line information for PC. */
_Bool
__go_file_line (uintptr pc, String *fn, String *file, intgo *line)
{
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;
}
/* Collect symbol information. */
static void
syminfo_callback (void *data, uintptr_t pc __attribute__ ((unused)),
const char *symname __attribute__ ((unused)),
uintptr_t address, uintptr_t size __attribute__ ((unused)))
{
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;
}
/* The values returned by runtime.Caller. */
struct caller_ret
{
uintptr_t pc;
String file;
intgo line;
_Bool ok;
};
struct caller_ret Caller (int n) __asm__ (GOSYM_PREFIX "runtime.Caller");
Func *FuncForPC (uintptr_t) __asm__ (GOSYM_PREFIX "runtime.FuncForPC");
/* Implement runtime.Caller. */
struct caller_ret
Caller (int skip)
{
struct caller_ret ret;
Location loc;
int32 n;
runtime_memclr (&ret, sizeof ret);
n = runtime_callers (skip + 1, &loc, 1, false);
if (n < 1 || loc.pc == 0)
return ret;
ret.pc = loc.pc;
ret.file = loc.filename;
ret.line = loc.lineno;
ret.ok = 1;
return ret;
}
/* Implement runtime.FuncForPC. */
Func *
FuncForPC (uintptr_t pc)
{
Func *ret;
String fn;
String file;
intgo line;
uintptr_t val;
if (!__go_file_line (pc, &fn, &file, &line))
return NULL;
ret = (Func *) runtime_malloc (sizeof (*ret));
ret->name = fn;
if (__go_symbol_value (pc, &val))
ret->entry = val;
else
ret->entry = 0;
return ret;
}
/* Look up the file and line information for a PC within a
function. */
struct funcline_go_return
{
String retfile;
intgo retline;
};
struct funcline_go_return
runtime_funcline_go (Func *f, uintptr targetpc)
__asm__ (GOSYM_PREFIX "runtime.funcline_go");
struct funcline_go_return
runtime_funcline_go (Func *f __attribute__((unused)), uintptr targetpc)
{
struct funcline_go_return ret;
String fn;
if (!__go_file_line (targetpc, &fn, &ret.retfile, &ret.retline))
runtime_memclr (&ret, sizeof ret);
return ret;
}
/* Return the name of a function. */
String runtime_funcname_go (Func *f)
__asm__ (GOSYM_PREFIX "runtime.funcname_go");
String
runtime_funcname_go (Func *f)
{
if (f == NULL)
return runtime_gostringnocopy ((const byte *) "");
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;
}
|