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