Subject: [PATCH] crash-trace-command: generate trace.dat from core-file

This patch adds a command "trace dump -t" for crash-trace-command
which is extension of crash(8) for analyzing and dumping ftrace from core-file.

This extension already has "trace show" to show what events had happened
before crash, but it is just 1000 lines of code and it is not so
smart as "trace-cmd report". New command "trace dump -t" generates a trace.dat,
and then, we can use "trace-cmd report" for this trace.dat.

trace-cmd(1) is a ftrace tool written by Steven Rostedt <[email protected]>.
It includes two tools for showing ftrace events "trace-cmd report" and 
"kernelshark".
SEE ALSO: trace-cmd(1), trace-cmd-report(1),  trace-cmd.dat(5)

Note, this patch does not just improve crash-trace-command, it improves 
trace-cmd
also. It makes trace-cmd can handle ftrace even the kernel crashed.

Usage:
1) start crash(8) for a core-file, and execute the following command in 
crash(8):
2) extend <your-path-to-crash-trace-command, example: /usr/lib/crash/trace.so>
3) trace dump -t
4) q<enter> to quit crash
5) trace-cmd report or kernelshark

Signed-off-by: Lai Jiangshan <[email protected]>
---
 trace.c |  778 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 693 insertions(+), 85 deletions(-)
diff --git a/extensions/trace.c b/extensions/trace.c
index 89eb477..d100869 100755
--- a/extensions/trace.c
+++ b/extensions/trace.c
@@ -1,7 +1,7 @@
 /*
  * trace extension module for crash
  *
- * Copyright (C) 2009 FUJITSU LIMITED
+ * Copyright (C) 2009, 2010 FUJITSU LIMITED
  * Author: Lai Jiangshan <[email protected]>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -53,6 +53,7 @@ static int koffset(list_head, next);
 static int koffset(ftrace_event_call, list);
 static int koffset(ftrace_event_call, name);
 static int koffset(ftrace_event_call, system);
+static int koffset(ftrace_event_call, print_fmt);
 static int koffset(ftrace_event_call, id);
 static int koffset(ftrace_event_call, fields);
 
@@ -74,8 +75,11 @@ struct ring_buffer_per_cpu {
        ulong reader_page;
        ulong real_head_page;
 
-       ulong *pages;
        int head_page_index;
+       ulong *pages;
+
+       ulong *linear_pages;
+       int nr_linear_pages;
 
        ulong overrun;
        ulong entries;
@@ -112,6 +116,22 @@ static void ftrace_show_destroy(void);
 /* Remove the "const" qualifiers for ptr */
 #define free(ptr) free((void *)(ptr))
 
+static int write_and_check(int fd, void *data, size_t size)
+{
+       size_t tot = 0;
+       size_t w;
+
+       do {
+               w = write(fd, data, size - tot);
+               tot += w;
+
+               if (w <= 0)
+                       return -1;
+       } while (tot != size);
+
+       return 0;
+}
+
 #ifndef PATH_MAX
 #define PATH_MAX 4096
 #endif
@@ -153,6 +173,7 @@ static void init_offsets(void)
        init_offset(ftrace_event_call, list);
        init_offset(ftrace_event_call, name);
        init_offset(ftrace_event_call, system);
+       init_offset(ftrace_event_call, print_fmt);
        init_offset(ftrace_event_call, id);
        init_offset(ftrace_event_call, fields);
 
@@ -199,6 +220,7 @@ static void print_offsets(void)
        print_offset(ftrace_event_call, list);
        print_offset(ftrace_event_call, name);
        print_offset(ftrace_event_call, system);
+       print_offset(ftrace_event_call, print_fmt);
        print_offset(ftrace_event_call, id);
        print_offset(ftrace_event_call, fields);
 
