[sheepdog] [PATCH v3 2/3] collie/trace: add graph stat function

Liu Yuan namei.unix at gmail.com
Mon Aug 12 08:08:36 CEST 2013


This is useful to get how much time each function takes during the trace.

$ collie trace graph stat
   Total (s)   Per Call (ms)   Calls   Name
   100.659       51.356         1960   eventfd_xread
    79.315       68.790         1153   do_process_work
    76.645      348.388          220   do_push_object
    76.637      348.349          220   push_cache_object
    74.611      185.139          403   gateway_create_and_write_obj
    74.338      345.757          215   exec_local_req
    74.276      350.361          212   gateway_forward_request
    35.555       34.722         1024   xpwrite
    35.547        7.119         4993   event_loop
    35.538        7.118         4993   do_event_loop
    34.299      385.380           89   sheep_do_op_work
    33.778      427.565           79   peer_create_and_write_obj
    33.776      427.547           79   default_create_and_write
    26.324     2632.399           10   object_cache_push
    26.259     2917.706            9   do_background_push
     2.525        2.701          935   object_cache_handle_request
...

Signed-off-by: Liu Yuan <namei.unix at gmail.com>
---
 collie/trace.c      |  153 ++++++++++++++++++++++++++++++++++++++++++++++-----
 include/sheep.h     |    4 +-
 sheep/trace/graph.c |    1 +
 3 files changed, 142 insertions(+), 16 deletions(-)

diff --git a/collie/trace.c b/collie/trace.c
index 18f062f..5e8fd59 100644
--- a/collie/trace.c
+++ b/collie/trace.c
@@ -21,6 +21,8 @@
 #include <sys/mman.h>
 
 #include "collie.h"
+#include "rbtree.h"
+#include "list.h"
 
 static inline void print_thread_name(struct trace_graph_item *item)
 {
@@ -58,10 +60,10 @@ static void print_trace_item(struct trace_graph_item *item)
 	print_finale(item);
 }
 
-static void parse_trace_buffer(char *buf, int size)
+static void cat_trace_file(void *buf, size_t size)
 {
 	struct trace_graph_item *item = (struct trace_graph_item *)buf;
-	int sz = size / sizeof(struct trace_graph_item), i;
+	size_t sz = size / sizeof(struct trace_graph_item), i;
 
 	printf("   Thread Name      |  Time(us)  |  Function Graph\n");
 	printf("--------------------------------------------------\n");
@@ -194,38 +196,159 @@ static int trace_status(int argc, char **argv)
 	return EXIT_SUCCESS;
 }
 
