The open source OpenXR runtime
at prediction-2 351 lines 8.9 kB view raw
1/* fileline.c -- Get file and line number information in a backtrace. 2 Copyright (C) 2012-2021 Free Software Foundation, Inc. 3 Written by Ian Lance Taylor, Google. 4 5Redistribution and use in source and binary forms, with or without 6modification, are permitted provided that the following conditions are 7met: 8 9 (1) Redistributions of source code must retain the above copyright 10 notice, this list of conditions and the following disclaimer. 11 12 (2) Redistributions in binary form must reproduce the above copyright 13 notice, this list of conditions and the following disclaimer in 14 the documentation and/or other materials provided with the 15 distribution. 16 17 (3) The name of the author may not be used to 18 endorse or promote products derived from this software without 19 specific prior written permission. 20 21THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31POSSIBILITY OF SUCH DAMAGE. */ 32 33#include "config.h" 34 35#include <sys/types.h> 36#include <sys/stat.h> 37#include <errno.h> 38#include <fcntl.h> 39#include <stdlib.h> 40#include <unistd.h> 41 42#if defined (HAVE_KERN_PROC_ARGS) || defined (HAVE_KERN_PROC) 43#include <sys/sysctl.h> 44#endif 45 46#ifdef HAVE_MACH_O_DYLD_H 47#include <mach-o/dyld.h> 48#endif 49 50#include "backtrace.hpp" 51#include "internal.hpp" 52 53#ifndef HAVE_GETEXECNAME 54#define getexecname() NULL 55#endif 56 57namespace tracy 58{ 59 60#if !defined (HAVE_KERN_PROC_ARGS) && !defined (HAVE_KERN_PROC) 61 62#define sysctl_exec_name1(state, error_callback, data) NULL 63#define sysctl_exec_name2(state, error_callback, data) NULL 64 65#else /* defined (HAVE_KERN_PROC_ARGS) || |defined (HAVE_KERN_PROC) */ 66 67static char * 68sysctl_exec_name (struct backtrace_state *state, 69 int mib0, int mib1, int mib2, int mib3, 70 backtrace_error_callback error_callback, void *data) 71{ 72 int mib[4]; 73 size_t len; 74 char *name; 75 size_t rlen; 76 77 mib[0] = mib0; 78 mib[1] = mib1; 79 mib[2] = mib2; 80 mib[3] = mib3; 81 82 if (sysctl (mib, 4, NULL, &len, NULL, 0) < 0) 83 return NULL; 84 name = (char *) backtrace_alloc (state, len, error_callback, data); 85 if (name == NULL) 86 return NULL; 87 rlen = len; 88 if (sysctl (mib, 4, name, &rlen, NULL, 0) < 0) 89 { 90 backtrace_free (state, name, len, error_callback, data); 91 return NULL; 92 } 93 return name; 94} 95 96#ifdef HAVE_KERN_PROC_ARGS 97 98static char * 99sysctl_exec_name1 (struct backtrace_state *state, 100 backtrace_error_callback error_callback, void *data) 101{ 102 /* This variant is used on NetBSD. */ 103 return sysctl_exec_name (state, CTL_KERN, KERN_PROC_ARGS, -1, 104 KERN_PROC_PATHNAME, error_callback, data); 105} 106 107#else 108 109#define sysctl_exec_name1(state, error_callback, data) NULL 110 111#endif 112 113#ifdef HAVE_KERN_PROC 114 115static char * 116sysctl_exec_name2 (struct backtrace_state *state, 117 backtrace_error_callback error_callback, void *data) 118{ 119 /* This variant is used on FreeBSD. */ 120 return sysctl_exec_name (state, CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1, 121 error_callback, data); 122} 123 124#else 125 126#define sysctl_exec_name2(state, error_callback, data) NULL 127 128#endif 129 130#endif /* defined (HAVE_KERN_PROC_ARGS) || |defined (HAVE_KERN_PROC) */ 131 132#ifdef HAVE_MACH_O_DYLD_H 133 134static char * 135macho_get_executable_path (struct backtrace_state *state, 136 backtrace_error_callback error_callback, void *data) 137{ 138 uint32_t len; 139 char *name; 140 141 len = 0; 142 if (_NSGetExecutablePath (NULL, &len) == 0) 143 return NULL; 144 name = (char *) backtrace_alloc (state, len, error_callback, data); 145 if (name == NULL) 146 return NULL; 147 if (_NSGetExecutablePath (name, &len) != 0) 148 { 149 backtrace_free (state, name, len, error_callback, data); 150 return NULL; 151 } 152 return name; 153} 154 155#else /* !defined (HAVE_MACH_O_DYLD_H) */ 156 157#define macho_get_executable_path(state, error_callback, data) NULL 158 159#endif /* !defined (HAVE_MACH_O_DYLD_H) */ 160 161/* Initialize the fileline information from the executable. Returns 1 162 on success, 0 on failure. */ 163 164static int 165fileline_initialize (struct backtrace_state *state, 166 backtrace_error_callback error_callback, void *data) 167{ 168 int failed; 169 fileline fileline_fn; 170 int pass; 171 int called_error_callback; 172 int descriptor; 173 const char *filename; 174 char buf[64]; 175 176 if (!state->threaded) 177 failed = state->fileline_initialization_failed; 178 else 179 failed = backtrace_atomic_load_int (&state->fileline_initialization_failed); 180 181 if (failed) 182 { 183 error_callback (data, "failed to read executable information", -1); 184 return 0; 185 } 186 187 if (!state->threaded) 188 fileline_fn = state->fileline_fn; 189 else 190 fileline_fn = backtrace_atomic_load_pointer (&state->fileline_fn); 191 if (fileline_fn != NULL) 192 return 1; 193 194 /* We have not initialized the information. Do it now. */ 195 196 descriptor = -1; 197 called_error_callback = 0; 198 for (pass = 0; pass < 8; ++pass) 199 { 200 int does_not_exist; 201 202 switch (pass) 203 { 204 case 0: 205 filename = state->filename; 206 break; 207 case 1: 208 filename = getexecname (); 209 break; 210 case 2: 211 filename = "/proc/self/exe"; 212 break; 213 case 3: 214 filename = "/proc/curproc/file"; 215 break; 216 case 4: 217 snprintf (buf, sizeof (buf), "/proc/%ld/object/a.out", 218 (long) getpid ()); 219 filename = buf; 220 break; 221 case 5: 222 filename = sysctl_exec_name1 (state, error_callback, data); 223 break; 224 case 6: 225 filename = sysctl_exec_name2 (state, error_callback, data); 226 break; 227 case 7: 228 filename = macho_get_executable_path (state, error_callback, data); 229 break; 230 default: 231 abort (); 232 } 233 234 if (filename == NULL) 235 continue; 236 237 descriptor = backtrace_open (filename, error_callback, data, 238 &does_not_exist); 239 if (descriptor < 0 && !does_not_exist) 240 { 241 called_error_callback = 1; 242 break; 243 } 244 if (descriptor >= 0) 245 break; 246 } 247 248 if (descriptor < 0) 249 { 250 if (!called_error_callback) 251 { 252 if (state->filename != NULL) 253 error_callback (data, state->filename, ENOENT); 254 else 255 error_callback (data, 256 "libbacktrace could not find executable to open", 257 0); 258 } 259 failed = 1; 260 } 261 262 if (!failed) 263 { 264 if (!backtrace_initialize (state, filename, descriptor, error_callback, 265 data, &fileline_fn)) 266 failed = 1; 267 } 268 269 if (failed) 270 { 271 if (!state->threaded) 272 state->fileline_initialization_failed = 1; 273 else 274 backtrace_atomic_store_int (&state->fileline_initialization_failed, 1); 275 return 0; 276 } 277 278 if (!state->threaded) 279 state->fileline_fn = fileline_fn; 280 else 281 { 282 backtrace_atomic_store_pointer (&state->fileline_fn, fileline_fn); 283 284 /* Note that if two threads initialize at once, one of the data 285 sets may be leaked. */ 286 } 287 288 return 1; 289} 290 291/* Given a PC, find the file name, line number, and function name. */ 292 293int 294backtrace_pcinfo (struct backtrace_state *state, uintptr_t pc, 295 backtrace_full_callback callback, 296 backtrace_error_callback error_callback, void *data) 297{ 298 if (!fileline_initialize (state, error_callback, data)) 299 return 0; 300 301 if (state->fileline_initialization_failed) 302 return 0; 303 304 return state->fileline_fn (state, pc, callback, error_callback, data); 305} 306 307/* Given a PC, find the symbol for it, and its value. */ 308 309int 310backtrace_syminfo (struct backtrace_state *state, uintptr_t pc, 311 backtrace_syminfo_callback callback, 312 backtrace_error_callback error_callback, void *data) 313{ 314 if (!fileline_initialize (state, error_callback, data)) 315 return 0; 316 317 if (state->fileline_initialization_failed) 318 return 0; 319 320 state->syminfo_fn (state, pc, callback, error_callback, data); 321 return 1; 322} 323 324/* A backtrace_syminfo_callback that can call into a 325 backtrace_full_callback, used when we have a symbol table but no 326 debug info. */ 327 328void 329backtrace_syminfo_to_full_callback (void *data, uintptr_t pc, 330 const char *symname, 331 uintptr_t symval ATTRIBUTE_UNUSED, 332 uintptr_t symsize ATTRIBUTE_UNUSED) 333{ 334 struct backtrace_call_full *bdata = (struct backtrace_call_full *) data; 335 336 bdata->ret = bdata->full_callback (bdata->full_data, pc, 0, NULL, 0, symname); 337} 338 339/* An error callback that corresponds to 340 backtrace_syminfo_to_full_callback. */ 341 342void 343backtrace_syminfo_to_full_error_callback (void *data, const char *msg, 344 int errnum) 345{ 346 struct backtrace_call_full *bdata = (struct backtrace_call_full *) data; 347 348 bdata->full_error_callback (bdata->full_data, msg, errnum); 349} 350 351}