tools lib traceevent: Add support for more printk format specifiers
authorTzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
Thu, 2 Jul 2020 18:53:50 +0000 (14:53 -0400)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Mon, 6 Jul 2020 11:35:23 +0000 (08:35 -0300)
The printk format specifiers used in event's print format files extend
the standard printf formats. There are a lot of new options related to
printing pointers and kernel specific structures. Currently trace-cmd
does not support many of them.

Support for these new printk specifiers is added to the pretty_print()
function:

 - UUID/GUID address: %pU[bBlL]
 - Raw buffer as a hex string: %*ph[CDN]

These are improved:

 - MAC address: %pMF, %pM and %pmR
 - IPv4 adderss: %p[Ii]4[hnbl]

Function pretty_print() is refactored. The logic for printing pointers
%p[...] is moved to its own function.

Link: https://lore.kernel.org/linux-trace-devel/20200515053754.3695335-1-tz.stoyanov@gmail.com
Link: http://lore.kernel.org/linux-trace-devel/20200625100516.365338-7-tz.stoyanov@gmail.com
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=207605
Reported-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: linux-trace-devel@vger.kernel.org
Link: http://lore.kernel.org/lkml/20200702185704.401148804@goodmis.org
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/lib/traceevent/event-parse.c

index 629437d4c816e1dc410ea79fca1e939d90997727..3990024d2a989c91d8c37f986f9ca196869e232d 100644 (file)
@@ -4564,43 +4564,93 @@ get_bprint_format(void *data, int size __maybe_unused,
        return format;
 }
 
-static void print_mac_arg(struct trace_seq *s, int mac, void *data, int size,
-                         struct tep_event *event, struct tep_print_arg *arg)
+static int print_mac_arg(struct trace_seq *s, const char *format,
+                        void *data, int size, struct tep_event *event,
+                        struct tep_print_arg *arg)
 {
-       unsigned char *buf;
        const char *fmt = "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x";
+       bool reverse = false;
+       unsigned char *buf;
+       int ret = 0;
 
        if (arg->type == TEP_PRINT_FUNC) {
                process_defined_func(s, data, size, event, arg);
-               return;
+               return 0;
        }
 
        if (arg->type != TEP_PRINT_FIELD) {
                trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d",
                                 arg->type);
-               return;
+               return 0;
        }
 
-       if (mac == 'm')
+       if (format[0] == 'm') {
                fmt = "%.2x%.2x%.2x%.2x%.2x%.2x";
+       } else if (format[0] == 'M' && format[1] == 'F') {
+               fmt = "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x";
+               ret++;
+       }
+       if (format[1] == 'R') {
+               reverse = true;
+               ret++;
+       }
+
        if (!arg->field.field) {
                arg->field.field =
                        tep_find_any_field(event, arg->field.name);
                if (!arg->field.field) {
                        do_warning_event(event, "%s: field %s not found",
                                         __func__, arg->field.name);
-                       return;
+                       return ret;
                }
        }
        if (arg->field.field->size != 6) {
                trace_seq_printf(s, "INVALIDMAC");
-               return;
+               return ret;
        }
+
        buf = data + arg->field.field->offset;
