Commit 487ae1f4 authored by Tzvetomir Stoyanov (VMware)'s avatar Tzvetomir Stoyanov (VMware) Committed by Arnaldo Carvalho de Melo
Browse files

tools lib traceevent: Add support for more printk format specifiers

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: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarTzvetomir 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: default avatarSteven Rostedt (VMware) <rostedt@goodmis.org>
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 4d70caef
Loading
Loading
Loading
Loading
+289 −74
Original line number Diff line number Diff line
@@ -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;
	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 void print_ip4_addr(struct trace_seq *s, char i, unsigned char *buf)
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, 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";

	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;
	int rc = 1;

	/* IP version */
	ptr++;
	rc++;

	ver = *ptr;
	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 (arg->type == TEP_PRINT_BSTRING) {
					if (isalnum(ptr[1]))
						ptr++;

				if (arg->type == TEP_PRINT_BSTRING) {
					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);
				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;
				} 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 */
			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) {