mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	tracing: probeevent: Add array type support
Add array type support for probe events. This allows user to get arraied types from memory address. The array type syntax is TYPE[N] Where TYPE is one of types (u8/16/32/64,s8/16/32/64, x8/16/32/64, symbol, string) and N is a fixed value less than 64. The string array type is a bit different from other types. For other base types, <base-type>[1] is equal to <base-type> (e.g. +0(%di):x32[1] is same as +0(%di):x32.) But string[1] is not equal to string. The string type itself represents "char array", but string array type represents "char * array". So, for example, +0(%di):string[1] is equal to +0(+0(%di)):string. Link: http://lkml.kernel.org/r/152465891533.26224.6150658225601339931.stgit@devbox Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org> Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
This commit is contained in:
		
							parent
							
								
									60c2e0cebf
								
							
						
					
					
						commit
						40b53b7718
					
				
					 5 changed files with 180 additions and 39 deletions
				
			
		| 
						 | 
					@ -64,9 +64,20 @@ respectively. 'x' prefix implies it is unsigned. Traced arguments are shown
 | 
				
			||||||
in decimal ('s' and 'u') or hexadecimal ('x'). Without type casting, 'x32'
 | 
					in decimal ('s' and 'u') or hexadecimal ('x'). Without type casting, 'x32'
 | 
				
			||||||
or 'x64' is used depends on the architecture (e.g. x86-32 uses x32, and
 | 
					or 'x64' is used depends on the architecture (e.g. x86-32 uses x32, and
 | 
				
			||||||
x86-64 uses x64).
 | 
					x86-64 uses x64).
 | 
				
			||||||
 | 
					These value types can be an array. To record array data, you can add '[N]'
 | 
				
			||||||
 | 
					(where N is a fixed number, less than 64) to the base type.
 | 
				
			||||||
 | 
					E.g. 'x16[4]' means an array of x16 (2bytes hex) with 4 elements.
 | 
				
			||||||
 | 
					Note that the array can be applied to memory type fetchargs, you can not
 | 
				
			||||||
 | 
					apply it to registers/stack-entries etc. (for example, '$stack1:x8[8]' is
 | 
				
			||||||
 | 
					wrong, but '+8($stack):x8[8]' is OK.)
 | 
				
			||||||
String type is a special type, which fetches a "null-terminated" string from
 | 
					String type is a special type, which fetches a "null-terminated" string from
 | 
				
			||||||
kernel space. This means it will fail and store NULL if the string container
 | 
					kernel space. This means it will fail and store NULL if the string container
 | 
				
			||||||
has been paged out.
 | 
					has been paged out.
 | 
				
			||||||
 | 
					The string array type is a bit different from other types. For other base
 | 
				
			||||||
 | 
					types, <base-type>[1] is equal to <base-type> (e.g. +0(%di):x32[1] is same
 | 
				
			||||||
 | 
					as +0(%di):x32.) But string[1] is not equal to string. The string type itself
 | 
				
			||||||
 | 
					represents "char array", but string array type represents "char * array".
 | 
				
			||||||
 | 
					So, for example, +0(%di):string[1] is equal to +0(+0(%di)):string.
 | 
				
			||||||
Bitfield is another special type, which takes 3 parameters, bit-width, bit-
 | 
					Bitfield is another special type, which takes 3 parameters, bit-width, bit-
 | 
				
			||||||
