github.com/golang/gofrontend@v0.0.0-20240429183944-60f985a78526/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 we should skip the frame we have, then see if we can get
    69  	 another one.  */
    70        if (c->fn.len > 0
    71  	  && runtime_skipInCallback((const char *) c->fn.str, NULL))
    72  	return 0;
    73  
    74        return 1;
    75      }
    76  
    77    if (c->index > 0)
    78      --c->index;
    79  
    80    return 0;
    81  }
    82  
    83  /* The error callback for backtrace_pcinfo and backtrace_syminfo.  */
    84  
    85  static void
    86  error_callback (void *data __attribute__ ((unused)),
    87  		const char *msg, int errnum)
    88  {
    89    if (errnum == -1)
    90      return;
    91    if (errnum > 0)
    92      runtime_printf ("%s errno %d\n", msg, errnum);
    93    runtime_throw (msg);
    94  }
    95  
    96  /* The backtrace library state.  */
    97  
    98  static void *back_state;
    99  
   100  /* A lock to control creating back_state.  */
   101  
   102  static uint32 back_state_lock;
   103  
   104  /* The program arguments.  */
   105  
   106  extern Slice runtime_get_args(void);
   107  
   108  /* Fetch back_state, creating it if necessary.  */
   109  
   110  struct backtrace_state *
   111  __go_get_backtrace_state ()
   112  {
   113    uint32 set;
   114  
   115    /* We may not have a g here, so we can't use runtime_lock.  */
   116    set = 0;
   117    while (!__atomic_compare_exchange_n (&back_state_lock, &set, 1, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
   118      {
   119        runtime_osyield ();
   120        set = 0;
   121      }
   122    if (back_state == NULL)
   123      {
   124        Slice args;
   125        const char *filename;
   126        struct stat s;
   127  
   128        args = runtime_get_args();
   129        filename = NULL;
   130        if (args.__count > 0)
   131  	filename = (const char*)((String*)args.__values)[0].str;
   132  
   133        /* If there is no '/' in FILENAME, it was found on PATH, and
   134  	 might not be the same as the file with the same name in the
   135  	 current directory.  */
   136        if (filename != NULL && __builtin_strchr (filename, '/') == NULL)
   137  	filename = NULL;
   138  
   139        /* If the file is small, then it's not the real executable.
   140  	 This is specifically to deal with Docker, which uses a bogus
   141  	 argv[0] (http://gcc.gnu.org/PR61895).  It would be nice to
   142  	 have a better check for whether this file is the real
   143  	 executable.  */
   144        if (filename != NULL && (stat (filename, &s) < 0 || s.st_size < 1024))
   145  	filename = NULL;
   146  
   147        back_state = backtrace_create_state (filename, 1, error_callback, NULL);
   148      }
   149    __atomic_store_n (&back_state_lock, 0, __ATOMIC_RELEASE);
   150    return back_state;
   151  }
   152  
   153  /* Return function/file/line/nframes information for PC.  The index
   154     parameter is the entry on the stack of inlined functions; -1 means
   155     the last one, with *nframes set to the count of inlined frames for
   156     this PC.  If index is not -1, more is whether there are more frames
   157     after this one.  */
   158  
   159  static _Bool
   160  __go_file_line (uintptr pc, int index, bool more, String *fn, String *file, intgo *line, intgo *nframes)
   161  {
   162    struct caller c;
   163    struct backtrace_state *state;
   164  
   165    runtime_memclr (&c, sizeof c);
   166    c.index = index;
   167    c.more = more;
   168    c.frames = 0;
   169    runtime_xadd (&__go_runtime_in_callers, 1);
   170    state = __go_get_backtrace_state ();
   171    runtime_xadd (&__go_runtime_in_callers, -1);
   172    backtrace_pcinfo (state, pc, callback, error_callback, &c);
   173    *fn = c.fn;
   174    *file = c.file;
   175    *line = c.line;
   176    *nframes = c.frames;
   177  
   178    // If backtrace_pcinfo didn't get the function name from the debug
   179    // info, try to get it from the symbol table.
   180    if (fn->len == 0)
   181      backtrace_syminfo (state, pc, __go_syminfo_fnname_callback,
   182  		       error_callback, fn);
   183  
   184    return c.file.len > 0;
   185  }
   186  
   187  /* Collect symbol information.  */
   188  
   189  static void
   190  syminfo_callback (void *data, uintptr_t pc __attribute__ ((unused)),
   191  		  const char *symname __attribute__ ((unused)),
   192  		  uintptr_t address, uintptr_t size __attribute__ ((unused)))
   193  {
   194    uintptr_t *pval = (uintptr_t *) data;
   195  
   196    *pval = address;
   197  }
   198  
   199  /* Set *VAL to the value of the symbol for PC.  */
   200  
   201  static _Bool
   202  __go_symbol_value (uintptr pc, uintptr *val)
   203  {
   204    struct backtrace_state *state;
   205  
   206    *val = 0;
   207    runtime_xadd (&__go_runtime_in_callers, 1);
   208    state = __go_get_backtrace_state ();
   209    runtime_xadd (&__go_runtime_in_callers, -1);
   210    backtrace_syminfo (state, pc, syminfo_callback,
   211  		     error_callback, val);
   212    return *val != 0;
   213  }
   214  
   215  /* The values returned by runtime.Caller.  */
   216  
   217  struct caller_ret
   218  {
   219    uintptr_t pc;
   220    String file;
   221    intgo line;
   222    _Bool ok;
   223  };
   224  
   225  struct caller_ret Caller (intgo n) __asm__ (GOSYM_PREFIX "runtime.Caller");
   226  
   227  /* Implement runtime.Caller.  */
   228  
   229  struct caller_ret
   230  Caller (intgo skip)
   231  {
   232    struct caller_ret ret;
   233    Location loc;
   234    int32 n;
   235  
   236    runtime_memclr (&ret, sizeof ret);
   237    n = runtime_callers (skip + 1, &loc, 1, false);
   238    if (n < 1 || loc.pc == 0)
   239      return ret;
   240    ret.pc = loc.pc;
   241    ret.file = loc.filename;
   242    ret.line = loc.lineno;
   243    ret.ok = 1;
   244    return ret;
   245  }
   246  
   247  /* Look up the function name, file name, and line number for a PC.  */
   248  
   249  struct funcfileline_return
   250  runtime_funcfileline (uintptr targetpc, int32 index, bool more)
   251  {
   252    struct funcfileline_return ret;
   253  
   254    if (!__go_file_line (targetpc, index, more, &ret.retfn, &ret.retfile,
   255  		       &ret.retline, &ret.retframes))
   256      runtime_memclr (&ret, sizeof ret);
   257    return ret;
   258  }
   259  
   260  /* Return the entry point of a function.  */
   261  uintptr runtime_funcentry(uintptr)
   262    __asm__ (GOSYM_PREFIX "runtime.funcentry");
   263  
   264  uintptr
   265  runtime_funcentry (uintptr pc)
   266  {
   267    uintptr val;
   268  
   269    if (!__go_symbol_value (pc, &val))
   270      return 0;
   271    return val;
   272  }