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