-static int graph_cat(int argc, char **argv)
+static void *map_trace_file(struct stat *st)
 {
 	int fd = open(tracefile, O_RDONLY);
-	struct stat st;
 	void *map;
 
 	if (fd < 0) {
 		sd_err("%m");
-		return EXIT_SYSFAIL;
+		return NULL;
 	}
 
-	if (fstat(fd, &st) < 0) {
+	if (fstat(fd, st) < 0) {
 		sd_err("%m");
 		close(fd);
-		return EXIT_SYSFAIL;
+		return NULL;
 	}
 
-	if (st.st_size == 0)
-		return EXIT_SUCCESS;
+	if (st->st_size == 0) {
+		sd_err("trace file is empty");
+		return NULL;
+	}
 
-	map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+	map = mmap(NULL, st->st_size, PROT_READ, MAP_PRIVATE, fd, 0);
 	close(fd);
 	if (map == MAP_FAILED) {
 		sd_err("%m");
-		return EXIT_SYSFAIL;
+		return NULL;
 	}
-	parse_trace_buffer(map, st.st_size);
+
+	return map;
+}
+
+static int graph_cat(int argc, char **argv)
+{
+	struct stat st;
+	void *map = map_trace_file(&st);
+
+	if (!map)
+		return EXIT_FAILURE;
+
+	cat_trace_file(map, st.st_size);
 	munmap(map, st.st_size);
 
 	return EXIT_SUCCESS;
 }
 
+struct graph_stat_entry {
+	struct rb_node rb;
+	struct list_head list;
+	char fname[TRACE_FNAME_LEN];
+	uint64_t duration;
+	uint16_t nr_calls;
+};
+
+static struct rb_root stat_tree_root;
+
+static LIST_HEAD(stat_list);
+
+static struct graph_stat_entry *
+stat_tree_insert(struct graph_stat_entry *new)
+{
+	struct rb_node **p = &stat_tree_root.rb_node;
+	struct rb_node *parent = NULL;
+	struct graph_stat_entry *entry;
+
+	while (*p) {
+		int cmp;
+
+		parent = *p;
+		entry = rb_entry(parent, struct graph_stat_entry, rb);
+		cmp = strcmp(new->fname, entry->fname);
+
+		if (cmp < 0)
+			p = &(*p)->rb_left;
+		else if (cmp > 0)
+			p = &(*p)->rb_right;
+		else {
+			entry->duration += new->duration;
+			entry->nr_calls++;
+			return entry;
+		}
+	}
+	rb_link_node(&new->rb, parent, p);
+	rb_insert_color(&new->rb, &stat_tree_root);
+
+	return NULL; /* insert successfully */
+}
+
+static void prepare_stat_tree(struct trace_graph_item *item)
+{
+	struct graph_stat_entry *new;
+
+	if (item->type != TRACE_GRAPH_RETURN)
+		return;
+	new = xmalloc(sizeof(*new));
+	pstrcpy(new->fname, sizeof(new->fname), item->fname);
+	new->duration = item->return_time - item->entry_time;
+	new->nr_calls = 1;
+	INIT_LIST_HEAD(&new->list);
+	if (stat_tree_insert(new)) {
+		free(new);
+		return;
+	}
+	list_add(&new->list, &stat_list);
+}
+
+static void stat_list_print(void)
+{
+	struct graph_stat_entry *entry;
+
+	list_for_each_entry(entry, &stat_list, list) {
+		float total = (float)entry->duration / 1000000000;
+		float per = (float)entry->duration / entry->nr_calls / 1000000;
+
+		printf("%10.3f   %10.3f        %5"PRIu16"   %-*s\n", total, per,
+		       entry->nr_calls, TRACE_FNAME_LEN, entry->fname);
+	}
+}
+
+static int stat_list_cmp(void *priv, struct list_head *a, struct list_head *b)
+{
+	struct graph_stat_entry *ga = container_of(a, struct graph_stat_entry,
+						   list);
+	struct graph_stat_entry *gb = container_of(b, struct graph_stat_entry,
+						   list);
+	/* '-' is for reverse sort, largest first */
+	return -intcmp(ga->duration, gb->duration);
+}
+
+static void stat_trace_file(void *buf, size_t size)
+{
+	struct trace_graph_item *item = (struct trace_graph_item *)buf;
+	size_t sz = size / sizeof(struct trace_graph_item), i;
+
+	printf("   Total (s)   Per Call (ms)   Calls   Name\n");
+	for (i = 0; i < sz; i++)
+		prepare_stat_tree(item++);
+	list_sort(NULL, &stat_list, stat_list_cmp);
+	stat_list_print();
+}
+
+static int graph_stat(int argc, char **argv)
+{
+	struct stat st;
+	void *map = map_trace_file(&st);
+
+	if (!map)
+		return EXIT_FAILURE;
+
+	stat_trace_file(map, st.st_size);
+	munmap(map, st.st_size);
+	return EXIT_SUCCESS;
+}
+
 static int trace_parser(int ch, char *opt)
 {
 	return 0;
@@ -234,6 +357,8 @@ static int trace_parser(int ch, char *opt)
 static struct subcommand graph_cmd[] = {
 	{"cat", NULL, NULL, "cat the output of graph tracer",
 	 NULL, 0, graph_cat},
+	{"stat", NULL, NULL, "get the stat of the graph calls",
+	 NULL, 0, graph_stat},
 	{NULL,},
 };
 
@@ -250,8 +375,8 @@ static struct subcommand trace_cmd[] = {
 	 CMD_NEED_ARG, trace_disable},
 	{"status", NULL, "aph", "show tracer statuses", NULL,
 	 0, trace_status},
-	{"graph", NULL, "aph", "cat the output of tracers if any", graph_cmd,
-	 CMD_NEED_ARG, trace_graph},
+	{"graph", NULL, "aph", "run collie trace graph for more information",
+	 graph_cmd, CMD_NEED_ARG, trace_graph},
 	{NULL},
 };
 
diff --git a/include/sheep.h b/include/sheep.h
index 7a98ce7..1d146bd 100644
--- a/include/sheep.h
+++ b/include/sheep.h
@@ -47,8 +47,8 @@ struct trace_graph_item {
 	int type;
 	char fname[TRACE_FNAME_LEN];
 	int depth;
-	unsigned long long entry_time;
-	unsigned long long return_time;
+	uint64_t entry_time;
+	uint64_t return_time;
 };
 
 static inline void sd_init_req(struct sd_req *req, uint8_t opcode)
diff --git a/sheep/trace/graph.c b/sheep/trace/graph.c
index 33c6365..554668b 100644
--- a/sheep/trace/graph.c
+++ b/sheep/trace/graph.c
@@ -24,6 +24,7 @@ static void graph_tracer_exit(const struct caller *this_fn, int depth)
 		.return_time = clock_get_time(),
 	};
 
+	pstrcpy(trace.fname, sizeof(trace.fname), this_fn->name);
 	get_thread_name(trace.tname);
 
 	trace_buffer_push(sched_getcpu(), &trace);
-- 
1.7.9.5




More information about the sheepdog mailing list