offset, and container-size (usually 32). The syntax is::
 | 
					offset, and container-size (usually 32). The syntax is::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4627,7 +4627,8 @@ static const char readme_msg[] =
 | 
				
			||||||
	"\t fetcharg: %<register>, @<address>, @<symbol>[+|-<offset>],\n"
 | 
						"\t fetcharg: %<register>, @<address>, @<symbol>[+|-<offset>],\n"
 | 
				
			||||||
	"\t           $stack<index>, $stack, $retval, $comm\n"
 | 
						"\t           $stack<index>, $stack, $retval, $comm\n"
 | 
				
			||||||
	"\t     type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string, symbol,\n"
 | 
						"\t     type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string, symbol,\n"
 | 
				
			||||||
	"\t           b<bit-width>@<bit-offset>/<container-size>\n"
 | 
						"\t           b<bit-width>@<bit-offset>/<container-size>,\n"
 | 
				
			||||||
 | 
						"\t           <type>\\[<array-size>\\]\n"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	"  events/\t\t- Directory containing all trace event subsystems:\n"
 | 
						"  events/\t\t- Directory containing all trace event subsystems:\n"
 | 
				
			||||||
	"      enable\t\t- Write 0/1 to enable/disable tracing of all events\n"
 | 
						"      enable\t\t- Write 0/1 to enable/disable tracing of all events\n"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -341,9 +341,9 @@ static int __parse_bitfield_probe_arg(const char *bf,
 | 
				
			||||||
int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
 | 
					int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
 | 
				
			||||||
		struct probe_arg *parg, bool is_return, bool is_kprobe)
 | 
							struct probe_arg *parg, bool is_return, bool is_kprobe)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct fetch_insn *code, *tmp = NULL;
 | 
						struct fetch_insn *code, *scode, *tmp = NULL;
 | 
				
			||||||
	const char *t;
 | 
						char *t, *t2;
 | 
				
			||||||
	int ret;
 | 
						int ret, len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (strlen(arg) > MAX_ARGSTR_LEN) {
 | 
						if (strlen(arg) > MAX_ARGSTR_LEN) {
 | 
				
			||||||
		pr_info("Argument is too long.: %s\n",  arg);
 | 
							pr_info("Argument is too long.: %s\n",  arg);
 | 
				
			||||||
| 
						 | 
					@ -354,24 +354,42 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
 | 
				
			||||||
		pr_info("Failed to allocate memory for command '%s'.\n", arg);
 | 
							pr_info("Failed to allocate memory for command '%s'.\n", arg);
 | 
				
			||||||
		return -ENOMEM;
 | 
							return -ENOMEM;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	t = strchr(parg->comm, ':');
 | 
						t = strchr(arg, ':');
 | 
				
			||||||
	if (t) {
 | 
						if (t) {
 | 
				
			||||||
		arg[t - parg->comm] = '\0';
 | 
							*t = '\0';
 | 
				
			||||||
		t++;
 | 
							t2 = strchr(++t, '[');
 | 
				
			||||||
 | 
							if (t2) {
 | 
				
			||||||
 | 
								*t2 = '\0';
 | 
				
			||||||
 | 
								parg->count = simple_strtoul(t2 + 1, &t2, 0);
 | 
				
			||||||
 | 
								if (strcmp(t2, "]") || parg->count == 0)
 | 
				
			||||||
 | 
									return -EINVAL;
 | 
				
			||||||
 | 
								if (parg->count > MAX_ARRAY_LEN)
 | 
				
			||||||
 | 
									return -E2BIG;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * The default type of $comm should be "string", and it can't be
 | 
						 * The default type of $comm should be "string", and it can't be
 | 
				
			||||||
	 * dereferenced.
 | 
						 * dereferenced.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (!t && strcmp(arg, "$comm") == 0)
 | 
						if (!t && strcmp(arg, "$comm") == 0)
 | 
				
			||||||
		t = "string";
 | 
							parg->type = find_fetch_type("string");
 | 
				
			||||||
	parg->type = find_fetch_type(t);
 | 
						else
 | 
				
			||||||
 | 
							parg->type = find_fetch_type(t);
 | 
				
			||||||
	if (!parg->type) {
 | 
						if (!parg->type) {
 | 
				
			||||||
		pr_info("Unsupported type: %s\n", t);
 | 
							pr_info("Unsupported type: %s\n", t);
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	parg->offset = *size;
 | 
						parg->offset = *size;
 | 
				
			||||||
	*size += parg->type->size;
 | 
						*size += parg->type->size * (parg->count ?: 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (parg->count) {
 | 
				
			||||||
 | 
							len = strlen(parg->type->fmttype) + 6;
 | 
				
			||||||
 | 
							parg->fmt = kmalloc(len, GFP_KERNEL);
 | 
				
			||||||
 | 
							if (!parg->fmt)
 | 
				
			||||||
 | 
								return -ENOMEM;
 | 
				
			||||||
 | 
							snprintf(parg->fmt, len, "%s[%d]", parg->type->fmttype,
 | 
				
			||||||
 | 
								 parg->count);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	code = tmp = kzalloc(sizeof(*code) * FETCH_INSN_MAX, GFP_KERNEL);
 | 
						code = tmp = kzalloc(sizeof(*code) * FETCH_INSN_MAX, GFP_KERNEL);
 | 
				
			||||||
	if (!code)
 | 
						if (!code)
 | 
				
			||||||
| 
						 | 
					@ -391,10 +409,20 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
 | 
				
			||||||
			ret = -EINVAL;
 | 
								ret = -EINVAL;
 | 
				
			||||||
			goto fail;
 | 
								goto fail;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		/* Since IMM or COMM must be the 1st insn, this is safe */
 | 
							if (code->op != FETCH_OP_DEREF || parg->count) {
 | 
				
			||||||
		if (code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM)
 | 
								/*
 | 
				
			||||||
 | 
								 * IMM and COMM is pointing actual address, those must
 | 
				
			||||||
 | 
								 * be kept, and if parg->count != 0, this is an array
 | 
				
			||||||
 | 
								 * of string pointers instead of string address itself.
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
			code++;
 | 
								code++;
 | 
				
			||||||
 | 
								if (code->op != FETCH_OP_NOP) {
 | 
				
			||||||
 | 
									ret = -E2BIG;
 | 
				
			||||||
 | 
									goto fail;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		code->op = FETCH_OP_ST_STRING;	/* In DEREF case, replace it */
 | 
							code->op = FETCH_OP_ST_STRING;	/* In DEREF case, replace it */
 | 
				
			||||||
 | 
							code->size = parg->type->size;
 | 
				
			||||||
		parg->dynamic = true;
 | 
							parg->dynamic = true;
 | 
				
			||||||
	} else if (code->op == FETCH_OP_DEREF) {
 | 
						} else if (code->op == FETCH_OP_DEREF) {
 | 
				
			||||||
		code->op = FETCH_OP_ST_MEM;
 | 
							code->op = FETCH_OP_ST_MEM;
 | 
				
			||||||
| 
						 | 
					@ -408,12 +436,29 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
 | 
				
			||||||
		code->op = FETCH_OP_ST_RAW;
 | 
							code->op = FETCH_OP_ST_RAW;
 | 
				
			||||||
		code->size = parg->type->size;
 | 
							code->size = parg->type->size;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						scode = code;
 | 
				
			||||||
	/* Modify operation */
 | 
						/* Modify operation */
 | 
				
			||||||
	if (t != NULL) {
 | 
						if (t != NULL) {
 | 
				
			||||||
		ret = __parse_bitfield_probe_arg(t, parg->type, &code);
 | 
							ret = __parse_bitfield_probe_arg(t, parg->type, &code);
 | 
				
			||||||
		if (ret)
 | 
							if (ret)
 | 
				
			||||||
			goto fail;
 | 
								goto fail;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						/* Loop(Array) operation */
 | 
				
			||||||
 | 
						if (parg->count) {
 | 
				
			||||||
 | 
							if (scode->op != FETCH_OP_ST_MEM &&
 | 
				
			||||||
 | 
							    scode->op != FETCH_OP_ST_STRING) {
 | 
				
			||||||
 | 
								pr_info("array only accepts memory or address\n");
 | 
				
			||||||
 | 
								ret = -EINVAL;
 | 
				
			||||||
 | 
								goto fail;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							code++;
 | 
				
			||||||
 | 
							if (code->op != FETCH_OP_NOP) {
 | 
				
			||||||
 | 
								ret = -E2BIG;
 | 
				
			||||||
 | 
								goto fail;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							code->op = FETCH_OP_LP_ARRAY;
 | 
				
			||||||
 | 
							code->param = parg->count;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	code++;
 | 
						code++;
 | 
				
			||||||
	code->op = FETCH_OP_END;
 | 
						code->op = FETCH_OP_END;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -452,14 +497,17 @@ void traceprobe_free_probe_arg(struct probe_arg *arg)
 | 
				
			||||||
	kfree(arg->code);
 | 
						kfree(arg->code);
 | 
				
			||||||
	kfree(arg->name);
 | 
						kfree(arg->name);
 | 
				
			||||||
	kfree(arg->comm);
 | 
						kfree(arg->comm);
 | 
				
			||||||
 | 
						kfree(arg->fmt);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* When len=0, we just calculate the needed length */
 | 
				
			||||||
 | 
					#define LEN_OR_ZERO (len ? len - pos : 0)
 | 
				
			||||||
static int __set_print_fmt(struct trace_probe *tp, char *buf, int len,
 | 
					static int __set_print_fmt(struct trace_probe *tp, char *buf, int len,
 | 
				
			||||||
			   bool is_return)
 | 
								   bool is_return)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int i;
 | 
						struct probe_arg *parg;
 | 
				
			||||||
 | 
						int i, j;
 | 
				
			||||||
	int pos = 0;
 | 
						int pos = 0;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	const char *fmt, *arg;
 | 
						const char *fmt, *arg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!is_return) {
 | 
						if (!is_return) {
 | 
				
			||||||
| 
						 | 
					@ -470,33 +518,49 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len,
 | 
				
			||||||
		arg = "REC->" FIELD_STRING_FUNC ", REC->" FIELD_STRING_RETIP;
 | 
							arg = "REC->" FIELD_STRING_FUNC ", REC->" FIELD_STRING_RETIP;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* When len=0, we just calculate the needed length */
 | 
					 | 
				
			||||||
#define LEN_OR_ZERO (len ? len - pos : 0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pos += snprintf(buf + pos, LEN_OR_ZERO, "\"%s", fmt);
 | 
						pos += snprintf(buf + pos, LEN_OR_ZERO, "\"%s", fmt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = 0; i < tp->nr_args; i++) {
 | 
						for (i = 0; i < tp->nr_args; i++) {
 | 
				
			||||||
		pos += snprintf(buf + pos, LEN_OR_ZERO, " %s=%s",
 | 
							parg = tp->args + i;
 | 
				
			||||||
				tp->args[i].name, tp->args[i].type->fmt);
 | 
							pos += snprintf(buf + pos, LEN_OR_ZERO, " %s=", parg->name);
 | 
				
			||||||
 | 
							if (parg->count) {
 | 
				
			||||||
 | 
								pos += snprintf(buf + pos, LEN_OR_ZERO, "{%s",
 | 
				
			||||||
 | 
										parg->type->fmt);
 | 
				
			||||||
 | 
								for (j = 1; j < parg->count; j++)
 | 
				
			||||||
 | 
									pos += snprintf(buf + pos, LEN_OR_ZERO, ",%s",
 | 
				
			||||||
 | 
											parg->type->fmt);
 | 
				
			||||||
 | 
								pos += snprintf(buf + pos, LEN_OR_ZERO, "}");
 | 
				
			||||||
 | 
							} else
 | 
				
			||||||
 | 
								pos += snprintf(buf + pos, LEN_OR_ZERO, "%s",
 | 
				
			||||||
 | 
										parg->type->fmt);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pos += snprintf(buf + pos, LEN_OR_ZERO, "\", %s", arg);
 | 
						pos += snprintf(buf + pos, LEN_OR_ZERO, "\", %s", arg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = 0; i < tp->nr_args; i++) {
 | 
						for (i = 0; i < tp->nr_args; i++) {
 | 
				
			||||||
		if (strcmp(tp->args[i].type->name, "string") == 0)
 | 
							parg = tp->args + i;
 | 
				
			||||||
 | 
							if (parg->count) {
 | 
				
			||||||
 | 
								if (strcmp(parg->type->name, "string") == 0)
 | 
				
			||||||
 | 
									fmt = ", __get_str(%s[%d])";
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									fmt = ", REC->%s[%d]";
 | 
				
			||||||
 | 
								for (j = 0; j < parg->count; j++)
 | 
				
			||||||
 | 
									pos += snprintf(buf + pos, LEN_OR_ZERO,
 | 
				
			||||||
 | 
											fmt, parg->name, j);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								if (strcmp(parg->type->name, "string") == 0)
 | 
				
			||||||
 | 
									fmt = ", __get_str(%s)";
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									fmt = ", REC->%s";
 | 
				
			||||||
			pos += snprintf(buf + pos, LEN_OR_ZERO,
 | 
								pos += snprintf(buf + pos, LEN_OR_ZERO,
 | 
				
			||||||
					", __get_str(%s)",
 | 
										fmt, parg->name);
 | 
				
			||||||
					tp->args[i].name);
 | 
							}
 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			pos += snprintf(buf + pos, LEN_OR_ZERO, ", REC->%s",
 | 
					 | 
				
			||||||
					tp->args[i].name);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#undef LEN_OR_ZERO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* return the length of print_fmt */
 | 
						/* return the length of print_fmt */
 | 
				
			||||||
	return pos;
 | 
						return pos;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					#undef LEN_OR_ZERO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int traceprobe_set_print_fmt(struct trace_probe *tp, bool is_return)
 | 
					int traceprobe_set_print_fmt(struct trace_probe *tp, bool is_return)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -524,11 +588,15 @@ int traceprobe_define_arg_fields(struct trace_event_call *event_call,
 | 
				
			||||||
	/* Set argument names as fields */
 | 
						/* Set argument names as fields */
 | 
				
			||||||
	for (i = 0; i < tp->nr_args; i++) {
 | 
						for (i = 0; i < tp->nr_args; i++) {
 | 
				
			||||||
		struct probe_arg *parg = &tp->args[i];
 | 
							struct probe_arg *parg = &tp->args[i];
 | 
				
			||||||
 | 
							const char *fmt = parg->type->fmttype;
 | 
				
			||||||
 | 
							int size = parg->type->size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ret = trace_define_field(event_call, parg->type->fmttype,
 | 
							if (parg->fmt)
 | 
				
			||||||
					 parg->name,
 | 
								fmt = parg->fmt;
 | 
				
			||||||
					 offset + parg->offset,
 | 
							if (parg->count)
 | 
				
			||||||
					 parg->type->size,
 | 
								size *= parg->count;
 | 
				
			||||||
 | 
							ret = trace_define_field(event_call, fmt, parg->name,
 | 
				
			||||||
 | 
										 offset + parg->offset, size,
 | 
				
			||||||
					 parg->type->is_signed,
 | 
										 parg->type->is_signed,
 | 
				
			||||||
					 FILTER_OTHER);
 | 
										 FILTER_OTHER);
 | 
				
			||||||
		if (ret)
 | 
							if (ret)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,6 +30,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define MAX_TRACE_ARGS		128
 | 
					#define MAX_TRACE_ARGS		128
 | 
				
			||||||
#define MAX_ARGSTR_LEN		63
 | 
					#define MAX_ARGSTR_LEN		63
 | 
				
			||||||
 | 
					#define MAX_ARRAY_LEN		64
 | 
				
			||||||
#define MAX_STRING_SIZE		PATH_MAX
 | 
					#define MAX_STRING_SIZE		PATH_MAX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Reserved field names */
 | 
					/* Reserved field names */
 | 
				
			||||||
| 
						 | 
					@ -65,6 +66,14 @@ static nokprobe_inline void *get_loc_data(u32 *dl, void *ent)
 | 
				
			||||||
	return (u8 *)ent + get_loc_offs(*dl);
 | 
						return (u8 *)ent + get_loc_offs(*dl);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static nokprobe_inline u32 update_data_loc(u32 loc, int consumed)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u32 maxlen = get_loc_len(loc);
 | 
				
			||||||
 | 
						u32 offset = get_loc_offs(loc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return make_data_loc(maxlen - consumed, offset + consumed);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Printing function type */
 | 
					/* Printing function type */
 | 
				
			||||||
typedef int (*print_type_func_t)(struct trace_seq *, void *, void *);
 | 
					typedef int (*print_type_func_t)(struct trace_seq *, void *, void *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -86,6 +95,8 @@ enum fetch_op {
 | 
				
			||||||
	FETCH_OP_ST_STRING,	/* String: .offset, .size */
 | 
						FETCH_OP_ST_STRING,	/* String: .offset, .size */
 | 
				
			||||||
	// Stage 4 (modify) op
 | 
						// Stage 4 (modify) op
 | 
				
			||||||
	FETCH_OP_MOD_BF,	/* Bitfield: .basesize, .lshift, .rshift */
 | 
						FETCH_OP_MOD_BF,	/* Bitfield: .basesize, .lshift, .rshift */
 | 
				
			||||||
 | 
						// Stage 5 (loop) op
 | 
				
			||||||
 | 
						FETCH_OP_LP_ARRAY,	/* Array: .param = loop count */
 | 
				
			||||||
	FETCH_OP_END,
 | 
						FETCH_OP_END,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -175,6 +186,7 @@ DECLARE_BASIC_PRINT_TYPE_FUNC(symbol);
 | 
				
			||||||
	_ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, atype)
 | 
						_ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, atype)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define ASSIGN_FETCH_TYPE_END {}
 | 
					#define ASSIGN_FETCH_TYPE_END {}
 | 
				
			||||||
 | 
					#define MAX_ARRAY_LEN	64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_KPROBE_EVENTS
 | 
					#ifdef CONFIG_KPROBE_EVENTS
 | 
				
			||||||
bool trace_kprobe_on_func_entry(struct trace_event_call *call);
 | 
					bool trace_kprobe_on_func_entry(struct trace_event_call *call);
 | 
				
			||||||
| 
						 | 
					@ -195,8 +207,10 @@ struct probe_arg {
 | 
				
			||||||
	struct fetch_insn	*code;
 | 
						struct fetch_insn	*code;
 | 
				
			||||||
	bool			dynamic;/* Dynamic array (string) is used */
 | 
						bool			dynamic;/* Dynamic array (string) is used */
 | 
				
			||||||
	unsigned int		offset;	/* Offset from argument entry */
 | 
						unsigned int		offset;	/* Offset from argument entry */
 | 
				
			||||||
 | 
						unsigned int		count;	/* Array count */
 | 
				
			||||||
	const char		*name;	/* Name of this argument */
 | 
						const char		*name;	/* Name of this argument */
 | 
				
			||||||
	const char		*comm;	/* Command of this argument */
 | 
						const char		*comm;	/* Command of this argument */
 | 
				
			||||||
 | 
						char			*fmt;	/* Format string if needed */
 | 
				
			||||||
	const struct fetch_type	*type;	/* Type of this argument */
 | 
						const struct fetch_type	*type;	/* Type of this argument */
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -67,10 +67,15 @@ static nokprobe_inline int
 | 
				
			||||||
process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
 | 
					process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
 | 
				
			||||||
			   void *dest, void *base)
 | 
								   void *dest, void *base)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int ret = 0;
 | 
						struct fetch_insn *s3 = NULL;
 | 
				
			||||||
 | 
						int total = 0, ret = 0, i = 0;
 | 
				
			||||||
 | 
						u32 loc = 0;
 | 
				
			||||||
 | 
						unsigned long lval = val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					stage2:
 | 
				
			||||||
	/* 2nd stage: dereference memory if needed */
 | 
						/* 2nd stage: dereference memory if needed */
 | 
				
			||||||
	while (code->op == FETCH_OP_DEREF) {
 | 
						while (code->op == FETCH_OP_DEREF) {
 | 
				
			||||||
 | 
							lval = val;
 | 
				
			||||||
		ret = probe_mem_read(&val, (void *)val + code->offset,
 | 
							ret = probe_mem_read(&val, (void *)val + code->offset,
 | 
				
			||||||
					sizeof(val));
 | 
										sizeof(val));
 | 
				
			||||||
		if (ret)
 | 
							if (ret)
 | 
				
			||||||
| 
						 | 
					@ -78,11 +83,15 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
 | 
				
			||||||
		code++;
 | 
							code++;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						s3 = code;
 | 
				
			||||||
 | 
					stage3:
 | 
				
			||||||
	/* 3rd stage: store value to buffer */
 | 
						/* 3rd stage: store value to buffer */
 | 
				
			||||||
	if (unlikely(!dest)) {
 | 
						if (unlikely(!dest)) {
 | 
				
			||||||
		if (code->op == FETCH_OP_ST_STRING)
 | 
							if (code->op == FETCH_OP_ST_STRING) {
 | 
				
			||||||
			return fetch_store_strlen(val + code->offset);
 | 
								ret += fetch_store_strlen(val + code->offset);
 | 
				
			||||||
		else
 | 
								code++;
 | 
				
			||||||
 | 
								goto array;
 | 
				
			||||||
 | 
							} else
 | 
				
			||||||
			return -EILSEQ;
 | 
								return -EILSEQ;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -94,6 +103,7 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
 | 
				
			||||||
		probe_mem_read(dest, (void *)val + code->offset, code->size);
 | 
							probe_mem_read(dest, (void *)val + code->offset, code->size);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case FETCH_OP_ST_STRING:
 | 
						case FETCH_OP_ST_STRING:
 | 
				
			||||||
 | 
							loc = *(u32 *)dest;
 | 
				
			||||||
		ret = fetch_store_string(val + code->offset, dest, base);
 | 
							ret = fetch_store_string(val + code->offset, dest, base);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
| 
						 | 
					@ -107,6 +117,29 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
 | 
				
			||||||
		code++;
 | 
							code++;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					array:
 | 
				
			||||||
 | 
						/* the last stage: Loop on array */
 | 
				
			||||||
 | 
						if (code->op == FETCH_OP_LP_ARRAY) {
 | 
				
			||||||
 | 
							total += ret;
 | 
				
			||||||
 | 
							if (++i < code->param) {
 | 
				
			||||||
 | 
								code = s3;
 | 
				
			||||||
 | 
								if (s3->op != FETCH_OP_ST_STRING) {
 | 
				
			||||||
 | 
									dest += s3->size;
 | 
				
			||||||
 | 
									val += s3->size;
 | 
				
			||||||
 | 
									goto stage3;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								code--;
 | 
				
			||||||
 | 
								val = lval + sizeof(char *);
 | 
				
			||||||
 | 
								if (dest) {
 | 
				
			||||||
 | 
									dest += sizeof(u32);
 | 
				
			||||||
 | 
									*(u32 *)dest = update_data_loc(loc, ret);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								goto stage2;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							code++;
 | 
				
			||||||
 | 
							ret = total;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return code->op == FETCH_OP_END ? ret : -EILSEQ;
 | 
						return code->op == FETCH_OP_END ? ret : -EILSEQ;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -158,12 +191,26 @@ static inline int
 | 
				
			||||||
print_probe_args(struct trace_seq *s, struct probe_arg *args, int nr_args,
 | 
					print_probe_args(struct trace_seq *s, struct probe_arg *args, int nr_args,
 | 
				
			||||||
		 u8 *data, void *field)
 | 
							 u8 *data, void *field)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int i;
 | 
						void *p;
 | 
				
			||||||
 | 
						int i, j;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = 0; i < nr_args; i++) {
 | 
						for (i = 0; i < nr_args; i++) {
 | 
				
			||||||
		trace_seq_printf(s, " %s=", args[i].name);
 | 
							struct probe_arg *a = args + i;
 | 
				
			||||||
		if (!args[i].type->print(s, data + args[i].offset, field))
 | 
					
 | 
				
			||||||
			return -ENOMEM;
 | 
							trace_seq_printf(s, " %s=", a->name);
 | 
				
			||||||
 | 
							if (likely(!a->count)) {
 | 
				
			||||||
 | 
								if (!a->type->print(s, data + a->offset, field))
 | 
				
			||||||
 | 
									return -ENOMEM;
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							trace_seq_putc(s, '{');
 | 
				
			||||||
 | 
							p = data + a->offset;
 | 
				
			||||||
 | 
							for (j = 0; j < a->count; j++) {
 | 
				
			||||||
 | 
								if (!a->type->print(s, p, field))
 | 
				
			||||||
 | 
									return -ENOMEM;
 | 
				
			||||||
 | 
								trace_seq_putc(s, j == a->count - 1 ? '}' : ',');
 | 
				
			||||||
 | 
								p += a->type->size;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue