]>
Commit | Line | Data |
---|---|---|
35a04c8e FCE |
1 | /* |
2 | crash shared object for retrieving systemtap buffer | |
3 | Copyright (c) 2007 Hitachi,Ltd., | |
1673e81e | 4 | Created by Satoru Moriya <satoru.moriya.br@hitachi.com> |
35a04c8e FCE |
5 | |
6 | This program is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation; either version 2 of the License, or | |
9 | (at your option) any later version. | |
10 | ||
11 | This program is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with this program; if not, write to the Free Software | |
18 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 | */ | |
20 | ||
21 | ||
22 | /* crash/defs.h defines NR_CPUS based upon architecture macros | |
23 | X86, X86_64, etc. See crash/configure.c (!). */ | |
24 | #ifdef __alpha__ | |
25 | #define ALPHA | |
26 | #endif | |
27 | #ifdef __i386__ | |
28 | #define X86 | |
29 | #endif | |
30 | #ifdef __powerpc__ | |
31 | #define PPC | |
32 | #endif | |
33 | #ifdef __ia64__ | |
34 | #define IA64 | |
35 | #endif | |
36 | #ifdef __s390__ | |
37 | #define S390 | |
38 | #endif | |
39 | #ifdef __s390x__ | |
40 | #define S390X | |
41 | #endif | |
42 | #ifdef __powerpc64__ | |
43 | #define PPC64 | |
44 | #endif | |
45 | #ifdef __x86_64__ | |
46 | #define X86_64 | |
47 | #endif | |
48 | ||
49 | #include <crash/defs.h> | |
50 | ||
35a04c8e FCE |
51 | struct rchan_offsets { |
52 | long subbuf_size; | |
53 | long n_subbufs; | |
54 | long buf; | |
55 | long buf_start; | |
56 | long buf_offset; | |
57 | long buf_subbufs_produced; | |
58 | long buf_padding; | |
59 | }; | |
60 | ||
61 | struct fake_rchan_buf { | |
62 | void *start; | |
63 | size_t offset; | |
64 | size_t subbufs_produced; | |
65 | size_t *padding; | |
66 | }; | |
67 | ||
68 | struct fake_rchan { | |
69 | size_t subbuf_size; | |
70 | size_t n_subbufs; | |
71 | }; | |
72 | ||
73 | struct per_cpu_data { | |
74 | struct fake_rchan_buf buf; | |
75 | }; | |
76 | ||
77 | static struct rchan_offsets rchan_offsets; | |
78 | static struct fake_rchan chan; | |
79 | static struct per_cpu_data per_cpu[NR_CPUS]; | |
80 | static FILE *outfp; | |
81 | static char *subbuf; | |
82 | static int is_global; | |
1673e81e | 83 | static int is_broken; |
35a04c8e | 84 | static int old_format; |
1673e81e | 85 | static int retrieve_all; |
35a04c8e FCE |
86 | |
87 | void cmd_staplog(void); | |
88 | void cmd_staplog_cleanup(void); | |
89 | char *help_staplog[]; | |
90 | char *help_staplog_cleanup[]; | |
91 | ||
92 | static struct command_table_entry command_table[] = { | |
93 | {"staplog", cmd_staplog, help_staplog, 0}, | |
94 | {"staplog_cleanup", cmd_staplog_cleanup, help_staplog_cleanup, CLEANUP}, | |
95 | {NULL, NULL, NULL, 0}, | |
96 | }; | |
97 | ||
98 | static void get_rchan_offsets(void) | |
99 | { | |
100 | rchan_offsets.subbuf_size = MEMBER_OFFSET("rchan", "subbuf_size"); | |
101 | if (rchan_offsets.subbuf_size < 0) | |
102 | goto ERR; | |
103 | rchan_offsets.n_subbufs = MEMBER_OFFSET("rchan", "n_subbufs"); | |
104 | if (rchan_offsets.n_subbufs < 0) | |
105 | goto ERR; | |
106 | rchan_offsets.buf = MEMBER_OFFSET("rchan", "buf"); | |
107 | if (rchan_offsets.buf < 0) | |
108 | goto ERR; | |
109 | rchan_offsets.buf_start = MEMBER_OFFSET("rchan_buf", "start"); | |
110 | if (rchan_offsets.buf_start < 0) | |
111 | goto ERR; | |
112 | rchan_offsets.buf_offset = MEMBER_OFFSET("rchan_buf", "offset"); | |
113 | if (rchan_offsets.buf_offset < 0) | |
114 | goto ERR; | |
115 | rchan_offsets.buf_subbufs_produced | |
116 | = MEMBER_OFFSET("rchan_buf", "subbufs_produced"); | |
117 | if (rchan_offsets.buf_subbufs_produced < 0) | |
118 | goto ERR; | |
119 | rchan_offsets.buf_padding = MEMBER_OFFSET("rchan_buf", "padding"); | |
120 | if (rchan_offsets.buf_padding < 0) | |
121 | goto ERR; | |
122 | return; | |
123 | ERR: | |
124 | error(FATAL, "cannot get rchan offset\n"); | |
125 | } | |
126 | ||
127 | static ulong get_rchan(ulong chan_addr) | |
128 | { | |
129 | ulong rchan; | |
130 | ||
131 | readmem(chan_addr, KVADDR, &rchan, sizeof(void*), | |
132 | "stp_channel", FAULT_ON_ERROR); | |
133 | readmem(rchan + rchan_offsets.subbuf_size, | |
134 | KVADDR, &chan.subbuf_size, sizeof(size_t), | |
135 | "stp_channel.subbuf_size", FAULT_ON_ERROR); | |
136 | readmem(rchan + rchan_offsets.n_subbufs, | |
137 | KVADDR, &chan.n_subbufs, sizeof(size_t), | |
138 | "stp_channel.n_subbufs", FAULT_ON_ERROR); | |
139 | return rchan; | |
140 | } | |
141 | ||
142 | static void get_rchan_buf(int cpu, ulong rchan) | |
143 | { | |
144 | ulong rchan_buf; | |
145 | struct per_cpu_data *pcd; | |
146 | ||
147 | pcd = &per_cpu[cpu]; | |
148 | readmem(rchan + rchan_offsets.buf + sizeof(void*) * cpu, | |
149 | KVADDR, &rchan_buf, sizeof(void*), | |
150 | "stp_channel.buf", FAULT_ON_ERROR); | |
151 | readmem(rchan_buf + rchan_offsets.buf_start, | |
152 | KVADDR, &pcd->buf.start, sizeof(void*), | |
153 | "stp_channel.buf.start", FAULT_ON_ERROR); | |
154 | readmem(rchan_buf + rchan_offsets.buf_offset, | |
155 | KVADDR, &pcd->buf.offset, sizeof(size_t), | |
156 | "stp_channel.buf.offset", FAULT_ON_ERROR); | |
157 | readmem(rchan_buf + rchan_offsets.buf_subbufs_produced, | |
158 | KVADDR, &pcd->buf.subbufs_produced, sizeof(size_t), | |
159 | "stp_channel.buf.subbufs_produced", FAULT_ON_ERROR); | |
160 | readmem(rchan_buf + rchan_offsets.buf_padding, | |
161 | KVADDR, &pcd->buf.padding, sizeof(size_t*), | |
162 | "stp_channel.buf.padding", FAULT_ON_ERROR); | |
163 | return; | |
164 | } | |
165 | ||
166 | static ulong get_rchan_addr(ulong stp_utt_addr) | |
167 | { | |
168 | ulong stp_utt; | |
faf96009 | 169 | long offset; |
35a04c8e FCE |
170 | |
171 | readmem(stp_utt_addr, KVADDR, &stp_utt, sizeof(void*), | |
172 | "stp_utt", FAULT_ON_ERROR); | |
faf96009 FCE |
173 | |
174 | /* | |
175 | * If we couldn't get the member offset of struct utt_trace.rchan, | |
176 | * i.e. the debuginfo of the trace module isn't available, we use | |
177 | * sizeof(long) as the offset instead. Currently struct utt_trace | |
178 | * is defined as below: | |
179 | * | |
180 | * struct utt_trace { | |
181 | * int trace_state; | |
182 | * struct rchan *rchan; | |
183 | * ... | |
184 | * } | |
185 | * | |
186 | * Although the type of the preceding member is int, sizeof(long) | |
187 | * is OK, because rchan is aligned with long size on both 32-bit | |
188 | * and 64-bit environment. When the definision of struct utt_trace | |
189 | * changed, we must check if this code is correct. | |
190 | */ | |
191 | if ((offset = MEMBER_OFFSET("utt_trace", "rchan")) < 0) { | |
192 | error(WARNING, "The debuginfo of the trace module hasn't been loaded. " | |
193 | "You may not be able to retrieve the correct trace data.\n"); | |
194 | offset = sizeof(long); | |
195 | } | |
196 | ||
197 | return (stp_utt + (ulong)offset); | |
35a04c8e FCE |
198 | } |
199 | ||
200 | static int check_global_buffer(ulong rchan) | |
201 | { | |
202 | int cpu; | |
203 | ulong rchan_buf[2]; | |
204 | ||
205 | for (cpu = 0; cpu < 2; cpu++) { | |
206 | readmem(rchan + rchan_offsets.buf + sizeof(void*) * cpu, | |
207 | KVADDR, &rchan_buf[cpu], sizeof(void*), | |
208 | "stp_channel.buf", FAULT_ON_ERROR); | |
209 | } | |
210 | if (rchan_buf[0] == rchan_buf[1]) | |
211 | return 1; | |
212 | return 0; | |
213 | } | |
214 | ||
215 | static void setup_global_data(char *module) | |
216 | { | |
217 | int i; | |
218 | ulong stp_utt_addr = 0; | |
219 | ulong stp_rchan_addr = 0; | |
220 | ulong rchan; | |
221 | ||
222 | stp_utt_addr = symbol_value_module("_stp_utt", module); | |
223 | if (stp_utt_addr == 0) { | |
224 | stp_rchan_addr = symbol_value_module("_stp_chan", module); | |
225 | if (stp_rchan_addr == 0) { | |
226 | error(FATAL, "Failed to find _stp_utt/_stp_chan.\n", | |
227 | module); | |
228 | } | |
229 | old_format = 1; | |
230 | } else { | |
231 | stp_rchan_addr = get_rchan_addr(stp_utt_addr); | |
232 | if (stp_rchan_addr == 0) { | |
233 | error(FATAL, "Failed to find _stp_utt/_stp_chan.\n", | |
234 | module); | |
235 | } | |
236 | } | |
237 | rchan = get_rchan(stp_rchan_addr); | |
238 | for (i = 0; i < kt->cpus; i++) | |
239 | get_rchan_buf(i, rchan); | |
240 | ||
241 | if (kt->cpus > 1) { | |
242 | is_global = check_global_buffer(rchan); | |
243 | } | |
244 | return; | |
245 | } | |
246 | ||
1673e81e MH |
247 | static char *create_output_filename(int cpu) |
248 | { | |
249 | size_t max = 128; | |
250 | char *fname; | |
251 | ||
252 | fname = (char *)malloc(sizeof(char) * max); | |
253 | if (is_global) { | |
254 | sprintf(fname, "global"); | |
255 | } else { | |
256 | sprintf(fname, "cpu%d", cpu); | |
257 | } | |
258 | if (is_broken) { | |
259 | strcat(fname, ".may_broken"); | |
260 | } | |
261 | return fname; | |
262 | } | |
263 | ||
264 | static void print_rchan_info(size_t start, size_t end, size_t ready, | |
265 | size_t offset, char *dname, char *fname, int cpu) | |
266 | { | |
267 | size_t start_subbuf, end_subbuf; | |
268 | ||
269 | start_subbuf = start ? start - chan.n_subbufs : start; | |
270 | end_subbuf = start ? end - 1 - chan.n_subbufs : end - 1; | |
271 | ||
272 | fprintf(fp, "--- generating '%s/%s' ---\n", dname, fname); | |
273 | if (is_broken) { | |
274 | fprintf(fp, " read subbuf %ld(%ld) (offset:%ld-%ld)\n", | |
275 | (long)end_subbuf - chan.n_subbufs, | |
276 | (long)(end_subbuf % chan.n_subbufs), | |
277 | (long)offset, | |
278 | (long)chan.subbuf_size); | |
279 | } else { | |
280 | fprintf(fp, " subbufs ready on relayfs:%ld\n", (long)ready); | |
281 | fprintf(fp, " n_subbufs:%ld, read subbuf from:%ld(%ld) " | |
282 | "to:%ld(%ld) (offset:0-%ld)\n\n", | |
283 | (long)chan.n_subbufs, | |
284 | (long)start_subbuf, | |
285 | (long)(start_subbuf % chan.n_subbufs), | |
286 | (long)end_subbuf, | |
287 | (long)(end_subbuf % chan.n_subbufs), | |
288 | (long)offset); | |
289 | } | |
290 | } | |
291 | ||
292 | static void create_output_dir(char *dirname) | |
293 | { | |
294 | DIR *dir; | |
295 | dir = opendir(dirname); | |
296 | if (dir) { | |
297 | closedir(dir); | |
298 | } else { | |
299 | if (mkdir(dirname, S_IRWXU) < 0) { | |
300 | error(FATAL, "cannot create log directory '%s\n'", dirname); | |
301 | } | |
302 | } | |
303 | } | |
304 | ||
305 | static FILE *open_output_file(char *dname, char *fname) | |
306 | { | |
307 | FILE *filp = NULL; | |
308 | char *output_file; | |
309 | size_t dlength, flength; | |
310 | ||
311 | dlength = strlen(dname); | |
312 | flength = strlen(fname); | |
313 | ||
314 | output_file = (char *)malloc(sizeof(char) * (dlength + flength) + 1); | |
315 | output_file[dlength + flength] = '\0'; | |
316 | ||
317 | create_output_dir(dname); | |
318 | sprintf(output_file,"%s/%s", dname, fname); | |
319 | ||
320 | filp = fopen(output_file, "w"); | |
321 | if (!filp) { | |
322 | error(FATAL, "cannot create log file '%s'\n", output_file); | |
323 | } | |
324 | ||
325 | return filp; | |
326 | } | |
327 | ||
faf96009 | 328 | static void output_cpu_logs(char *dirname) |
35a04c8e | 329 | { |
1673e81e | 330 | int i; |
35a04c8e | 331 | struct per_cpu_data *pcd; |
1673e81e | 332 | size_t n, idx, start, end, len; |
faf96009 | 333 | size_t padding; |
1673e81e | 334 | char *source, *fname; |
35a04c8e | 335 | |
35a04c8e FCE |
336 | /* allocate subbuf memory */ |
337 | subbuf = GETBUF(chan.subbuf_size); | |
338 | if (!subbuf) { | |
339 | error(FATAL, "cannot allocate memory\n"); | |
340 | } | |
341 | ||
35a04c8e | 342 | for (i = 0; i < kt->cpus; i++) { |
1673e81e | 343 | is_broken = 0; |
35a04c8e FCE |
344 | pcd = &per_cpu[i]; |
345 | ||
1673e81e | 346 | if (pcd->buf.subbufs_produced == 0 && pcd->buf.offset == 0) { |
faf96009 FCE |
347 | if (is_global == 1) { |
348 | error(WARNING, "There is no data in the relay buffer.\n"); | |
349 | break; | |
350 | } else { | |
351 | error(WARNING, "[cpu:%d]There is no data in the relay buffer.\n", i); | |
352 | continue; | |
353 | } | |
354 | } | |
35a04c8e | 355 | |
1673e81e MH |
356 | if (pcd->buf.subbufs_produced >= chan.n_subbufs) { |
357 | start = pcd->buf.subbufs_produced + 1; | |
35a04c8e FCE |
358 | end = start + chan.n_subbufs; |
359 | } else { | |
360 | start = 0; | |
1673e81e | 361 | end = pcd->buf.subbufs_produced + 1; |
35a04c8e | 362 | } |
1673e81e MH |
363 | |
364 | fname = create_output_filename(i); | |
365 | print_rchan_info(start, end, pcd->buf.subbufs_produced + 1, | |
366 | pcd->buf.offset, dirname, fname, i); | |
367 | outfp = open_output_file(dirname, fname); | |
368 | ||
35a04c8e FCE |
369 | for (n = start; n < end; n++) { |
370 | /* read relayfs subbufs and write to log file */ | |
371 | idx = n % chan.n_subbufs; | |
372 | source = pcd->buf.start + idx * chan.subbuf_size; | |
373 | readmem((ulong)pcd->buf.padding + sizeof(padding) * idx, | |
374 | KVADDR, &padding, sizeof(padding), | |
375 | "padding", FAULT_ON_ERROR); | |
1673e81e | 376 | if (n == end - 1 && |
35a04c8e FCE |
377 | pcd->buf.offset < chan.subbuf_size) { |
378 | len = pcd->buf.offset; | |
379 | } else { | |
380 | len = chan.subbuf_size; | |
381 | } | |
382 | if (old_format == 1) { | |
1673e81e MH |
383 | source += sizeof(unsigned int); |
384 | len -= sizeof(unsigned int) + padding; | |
35a04c8e FCE |
385 | } else { |
386 | len -= padding; | |
387 | } | |
388 | if (len) { | |
389 | readmem((ulong)source, KVADDR, subbuf, len, | |
390 | "subbuf", FAULT_ON_ERROR); | |
391 | if (fwrite(subbuf, len, 1, outfp) != 1) { | |
392 | error(FATAL, "cannot write log data\n"); | |
393 | } | |
394 | } | |
395 | } | |
396 | fclose(outfp); | |
397 | outfp = NULL; | |
1673e81e MH |
398 | free(fname); |
399 | fname = NULL; | |
400 | ||
401 | /* | |
402 | * -a option retrieve the old data of subbuffer where the | |
403 | * probe record is written at that time. | |
404 | */ | |
405 | if (retrieve_all == 1 && | |
406 | pcd->buf.subbufs_produced >= chan.n_subbufs) { | |
407 | is_broken = 1; | |
408 | fname = create_output_filename(i); | |
409 | print_rchan_info(start, end, | |
410 | pcd->buf.subbufs_produced + 1, | |
411 | pcd->buf.offset, dirname, fname, i); | |
412 | outfp = open_output_file(dirname, fname); | |
413 | idx = (end - 1) % chan.n_subbufs; | |
414 | source = pcd->buf.start + idx * chan.subbuf_size + | |
415 | pcd->buf.offset; | |
416 | readmem((ulong)pcd->buf.padding + sizeof(padding)*idx, | |
417 | KVADDR, &padding, sizeof(padding), | |
418 | "padding", FAULT_ON_ERROR); | |
419 | len = chan.subbuf_size - pcd->buf.offset; | |
420 | if (len) { | |
421 | readmem((ulong)source, KVADDR, subbuf, len, | |
422 | "may_broken_subbuf", FAULT_ON_ERROR); | |
423 | if(fwrite(subbuf, len, 1, outfp) != 1) { | |
424 | error(FATAL, | |
425 | "cannot write log data(may_broken)\n"); | |
426 | } | |
427 | } | |
428 | fclose(outfp); | |
429 | outfp = NULL; | |
430 | free(fname); | |
431 | fname = NULL; | |
432 | } | |
35a04c8e FCE |
433 | if (is_global == 1) |
434 | break; | |
435 | } | |
436 | if (subbuf) { | |
437 | FREEBUF(subbuf); | |
438 | subbuf = NULL; | |
439 | } | |
440 | return; | |
441 | } | |
442 | ||
faf96009 | 443 | static void do_staplog(char *module, char *dirname) |
35a04c8e FCE |
444 | { |
445 | setup_global_data(module); | |
faf96009 | 446 | output_cpu_logs(dirname); |
35a04c8e FCE |
447 | return; |
448 | } | |
449 | ||
450 | void cmd_staplog(void) | |
451 | { | |
452 | ||
453 | int c; | |
454 | char *module = NULL; | |
faf96009 | 455 | char *dirname = NULL; |
35a04c8e | 456 | |
1673e81e | 457 | while ((c = getopt(argcnt, args, "+ao:")) != EOF) { |
35a04c8e | 458 | switch (c) { |
1673e81e MH |
459 | case 'a': |
460 | retrieve_all = 1; | |
461 | break; | |
35a04c8e | 462 | case 'o': |
faf96009 | 463 | dirname = optarg; |
35a04c8e FCE |
464 | break; |
465 | default: | |
466 | argerrs++; | |
467 | break; | |
468 | } | |
469 | } | |
470 | module = args[optind]; | |
471 | ||
472 | if (!module || argerrs) | |
473 | cmd_usage(pc->curcmd, SYNOPSIS); | |
474 | ||
faf96009 FCE |
475 | if (dirname == NULL && module != NULL) |
476 | dirname = module; | |
477 | do_staplog(module, dirname); | |
35a04c8e FCE |
478 | return; |
479 | } | |
480 | ||
481 | void cmd_staplog_cleanup(void) | |
482 | { | |
483 | if (outfp) { | |
484 | fclose(outfp); | |
485 | outfp = NULL; | |
486 | } | |
487 | return; | |
488 | } | |
489 | ||
490 | char *help_staplog[] = { | |
491 | "systemtaplog", | |
492 | "Retrieve SystemTap log data", | |
1673e81e | 493 | "[-a] [-o dir_name] module_name", |
35a04c8e | 494 | " Retrieve SystemTap's log data and write them to files.\n", |
1673e81e MH |
495 | " All valid SystemTap's log data made by the trace module which name", |
496 | " is 'module_name' are written into log files. This command starts", | |
497 | " to retrieve log data from the subbuffer which is next to current", | |
498 | " written subbuffer. Therefore some old data in the current written", | |
499 | " subbuffer may not be retrieved. But -a option retrieve these data", | |
500 | " and write them into another log file which have the special ", | |
501 | " postfix `.may_broken`.", | |
502 | " If you don't use -o option, the log files are created in", | |
503 | " `module_name` directory. The name of each log file is cpu0, cpu1..", | |
504 | " ...cpuN. This command don't change the log data format, but remove", | |
505 | " only padding.", | |
35a04c8e | 506 | "", |
1673e81e MH |
507 | " -a Retrieve the old data which is recorded in", |
508 | " current written subbufer and create another file", | |
509 | " that have the special postfix `.may_broken`", | |
510 | " for these data.", | |
35a04c8e FCE |
511 | " -o file_name Specify the output directory.", |
512 | NULL, | |
513 | }; | |
514 | ||
515 | char *help_staplog_cleanup[] = { | |
516 | "systemtaplog cleanup (hidden)", | |
517 | "Cleanup command for staplog", | |
518 | "", | |
519 | " This command is called during restore_sanity() prior to each ", | |
520 | " command prompt to close the files which was opened and failed to", | |
521 | " close by staplog command.", | |
522 | NULL, | |
523 | }; | |
524 | ||
525 | static void __attribute__ ((constructor)) _init(void) | |
526 | { | |
527 | get_rchan_offsets(); | |
528 | register_extension(command_table); | |
529 | return; | |
530 | } | |
531 | ||
532 | ||
533 | static void __attribute__ ((destructor)) _fini(void) | |
534 | { | |
535 | return; | |
536 | } |