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