ktrace: Add completions for create kprobe

Add the completion callback for creating kprobes. Add the kprobe
functions, and offset and types.

Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
diff --git a/src/create.c b/src/create.c
index ce0753c..d66cc34 100644
--- a/src/create.c
+++ b/src/create.c
@@ -200,10 +200,200 @@
 	return 0;
 }
 
+static int get_mods(char ***mods)
+{
+	FILE *fp;
+	char **list = NULL;
+	char **tmp;
+	char *line = NULL;
+	char *mod;
+	char *sav;
+	size_t len = 0;
+	size_t mlen;
+	int cnt = 0;
+
+	fp = fopen("/proc/modules", "r");
+	if (!fp)
+		return 0;
+
+	while (getline(&line, &len, fp) > 0) {
+		mod = strtok_r(line, " ", &sav);
+		tmp = realloc(list, sizeof(*list) * (cnt + 1));
+		if (!tmp) {
+			for (cnt--; cnt >= 0; cnt--)
+				free(list[cnt]);
+			free(list);
+			free(line);
+			return 0;
+		}
+		tmp[cnt++] = mod;
+		mlen = strlen(mod);
+		/* Append the ":" on modules to denote these are modules */
+		if (mlen + 2 < len) {
+			mod[mlen++] = ':';
+			mod[mlen] = '\0';
+		}
+		list = tmp;
+		line = NULL;
+		len = 0;
+	}
+	free(line);
+	*mods = list;
+	fclose(fp);
+	return cnt;
+}
+
+static int load_available_filter(char ***list, char *match)
+{
+	char **funcs = NULL;
+	char **mods = NULL;
+	char *filter = NULL;
+	char *mod = NULL;
+	char *p;
+	int ret;
+	int cnt = 0;
+	int mcnt = 0;
+	int i;
+
+	*list = NULL;
+	if ((p = strchr(match, ':'))) {
+		mod = strdup(match);
+		if (!mod)
+			return 0;
+		mod[p - match] = '\0';
+		match = p + 1;
+	} else {
+		mcnt = get_mods(&mods);
+	}
+	ret = asprintf(&filter, "%s*", match);
+	if (ret < 0)
+		goto out;
+
+	ret = tracefs_filter_functions(filter, mod, &funcs);
+	if (ret < 0)
+		goto out;
+
+	cnt = tracefs_list_size(funcs);
+	if (cnt < 0)
+		goto out;
+
+	*list = calloc(cnt + mcnt, sizeof(**list));
+	for (i = 0; i < mcnt; i++) {
+		(*list)[i] = mods[i];
+		mods[i] = NULL;
+	}
+
+	for (i = 0; i < cnt; i++) {
+		if (mod) {
+			ret = asprintf(&p, "%s:%s", mod, funcs[i]);
+			if (ret < 0)
+				p = NULL;
+		} else
+			p = strdup(funcs[i]);
+		(*list)[i + mcnt] = p;
+	}
+ out:
+	tracefs_list_free(funcs);
+	free(filter);
+	for (i = 0; i < mcnt; i++)
+		free(mods[i]);
+	free(mods);
+	free(mod);
+	/* It's possible to get here without allocating list */
+	return *list ? cnt + mcnt : 0;
+}
+
+static int type_completion(char ***list, char *match, int len)
+{
+	static char *types[] = {"string" , "ustring", "x8", "x16", "x32", "x64",
+		"u8", "u16", "u32", "u64", "s8", "s16", "s32", "s64" };
+	char **words;
+	int i, x;
+
+	x = ARRAY_SIZE(types);
+	words = calloc(x, sizeof(char *));
+	if (!words)
+		return 0;
+	for (i = 0; i < x; i++) {
+		asprintf(&words[i], "%.*s.%s",
+			 len, match, types[i]);
+	}
+	*list = words;
+	match[strlen(match)] = CCLI_NOSPACE;
+	return x;
+}
+
+static int offset_completion(char m, char ***list, char *match, int len)
+{
+	char **words;
+
+	if (!m) {
+		words = calloc(1, sizeof(char *));
+		if (!words)
+			return 0;
+		asprintf(&words[0], "%.*s->",
+			 len, match);
+		*list = words;
+		match[strlen(match)] = CCLI_NOSPACE;
+		return 1;
+	}
+	return 0;
+}
 static int kprobe_completion(struct ccli *ccli, void *data,
 			     int argc, char **argv,
 			     char ***list, int word, char *match)
 {
+	char *p, *m;
+	int len;
+	int ret;
+
+	switch (word) {
+	case 0:
+		ccli_printf(ccli, "\n# Name the kprobe\n");
+		ccli_line_refresh(ccli);
+		return 0;
+	case 1:
+		if (!strlen(match)) {
+			ccli_printf(ccli, "\n# [mod:]function-name[+offset] | @memory-address\n");
+			ccli_line_refresh(ccli);
+			return 0;
+		}
+		if (match[0] == '@') {
+			ccli_printf(ccli, "\n# @memory-address\n");
+			ccli_line_refresh(ccli);
+			return 0;
+		}
+		if (strchr(match, '+')) {
+			ccli_printf(ccli, "\n# [mod:]function-name+offset\n");
+			ccli_line_refresh(ccli);
+			return 0;
+		}
+		ret = load_available_filter(list, match);
+		match[strlen(match)] = CCLI_NOSPACE;
+		return ret;
+	default:
+		p = strchr(match, '=');
+		if (!p) {
+			ccli_printf(ccli, "\n# var=ARG[.type][->offset[.type]\n");
+			ccli_line_refresh(ccli);
+			return 0;
+		}
+		m = p;
+		while (*p) {
+			if (*p == '.' || *p == '-')
+				m = p;
+			p++;
+		}
+
+		len = m - match;
+
+		switch (*m) {
+		case '.':
+			return type_completion(list, match, len);
+		case '-':
+			return offset_completion(m[1], list, match, len);
+		}
+	}
 	return 0;
 }
 
@@ -215,8 +405,6 @@
 	struct tep_format_field **common_fields;
 	struct tep_format_field **fields;
 	struct tep_event *event;
-	static char *types[] = {"string" , "ustring", "x8", "x16", "x32", "x64",
-		"u8", "u16", "u32", "u64", "s8", "s16", "s32", "s64" };
 	char **systems;
 	char **events;
 	char **words;
@@ -331,29 +519,9 @@
 			match[strlen(match)] = CCLI_NOSPACE;
 			return x;
 		case '.':
-			x = ARRAY_SIZE(types);
-			words = calloc(x, sizeof(char *));
-			if (!words)
-				return 0;
-			for (i = 0; i < x; i++) {
-				asprintf(&words[i], "%.*s.%s",
-					 len, match, types[i]);
-			}
-			*list = words;
-			match[strlen(match)] = CCLI_NOSPACE;
-			return x;
+			return type_completion(list, match, len);
 		case '-':
-			if (!m[1]) {
-				words = calloc(1, sizeof(char *));
-				if (!words)
-					return 0;
-				asprintf(&words[0], "%.*s->",
-					 len, match);
-				*list = words;
-				match[strlen(match)] = CCLI_NOSPACE;
-				return 1;
-			}
-			return 0;
+			return offset_completion(m[1], list, match, len);
 		}
 	}
 	printf("\neprobe word=%d match=%s\n", word, match);