@@ -214,7 +236,7 @@ static void print_offsets(void)
 static int ftrace_init_pages(struct ring_buffer_per_cpu *cpu_buffer,
                unsigned nr_pages)
 {
-       unsigned j = 0;
+       unsigned j = 0, count = 0;
        ulong head, page;
        ulong real_head_page = cpu_buffer->head_page;
 
@@ -222,6 +244,12 @@ static int ftrace_init_pages(struct ring_buffer_per_cpu 
*cpu_buffer,
        if (cpu_buffer->pages == NULL)
                return -1;
 
+       cpu_buffer->linear_pages = calloc(sizeof(ulong), nr_pages + 1);
+       if (cpu_buffer->linear_pages == NULL) {
+               free(cpu_buffer->pages);
+               return -1;
+       }
+
        if (lockless_ring_buffer) {
                read_value(head, cpu_buffer->kaddr, ring_buffer_per_cpu, pages);
                cpu_buffer->pages[j++] = head - koffset(buffer_page, list);
@@ -253,6 +281,8 @@ static int ftrace_init_pages(struct ring_buffer_per_cpu 
*cpu_buffer,
                goto out_fail;
        }
 
+       /* find head page and head_page_index */
+
        cpu_buffer->real_head_page = real_head_page;
        cpu_buffer->head_page_index = -1;
 
@@ -268,10 +298,38 @@ static int ftrace_init_pages(struct ring_buffer_per_cpu 
*cpu_buffer,
                goto out_fail;
        }
 
+       /* Setup linear pages */
+
+       cpu_buffer->linear_pages[count++] = cpu_buffer->reader_page;
+
+       if (cpu_buffer->reader_page == cpu_buffer->commit_page)
+               goto done;
+
+       j = cpu_buffer->head_page_index;
+       for (;;) {
+               cpu_buffer->linear_pages[count++] = cpu_buffer->pages[j];
+
+               if (cpu_buffer->pages[j] == cpu_buffer->commit_page)
+                       break;
+
+               j++;
+               if (j == nr_pages)
+                       j = 0;
+
+               if (j == cpu_buffer->head_page_index) {
+                       /* cpu_buffer->commit_page may be corrupted */
+                       break;
+               }
+       }
+
+done:
+       cpu_buffer->nr_linear_pages = count;
+
        return 0;
 
 out_fail:
        free(cpu_buffer->pages);
+       free(cpu_buffer->linear_pages);
        return -1;
 }
 
@@ -284,6 +342,7 @@ static void ftrace_destroy_buffers(struct 
ring_buffer_per_cpu *buffers)
                        continue;
 
                free(buffers[i].pages);
+               free(buffers[i].linear_pages);
        }
 }
 
@@ -470,7 +529,7 @@ static void ftrace_destroy(void)
        free(global_buffers);
 }
 
-static int ftrace_dump_page(FILE *out, ulong page, void *page_tmp)
+static int ftrace_dump_page(int fd, ulong page, void *page_tmp)
 {
        ulong raw_page;
 
@@ -480,7 +539,8 @@ static int ftrace_dump_page(FILE *out, ulong page, void 
*page_tmp)
                        RETURN_ON_ERROR))
                goto out_fail;
 
-       fwrite(page_tmp, 1, PAGESIZE(), out);
+       if (write_and_check(fd, page_tmp, PAGESIZE()))
+               return -1;
 
        return 0;
 
@@ -489,33 +549,15 @@ out_fail:
 }
 
 static
-void ftrace_dump_buffer(FILE *out, struct ring_buffer_per_cpu *cpu_buffer,
+void ftrace_dump_buffer(int fd, struct ring_buffer_per_cpu *cpu_buffer,
                unsigned pages, void *page_tmp)
 {
-       unsigned i;
-
-       if (ftrace_dump_page(out, cpu_buffer->reader_page, page_tmp) < 0)
-               return;
-
-       if (cpu_buffer->reader_page == cpu_buffer->commit_page)
-               return;
-
-       i = cpu_buffer->head_page_index;
-       for (;;) {
-               if (ftrace_dump_page(out, cpu_buffer->pages[i], page_tmp) < 0)
-                       break;
-
-               if (cpu_buffer->pages[i] == cpu_buffer->commit_page)
-                       break;
-
-               i++;
-               if (i == pages)
-                       i = 0;
+       int i;
 
-               if (i == cpu_buffer->head_page_index) {
-                       /* cpu_buffer->commit_page may be corrupted */
+       for (i = 0; i < cpu_buffer->nr_linear_pages; i++) {
+               if (ftrace_dump_page(fd, cpu_buffer->linear_pages[i],
+                               page_tmp) < 0)
                        break;
-               }
        }
 }
 
@@ -540,7 +582,7 @@ static int ftrace_dump_buffers(const char *per_cpu_path)
        int i;
        void *page_tmp;
        char path[PATH_MAX];
-       FILE *out;
+       int fd;
 
        page_tmp = malloc(PAGESIZE());
        if (page_tmp == NULL)
@@ -558,12 +600,12 @@ static int ftrace_dump_buffers(const char *per_cpu_path)
 
                snprintf(path, sizeof(path), "%s/cpu%d/trace_pipe_raw",
                                per_cpu_path, i);
-               out = fopen(path, "wb");
-               if (out == NULL)
+               fd = open(path, O_WRONLY | O_CREAT, 0644);
+               if (fd < 0)
                        goto out_fail;
 
-               ftrace_dump_buffer(out, cpu_buffer, global_pages, page_tmp);
-               fclose(out);
+               ftrace_dump_buffer(fd, cpu_buffer, global_pages, page_tmp);
+               close(fd);
        }
 
        free(page_tmp);
@@ -727,6 +769,7 @@ static void ftrace_destroy_event_types(void)
                free(event_types[i]->fields);
                free(event_types[i]->system);
                free(event_types[i]->name);
+               free(event_types[i]->print_fmt);
                free(event_types[i]);
        }
 
