123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- /* go-caller.c -- look up function/file/line/entry info
- 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;
- intgo index;
- intgo frames;
- bool more;
- };
- /* Collect file/line information for a PC value. If this is called
- more than once, due to inlined functions, we record the number of
- inlined frames but return file/func/line for 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;
- /* We want to make sure we return at least one frame. If we already
- have at least one frame, see if we should skip this one. */
- if (c->frames > 0
- && function != NULL
- && runtime_skipInCallback (function, NULL))
- return 0;
- /* If we already have a frame, don't increment frames if we should
- skip that one. */
- if (c->frames == 0
- || c->fn.len == 0
- || !runtime_skipInCallback ((const char *) c->fn.str, NULL))
- c->frames++;
- /* 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;
- if (c->index == 0)
- {
- /* If we should skip the frame we have, then see if we can get
- another one. */
- if (c->fn.len > 0
- && runtime_skipInCallback((const char *) c->fn.str, NULL))
- return 0;
- return 1;
- }
- if (c->index > 0)
- --c->index;
- 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 uint32 back_state_lock;
- /* The program arguments. */
- extern Slice runtime_get_args(void);
- /* Fetch back_state, creating it if necessary. */
- struct backtrace_state *
- __go_get_backtrace_state ()
- {
- uint32 set;
- /* We may not have a g here, so we can't use runtime_lock. */
- set = 0;
- while (!__atomic_compare_exchange_n (&back_state_lock, &set, 1, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
- {
- runtime_osyield ();
- set = 0;
- }
- if (back_state == NULL)
- {
- Slice args;
- const char *filename;
- struct stat s;
- args = runtime_get_args();
- filename = NULL;
- if (args.__count > 0)
- filename = (const char*)((String*)args.__values)[0].str;
- /* 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 (filename != NULL && __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 (filename != NULL && (stat (filename, &s) < 0 || s.st_size < 1024))
- filename = NULL;
- back_state = backtrace_create_state (filename, 1, error_callback, NULL);
- }
- __atomic_store_n (&back_state_lock, 0, __ATOMIC_RELEASE);
- return back_state;
- }
- /* Return function/file/line/nframes information for PC. The index
- parameter is the entry on the stack of inlined functions; -1 means
- the last one, with *nframes set to the count of inlined frames for
- this PC. If index is not -1, more is whether there are more frames
- after this one. */
- static _Bool
- __go_file_line (uintptr pc, int index, bool more, String *fn, String *file, intgo *line, intgo *nframes)
- {
- struct caller c;
- struct backtrace_state *state;
- runtime_memclr (&c, sizeof c);
- c.index = index;
- c.more = more;
- c.frames = 0;
- runtime_xadd (&__go_runtime_in_callers, 1);
- state = __go_get_backtrace_state ();
- runtime_xadd (&__go_runtime_in_callers, -1);
- backtrace_pcinfo (state, pc, callback, error_callback, &c);
- *fn = c.fn;
- *file = c.file;
- *line = c.line;
- *nframes = c.frames;
- // If backtrace_pcinfo didn't get the function name from the debug
- // info, try to get it from the symbol table.
- if (fn->len == 0)
- backtrace_syminfo (state, pc, __go_syminfo_fnname_callback,
- error_callback, fn);
- 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 pc, uintptr *val)
- {
- struct backtrace_state *state;
- *val = 0;
- runtime_xadd (&__go_runtime_in_callers, 1);
- state = __go_get_backtrace_state ();
- runtime_xadd (&__go_runtime_in_callers, -1);
- backtrace_syminfo (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 (intgo n) __asm__ (GOSYM_PREFIX "runtime.Caller");
- /* Implement runtime.Caller. */
- struct caller_ret
- Caller (intgo 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;
- }
- /* Look up the function name, file name, and line number for a PC. */
- struct funcfileline_return
- runtime_funcfileline (uintptr targetpc, int32 index, bool more)
- {
- struct funcfileline_return ret;
- if (!__go_file_line (targetpc, index, more, &ret.retfn, &ret.retfile,
- &ret.retline, &ret.retframes))
- runtime_memclr (&ret, sizeof ret);
- return ret;
- }
- /* Return the entry point of a function. */
- uintptr runtime_funcentry(uintptr)
- __asm__ (GOSYM_PREFIX "runtime.funcentry");
- uintptr
- runtime_funcentry (uintptr pc)
- {
- uintptr val;
- if (!__go_symbol_value (pc, &val))
- return 0;
- return val;
- }
|