]>
Commit | Line | Data |
---|---|---|
db2fb304 | 1 | /* go-caller.c -- look up function/file/line/entry info |
7a938933 ILT |
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> | |
dc14e88e ILT |
10 | #include <sys/types.h> |
11 | #include <sys/stat.h> | |
12 | #include <unistd.h> | |
7a938933 | 13 | |
0e56e590 ILT |
14 | #include "backtrace.h" |
15 | ||
0effc3f9 | 16 | #include "runtime.h" |
7a938933 | 17 | |
0effc3f9 | 18 | /* Get the function name, file name, and line number for a PC value. |
0e56e590 ILT |
19 | We use the backtrace library to get this. */ |
20 | ||
21 | /* Data structure to gather file/line information. */ | |
22 | ||
23 | struct caller | |
24 | { | |
776f27a6 ILT |
25 | String fn; |
26 | String file; | |
27 | intgo line; | |
22b955cc | 28 | intgo index; |
1ac09ef2 | 29 | intgo frames; |
5a8ea165 | 30 | bool more; |
0e56e590 ILT |
31 | }; |
32 | ||
33 | /* Collect file/line information for a PC value. If this is called | |
1ac09ef2 ILT |
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. */ | |
0e56e590 ILT |
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 | ||
5a8ea165 ILT |
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++; | |
1ac09ef2 | 57 | |
4729d772 ILT |
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); | |
0e56e590 ILT |
64 | c->line = lineno; |
65 | ||
22b955cc | 66 | if (c->index == 0) |
5a8ea165 ILT |
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 | } | |
22b955cc ILT |
77 | |
78 | if (c->index > 0) | |
79 | --c->index; | |
80 | ||
0e56e590 ILT |
81 | return 0; |
82 | } | |
0effc3f9 | 83 | |
0e56e590 | 84 | /* The error callback for backtrace_pcinfo and backtrace_syminfo. */ |
0effc3f9 | 85 | |
0e56e590 ILT |
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. */ | |
0effc3f9 | 98 | |
0e56e590 | 99 | static void *back_state; |
0effc3f9 | 100 | |
0e56e590 | 101 | /* A lock to control creating back_state. */ |
0effc3f9 | 102 | |
df206c6e | 103 | static uint32 back_state_lock; |
0effc3f9 | 104 | |
67487878 ILT |
105 | /* The program arguments. */ |
106 | ||
107 | extern Slice runtime_get_args(void); | |
108 | ||
0e56e590 | 109 | /* Fetch back_state, creating it if necessary. */ |
0effc3f9 | 110 | |
0e56e590 ILT |
111 | struct backtrace_state * |
112 | __go_get_backtrace_state () | |
0effc3f9 | 113 | { |
df206c6e ILT |
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 | } | |
0e56e590 | 123 | if (back_state == NULL) |
686750d2 | 124 | { |
67487878 | 125 | Slice args; |
686750d2 | 126 | const char *filename; |
dc14e88e | 127 | struct stat s; |
686750d2 | 128 | |
67487878 ILT |
129 | args = runtime_get_args(); |
130 | filename = NULL; | |
131 | if (args.__count > 0) | |
132 | filename = (const char*)((String*)args.__values)[0].str; | |
37512c03 ILT |
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. */ | |
67487878 | 137 | if (filename != NULL && __builtin_strchr (filename, '/') == NULL) |
37512c03 ILT |
138 | filename = NULL; |
139 | ||
dc14e88e ILT |
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. */ | |
94e12bd4 | 145 | if (filename != NULL && (stat (filename, &s) < 0 || s.st_size < 1024)) |
dc14e88e ILT |
146 | filename = NULL; |
147 | ||
686750d2 ILT |
148 | back_state = backtrace_create_state (filename, 1, error_callback, NULL); |
149 | } | |
df206c6e | 150 | __atomic_store_n (&back_state_lock, 0, __ATOMIC_RELEASE); |
0e56e590 | 151 | return back_state; |
0effc3f9 ILT |
152 | } |
153 | ||
5a8ea165 ILT |
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. */ | |
0effc3f9 | 159 | |
afedc99b | 160 | static _Bool |
5a8ea165 | 161 | __go_file_line (uintptr pc, int index, bool more, String *fn, String *file, intgo *line, intgo *nframes) |
0effc3f9 | 162 | { |
0e56e590 | 163 | struct caller c; |
afedc99b | 164 | struct backtrace_state *state; |
0e56e590 ILT |
165 | |
166 | runtime_memclr (&c, sizeof c); | |
22b955cc | 167 | c.index = index; |
5a8ea165 | 168 | c.more = more; |
1ac09ef2 | 169 | c.frames = 0; |
f6be1179 | 170 | runtime_xadd (&__go_runtime_in_callers, 1); |
afedc99b | 171 | state = __go_get_backtrace_state (); |
f6be1179 | 172 | runtime_xadd (&__go_runtime_in_callers, -1); |
afedc99b | 173 | backtrace_pcinfo (state, pc, callback, error_callback, &c); |
0e56e590 ILT |
174 | *fn = c.fn; |
175 | *file = c.file; | |
176 | *line = c.line; | |
1ac09ef2 | 177 | *nframes = c.frames; |
afedc99b ILT |
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 | ||
776f27a6 | 185 | return c.file.len > 0; |
0effc3f9 ILT |
186 | } |
187 | ||
0e56e590 | 188 | /* Collect symbol information. */ |
0effc3f9 | 189 | |
0e56e590 ILT |
190 | static void |
191 | syminfo_callback (void *data, uintptr_t pc __attribute__ ((unused)), | |
192 | const char *symname __attribute__ ((unused)), | |
92495ff6 | 193 | uintptr_t address, uintptr_t size __attribute__ ((unused))) |
0e56e590 ILT |
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 | |
3ff1b2b0 | 203 | __go_symbol_value (uintptr pc, uintptr *val) |
0effc3f9 | 204 | { |
f6be1179 ILT |
205 | struct backtrace_state *state; |
206 | ||
0e56e590 | 207 | *val = 0; |
f6be1179 ILT |
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, | |
fb521d54 | 212 | error_callback, val); |
0e56e590 | 213 | return *val != 0; |
0effc3f9 ILT |
214 | } |
215 | ||
7a938933 ILT |
216 | /* The values returned by runtime.Caller. */ |
217 | ||
218 | struct caller_ret | |
219 | { | |
220 | uintptr_t pc; | |
776f27a6 ILT |
221 | String file; |
222 | intgo line; | |
7a938933 ILT |
223 | _Bool ok; |
224 | }; | |
225 | ||
8a9f2a6b | 226 | struct caller_ret Caller (intgo n) __asm__ (GOSYM_PREFIX "runtime.Caller"); |
7a938933 | 227 | |
0effc3f9 ILT |
228 | /* Implement runtime.Caller. */ |
229 | ||
7a938933 | 230 | struct caller_ret |
8a9f2a6b | 231 | Caller (intgo skip) |
7a938933 ILT |
232 | { |
233 | struct caller_ret ret; | |
27741f93 | 234 | Location loc; |
0effc3f9 | 235 | int32 n; |
7a938933 | 236 | |
0effc3f9 | 237 | runtime_memclr (&ret, sizeof ret); |
dffa7328 | 238 | n = runtime_callers (skip + 1, &loc, 1, false); |
46f120ca | 239 | if (n < 1 || loc.pc == 0) |
0effc3f9 | 240 | return ret; |
27741f93 ILT |
241 | ret.pc = loc.pc; |
242 | ret.file = loc.filename; | |
243 | ret.line = loc.lineno; | |
92aecb44 | 244 | ret.ok = 1; |
7a938933 ILT |
245 | return ret; |
246 | } | |
247 | ||
db2fb304 | 248 | /* Look up the function name, file name, and line number for a PC. */ |
7a938933 | 249 | |
db2fb304 | 250 | struct funcfileline_return |
5a8ea165 | 251 | runtime_funcfileline (uintptr targetpc, int32 index, bool more) |
0effc3f9 | 252 | { |
db2fb304 | 253 | struct funcfileline_return ret; |
0effc3f9 | 254 | |
5a8ea165 | 255 | if (!__go_file_line (targetpc, index, more, &ret.retfn, &ret.retfile, |
1ac09ef2 | 256 | &ret.retline, &ret.retframes)) |
0effc3f9 ILT |
257 | runtime_memclr (&ret, sizeof ret); |
258 | return ret; | |
7a938933 | 259 | } |
f038dae6 | 260 | |
f038dae6 | 261 | /* Return the entry point of a function. */ |
db2fb304 ILT |
262 | uintptr runtime_funcentry(uintptr) |
263 | __asm__ (GOSYM_PREFIX "runtime.funcentry"); | |
f038dae6 ILT |
264 | |
265 | uintptr | |
db2fb304 | 266 | runtime_funcentry (uintptr pc) |
f038dae6 | 267 | { |
db2fb304 | 268 | uintptr val; |
22b955cc | 269 | |
db2fb304 ILT |
270 | if (!__go_symbol_value (pc, &val)) |
271 | return 0; | |
272 | return val; | |
22b955cc | 273 | } |