github.com/guyezi/gofrontend@v0.0.0-20200228202240-7a62a49e62c0/libgo/runtime/go-caller.c (about)

     1  /* go-caller.c -- look up function/file/line/entry info
     2  
     3     Copyright 2009 The Go Authors. All rights reserved.
     4     Use of this source code is governed by a BSD-style
     5     license that can be found in the LICENSE file.  */
     6  
     7  /* Implement runtime.Caller.  */
     8  
     9  #include <stdint.h>
    10  #include <sys/types.h>
    11  #include <sys/stat.h>
    12  #include <unistd.h>
    13  
    14  #include "backtrace.h"
    15  
    16  #include "runtime.h"
    17  
    18  /* Get the function name, file name, and line number for a PC value.
    19     We use the backtrace library to get this.  */
    20  
    21  /* Data structure to gather file/line information.  */
    22  
    23  struct caller
    24  {
    25    String fn;
    26    String file;
    27    intgo line;
    28    intgo index;
    29    intgo frames;
    30    bool more;
    31  };
    32  
    33  /* Collect file/line information for a PC value.  If this is called
    34     more than once, due to inlined functions, we record the number of
    35     inlined frames but return file/func/line for the last call, as
    36     that is usually the most useful one.   */
    37  
    38  static int
    39  callback (void *data, uintptr_t pc __attribute__ ((unused)),
    40  	  const char *filename, int lineno, const char *function)
    41  {
    42    struct caller *c = (struct caller *) data;
    43  
    44    /* We want to make sure we return at least one frame.  If we already
    45       have at least one frame, see if we should skip this one.  */
    46    if (c->frames > 0
    47        && function != NULL
    48        && runtime_skipInCallback (function, NULL))
    49      return 0;
    50  
    51    /* If we already have a frame, don't increment frames if we should
    52       skip that one.  */
    53    if (c->frames == 0
    54        || c->fn.len == 0
    55        || !runtime_skipInCallback ((const char *) c->fn.str, NULL))
    56      c->frames++;
    57  
    58    /* The libbacktrace library says that these strings might disappear,
    59       but with the current implementation they won't.  We can't easily
    60       allocate memory here, so for now assume that we can save a
    61       pointer to the strings.  */
    62    c->fn = runtime_gostringnocopy ((const byte *) function);
    63    c->file = runtime_gostringnocopy ((const byte *) filename);
    64    c->line = lineno;
    65  
    66    if (c->index == 0)
    67      {
    68        /* If there are more frames after the indexed one, and we should
    69  	 skip this one, then skip it.  */
    70        if (c->more
    71  	  && c->fn.len > 0
    72  	  && runtime_skipInCallback((const char *) c->fn.str, NULL))
    73  	return 0;
    74  
    75        return 1;
    76      }
    77  
    78    if (c->index > 0)
    79      --c->index;
    80  
    81    return 0;
    82  }
    83  
    84  /* The error callback for backtrace_pcinfo and backtrace_syminfo.  */
    85  
    86  static void
    87  error_callback (void *data __attribute__ ((unused)),
    88  		const char *msg, int errnum)
    89  {
    90    if (errnum == -1)
    91      return;
    92    if (errnum > 0)
    93      runtime_printf ("%s errno %d\n", msg, errnum);
    94    runtime_throw (msg);
    95  }
    96  
    97  /* The backtrace library state.  */
    98  
    99  static void *back_state;
   100  
   101  /* A lock to control creating back_state.  */
   102  
   103  static uint32 back_state_lock;
   104  
   105  /* The program arguments.  */
   106  
   107  extern Slice runtime_get_args(void);
   108  
   109  /* Fetch back_state, creating it if necessary.  */
   110  
   111  struct backtrace_state *
   112  __go_get_backtrace_state ()
   113  {
   114    uint32 set;
   115  
   116    /* We may not have a g here, so we can't use runtime_lock.  */
   117    set = 0;
   118    while (!__atomic_compare_exchange_n (&back_state_lock, &set, 1, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
   119      {
   120        runtime_osyield ();
   121        set = 0;
   122      }
   123    if (back_state == NULL)
   124      {
   125        Slice args;
   126        const char *filename;
   127        struct stat s;
   128  
   129        args = runtime_get_args();
   130        filename = NULL;
   131        if (args.__count > 0)
   132  	filename = (const char*)((String*)args.__values)[0].str;
   133  
   134        /* If there is no '/' in FILENAME, it was found on PATH, and
   135  	 might not be the same as the file with the same name in the
   136  	 current directory.  */
   137        if (filename != NULL && __builtin_strchr (filename, '/') == NULL)
   138  	filename = NULL;
   139  
   140        /* If the file is small, then it's not the real executable.
   141  	 This is specifically to deal with Docker, which uses a bogus
   142  	 argv[0] (http://gcc.gnu.org/PR61895).  It would be nice to
   143  	 have a better check for whether this file is the real
   144  	 executable.  */
   145        if (filename != NULL && (stat (filename, &s) < 0 || s.st_size < 1024))
   146  	filename = NULL;
   147  
   148        back_state = backtrace_create_state (filename, 1, error_callback, NULL);
   149      }
   150    __atomic_store_n (&back_state_lock, 0, __ATOMIC_RELEASE);
   151    return back_state;
   152  }
   153  
   154  /* Return function/file/line/nframes information for PC.  The index
   155     parameter is the entry on the stack of inlined functions; -1 means
   156     the last one, with *nframes set to the count of inlined frames for
   157     this PC.  If index is not -1, more is whether there are more frames
   158     after this one.  */
   159  
   160  static _Bool
   161  __go_file_line (uintptr pc, int index, bool more, String *fn, String *file, intgo *line, intgo *nframes)
   162  {
   163    struct caller c;
   164    struct backtrace_state *state;
   165  
   166    runtime_memclr (&c, sizeof c);
   167    c.index = index;
   168    c.more = more;
   169    c.frames = 0;
   170    runtime_xadd (&__go_runtime_in_callers, 1);
   171    state = __go_get_backtrace_state ();
   172    runtime_xadd (&__go_runtime_in_callers, -1);
   173    backtrace_pcinfo (state, pc, callback, error_callback, &c);
   174    *fn = c.fn;
   175    *file = c.file;
   176    *line = c.line;
   177    *nframes = c.frames;
   178  
   179    // If backtrace_pcinfo didn't get the function name from the debug
   180    // info, try to get it from the symbol table.
   181    if (fn->len == 0)
   182      backtrace_syminfo (state, pc, __go_syminfo_fnname_callback,
   183  		       error_callback, fn);
   184  
   185    return c.file.len > 0;
   186  }
   187  
   188  /* Collect symbol information.  */
   189  
   190  static void
   191  syminfo_callback (void *data, uintptr_t pc __attribute__ ((unused)),
   192  		  const char *symname __attribute__ ((unused)),
   193  		  uintptr_t address, uintptr_t size __attribute__ ((unused)))
   194  {
   195    uintptr_t *pval = (uintptr_t *) data;
   196  
   197    *pval = address;
   198  }
   199  
   200  /* Set *VAL to the value of the symbol for PC.  */
   201  
   202  static _Bool
   203  __go_symbol_value (uintptr pc, uintptr *val)
   204  {
   205    struct backtrace_state *state;
   206  
   207    *val = 0;
   208    runtime_xadd (&__go_runtime_in_callers, 1);
   209    state = __go_get_backtrace_state ();
   210    runtime_xadd (&__go_runtime_in_callers, -1);
   211    backtrace_syminfo (state, pc, syminfo_callback,
   212  		     error_callback, val);
   213    return *val != 0;
   214  }
   215  
   216  /* The values returned by runtime.Caller.  */
   217  
   218  struct caller_ret
   219  {
   220    uintptr_t pc;
   221    String file;
   222    intgo line;
   223    _Bool ok;
   224  };
   225  
   226  struct caller_ret Caller (intgo n) __asm__ (GOSYM_PREFIX "runtime.Caller");
   227  
   228  /* Implement runtime.Caller.  */
   229  
   230  struct caller_ret
   231  Caller (intgo skip)
   232  {
   233    struct caller_ret ret;
   234    Location loc;
   235    int32 n;
   236  
   237    runtime_memclr (&ret, sizeof ret);
   238    n = runtime_callers (skip + 1, &loc, 1, false);
   239    if (n < 1 || loc.pc == 0)
   240      return ret;
   241    ret.pc = loc.pc;
   242    ret.file = loc.filename;
   243    ret.line = loc.lineno;
   244    ret.ok = 1;
   245    return ret;
   246  }
   247  
   248  /* Look up the function name, file name, and line number for a PC.  */
   249  
   250  struct funcfileline_return
   251  runtime_funcfileline (uintptr targetpc, int32 index, bool more)
   252  {
   253    struct funcfileline_return ret;
   254  
   255    if (!__go_file_line (targetpc, index, more, &ret.retfn, &ret.retfile,
   256  		       &ret.retline, &ret.retframes))
   257      runtime_memclr (&ret, sizeof ret);
   258    return ret;
   259  }
   260  
   261  /* Return the entry point of a function.  */
   262  uintptr runtime_funcentry(uintptr)
   263    __asm__ (GOSYM_PREFIX "runtime.funcentry");
   264  
   265  uintptr
   266  runtime_funcentry (uintptr pc)
   267  {
   268    uintptr val;
   269  
   270    if (!__go_symbol_value (pc, &val))
   271      return 0;
   272    return val;
   273  }