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