-       trace_seq_printf(s, fmt, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
+       if (reverse)
+               trace_seq_printf(s, fmt, buf[5], buf[4], buf[3], buf[2], buf[1], buf[0]);
+       else
+               trace_seq_printf(s, fmt, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
+
+       return ret;
+}
+
+static int parse_ip4_print_args(struct tep_handle *tep,
+                               const char *ptr, bool *reverse)
+{
+       int ret = 0;
+
+       *reverse = false;
+
+       /* hnbl */
+       switch (*ptr) {
+       case 'h':
+               if (tep->file_bigendian)
+                       *reverse = false;
+               else
+                       *reverse = true;
+               ret++;
+       break;
+       case 'l':
+               *reverse = true;
+               ret++;
+       break;
+       case 'n':
+       case 'b':
+               ret++;
+               /* fall through */
+       default:
+               *reverse = false;
+               break;
+       }
+
+       return ret;
 }
 
-static void print_ip4_addr(struct trace_seq *s, char i, unsigned char *buf)
+static void print_ip4_addr(struct trace_seq *s, char i, bool reverse, unsigned char *buf)
 {
        const char *fmt;
 
@@ -4609,7 +4659,11 @@ static void print_ip4_addr(struct trace_seq *s, char i, unsigned char *buf)
        else
                fmt = "%d.%d.%d.%d";
 
-       trace_seq_printf(s, fmt, buf[0], buf[1], buf[2], buf[3]);
+       if (reverse)
+               trace_seq_printf(s, fmt, buf[3], buf[2], buf[1], buf[0]);
+       else
+               trace_seq_printf(s, fmt, buf[0], buf[1], buf[2], buf[3]);
+
 }
 
 static inline bool ipv6_addr_v4mapped(const struct in6_addr *a)
@@ -4692,7 +4746,7 @@ static void print_ip6c_addr(struct trace_seq *s, unsigned char *addr)
        if (useIPv4) {
                if (needcolon)
                        trace_seq_printf(s, ":");
-               print_ip4_addr(s, 'I', &in6.s6_addr[12]);
+               print_ip4_addr(s, 'I', false, &in6.s6_addr[12]);
        }
 
        return;
@@ -4721,16 +4775,20 @@ static int print_ipv4_arg(struct trace_seq *s, const char *ptr, char i,
                          void *data, int size, struct tep_event *event,
                          struct tep_print_arg *arg)
 {
+       bool reverse = false;
        unsigned char *buf;
+       int ret;
+
+       ret = parse_ip4_print_args(event->tep, ptr, &reverse);
 
        if (arg->type == TEP_PRINT_FUNC) {
                process_defined_func(s, data, size, event, arg);
-               return 0;
+               return ret;
        }
 
        if (arg->type != TEP_PRINT_FIELD) {
                trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d", arg->type);
-               return 0;
+               return ret;
        }
 
        if (!arg->field.field) {
@@ -4739,7 +4797,7 @@ static int print_ipv4_arg(struct trace_seq *s, const char *ptr, char i,
                if (!arg->field.field) {
                        do_warning("%s: field %s not found",
                                   __func__, arg->field.name);
-                       return 0;
+                       return ret;
                }
        }
 
@@ -4747,11 +4805,12 @@ static int print_ipv4_arg(struct trace_seq *s, const char *ptr, char i,
 
        if (arg->field.field->size != 4) {
                trace_seq_printf(s, "INVALIDIPv4");
-               return 0;
+               return ret;
        }
-       print_ip4_addr(s, i, buf);
 
-       return 0;
+       print_ip4_addr(s, i, reverse, buf);
+       return ret;
+
 }
 
 static int print_ipv6_arg(struct trace_seq *s, const char *ptr, char i,
@@ -4811,7 +4870,9 @@ static int print_ipsa_arg(struct trace_seq *s, const char *ptr, char i,
        char have_c = 0, have_p = 0;
        unsigned char *buf;
        struct sockaddr_storage *sa;
+       bool reverse = false;
        int rc = 0;
+       int ret;
 
        /* pISpc */
        if (i == 'I') {
@@ -4826,6 +4887,9 @@ static int print_ipsa_arg(struct trace_seq *s, const char *ptr, char i,
                        rc++;
                }
        }
+       ret = parse_ip4_print_args(event->tep, ptr, &reverse);
+       ptr += ret;
+       rc += ret;
 
        if (arg->type == TEP_PRINT_FUNC) {
                process_defined_func(s, data, size, event, arg);
@@ -4857,7 +4921,7 @@ static int print_ipsa_arg(struct trace_seq *s, const char *ptr, char i,
                        return rc;
                }
 
-               print_ip4_addr(s, i, (unsigned char *) &sa4->sin_addr);
+               print_ip4_addr(s, i, reverse, (unsigned char *) &sa4->sin_addr);
                if (have_p)
                        trace_seq_printf(s, ":%d", ntohs(sa4->sin_port));
 
@@ -4891,25 +4955,20 @@ static int print_ip_arg(struct trace_seq *s, const char *ptr,
                        struct tep_print_arg *arg)
 {
        char i = *ptr;  /* 'i' or 'I' */
-       char ver;
-       int rc = 0;
-
-       ptr++;
-       rc++;
+       int rc = 1;
 
-       ver = *ptr;
+       /* IP version */
        ptr++;
-       rc++;
 
-       switch (ver) {
+       switch (*ptr) {
        case '4':
-               rc += print_ipv4_arg(s, ptr, i, data, size, event, arg);
+               rc += print_ipv4_arg(s, ptr + 1, i, data, size, event, arg);
                break;
        case '6':
-               rc += print_ipv6_arg(s, ptr, i, data, size, event, arg);
+               rc += print_ipv6_arg(s, ptr + 1, i, data, size, event, arg);
                break;
        case 'S':
-               rc += print_ipsa_arg(s, ptr, i, data, size, event, arg);
+               rc += print_ipsa_arg(s, ptr + 1, i, data, size, event, arg);
                break;
        default:
                return 0;
@@ -4918,6 +4977,133 @@ static int print_ip_arg(struct trace_seq *s, const char *ptr,
        return rc;
 }
 
+static const int guid_index[16] = {3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15};
+static const int uuid_index[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+
+static int print_uuid_arg(struct trace_seq *s, const char *ptr,
+                       void *data, int size, struct tep_event *event,
+                       struct tep_print_arg *arg)
+{
+       const int *index = uuid_index;
+       char *format = "%02x";
+       int ret = 0;
+       char *buf;
+       int i;
+
+       switch (*(ptr + 1)) {
+       case 'L':
+               format = "%02X";
+               /* fall through */
+       case 'l':
+               index = guid_index;
+               ret++;
+               break;
+       case 'B':
+               format = "%02X";
+               /* fall through */
+       case 'b':
+               ret++;
+               break;
+       }
+
+       if (arg->type == TEP_PRINT_FUNC) {
+               process_defined_func(s, data, size, event, arg);
+               return ret;
+       }
+
+       if (arg->type != TEP_PRINT_FIELD) {
+               trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d", arg->type);
+               return ret;
+       }
+
+       if (!arg->field.field) {
+               arg->field.field =
+                       tep_find_any_field(event, arg->field.name);
+               if (!arg->field.field) {
+                       do_warning("%s: field %s not found",
+                                  __func__, arg->field.name);
+                       return ret;
+               }
+       }
+
+       if (arg->field.field->size != 16) {
+               trace_seq_printf(s, "INVALIDUUID");
+               return ret;
+       }
+
+       buf = data + arg->field.field->offset;
+
+       for (i = 0; i < 16; i++) {
+               trace_seq_printf(s, format, buf[index[i]] & 0xff);
+               switch (i) {
+               case 3:
+               case 5:
+               case 7:
+               case 9:
+                       trace_seq_printf(s, "-");
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int print_raw_buff_arg(struct trace_seq *s, const char *ptr,
+                             void *data, int size, struct tep_event *event,
+                             struct tep_print_arg *arg, int print_len)
+{
+       int plen = print_len;
+       char *delim = " ";
+       int ret = 0;
+       char *buf;
+       int i;
+       unsigned long offset;
+       int arr_len;
+
+       switch (*(ptr + 1)) {
+       case 'C':
+               delim = ":";
+               ret++;
+               break;
+       case 'D':
+               delim = "-";
+               ret++;
+               break;
+       case 'N':
+               delim = "";
+               ret++;
+               break;
+       }
+
+       if (arg->type == TEP_PRINT_FUNC) {
+               process_defined_func(s, data, size, event, arg);
+               return ret;
+       }
+
+       if (arg->type != TEP_PRINT_DYNAMIC_ARRAY) {
+               trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d", arg->type);
+               return ret;
+       }
+
+       offset = tep_read_number(event->tep,
+                                data + arg->dynarray.field->offset,
+                                arg->dynarray.field->size);
+       arr_len = (unsigned long long)(offset >> 16);
+       buf = data + (offset & 0xffff);
+
+       if (arr_len < plen)
+               plen = arr_len;
+
+       if (plen < 1)
+               return ret;
+
+       trace_seq_printf(s, "%02x", buf[0] & 0xff);
+       for (i = 1; i < plen; i++)
+               trace_seq_printf(s, "%s%02x", delim, buf[i] & 0xff);
+
+       return ret;
+}
+
 static int is_printable_array(char *p, unsigned int len)
 {
        unsigned int i;
@@ -5006,6 +5192,68 @@ void tep_print_fields(struct trace_seq *s, void *data,
        }
 }
 
+static int print_function(struct trace_seq *s, const char *format,
+                         void *data, int size, struct tep_event *event,
+                         struct tep_print_arg *arg)
+{
+       struct func_map *func;
+       unsigned long long val;
+
+       val = eval_num_arg(data, size, event, arg);
+       func = find_func(event->tep, val);
+       if (func) {
+               trace_seq_puts(s, func->func);
+               if (*format == 'F' || *format == 'S')
+                       trace_seq_printf(s, "+0x%llx", val - func->addr);
+       } else {
+               if (event->tep->long_size == 4)
+                       trace_seq_printf(s, "0x%lx", (long)val);
+               else
+                       trace_seq_printf(s, "0x%llx", (long long)val);
+       }
+
+       return 0;
+}
+
+static int print_pointer(struct trace_seq *s, const char *format, int plen,
+                        void *data, int size,
+                        struct tep_event *event, struct tep_print_arg *arg)
+{
+       unsigned long long val;
+       int ret = 1;
+
+       switch (*format) {
+       case 'F':
+       case 'f':
+       case 'S':
+       case 's':
+               ret += print_function(s, format, data, size, event, arg);
+               break;
+       case 'M':
+       case 'm':
+               ret += print_mac_arg(s, format, data, size, event, arg);
+               break;
+       case 'I':
+       case 'i':
+               ret += print_ip_arg(s, format, data, size, event, arg);
+               break;
+       case 'U':
+               ret += print_uuid_arg(s, format, data, size, event, arg);
+               break;
+       case 'h':
+               ret += print_raw_buff_arg(s, format, data, size, event, arg, plen);
+               break;
+       default:
+               ret = 0;
+               val = eval_num_arg(data, size, event, arg);
+               trace_seq_printf(s, "%p", (void *)val);
+               break;
+       }
+
+       return ret;
+
+}
+
 static void pretty_print(struct trace_seq *s, void *data, int size, struct tep_event *event)
 {
        struct tep_handle *tep = event->tep;
@@ -5014,16 +5262,15 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct tep_e
        struct tep_print_arg *args = NULL;
        const char *ptr = print_fmt->format;
        unsigned long long val;
-       struct func_map *func;
        const char *saveptr;
        struct trace_seq p;
        char *bprint_fmt = NULL;
        char format[32];
-       int show_func;
        int len_as_arg;
        int len_arg = 0;
        int len;
        int ls;
+       int ret;
 
        if (event->flags & TEP_EVENT_FL_FAILED) {
                trace_seq_printf(s, "[FAILED TO PARSE]");
@@ -5062,7 +5309,6 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct tep_e
 
                } else if (*ptr == '%') {
                        saveptr = ptr;
-                       show_func = 0;
                        len_as_arg = 0;
  cont_process:
                        ptr++;
@@ -5100,39 +5346,21 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct tep_e
                        case '-':
                                goto cont_process;
                        case 'p':
-                               if (tep->long_size == 4)
-                                       ls = 1;
-                               else
-                                       ls = 2;
-
-                               if (isalnum(ptr[1]))
-                                       ptr++;
-
                                if (arg->type == TEP_PRINT_BSTRING) {
+                                       if (isalnum(ptr[1]))
+                                               ptr++;
                                        trace_seq_puts(s, arg->string.string);
                                        arg = arg->next;
                                        break;
                                }
-
-                               if (*ptr == 'F' || *ptr == 'f' ||
-                                   *ptr == 'S' || *ptr == 's') {
-                                       show_func = *ptr;
-                               } else if (*ptr == 'M' || *ptr == 'm') {
-                                       print_mac_arg(s, *ptr, data, size, event, arg);
-                                       arg = arg->next;
-                                       break;
-                               } else if (*ptr == 'I' || *ptr == 'i') {
-                                       int n;
-
-                                       n = print_ip_arg(s, ptr, data, size, event, arg);
-                                       if (n > 0) {
-                                               ptr += n - 1;
-                                               arg = arg->next;
-                                               break;
-                                       }
-                               }
-
-                               /* fall through */
+                               ret = print_pointer(s, ptr + 1,
+                                                   len_as_arg ? len_arg : 1,
+                                                   data, size,
+                                                   event, arg);
+                               arg = arg->next;
+                               if (ret > 0)
+                                       ptr += ret;
+                               break;
                        case 'd':
                        case 'u':
                        case 'i':
@@ -5161,17 +5389,6 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct tep_e
                                val = eval_num_arg(data, size, event, arg);
                                arg = arg->next;
 
-                               if (show_func) {
-                                       func = find_func(tep, val);
-                                       if (func) {
-                                               trace_seq_puts(s, func->func);
-                                               if (show_func == 'F')
-                                                       trace_seq_printf(s,
-                                                              "+0x%llx",
-                                                              val - func->addr);
-                                               break;
-                                       }
-                               }
                                if (tep->long_size == 8 && ls == 1 &&
                                    sizeof(long) != 8) {
                                        char *p;
@@ -5179,8 +5396,6 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct tep_e
                                        /* make %l into %ll */
                                        if (ls == 1 && (p = strchr(format, 'l')))
                                                memmove(p+1, p, strlen(p)+1);
-                                       else if (strcmp(format, "%p") == 0)
-                                               strcpy(format, "0x%llx");
                                        ls = 2;
                                }
                                switch (ls) {