perf header: Add HEADER_GROUP_DESC feature
Save group relationship information so that it can be restored when perf report is running. Signed-off-by: Namhyung Kim <namhyung@kernel.org> Acked-by: Jiri Olsa <jolsa@redhat.com> Cc: Ingo Molnar <mingo@kernel.org> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Stephane Eranian <eranian@google.com> Link: http://lkml.kernel.org/r/1358845787-1350-4-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
		
					parent
					
						
							
								8d7d8474d7
							
						
					
				
			
			
				commit
				
					
						a8bb559bd4
					
				
			
		
					 3 changed files with 169 additions and 0 deletions
				
			
		|  | @ -486,6 +486,9 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
| 		goto out_delete_session; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!evsel_list->nr_groups) | ||||
| 		perf_header__clear_feat(&session->header, HEADER_GROUP_DESC); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * perf_session__delete(session) will be called at perf_record__exit() | ||||
| 	 */ | ||||
|  |  | |||
|  | @ -1084,6 +1084,52 @@ static int write_pmu_mappings(int fd, struct perf_header *h __maybe_unused, | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * File format: | ||||
|  * | ||||
|  * struct group_descs { | ||||
|  *	u32	nr_groups; | ||||
|  *	struct group_desc { | ||||
|  *		char	name[]; | ||||
|  *		u32	leader_idx; | ||||
|  *		u32	nr_members; | ||||
|  *	}[nr_groups]; | ||||
|  * }; | ||||
|  */ | ||||
| static int write_group_desc(int fd, struct perf_header *h __maybe_unused, | ||||
| 			    struct perf_evlist *evlist) | ||||
| { | ||||
| 	u32 nr_groups = evlist->nr_groups; | ||||
| 	struct perf_evsel *evsel; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = do_write(fd, &nr_groups, sizeof(nr_groups)); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	list_for_each_entry(evsel, &evlist->entries, node) { | ||||
| 		if (perf_evsel__is_group_leader(evsel) && | ||||
| 		    evsel->nr_members > 1) { | ||||
| 			const char *name = evsel->group_name ?: "{anon_group}"; | ||||
| 			u32 leader_idx = evsel->idx; | ||||
| 			u32 nr_members = evsel->nr_members; | ||||
| 
 | ||||
| 			ret = do_write_string(fd, name); | ||||
| 			if (ret < 0) | ||||
| 				return ret; | ||||
| 
 | ||||
| 			ret = do_write(fd, &leader_idx, sizeof(leader_idx)); | ||||
| 			if (ret < 0) | ||||
| 				return ret; | ||||
| 
 | ||||
| 			ret = do_write(fd, &nr_members, sizeof(nr_members)); | ||||
| 			if (ret < 0) | ||||
| 				return ret; | ||||
| 		} | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * default get_cpuid(): nothing gets recorded | ||||
|  * actual implementation must be in arch/$(ARCH)/util/header.c | ||||
|  | @ -1447,6 +1493,31 @@ error: | |||
| 	fprintf(fp, "# pmu mappings: unable to read\n"); | ||||
| } | ||||
| 
 | ||||
| static void print_group_desc(struct perf_header *ph, int fd __maybe_unused, | ||||
| 			     FILE *fp) | ||||
| { | ||||
| 	struct perf_session *session; | ||||
| 	struct perf_evsel *evsel; | ||||
| 	u32 nr = 0; | ||||
| 
 | ||||
| 	session = container_of(ph, struct perf_session, header); | ||||
| 
 | ||||
| 	list_for_each_entry(evsel, &session->evlist->entries, node) { | ||||
| 		if (perf_evsel__is_group_leader(evsel) && | ||||
| 		    evsel->nr_members > 1) { | ||||
| 			fprintf(fp, "# group: %s{%s", evsel->group_name ?: "", | ||||
| 				perf_evsel__name(evsel)); | ||||
| 
 | ||||
| 			nr = evsel->nr_members - 1; | ||||
| 		} else if (nr) { | ||||
| 			fprintf(fp, ",%s", perf_evsel__name(evsel)); | ||||
| 
 | ||||
| 			if (--nr == 0) | ||||
| 				fprintf(fp, "}\n"); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int __event_process_build_id(struct build_id_event *bev, | ||||
| 				    char *filename, | ||||
| 				    struct perf_session *session) | ||||
|  | @ -1961,6 +2032,98 @@ error: | |||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| static int process_group_desc(struct perf_file_section *section __maybe_unused, | ||||
| 			      struct perf_header *ph, int fd, | ||||
| 			      void *data __maybe_unused) | ||||
| { | ||||
| 	size_t ret = -1; | ||||
| 	u32 i, nr, nr_groups; | ||||
| 	struct perf_session *session; | ||||
| 	struct perf_evsel *evsel, *leader = NULL; | ||||
| 	struct group_desc { | ||||
| 		char *name; | ||||
| 		u32 leader_idx; | ||||
| 		u32 nr_members; | ||||
| 	} *desc; | ||||
| 
 | ||||
| 	if (readn(fd, &nr_groups, sizeof(nr_groups)) != sizeof(nr_groups)) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	if (ph->needs_swap) | ||||
| 		nr_groups = bswap_32(nr_groups); | ||||
| 
 | ||||
| 	ph->env.nr_groups = nr_groups; | ||||
| 	if (!nr_groups) { | ||||
| 		pr_debug("group desc not available\n"); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	desc = calloc(nr_groups, sizeof(*desc)); | ||||
| 	if (!desc) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	for (i = 0; i < nr_groups; i++) { | ||||
| 		desc[i].name = do_read_string(fd, ph); | ||||
| 		if (!desc[i].name) | ||||
| 			goto out_free; | ||||
| 
 | ||||
| 		if (readn(fd, &desc[i].leader_idx, sizeof(u32)) != sizeof(u32)) | ||||
| 			goto out_free; | ||||
| 
 | ||||
| 		if (readn(fd, &desc[i].nr_members, sizeof(u32)) != sizeof(u32)) | ||||
| 			goto out_free; | ||||
| 
 | ||||
| 		if (ph->needs_swap) { | ||||
| 			desc[i].leader_idx = bswap_32(desc[i].leader_idx); | ||||
| 			desc[i].nr_members = bswap_32(desc[i].nr_members); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Rebuild group relationship based on the group_desc | ||||
| 	 */ | ||||
| 	session = container_of(ph, struct perf_session, header); | ||||
| 	session->evlist->nr_groups = nr_groups; | ||||
| 
 | ||||
| 	i = nr = 0; | ||||
| 	list_for_each_entry(evsel, &session->evlist->entries, node) { | ||||
| 		if (evsel->idx == (int) desc[i].leader_idx) { | ||||
| 			evsel->leader = evsel; | ||||
| 			/* {anon_group} is a dummy name */ | ||||
| 			if (strcmp(desc[i].name, "{anon_group}")) | ||||
| 				evsel->group_name = desc[i].name; | ||||
| 			evsel->nr_members = desc[i].nr_members; | ||||
| 
 | ||||
| 			if (i >= nr_groups || nr > 0) { | ||||
| 				pr_debug("invalid group desc\n"); | ||||
| 				goto out_free; | ||||
| 			} | ||||
| 
 | ||||
| 			leader = evsel; | ||||
| 			nr = evsel->nr_members - 1; | ||||
| 			i++; | ||||
| 		} else if (nr) { | ||||
| 			/* This is a group member */ | ||||
| 			evsel->leader = leader; | ||||
| 
 | ||||
| 			nr--; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (i != nr_groups || nr != 0) { | ||||
| 		pr_debug("invalid group desc\n"); | ||||
| 		goto out_free; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = 0; | ||||
| out_free: | ||||
| 	while ((int) --i >= 0) | ||||
| 		free(desc[i].name); | ||||
| 	free(desc); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| struct feature_ops { | ||||
| 	int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); | ||||
| 	void (*print)(struct perf_header *h, int fd, FILE *fp); | ||||
|  | @ -2000,6 +2163,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { | |||
| 	FEAT_OPF(HEADER_NUMA_TOPOLOGY,	numa_topology), | ||||
| 	FEAT_OPA(HEADER_BRANCH_STACK,	branch_stack), | ||||
| 	FEAT_OPP(HEADER_PMU_MAPPINGS,	pmu_mappings), | ||||
| 	FEAT_OPP(HEADER_GROUP_DESC,	group_desc), | ||||
| }; | ||||
| 
 | ||||
| struct header_print_data { | ||||
|  |  | |||
|  | @ -29,6 +29,7 @@ enum { | |||
| 	HEADER_NUMA_TOPOLOGY, | ||||
| 	HEADER_BRANCH_STACK, | ||||
| 	HEADER_PMU_MAPPINGS, | ||||
| 	HEADER_GROUP_DESC, | ||||
| 	HEADER_LAST_FEATURE, | ||||
| 	HEADER_FEAT_BITS	= 256, | ||||
| }; | ||||
|  | @ -79,6 +80,7 @@ struct perf_session_env { | |||
| 	char			*numa_nodes; | ||||
| 	int			nr_pmu_mappings; | ||||
| 	char			*pmu_mappings; | ||||
| 	int			nr_groups; | ||||
| }; | ||||
| 
 | ||||
| struct perf_header { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Namhyung Kim
				Namhyung Kim