1 /* Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 /* Resolve numeric stack dump produced by mysqld 3.23.30 and later
24 versions into symbolic names. By Sasha Pachev <sasha@mysql.com>
25 */
26
27 #include <my_config.h>
28 #include <stdio.h> // Needed on SunOS 5.10
29 #include <vector>
30 #include <string>
31
32 #include <my_global.h>
33 #include <m_ctype.h>
34 #include <my_sys.h>
35 #include <m_string.h>
36 #include <mysql_version.h>
37 #include <errno.h>
38 #include <my_getopt.h>
39 #include <welcome_copyright_notice.h> /* ORACLE_WELCOME_COPYRIGHT_NOTICE */
40
41 const int initial_symbol_table_size= 4096;
42
43 #define DUMP_VERSION "1.5"
44 #define HEX_INVALID (uchar)255
45
46 typedef ulong my_long_addr_t ; /* at some point, we need to fix configure
47 * to define this for us
48 */
49
50 typedef struct sym_entry
51 {
52 std::string symbol;
53 uchar* addr;
54 } SYM_ENTRY;
55
56
57 static char* dump_fname = 0, *sym_fname = 0;
58 static std::vector<sym_entry> sym_table;
59 static FILE* fp_dump, *fp_sym = 0, *fp_out;
60 static void die(const char* fmt, ...)
61 MY_ATTRIBUTE((noreturn)) MY_ATTRIBUTE((format(printf, 1, 2)));
62
63 static struct my_option my_long_options[] =
64 {
65 {"help", 'h', "Display this help and exit.",
66 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
67 {"version", 'V', "Output version information and exit.",
68 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
69 {"symbols-file", 's', "Use specified symbols file.", &sym_fname,
70 &sym_fname, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
71 {"numeric-dump-file", 'n', "Read the dump from specified file.",
72 &dump_fname, &dump_fname, 0, GET_STR, REQUIRED_ARG,
73 0, 0, 0, 0, 0, 0},
74 { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
75 };
76
77
78 static void verify_sort();
79
80
print_version(void)81 static void print_version(void)
82 {
83 printf("%s Ver %s Distrib %s, for %s (%s)\n",my_progname,DUMP_VERSION,
84 MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE);
85 }
86
87
usage()88 static void usage()
89 {
90 print_version();
91 puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2001"));
92 printf("Resolve numeric stack strace dump into symbols.\n\n");
93 printf("Usage: %s [OPTIONS] symbols-file [numeric-dump-file]\n",
94 my_progname);
95 my_print_help(my_long_options);
96 my_print_variables(my_long_options);
97 printf("\n\
98 The symbols-file should include the output from: 'nm --numeric-sort mysqld'.\n\
99 The numeric-dump-file should contain a numeric stack trace from mysqld.\n\
100 If the numeric-dump-file is not given, the stack trace is read from stdin.\n");
101 }
102
103
die(const char * fmt,...)104 static void die(const char* fmt, ...)
105 {
106 va_list args;
107 va_start(args, fmt);
108 fprintf(stderr, "%s: ", my_progname);
109 vfprintf(stderr, fmt, args);
110 fprintf(stderr, "\n");
111 va_end(args);
112 exit(1);
113 }
114
115
116 static my_bool
get_one_option(int optid,const struct my_option * opt MY_ATTRIBUTE ((unused)),char * argument MY_ATTRIBUTE ((unused)))117 get_one_option(int optid, const struct my_option *opt MY_ATTRIBUTE((unused)),
118 char *argument MY_ATTRIBUTE((unused)))
119 {
120 switch(optid) {
121 case 'V':
122 print_version();
123 exit(0);
124 case '?':
125 usage();
126 exit(0);
127 }
128 return 0;
129 }
130
131
parse_args(int argc,char ** argv)132 static int parse_args(int argc, char **argv)
133 {
134 int ho_error;
135
136 if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option)))
137 exit(ho_error);
138
139 /*
140 The following code is to make the command compatible with the old
141 version that required one to use the -n and -s options
142 */
143
144 if (argc == 2)
145 {
146 sym_fname= argv[0];
147 dump_fname= argv[1];
148 }
149 else if (argc == 1)
150 {
151 if (!sym_fname)
152 sym_fname = argv[0];
153 else if (!dump_fname)
154 dump_fname = argv[0];
155 else
156 {
157 usage();
158 exit(1);
159 }
160 }
161 else if (argc != 0 || !sym_fname)
162 {
163 usage();
164 exit(1);
165 }
166 return 0;
167 }
168
169
open_files()170 static void open_files()
171 {
172 fp_out = stdout;
173 fp_dump = stdin;
174
175 if (dump_fname && !(fp_dump = my_fopen(dump_fname, O_RDONLY, MYF(MY_WME))))
176 die("Could not open %s", dump_fname);
177 /* if name not given, assume stdin*/
178
179 if (!sym_fname)
180 die("Please run nm --numeric-sort on mysqld binary that produced stack \
181 trace dump and specify the path to it with -s or --symbols-file");
182 if (!(fp_sym = my_fopen(sym_fname, O_RDONLY, MYF(MY_WME))))
183 die("Could not open %s", sym_fname);
184
185 }
186
hex_val(char c)187 static uchar hex_val(char c)
188 {
189 uchar l;
190 if (my_isdigit(&my_charset_latin1,c))
191 return c - '0';
192 l = my_tolower(&my_charset_latin1,c);
193 if (l < 'a' || l > 'f')
194 return HEX_INVALID;
195 return (uchar)10 + ((uchar)c - (uchar)'a');
196 }
197
read_addr(char ** buf)198 static my_long_addr_t read_addr(char** buf)
199 {
200 uchar c;
201 char* p = *buf;
202 my_long_addr_t addr = 0;
203
204 while((c = hex_val(*p++)) != HEX_INVALID)
205 addr = (addr << 4) + c;
206
207 *buf = p;
208 return addr;
209 }
210
init_sym_entry(SYM_ENTRY * se,char * buf)211 static int init_sym_entry(SYM_ENTRY* se, char* buf)
212 {
213 char* p;
214 se->addr = (uchar*)read_addr(&buf);
215
216 if (!se->addr)
217 return -1;
218 while (my_isspace(&my_charset_latin1,*buf++))
219 /* empty */;
220
221 while (my_isspace(&my_charset_latin1,*buf++))
222 /* empty - skip more space */;
223 --buf;
224 /* now we are on the symbol */
225 for (p =buf; *buf != '\n' && *buf; ++buf)
226 ;
227 try {
228 se->symbol.assign(p, buf - p);
229 }
230 catch (...)
231 {
232 die("failed to allocate space for symbol %.*s", (int) (buf - p), p);
233 }
234
235 return 0;
236 }
237
init_sym_table()238 static void init_sym_table()
239 {
240 /*
241 A buffer of 100Kb should be big enough to hold any single line output from
242 'nm --demangle'
243 */
244 static char buf[1024 * 100];
245 try {
246 sym_table.reserve(initial_symbol_table_size);
247 }
248 catch (...)
249 {
250 die("Failed in std::vector.reserve() -- looks like out of memory problem");
251 }
252 while (fgets(buf, sizeof(buf), fp_sym))
253 {
254 SYM_ENTRY se;
255 if (init_sym_entry(&se, buf))
256 continue;
257 try {
258 sym_table.push_back(se);
259 }
260 catch (...)
261 {
262 die("std::vector.push_back() failed - looks like we are out of memory");
263 }
264 }
265
266 verify_sort();
267 }
268
clean_up()269 static void clean_up()
270 {
271 }
272
verify_sort()273 static void verify_sort()
274 {
275 uint i;
276 uchar* last = 0;
277
278 for (i = 0; i < sym_table.size(); i++)
279 {
280 SYM_ENTRY se= sym_table[i];
281 if (se.addr < last)
282 die("sym table does not appear to be sorted, did you forget "
283 "--numeric-sort arg to nm? trouble addr = %p, last = %p",
284 se.addr, last);
285 last = se.addr;
286 }
287 }
288
289
resolve_addr(uchar * addr,SYM_ENTRY * se)290 static SYM_ENTRY* resolve_addr(uchar* addr, SYM_ENTRY* se)
291 {
292 uint i;
293 *se= sym_table[0];
294 if (addr < se->addr)
295 return 0;
296
297 for (i = 1; i < sym_table.size(); i++)
298 {
299 *se= sym_table[i];
300 if (addr < se->addr)
301 {
302 *se= sym_table[i - 1];
303 return se;
304 }
305 }
306
307 return se;
308 }
309
310
do_resolve()311 static void do_resolve()
312 {
313 char buf[1024 * 8], *p;
314 while (fgets(buf, sizeof(buf), fp_dump))
315 {
316 /* skip bracket */
317 p= (p= strchr(buf, '[')) ? p+1 : buf;
318 /* skip space */
319 while (my_isspace(&my_charset_latin1,*p))
320 ++p;
321
322 if (*p++ == '0' && *p++ == 'x')
323 {
324 SYM_ENTRY se ;
325 uchar* addr = (uchar*)read_addr(&p);
326 if (resolve_addr(addr, &se))
327 fprintf(fp_out, "%p %s + %d\n", addr, se.symbol.c_str(),
328 (int) (addr - se.addr));
329 else
330 fprintf(fp_out, "%p (?)\n", addr);
331
332 }
333 else
334 {
335 fputs(buf, fp_out);
336 continue;
337 }
338 }
339 }
340
341
main(int argc,char ** argv)342 int main(int argc, char** argv)
343 {
344 MY_INIT(argv[0]);
345 parse_args(argc, argv);
346 open_files();
347 init_sym_table();
348 do_resolve();
349 clean_up();
350 return 0;
351 }
352