1 /* go-callers.c -- get callers for Go.
2 
3    Copyright 2012 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 #include "config.h"
8 
9 #include "backtrace.h"
10 
11 #include "runtime.h"
12 #include "array.h"
13 
14 /* This is set to non-zero when calling backtrace_full.  This is used
15    to avoid getting hanging on a recursive lock in dl_iterate_phdr on
16    older versions of glibc when a SIGPROF signal arrives while
17    collecting a backtrace.  */
18 
19 uint32 runtime_in_callers;
20 
21 /* Argument passed to callback function.  */
22 
23 struct callers_data
24 {
25   Location *locbuf;
26   int skip;
27   int index;
28   int max;
29   int keep_thunks;
30 };
31 
32 /* Callback function for backtrace_full.  Just collect the locations.
33    Return zero to continue, non-zero to stop.  */
34 
35 static int
callback(void * data,uintptr_t pc,const char * filename,int lineno,const char * function)36 callback (void *data, uintptr_t pc, const char *filename, int lineno,
37 	  const char *function)
38 {
39   struct callers_data *arg = (struct callers_data *) data;
40   Location *loc;
41 
42   /* Skip split stack functions.  */
43   if (function != NULL)
44     {
45       const char *p;
46 
47       p = function;
48       if (__builtin_strncmp (p, "___", 3) == 0)
49 	++p;
50       if (__builtin_strncmp (p, "__morestack_", 12) == 0)
51 	return 0;
52     }
53   else if (filename != NULL)
54     {
55       const char *p;
56 
57       p = strrchr (filename, '/');
58       if (p == NULL)
59 	p = filename;
60       if (__builtin_strncmp (p, "/morestack.S", 12) == 0)
61 	return 0;
62     }
63 
64   /* Skip thunks and recover functions.  There is no equivalent to
65      these functions in the gc toolchain, so returning them here means
66      significantly different results for runtime.Caller(N).  */
67   if (function != NULL && !arg->keep_thunks)
68     {
69       const char *p;
70 
71       p = __builtin_strchr (function, '.');
72       if (p != NULL && __builtin_strncmp (p + 1, "$thunk", 6) == 0)
73 	return 0;
74       p = __builtin_strrchr (function, '$');
75       if (p != NULL && __builtin_strcmp(p, "$recover") == 0)
76 	return 0;
77       if (p != NULL && __builtin_strncmp(p, "$stub", 5) == 0)
78 	return 0;
79     }
80 
81   if (arg->skip > 0)
82     {
83       --arg->skip;
84       return 0;
85     }
86 
87   loc = &arg->locbuf[arg->index];
88 
89   /* On the call to backtrace_full the pc value was most likely
90      decremented if there was a normal call, since the pc referred to
91      the instruction where the call returned and not the call itself.
92      This was done so that the line number referred to the call
93      instruction.  To make sure the actual pc from the call stack is
94      used, it is incremented here.
95 
96      In the case of a signal, the pc was not decremented by
97      backtrace_full but still incremented here.  That doesn't really
98      hurt anything since the line number is right and the pc refers to
99      the same instruction.  */
100 
101   loc->pc = pc + 1;
102 
103   /* The libbacktrace library says that these strings might disappear,
104      but with the current implementation they won't.  We can't easily
105      allocate memory here, so for now assume that we can save a
106      pointer to the strings.  */
107   loc->filename = runtime_gostringnocopy ((const byte *) filename);
108   loc->function = runtime_gostringnocopy ((const byte *) function);
109 
110   loc->lineno = lineno;
111   ++arg->index;
112 
113   /* There is no point to tracing past certain runtime functions.
114      Stopping the backtrace here can avoid problems on systems that
115      don't provide proper unwind information for makecontext, such as
116      Solaris (http://gcc.gnu.org/PR52583 comment #21).  */
117   if (function != NULL)
118     {
119       if (__builtin_strcmp (function, "makecontext") == 0)
120 	return 1;
121       if (filename != NULL)
122 	{
123 	  const char *p;
124 
125 	  p = strrchr (filename, '/');
126 	  if (p == NULL)
127 	    p = filename;
128 	  if (__builtin_strcmp (p, "/proc.c") == 0)
129 	    {
130 	      if (__builtin_strcmp (function, "kickoff") == 0
131 		  || __builtin_strcmp (function, "runtime_mstart") == 0
132 		  || __builtin_strcmp (function, "runtime_main") == 0)
133 		return 1;
134 	    }
135 	}
136     }
137 
138   return arg->index >= arg->max;
139 }
140 
141 /* Error callback.  */
142 
143 static void
error_callback(void * data,const char * msg,int errnum)144 error_callback (void *data __attribute__ ((unused)),
145 		const char *msg, int errnum)
146 {
147   if (errnum == -1)
148     {
149       /* No debug info available.  Carry on as best we can.  */
150       return;
151     }
152   if (errnum != 0)
153     runtime_printf ("%s errno %d\n", msg, errnum);
154   runtime_throw (msg);
155 }
156 
157 /* Gather caller PC's.  */
158 
159 int32
runtime_callers(int32 skip,Location * locbuf,int32 m,bool keep_thunks)160 runtime_callers (int32 skip, Location *locbuf, int32 m, bool keep_thunks)
161 {
162   struct callers_data data;
163 
164   data.locbuf = locbuf;
165   data.skip = skip + 1;
166   data.index = 0;
167   data.max = m;
168   data.keep_thunks = keep_thunks;
169   runtime_xadd (&runtime_in_callers, 1);
170   backtrace_full (__go_get_backtrace_state (), 0, callback, error_callback,
171 		  &data);
172   runtime_xadd (&runtime_in_callers, -1);
173   return data.index;
174 }
175 
176 int Callers (int, struct __go_open_array)
177   __asm__ (GOSYM_PREFIX "runtime.Callers");
178 
179 int
Callers(int skip,struct __go_open_array pc)180 Callers (int skip, struct __go_open_array pc)
181 {
182   Location *locbuf;
183   int ret;
184   int i;
185 
186   locbuf = (Location *) runtime_mal (pc.__count * sizeof (Location));
187 
188   /* In the Go 1 release runtime.Callers has an off-by-one error,
189      which we can not correct because it would break backward
190      compatibility.  Normally we would add 1 to SKIP here, but we
191      don't so that we are compatible.  */
192   ret = runtime_callers (skip, locbuf, pc.__count, false);
193 
194   for (i = 0; i < ret; i++)
195     ((uintptr *) pc.__values)[i] = locbuf[i].pc;
196 
197   return ret;
198 }
199