diff options
author | Namhyung Kim <namhyung.kim@lge.com> | 2013-01-22 04:09:31 -0500 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2013-01-31 11:07:44 -0500 |
commit | a8bb559bd4eff5c71601e2e61a4bd1deef44a03c (patch) | |
tree | 2e1f82e4e6024aa66e10b7a73241ff25e0d10105 /tools/perf | |
parent | 8d7d8474d7b04dc89aa653d67425b61d3ff5c6f0 (diff) |
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>
Diffstat (limited to 'tools/perf')
-rw-r--r-- | tools/perf/builtin-record.c | 3 | ||||
-rw-r--r-- | tools/perf/util/header.c | 164 | ||||
-rw-r--r-- | tools/perf/util/header.h | 2 |
3 files changed, 169 insertions, 0 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 2ac690cad411..774c90713a53 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -486,6 +486,9 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
486 | goto out_delete_session; | 486 | goto out_delete_session; |
487 | } | 487 | } |
488 | 488 | ||
489 | if (!evsel_list->nr_groups) | ||
490 | perf_header__clear_feat(&session->header, HEADER_GROUP_DESC); | ||
491 | |||
489 | /* | 492 | /* |
490 | * perf_session__delete(session) will be called at perf_record__exit() | 493 | * perf_session__delete(session) will be called at perf_record__exit() |
491 | */ | 494 | */ |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index f6081cb3fca3..8022b5254f75 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -1085,6 +1085,52 @@ static int write_pmu_mappings(int fd, struct perf_header *h __maybe_unused, | |||
1085 | } | 1085 | } |
1086 | 1086 | ||
1087 | /* | 1087 | /* |
1088 | * File format: | ||
1089 | * | ||
1090 | * struct group_descs { | ||
1091 | * u32 nr_groups; | ||
1092 | * struct group_desc { | ||
1093 | * char name[]; | ||
1094 | * u32 leader_idx; | ||
1095 | * u32 nr_members; | ||
1096 | * }[nr_groups]; | ||
1097 | * }; | ||
1098 | */ | ||
1099 | static int write_group_desc(int fd, struct perf_header *h __maybe_unused, | ||
1100 | struct perf_evlist *evlist) | ||
1101 | { | ||
1102 | u32 nr_groups = evlist->nr_groups; | ||
1103 | struct perf_evsel *evsel; | ||
1104 | int ret; | ||
1105 | |||
1106 | ret = do_write(fd, &nr_groups, sizeof(nr_groups)); | ||
1107 | if (ret < 0) | ||
1108 | return ret; | ||
1109 | |||
1110 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
1111 | if (perf_evsel__is_group_leader(evsel) && | ||
1112 | evsel->nr_members > 1) { | ||
1113 | const char *name = evsel->group_name ?: "{anon_group}"; | ||
1114 | u32 leader_idx = evsel->idx; | ||
1115 | u32 nr_members = evsel->nr_members; | ||
1116 | |||
1117 | ret = do_write_string(fd, name); | ||
1118 | if (ret < 0) | ||
1119 | return ret; | ||
1120 | |||
1121 | ret = do_write(fd, &leader_idx, sizeof(leader_idx)); | ||
1122 | if (ret < 0) | ||
1123 | return ret; | ||
1124 | |||
1125 | ret = do_write(fd, &nr_members, sizeof(nr_members)); | ||
1126 | if (ret < 0) | ||
1127 | return ret; | ||
1128 | } | ||
1129 | } | ||
1130 | return 0; | ||
1131 | } | ||
1132 | |||
1133 | /* | ||
1088 | * default get_cpuid(): nothing gets recorded | 1134 | * default get_cpuid(): nothing gets recorded |
1089 | * actual implementation must be in arch/$(ARCH)/util/header.c | 1135 | * actual implementation must be in arch/$(ARCH)/util/header.c |
1090 | */ | 1136 | */ |
@@ -1447,6 +1493,31 @@ error: | |||
1447 | fprintf(fp, "# pmu mappings: unable to read\n"); | 1493 | fprintf(fp, "# pmu mappings: unable to read\n"); |
1448 | } | 1494 | } |
1449 | 1495 | ||
1496 | static void print_group_desc(struct perf_header *ph, int fd __maybe_unused, | ||
1497 | FILE *fp) | ||
1498 | { | ||
1499 | struct perf_session *session; | ||
1500 | struct perf_evsel *evsel; | ||
1501 | u32 nr = 0; | ||
1502 | |||
1503 | session = container_of(ph, struct perf_session, header); | ||
1504 | |||
1505 | list_for_each_entry(evsel, &session->evlist->entries, node) { | ||
1506 | if (perf_evsel__is_group_leader(evsel) && | ||
1507 | evsel->nr_members > 1) { | ||
1508 | fprintf(fp, "# group: %s{%s", evsel->group_name ?: "", | ||
1509 | perf_evsel__name(evsel)); | ||
1510 | |||
1511 | nr = evsel->nr_members - 1; | ||
1512 | } else if (nr) { | ||
1513 | fprintf(fp, ",%s", perf_evsel__name(evsel)); | ||
1514 | |||
1515 | if (--nr == 0) | ||
1516 | fprintf(fp, "}\n"); | ||
1517 | } | ||
1518 | } | ||
1519 | } | ||
1520 | |||
1450 | static int __event_process_build_id(struct build_id_event *bev, | 1521 | static int __event_process_build_id(struct build_id_event *bev, |
1451 | char *filename, | 1522 | char *filename, |
1452 | struct perf_session *session) | 1523 | struct perf_session *session) |
@@ -1961,6 +2032,98 @@ error: | |||
1961 | return -1; | 2032 | return -1; |
1962 | } | 2033 | } |
1963 | 2034 | ||
2035 | static int process_group_desc(struct perf_file_section *section __maybe_unused, | ||
2036 | struct perf_header *ph, int fd, | ||
2037 | void *data __maybe_unused) | ||
2038 | { | ||
2039 | size_t ret = -1; | ||
2040 | u32 i, nr, nr_groups; | ||
2041 | struct perf_session *session; | ||
2042 | struct perf_evsel *evsel, *leader = NULL; | ||
2043 | struct group_desc { | ||
2044 | char *name; | ||
2045 | u32 leader_idx; | ||
2046 | u32 nr_members; | ||
2047 | } *desc; | ||
2048 | |||
2049 | if (readn(fd, &nr_groups, sizeof(nr_groups)) != sizeof(nr_groups)) | ||
2050 | return -1; | ||
2051 | |||
2052 | if (ph->needs_swap) | ||
2053 | nr_groups = bswap_32(nr_groups); | ||
2054 | |||
2055 | ph->env.nr_groups = nr_groups; | ||
2056 | if (!nr_groups) { | ||
2057 | pr_debug("group desc not available\n"); | ||
2058 | return 0; | ||
2059 | } | ||
2060 | |||
2061 | desc = calloc(nr_groups, sizeof(*desc)); | ||
2062 | if (!desc) | ||
2063 | return -1; | ||
2064 | |||
2065 | for (i = 0; i < nr_groups; i++) { | ||
2066 | desc[i].name = do_read_string(fd, ph); | ||
2067 | if (!desc[i].name) | ||
2068 | goto out_free; | ||
2069 | |||
2070 | if (readn(fd, &desc[i].leader_idx, sizeof(u32)) != sizeof(u32)) | ||
2071 | goto out_free; | ||
2072 | |||
2073 | if (readn(fd, &desc[i].nr_members, sizeof(u32)) != sizeof(u32)) | ||
2074 | goto out_free; | ||
2075 | |||
2076 | if (ph->needs_swap) { | ||
2077 | desc[i].leader_idx = bswap_32(desc[i].leader_idx); | ||
2078 | desc[i].nr_members = bswap_32(desc[i].nr_members); | ||
2079 | } | ||
2080 | } | ||
2081 | |||
2082 | /* | ||
2083 | * Rebuild group relationship based on the group_desc | ||
2084 | */ | ||
2085 | session = container_of(ph, struct perf_session, header); | ||
2086 | session->evlist->nr_groups = nr_groups; | ||
2087 | |||
2088 | i = nr = 0; | ||
2089 | list_for_each_entry(evsel, &session->evlist->entries, node) { | ||
2090 | if (evsel->idx == (int) desc[i].leader_idx) { | ||
2091 | evsel->leader = evsel; | ||
2092 | /* {anon_group} is a dummy name */ | ||
2093 | if (strcmp(desc[i].name, "{anon_group}")) | ||
2094 | evsel->group_name = desc[i].name; | ||
2095 | evsel->nr_members = desc[i].nr_members; | ||
2096 | |||
2097 | if (i >= nr_groups || nr > 0) { | ||
2098 | pr_debug("invalid group desc\n"); | ||
2099 | goto out_free; | ||
2100 | } | ||
2101 | |||
2102 | leader = evsel; | ||
2103 | nr = evsel->nr_members - 1; | ||
2104 | i++; | ||
2105 | } else if (nr) { | ||
2106 | /* This is a group member */ | ||
2107 | evsel->leader = leader; | ||
2108 | |||
2109 | nr--; | ||
2110 | } | ||
2111 | } | ||
2112 | |||
2113 | if (i != nr_groups || nr != 0) { | ||
2114 | pr_debug("invalid group desc\n"); | ||
2115 | goto out_free; | ||
2116 | } | ||
2117 | |||
2118 | ret = 0; | ||
2119 | out_free: | ||
2120 | while ((int) --i >= 0) | ||
2121 | free(desc[i].name); | ||
2122 | free(desc); | ||
2123 | |||
2124 | return ret; | ||
2125 | } | ||
2126 | |||
1964 | struct feature_ops { | 2127 | struct feature_ops { |
1965 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); | 2128 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); |
1966 | void (*print)(struct perf_header *h, int fd, FILE *fp); | 2129 | void (*print)(struct perf_header *h, int fd, FILE *fp); |
@@ -2000,6 +2163,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { | |||
2000 | FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology), | 2163 | FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology), |
2001 | FEAT_OPA(HEADER_BRANCH_STACK, branch_stack), | 2164 | FEAT_OPA(HEADER_BRANCH_STACK, branch_stack), |
2002 | FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings), | 2165 | FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings), |
2166 | FEAT_OPP(HEADER_GROUP_DESC, group_desc), | ||
2003 | }; | 2167 | }; |
2004 | 2168 | ||
2005 | struct header_print_data { | 2169 | struct header_print_data { |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 20f0344accb1..c9fc55cada6d 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -29,6 +29,7 @@ enum { | |||
29 | HEADER_NUMA_TOPOLOGY, | 29 | HEADER_NUMA_TOPOLOGY, |
30 | HEADER_BRANCH_STACK, | 30 | HEADER_BRANCH_STACK, |
31 | HEADER_PMU_MAPPINGS, | 31 | HEADER_PMU_MAPPINGS, |
32 | HEADER_GROUP_DESC, | ||
32 | HEADER_LAST_FEATURE, | 33 | HEADER_LAST_FEATURE, |
33 | HEADER_FEAT_BITS = 256, | 34 | HEADER_FEAT_BITS = 256, |
34 | }; | 35 | }; |
@@ -79,6 +80,7 @@ struct perf_session_env { | |||
79 | char *numa_nodes; | 80 | char *numa_nodes; |
80 | int nr_pmu_mappings; | 81 | int nr_pmu_mappings; |
81 | char *pmu_mappings; | 82 | char *pmu_mappings; |
83 | int nr_groups; | ||
82 | }; | 84 | }; |
83 | 85 | ||
84 | struct perf_header { | 86 | struct perf_header { |