re PR other/68239 (libbacktrace allocation is sometimes very slow)
[platform/upstream/gcc.git] / libbacktrace / fileline.c
1 /* fileline.c -- Get file and line number information in a backtrace.
2    Copyright (C) 2012-2018 Free Software Foundation, Inc.
3    Written by Ian Lance Taylor, Google.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are
7 met:
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
21 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
25 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 POSSIBILITY 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 #include "backtrace.h"
43 #include "internal.h"
44
45 #ifndef HAVE_GETEXECNAME
46 #define getexecname() NULL
47 #endif
48
49 /* Initialize the fileline information from the executable.  Returns 1
50    on success, 0 on failure.  */
51
52 static int
53 fileline_initialize (struct backtrace_state *state,
54                      backtrace_error_callback error_callback, void *data)
55 {
56   int failed;
57   fileline fileline_fn;
58   int pass;
59   int called_error_callback;
60   int descriptor;
61   const char *filename;
62   char buf[64];
63
64   if (!state->threaded)
65     failed = state->fileline_initialization_failed;
66   else
67     failed = backtrace_atomic_load_int (&state->fileline_initialization_failed);
68
69   if (failed)
70     {
71       error_callback (data, "failed to read executable information", -1);
72       return 0;
73     }
74
75   if (!state->threaded)
76     fileline_fn = state->fileline_fn;
77   else
78     fileline_fn = backtrace_atomic_load_pointer (&state->fileline_fn);
79   if (fileline_fn != NULL)
80     return 1;
81
82   /* We have not initialized the information.  Do it now.  */
83
84   descriptor = -1;
85   called_error_callback = 0;
86   for (pass = 0; pass < 5; ++pass)
87     {
88       int does_not_exist;
89
90       switch (pass)
91         {
92         case 0:
93           filename = state->filename;
94           break;
95         case 1:
96           filename = getexecname ();
97           break;
98         case 2:
99           filename = "/proc/self/exe";
100           break;
101         case 3:
102           filename = "/proc/curproc/file";
103           break;
104         case 4:
105           snprintf (buf, sizeof (buf), "/proc/%ld/object/a.out",
106                     (long) getpid ());
107           filename = buf;
108           break;
109         default:
110           abort ();
111         }
112
113       if (filename == NULL)
114         continue;
115
116       descriptor = backtrace_open (filename, error_callback, data,
117                                    &does_not_exist);
118       if (descriptor < 0 && !does_not_exist)
119         {
120           called_error_callback = 1;
121           break;
122         }
123       if (descriptor >= 0)
124         break;
125     }
126
127   if (descriptor < 0)
128     {
129       if (!called_error_callback)
130         {
131           if (state->filename != NULL)
132             error_callback (data, state->filename, ENOENT);
133           else
134             error_callback (data,
135                             "libbacktrace could not find executable to open",
136                             0);
137         }
138       failed = 1;
139     }
140
141   if (!failed)
142     {
143       if (!backtrace_initialize (state, filename, descriptor, error_callback,
144                                  data, &fileline_fn))
145         failed = 1;
146     }
147
148   if (failed)
149     {
150       if (!state->threaded)
151         state->fileline_initialization_failed = 1;
152       else
153         backtrace_atomic_store_int (&state->fileline_initialization_failed, 1);
154       return 0;
155     }
156
157   if (!state->threaded)
158     state->fileline_fn = fileline_fn;
159   else
160     {
161       backtrace_atomic_store_pointer (&state->fileline_fn, fileline_fn);
162
163       /* Note that if two threads initialize at once, one of the data
164          sets may be leaked.  */
165     }
166
167   return 1;
168 }
169
170 /* Given a PC, find the file name, line number, and function name.  */
171
172 int
173 backtrace_pcinfo (struct backtrace_state *state, uintptr_t pc,
174                   backtrace_full_callback callback,
175                   backtrace_error_callback error_callback, void *data)
176 {
177   if (!fileline_initialize (state, error_callback, data))
178     return 0;
179
180   if (state->fileline_initialization_failed)
181     return 0;
182
183   return state->fileline_fn (state, pc, callback, error_callback, data);
184 }
185
186 /* Given a PC, find the symbol for it, and its value.  */
187
188 int
189 backtrace_syminfo (struct backtrace_state *state, uintptr_t pc,
190                    backtrace_syminfo_callback callback,
191                    backtrace_error_callback error_callback, void *data)
192 {
193   if (!fileline_initialize (state, error_callback, data))
194     return 0;
195
196   if (state->fileline_initialization_failed)
197     return 0;
198
199   state->syminfo_fn (state, pc, callback, error_callback, data);
200   return 1;
201 }