@@ -746,8 +789,8 @@ static int ftrace_init_event_types(void)
        read_value(event, ftrace_events, list_head, next);
        while (event != ftrace_events) {
                ulong call;
-               ulong name_addr, system_addr;
-               char name[128], system[128];
+               ulong name_addr, system_addr, print_fmt_addr;
+               char name[128], system[128], print_fmt[4096];
                int id;
 
                call = event - koffset(ftrace_event_call, list);
@@ -756,11 +799,14 @@ static int ftrace_init_event_types(void)
                read_value(id, call, ftrace_event_call, id);
                read_value(name_addr, call, ftrace_event_call, name);
                read_value(system_addr, call, ftrace_event_call, system);
+               read_value(print_fmt_addr, call, ftrace_event_call, print_fmt);
 
                if (!read_string(name_addr, name, 128))
                        goto out_fail;
                if (!read_string(system_addr, system, 128))
                        goto out_fail;
+               if (!read_string(print_fmt_addr, print_fmt, 4096))
+                       goto out_fail;
 
                /* Enlarge event types array when need */
                if (nr_event_types >= max_types) {
@@ -782,6 +828,7 @@ static int ftrace_init_event_types(void)
 
                aevent_type->system = strdup(system);
                aevent_type->name = strdup(name);
+               aevent_type->print_fmt = strdup(print_fmt);
                aevent_type->id = id;
                aevent_type->nfields = 0;
                aevent_type->fields = NULL;
@@ -812,6 +859,7 @@ static int ftrace_init_event_types(void)
 out_fail_free_aevent_type:
        free(aevent_type->system);
        free(aevent_type->name);
+       free(aevent_type->print_fmt);
        free(aevent_type);
 out_fail:
        ftrace_destroy_event_types();
@@ -868,6 +916,7 @@ static int ftrace_dump_event_type(struct event_type *t, 
const char *path)
        char format_path[PATH_MAX];
        FILE *out;
        int i;
+       int common_field_count = 5;
 
        snprintf(format_path, sizeof(format_path), "%s/format", path);
        out = fopen(format_path, "w");
@@ -878,15 +927,40 @@ static int ftrace_dump_event_type(struct event_type *t, 
const char *path)
        fprintf(out, "ID: %d\n", t->id);
        fprintf(out, "format:\n");
 
-       for (i = 0; i < t->nfields; i++) {
-               struct ftrace_field *f = &t->fields[i];
+       for (i = t->nfields - 1; i >= 0; i--) {
+               /*
+                * Smartly shows the array type(except dynamic array).
+                * Normal:
+                *      field:TYPE VAR
+                * If TYPE := TYPE[LEN], it is shown:
+                *      field:TYPE VAR[LEN]
+                */
+               struct ftrace_field *field = &t->fields[i];
+               const char *array_descriptor = strchr(field->type, '[');
+
+               if (!strncmp(field->type, "__data_loc", 10))
+                       array_descriptor = NULL;
+
+               if (!array_descriptor) {
+                       fprintf(out, "\tfield:%s %s;\toffset:%u;"
+                                       "\tsize:%u;\tsigned:%d;\n",
+                                       field->type, field->name, field->offset,
+                                       field->size, !!field->is_signed);
+               } else {
+                       fprintf(out, "\tfield:%.*s %s%s;\toffset:%u;"
+                                       "\tsize:%u;\tsigned:%d;\n",
+                                       (int)(array_descriptor - field->type),
+                                       field->type, field->name,
+                                       array_descriptor, field->offset,
+                                       field->size, !!field->is_signed);
+               }
 
-               fprintf(out, "\tfield:%s %s;\toffset:%d;\tsize:%d;\n",
-                               f->type, f->name, f->offset, f->size);
+               if (--common_field_count == 0)
+                       fprintf(out, "\n");
        }
 
-       /* TODO */
-       fprintf(out, "\nprint fmt: \"unknow fmt from dump\"\n");
+       fprintf(out, "\nprint fmt: %s\n", t->print_fmt);
+
        fclose(out);
 
        return 0;
@@ -918,9 +992,7 @@ static int ftrace_dump_event_types(const char *events_path)
 
 struct ring_buffer_per_cpu_stream {
        struct ring_buffer_per_cpu *cpu_buffer;
-       ulong *pages;
        void *curr_page;
-       int available_pages;
        int curr_page_indx;
 
        uint64_t ts;
@@ -932,43 +1004,11 @@ static
 int ring_buffer_per_cpu_stream_init(struct ring_buffer_per_cpu *cpu_buffer,
                unsigned pages, struct ring_buffer_per_cpu_stream *s)
 {
-       unsigned i, count = 0;
-
        s->cpu_buffer = cpu_buffer;
        s->curr_page = malloc(PAGESIZE());
        if (s->curr_page == NULL)
                return -1;
 
-       s->pages = malloc(sizeof(ulong) * (pages + 1));
-       if (s->pages == NULL) {
-               free(s->curr_page);
-               return -1;
-       }
-
-       s->pages[count++] = cpu_buffer->reader_page;
-
-       if (cpu_buffer->reader_page == cpu_buffer->commit_page)
-               goto pages_done;
-
-       i = cpu_buffer->head_page_index;
-       for (;;) {
-               s->pages[count++] = cpu_buffer->pages[i];
-               
-               if (cpu_buffer->pages[i] == cpu_buffer->commit_page)
-                       break;
-
-               i++;
-               if (i == pages)
-                       i = 0;
-
-               if (i == cpu_buffer->head_page_index) {
-                       /* cpu_buffer->commit_page may be corrupted */
-                       break;
-               }
-       }
-
-pages_done:
-       s->available_pages = count;
        s->curr_page_indx = -1;
        return 0;
 }
@@ -977,7 +1017,6 @@ static
 void ring_buffer_per_cpu_stream_destroy(struct ring_buffer_per_cpu_stream *s)
 {
        free(s->curr_page);
-       free(s->pages);
 }
 
 struct ftrace_event {
@@ -1003,7 +1042,8 @@ int ring_buffer_per_cpu_stream_get_page(struct 
ring_buffer_per_cpu_stream *s)
 {
        ulong raw_page;
 
-       read_value(raw_page, s->pages[s->curr_page_indx], buffer_page, page);
+       read_value(raw_page, s->cpu_buffer->linear_pages[s->curr_page_indx],
+                       buffer_page, page);
 
        if (!readmem(raw_page, KVADDR, s->curr_page, PAGESIZE(),
                        "get page context", RETURN_ON_ERROR))
@@ -1027,18 +1067,18 @@ int ring_buffer_per_cpu_stream_pop_event(struct 
ring_buffer_per_cpu_stream *s,
 
        res->data = NULL;
 
-       if (s->curr_page_indx >= s->available_pages)
+       if (s->curr_page_indx >= s->cpu_buffer->nr_linear_pages)
                return -1;
 
 again:
        if ((s->curr_page_indx == -1) || (s->offset >= s->commit)) {
                s->curr_page_indx++;
 
-               if (s->curr_page_indx == s->available_pages)
+               if (s->curr_page_indx == s->cpu_buffer->nr_linear_pages)
                        return -1;
 
                if (ring_buffer_per_cpu_stream_get_page(s) < 0) {
-                       s->curr_page_indx = s->available_pages;
+                       s->curr_page_indx = s->cpu_buffer->nr_linear_pages;
                        return -1;
                }
 
@@ -1350,6 +1390,39 @@ static int dump_saved_cmdlines(const char 
*dump_tracing_dir)
        return 0;
 }
 
+static int dump_kallsyms(const char *dump_tracing_dir)
+{
+       char path[PATH_MAX];
+       FILE *out;
+       int i;
+       struct syment *sp;
+
+       snprintf(path, sizeof(path), "%s/kallsyms", dump_tracing_dir);
+       out = fopen(path, "w");
+       if (out == NULL)
+               return -1;
+
+       for (sp = st->symtable; sp < st->symend; sp++)
+               fprintf(out, "%lx %c %s\n", sp->value, sp->type, sp->name);
+
+       for (i = 0; i < st->mods_installed; i++) {
+               struct load_module *lm = &st->load_modules[i];
+
+               for (sp = lm->mod_symtable; sp <= lm->mod_symend; sp++) {
+                       if (!strncmp(sp->name, "_MODULE_", strlen("_MODULE_")))
+                               continue;
+
+                       fprintf(out, "%lx %c %s\t[%s]\n", sp->value, sp->type,
+                                       sp->name, lm->mod_name);
+               }
+       }
+
+       fclose(out);
+       return 0;
+}
+
+static int trace_cmd_data_output(int fd);
+
 static void ftrace_dump(int argc, char *argv[])
 {
        int c;
@@ -1359,7 +1432,7 @@ static void ftrace_dump(int argc, char *argv[])
        char path[PATH_MAX];
        int ret;
 
-        while ((c = getopt(argc, argv, "sm")) != EOF) {
+        while ((c = getopt(argc, argv, "smt")) != EOF) {
                 switch(c)
                {
                case 's':
@@ -1368,6 +1441,23 @@ static void ftrace_dump(int argc, char *argv[])
                case 'm':
                        dump_meta_data = 1;
                        break;
+               case 't':
+                       if (dump_symbols || dump_meta_data || argc - optind > 1)
+                               cmd_usage(pc->curcmd, SYNOPSIS);
+                       else {
+                               char *trace_dat;
+                               int fd;
+
+                               if (argc - optind == 0)
+                                       trace_dat = "trace.dat";
+                               else if (argc - optind == 1)
+                                       trace_dat = argv[optind];
+                               fd = open(trace_dat, O_WRONLY | O_CREAT
+                                               | O_TRUNC, 0644);
+                               trace_cmd_data_output(fd);
+                               close(fd);
+                       }
+                       return;
                default:
                        cmd_usage(pc->curcmd, SYNOPSIS);
                        return;
@@ -1413,10 +1503,7 @@ static void ftrace_dump(int argc, char *argv[])
 
        if (dump_symbols) {
                /* Dump all symbols of the kernel */
-               fprintf(fp, "\n-s option hasn't been implemented.\n");
-               fprintf(fp, "symbols is not dumpped.\n");
-               fprintf(fp, "You can use `sym -l > %s/kallsyms`\n\n",
-                               dump_tracing_dir);
+               dump_kallsyms(dump_tracing_dir);
        }
 }
 
@@ -2508,6 +2595,9 @@ static char *help_ftrace[] = {
 "    the same as debugfs/tracing.",
 "    -m: also dump metadata of ftrace.",
 "    -s: also dump symbols of the kernel <not implemented>.",
+"trace dump -t [output-file-name]",
+"   dump ring_buffers and all meta data to a file that can",
+"   be parsed by trace-cmd. Default output file name is \"trace.dat\".",
 NULL
 };
 
@@ -2536,3 +2626,521 @@ int _fini(void)
 
        return 1;
 }
+
+#define TRACE_CMD_FILE_VERSION_STRING "6"
+
+static inline int host_bigendian(void)
+{
+       unsigned char str[] = { 0x1, 0x2, 0x3, 0x4 };
+       unsigned int *ptr;
+
+       ptr = (unsigned int *)str;
+       return *ptr == 0x01020304;
+}
+
+static char *tmp_file_buf;
+static unsigned long long tmp_file_pos;
+static unsigned long long tmp_file_size;
+static int tmp_file_error;
+
+static int init_tmp_file(void)
+{
+       tmp_file_buf = malloc(4096);
+       if (tmp_file_buf == NULL)
+               return -1;
+
+       tmp_file_pos = 0;
+       tmp_file_size = 4096;
+       tmp_file_error = 0;
+
+       return 0;
+}
+
+static void destory_tmp_file(void)
+{
+       free(tmp_file_buf);
+}
+
+#define tmp_fprintf(fmt...)                                            \
+do {                                                                   \
+       char *__buf = tmp_file_buf;                                     \
+       unsigned long long __pos;                                       \
+                                                                       \
+       if (tmp_file_error)                                             \
+               break;                                                  \
+       __pos = tmp_file_pos;                                           \
+       __pos += snprintf(__buf + __pos, tmp_file_size - __pos, fmt);   \
+       if (__pos > tmp_file_size) {                                    \
+               tmp_file_size = __pos + tmp_file_size;                  \
+               __buf = realloc(__buf, tmp_file_size);                  \
+               if (!__buf) {                                           \
+                       tmp_file_error = 1;                             \
+                       break;                                          \
+               }                                                       \
+               tmp_file_buf = __buf;                                   \
+               __pos = tmp_file_pos;                                   \
+               __pos += snprintf(__buf + __pos, tmp_file_size - __pos, fmt);\
+       }                                                               \
+       tmp_file_pos = __pos;                                           \
+} while (0)
+
+static int tmp_file_record_size4(int fd)
+{
+       unsigned int size = tmp_file_pos;
+
+       if (tmp_file_error)
+               return -1;
+       if (write_and_check(fd, &size, 4))
+               return -1;
+       return 0;
+}
+
+static int tmp_file_record_size8(int fd)
+{
+       if (tmp_file_error)
+               return -1;
+       if (write_and_check(fd, &tmp_file_pos, 8))
+               return -1;
+       return 0;
+}
+
+static int tmp_file_flush(int fd)
+{
+       if (tmp_file_error)
+               return -1;
+       if (write_and_check(fd, tmp_file_buf, tmp_file_pos))
+               return -1;
+       tmp_file_pos = 0;
+       return 0;
+}
+
+static int save_initial_data(int fd)
+{
+       int page_size;
+       char buf[20];
+
+       if (write_and_check(fd, "\027\010\104tracing", 10))
+               return -1;
+
+       if (write_and_check(fd, TRACE_CMD_FILE_VERSION_STRING,
+                               strlen(TRACE_CMD_FILE_VERSION_STRING) + 1))
+               return -1;
+
+       /* Crash ensure core file endian and the host endian are the same */
+       if (host_bigendian())
+               buf[0] = 1;
+       else
+               buf[0] = 0;
+
+       if (write_and_check(fd, buf, 1))
+               return -1;
+
+       /* save size of long (this may not be what the kernel is) */
+       buf[0] = sizeof(long);
+       if (write_and_check(fd, buf, 1))
+               return -1;
+
+       page_size = PAGESIZE();
+       if (write_and_check(fd, &page_size, 4))
+               return -1;
+
+       return 0;
+}
+
+static int save_header_files(int fd)
+{
+       /* save header_page */
+       if (write_and_check(fd, "header_page", 12))
+               return -1;
+
+       tmp_fprintf("\tfield: u64 timestamp;\toffset:0;\tsize:8;\tsigned:0;\n");
+
+       tmp_fprintf("\tfield: local_t commit;\toffset:8;\tsize:%u;\t"
+                       "signed:1;\n", (unsigned int)sizeof(long));
+
+       tmp_fprintf("\tfield: int overwrite;\toffset:8;\tsize:%u;\tsigned:1;\n",
+                       (unsigned int)sizeof(long));
+
+       tmp_fprintf("\tfield: char data;\toffset:%u;\tsize:%u;\tsigned:1;\n",
+                       (unsigned int)(8 + sizeof(long)),
+                       (unsigned int)(PAGESIZE() - 8 - sizeof(long)));
+
+       if (tmp_file_record_size8(fd))
+               return -1;
+       if (tmp_file_flush(fd))
+               return -1;
+
+       /* save header_event */
+       if (write_and_check(fd, "header_event", 13))
+               return -1;
+
+       tmp_fprintf(
+                       "# compressed entry header\n"
+                       "\ttype_len    :    5 bits\n"
+                       "\ttime_delta  :   27 bits\n"
+                       "\tarray       :   32 bits\n"
+                       "\n"
+                       "\tpadding     : type == 29\n"
+                       "\ttime_extend : type == 30\n"
+                       "\tdata max type_len  == 28\n"
+       );
+
+       if (tmp_file_record_size8(fd))
+               return -1;
+       if (tmp_file_flush(fd))
+               return -1;
+
+       return 0;
+}
+
+static int save_event_file(int fd, struct event_type *t)
+{
+       int i;
+       int common_field_count = 5;
+
+       tmp_fprintf("name: %s\n", t->name);
+       tmp_fprintf("ID: %d\n", t->id);
+       tmp_fprintf("format:\n");
+
+       for (i = t->nfields - 1; i >= 0; i--) {
+               /*
+                * Smartly shows the array type(except dynamic array).
+                * Normal:
+                *      field:TYPE VAR
+                * If TYPE := TYPE[LEN], it is shown:
+                *      field:TYPE VAR[LEN]
+                */
+               struct ftrace_field *field = &t->fields[i];
+               const char *array_descriptor = strchr(field->type, '[');
+
+               if (!strncmp(field->type, "__data_loc", 10))
+                       array_descriptor = NULL;
+
+               if (!array_descriptor) {
+                       tmp_fprintf("\tfield:%s %s;\toffset:%u;"
+                                       "\tsize:%u;\tsigned:%d;\n",
+                                       field->type, field->name, field->offset,
+                                       field->size, !!field->is_signed);
+               } else {
+                       tmp_fprintf("\tfield:%.*s %s%s;\toffset:%u;"
+                                       "\tsize:%u;\tsigned:%d;\n",
+                                       (int)(array_descriptor - field->type),
+                                       field->type, field->name,
+                                       array_descriptor, field->offset,
+                                       field->size, !!field->is_signed);
+               }
+
+               if (--common_field_count == 0)
+                       tmp_fprintf("\n");
+       }
+
+       tmp_fprintf("\nprint fmt: %s\n", t->print_fmt);
+
+       if (tmp_file_record_size8(fd))
+               return -1;
+       return tmp_file_flush(fd);
+}
+
+static int save_system_files(int fd, int *system_ids, int system_id)
+{
+       int i, total = 0;
+
+       for (i = 0; i < nr_event_types; i++) {
+               if (system_ids[i] == system_id)
+                       total++;
+       }
+
+       if (write_and_check(fd, &total, 4))
+               return -1;
+
+       for (i = 0; i < nr_event_types; i++) {
+               if (system_ids[i] != system_id)
+                       continue;
+
+               if (save_event_file(fd, event_types[i]))
+                       return -1;
+       }
+
+       return 0;
+}
+
+static int save_events_files(int fd)
+{
+       int system_id = 1, *system_ids;
+       const char *system = "ftrace";
+       int i;
+       int nr_systems;
+
+       system_ids = calloc(sizeof(*system_ids), nr_event_types);
+       if (system_ids == NULL)
+               return -1;
+
+       for (;;) {
+               for (i = 0; i < nr_event_types; i++) {
+                       if (system_ids[i])
+                               continue;
+                       if (!system) {
+                               system = event_types[i]->system;
+                               system_ids[i] = system_id;
+                               continue;
+                       }
+                       if (!strcmp(event_types[i]->system, system))
+                               system_ids[i] = system_id;
+               }
+               if (!system)
+                       break;
+               system_id++;
+               system = NULL;
+       }
+
+       /* ftrace events */
+       if (save_system_files(fd, system_ids, 1))
+               goto fail;
+
+       /* other systems events */
+       nr_systems = system_id - 2;
+       if (write_and_check(fd, &nr_systems, 4))
+               goto fail;
+       for (system_id = 2; system_id < nr_systems + 2; system_id++) {
+               for (i = 0; i < nr_event_types; i++) {
+                       if (system_ids[i] == system_id)
+                               break;
+               }
+               if (write_and_check(fd, (void *)event_types[i]->system,
+                               strlen(event_types[i]->system) + 1))
+                       goto fail;
+               if (save_system_files(fd, system_ids, system_id))
+                       goto fail;
+       }
+
+       free(system_ids);
+       return 0;
+
+fail:
+       free(system_ids);
+       return -1;
+}
+
+static int save_proc_kallsyms(int fd)
+{
+       int i;
+       struct syment *sp;
+
+       for (sp = st->symtable; sp < st->symend; sp++)
+               tmp_fprintf("%lx %c %s\n", sp->value, sp->type, sp->name);
+
+       for (i = 0; i < st->mods_installed; i++) {
+               struct load_module *lm = &st->load_modules[i];
+
+               for (sp = lm->mod_symtable; sp <= lm->mod_symend; sp++) {
+                       if (!strncmp(sp->name, "_MODULE_", strlen("_MODULE_")))
+                               continue;
+
+                       tmp_fprintf("%lx %c %s\t[%s]\n", sp->value, sp->type,
+                                       sp->name, lm->mod_name);
+               }
+       }
+
+       if (tmp_file_record_size4(fd))
+               return -1;
+       return tmp_file_flush(fd);
+}
+
+static int save_ftrace_printk(int fd)
+{
+       struct syment *s, *e;
+       long bprintk_fmt_s, bprintk_fmt_e;
+       char string[4096];
+       long *address;
+       size_t i, count;
+
+       s = symbol_search("__start___trace_bprintk_fmt");
+       e = symbol_search("__stop___trace_bprintk_fmt");
+       if (s == NULL || e == NULL)
+               return -1;
+
+       bprintk_fmt_s = s->value;
+       bprintk_fmt_e = e->value;
+       count = (bprintk_fmt_e - bprintk_fmt_s) / sizeof(long);
+
+       if (count == 0) {
+               unsigned int size = 0;
+               return write_and_check(fd, &size, 4);
+       }
+
+       address = malloc(count * sizeof(long));
+       if (address == NULL)
+               return -1;
+
+       if (!readmem(bprintk_fmt_s, KVADDR, address, count * sizeof(long),
+                       "get printk address", RETURN_ON_ERROR)) {
+               free(address);
+               return -1;
+       }
+
+       for (i = 0; i < count; i++) {
+               size_t len = read_string(address[i], string, sizeof(string));
+               if (!len) {
+                       free(address);
+                       return -1;
+               }
+
+               tmp_fprintf("0x%lx : \"", address[i]);
+
+               for (i = 0; string[i]; i++) {
+                       switch (string[i]) {
+                       case '\n':
+                               tmp_fprintf("\\n");
+                               break;
+                       case '\t':
+                               tmp_fprintf("\\t");
+                               break;
+                       case '\\':
+                               tmp_fprintf("\\\\");
+                               break;
+                       case '"':
+                               tmp_fprintf("\\\"");
+                               break;
+                       default:
+                               tmp_fprintf("%c", string[i]);
+                       }
+               }
+               tmp_fprintf("\"\n");
+       }
+
+       free(address);
+
+       if (tmp_file_record_size4(fd))
+               return -1;
+       return tmp_file_flush(fd);
+}
+
+static int save_ftrace_cmdlines(int fd)
+{
+       int i;
+       struct task_context *tc = FIRST_CONTEXT();
+
+       for (i = 0; i < RUNNING_TASKS(); i++)
+               tmp_fprintf("%d %s\n", (int)tc[i].pid, tc[i].comm);
+
+       if (tmp_file_record_size8(fd))
+               return -1;
+       return tmp_file_flush(fd);
+}
+
+static int save_res_data(int fd, int nr_cpu_buffers)
+{
+       unsigned short option = 0;
+
+       if (write_and_check(fd, &nr_cpu_buffers, 4))
+               return -1;
+
+       if (write_and_check(fd, "options  ", 10))
+               return -1;
+
+       if (write_and_check(fd, &option, 2))
+               return -1;
+
+       if (write_and_check(fd, "flyrecord", 10))
+               return -1;
+
+       return 0;
+}
+
+static int save_record_data(int fd, int nr_cpu_buffers)
+{
+       int i, j;
+       unsigned long long offset, buffer_offset;
+       void *page_tmp;
+
+       offset = lseek(fd, 0, SEEK_CUR);
+       offset += nr_cpu_buffers * 16;
+       offset = (offset + (PAGESIZE() - 1)) & ~(PAGESIZE() - 1);
+       buffer_offset = offset;
+
+       for (i = 0; i < nr_cpu_ids; i++) {
+               struct ring_buffer_per_cpu *cpu_buffer = &global_buffers[i];
+               unsigned long long buffer_size;
+
+               if (!cpu_buffer->kaddr)
+                       continue;
+
+               buffer_size = PAGESIZE() * cpu_buffer->nr_linear_pages;
+               if (write_and_check(fd, &buffer_offset, 8))
+                       return -1;
+               if (write_and_check(fd, &buffer_size, 8))
+                       return -1;
+               buffer_offset += buffer_size;
+       }
+
+       page_tmp = malloc(PAGESIZE());
+       if (page_tmp == NULL)
+               return -1;
+
+       lseek(fd, offset, SEEK_SET);
+       for (i = 0; i < nr_cpu_ids; i++) {
+               struct ring_buffer_per_cpu *cpu_buffer = &global_buffers[i];
+
+               if (!cpu_buffer->kaddr)
+                       continue;
+
+               for (j = 0; j < cpu_buffer->nr_linear_pages; j++) {
+                       if (ftrace_dump_page(fd, cpu_buffer->linear_pages[i],
+                                       page_tmp) < 0) {
+                               free(page_tmp);
+                               return -1;
+                       }
+               }
+       }
+
+       free(page_tmp);
+
+       return 0;
+}
+
+static int __trace_cmd_data_output(int fd)
+{
+       int i;
+       int nr_cpu_buffers = 0;
+
+       for (i = 0; i < nr_cpu_ids; i++) {
+               struct ring_buffer_per_cpu *cpu_buffer = &global_buffers[i];
+
+               if (!cpu_buffer->kaddr)
+                       continue;
+
+               nr_cpu_buffers++;
+       }
+
+       if (save_initial_data(fd))
+               return -1;
+       if (save_header_files(fd))
+               return -1;
+       if (save_events_files(fd)) /* ftrace events and other systems events */
+               return -1;
+       if (save_proc_kallsyms(fd))
+               return -1;
+       if (save_ftrace_printk(fd))
+               return -1;
+       if (save_ftrace_cmdlines(fd))
+               return -1;
+       if (save_res_data(fd, nr_cpu_buffers))
+               return -1;
+       if (save_record_data(fd, nr_cpu_buffers))
+               return -1;
+
+       return 0;
+}
+
+static int trace_cmd_data_output(int fd)
+{
+       int ret;
+
+       if (init_tmp_file())
+               return -1;
+
+       ret = __trace_cmd_data_output(fd);
+       destory_tmp_file();
+
+       return ret;
+}

--
Crash-utility mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/crash-utility

Reply via email to