diff options
Diffstat (limited to 'tools/perf/util/header.c')
-rw-r--r-- | tools/perf/util/header.c | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index f50b7235ecb6..73e38e472ecd 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -23,6 +23,8 @@ | |||
23 | #include "strbuf.h" | 23 | #include "strbuf.h" |
24 | #include "build-id.h" | 24 | #include "build-id.h" |
25 | #include "data.h" | 25 | #include "data.h" |
26 | #include <api/fs/fs.h> | ||
27 | #include "asm/bug.h" | ||
26 | 28 | ||
27 | /* | 29 | /* |
28 | * magic2 = "PERFILE2" | 30 | * magic2 = "PERFILE2" |
@@ -868,6 +870,199 @@ static int write_auxtrace(int fd, struct perf_header *h, | |||
868 | return err; | 870 | return err; |
869 | } | 871 | } |
870 | 872 | ||
873 | static int cpu_cache_level__sort(const void *a, const void *b) | ||
874 | { | ||
875 | struct cpu_cache_level *cache_a = (struct cpu_cache_level *)a; | ||
876 | struct cpu_cache_level *cache_b = (struct cpu_cache_level *)b; | ||
877 | |||
878 | return cache_a->level - cache_b->level; | ||
879 | } | ||
880 | |||
881 | static bool cpu_cache_level__cmp(struct cpu_cache_level *a, struct cpu_cache_level *b) | ||
882 | { | ||
883 | if (a->level != b->level) | ||
884 | return false; | ||
885 | |||
886 | if (a->line_size != b->line_size) | ||
887 | return false; | ||
888 | |||
889 | if (a->sets != b->sets) | ||
890 | return false; | ||
891 | |||
892 | if (a->ways != b->ways) | ||
893 | return false; | ||
894 | |||
895 | if (strcmp(a->type, b->type)) | ||
896 | return false; | ||
897 | |||
898 | if (strcmp(a->size, b->size)) | ||
899 | return false; | ||
900 | |||
901 | if (strcmp(a->map, b->map)) | ||
902 | return false; | ||
903 | |||
904 | return true; | ||
905 | } | ||
906 | |||
907 | static int cpu_cache_level__read(struct cpu_cache_level *cache, u32 cpu, u16 level) | ||
908 | { | ||
909 | char path[PATH_MAX], file[PATH_MAX]; | ||
910 | struct stat st; | ||
911 | size_t len; | ||
912 | |||
913 | scnprintf(path, PATH_MAX, "devices/system/cpu/cpu%d/cache/index%d/", cpu, level); | ||
914 | scnprintf(file, PATH_MAX, "%s/%s", sysfs__mountpoint(), path); | ||
915 | |||
916 | if (stat(file, &st)) | ||
917 | return 1; | ||
918 | |||
919 | scnprintf(file, PATH_MAX, "%s/level", path); | ||
920 | if (sysfs__read_int(file, (int *) &cache->level)) | ||
921 | return -1; | ||
922 | |||
923 | scnprintf(file, PATH_MAX, "%s/coherency_line_size", path); | ||
924 | if (sysfs__read_int(file, (int *) &cache->line_size)) | ||
925 | return -1; | ||
926 | |||
927 | scnprintf(file, PATH_MAX, "%s/number_of_sets", path); | ||
928 | if (sysfs__read_int(file, (int *) &cache->sets)) | ||
929 | return -1; | ||
930 | |||
931 | scnprintf(file, PATH_MAX, "%s/ways_of_associativity", path); | ||
932 | if (sysfs__read_int(file, (int *) &cache->ways)) | ||
933 | return -1; | ||
934 | |||
935 | scnprintf(file, PATH_MAX, "%s/type", path); | ||
936 | if (sysfs__read_str(file, &cache->type, &len)) | ||
937 | return -1; | ||
938 | |||
939 | cache->type[len] = 0; | ||
940 | cache->type = rtrim(cache->type); | ||
941 | |||
942 | scnprintf(file, PATH_MAX, "%s/size", path); | ||
943 | if (sysfs__read_str(file, &cache->size, &len)) { | ||
944 | free(cache->type); | ||
945 | return -1; | ||
946 | } | ||
947 | |||
948 | cache->size[len] = 0; | ||
949 | cache->size = rtrim(cache->size); | ||
950 | |||
951 | scnprintf(file, PATH_MAX, "%s/shared_cpu_list", path); | ||
952 | if (sysfs__read_str(file, &cache->map, &len)) { | ||
953 | free(cache->map); | ||
954 | free(cache->type); | ||
955 | return -1; | ||
956 | } | ||
957 | |||
958 | cache->map[len] = 0; | ||
959 | cache->map = rtrim(cache->map); | ||
960 | return 0; | ||
961 | } | ||
962 | |||
963 | static void cpu_cache_level__fprintf(FILE *out, struct cpu_cache_level *c) | ||
964 | { | ||
965 | fprintf(out, "L%d %-15s %8s [%s]\n", c->level, c->type, c->size, c->map); | ||
966 | } | ||
967 | |||
968 | static int build_caches(struct cpu_cache_level caches[], u32 size, u32 *cntp) | ||
969 | { | ||
970 | u32 i, cnt = 0; | ||
971 | long ncpus; | ||
972 | u32 nr, cpu; | ||
973 | u16 level; | ||
974 | |||
975 | ncpus = sysconf(_SC_NPROCESSORS_CONF); | ||
976 | if (ncpus < 0) | ||
977 | return -1; | ||
978 | |||
979 | nr = (u32)(ncpus & UINT_MAX); | ||
980 | |||
981 | for (cpu = 0; cpu < nr; cpu++) { | ||
982 | for (level = 0; level < 10; level++) { | ||
983 | struct cpu_cache_level c; | ||
984 | int err; | ||
985 | |||
986 | err = cpu_cache_level__read(&c, cpu, level); | ||
987 | if (err < 0) | ||
988 | return err; | ||
989 | |||
990 | if (err == 1) | ||
991 | break; | ||
992 | |||
993 | for (i = 0; i < cnt; i++) { | ||
994 | if (cpu_cache_level__cmp(&c, &caches[i])) | ||
995 | break; | ||
996 | } | ||
997 | |||
998 | if (i == cnt) | ||
999 | caches[cnt++] = c; | ||
1000 | else | ||
1001 | cpu_cache_level__free(&c); | ||
1002 | |||
1003 | if (WARN_ONCE(cnt == size, "way too many cpu caches..")) | ||
1004 | goto out; | ||
1005 | } | ||
1006 | } | ||
1007 | out: | ||
1008 | *cntp = cnt; | ||
1009 | return 0; | ||
1010 | } | ||
1011 | |||
1012 | #define MAX_CACHES 2000 | ||
1013 | |||
1014 | static int write_cache(int fd, struct perf_header *h __maybe_unused, | ||
1015 | struct perf_evlist *evlist __maybe_unused) | ||
1016 | { | ||
1017 | struct cpu_cache_level caches[MAX_CACHES]; | ||
1018 | u32 cnt = 0, i, version = 1; | ||
1019 | int ret; | ||
1020 | |||
1021 | ret = build_caches(caches, MAX_CACHES, &cnt); | ||
1022 | if (ret) | ||
1023 | goto out; | ||
1024 | |||
1025 | qsort(&caches, cnt, sizeof(struct cpu_cache_level), cpu_cache_level__sort); | ||
1026 | |||
1027 | ret = do_write(fd, &version, sizeof(u32)); | ||
1028 | if (ret < 0) | ||
1029 | goto out; | ||
1030 | |||
1031 | ret = do_write(fd, &cnt, sizeof(u32)); | ||
1032 | if (ret < 0) | ||
1033 | goto out; | ||
1034 | |||
1035 | for (i = 0; i < cnt; i++) { | ||
1036 | struct cpu_cache_level *c = &caches[i]; | ||
1037 | |||
1038 | #define _W(v) \ | ||
1039 | ret = do_write(fd, &c->v, sizeof(u32)); \ | ||
1040 | if (ret < 0) \ | ||
1041 | goto out; | ||
1042 | |||
1043 | _W(level) | ||
1044 | _W(line_size) | ||
1045 | _W(sets) | ||
1046 | _W(ways) | ||
1047 | #undef _W | ||
1048 | |||
1049 | #define _W(v) \ | ||
1050 | ret = do_write_string(fd, (const char *) c->v); \ | ||
1051 | if (ret < 0) \ | ||
1052 | goto out; | ||
1053 | |||
1054 | _W(type) | ||
1055 | _W(size) | ||
1056 | _W(map) | ||
1057 | #undef _W | ||
1058 | } | ||
1059 | |||
1060 | out: | ||
1061 | for (i = 0; i < cnt; i++) | ||
1062 | cpu_cache_level__free(&caches[i]); | ||
1063 | return ret; | ||
1064 | } | ||
1065 | |||
871 | static int write_stat(int fd __maybe_unused, | 1066 | static int write_stat(int fd __maybe_unused, |
872 | struct perf_header *h __maybe_unused, | 1067 | struct perf_header *h __maybe_unused, |
873 | struct perf_evlist *evlist __maybe_unused) | 1068 | struct perf_evlist *evlist __maybe_unused) |
@@ -1172,6 +1367,18 @@ static void print_stat(struct perf_header *ph __maybe_unused, | |||
1172 | fprintf(fp, "# contains stat data\n"); | 1367 | fprintf(fp, "# contains stat data\n"); |
1173 | } | 1368 | } |
1174 | 1369 | ||
1370 | static void print_cache(struct perf_header *ph __maybe_unused, | ||
1371 | int fd __maybe_unused, FILE *fp __maybe_unused) | ||
1372 | { | ||
1373 | int i; | ||
1374 | |||
1375 | fprintf(fp, "# CPU cache info:\n"); | ||
1376 | for (i = 0; i < ph->env.caches_cnt; i++) { | ||
1377 | fprintf(fp, "# "); | ||
1378 | cpu_cache_level__fprintf(fp, &ph->env.caches[i]); | ||
1379 | } | ||
1380 | } | ||
1381 | |||
1175 | static void print_pmu_mappings(struct perf_header *ph, int fd __maybe_unused, | 1382 | static void print_pmu_mappings(struct perf_header *ph, int fd __maybe_unused, |
1176 | FILE *fp) | 1383 | FILE *fp) |
1177 | { | 1384 | { |
@@ -1920,6 +2127,68 @@ static int process_auxtrace(struct perf_file_section *section, | |||
1920 | return err; | 2127 | return err; |
1921 | } | 2128 | } |
1922 | 2129 | ||
2130 | static int process_cache(struct perf_file_section *section __maybe_unused, | ||
2131 | struct perf_header *ph __maybe_unused, int fd __maybe_unused, | ||
2132 | void *data __maybe_unused) | ||
2133 | { | ||
2134 | struct cpu_cache_level *caches; | ||
2135 | u32 cnt, i, version; | ||
2136 | |||
2137 | if (readn(fd, &version, sizeof(version)) != sizeof(version)) | ||
2138 | return -1; | ||
2139 | |||
2140 | if (ph->needs_swap) | ||
2141 | version = bswap_32(version); | ||
2142 | |||
2143 | if (version != 1) | ||
2144 | return -1; | ||
2145 | |||
2146 | if (readn(fd, &cnt, sizeof(cnt)) != sizeof(cnt)) | ||
2147 | return -1; | ||
2148 | |||
2149 | if (ph->needs_swap) | ||
2150 | cnt = bswap_32(cnt); | ||
2151 | |||
2152 | caches = zalloc(sizeof(*caches) * cnt); | ||
2153 | if (!caches) | ||
2154 | return -1; | ||
2155 | |||
2156 | for (i = 0; i < cnt; i++) { | ||
2157 | struct cpu_cache_level c; | ||
2158 | |||
2159 | #define _R(v) \ | ||
2160 | if (readn(fd, &c.v, sizeof(u32)) != sizeof(u32))\ | ||
2161 | goto out_free_caches; \ | ||
2162 | if (ph->needs_swap) \ | ||
2163 | c.v = bswap_32(c.v); \ | ||
2164 | |||
2165 | _R(level) | ||
2166 | _R(line_size) | ||
2167 | _R(sets) | ||
2168 | _R(ways) | ||
2169 | #undef _R | ||
2170 | |||
2171 | #define _R(v) \ | ||
2172 | c.v = do_read_string(fd, ph); \ | ||
2173 | if (!c.v) \ | ||
2174 | goto out_free_caches; | ||
2175 | |||
2176 | _R(type) | ||
2177 | _R(size) | ||
2178 | _R(map) | ||
2179 | #undef _R | ||
2180 | |||
2181 | caches[i] = c; | ||
2182 | } | ||
2183 | |||
2184 | ph->env.caches = caches; | ||
2185 | ph->env.caches_cnt = cnt; | ||
2186 | return 0; | ||
2187 | out_free_caches: | ||
2188 | free(caches); | ||
2189 | return -1; | ||
2190 | } | ||
2191 | |||
1923 | struct feature_ops { | 2192 | struct feature_ops { |
1924 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); | 2193 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); |
1925 | void (*print)(struct perf_header *h, int fd, FILE *fp); | 2194 | void (*print)(struct perf_header *h, int fd, FILE *fp); |
@@ -1962,6 +2231,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { | |||
1962 | FEAT_OPP(HEADER_GROUP_DESC, group_desc), | 2231 | FEAT_OPP(HEADER_GROUP_DESC, group_desc), |
1963 | FEAT_OPP(HEADER_AUXTRACE, auxtrace), | 2232 | FEAT_OPP(HEADER_AUXTRACE, auxtrace), |
1964 | FEAT_OPA(HEADER_STAT, stat), | 2233 | FEAT_OPA(HEADER_STAT, stat), |
2234 | FEAT_OPF(HEADER_CACHE, cache), | ||
1965 | }; | 2235 | }; |
1966 | 2236 | ||
1967 | struct header_print_data { | 2237 | struct header_print_data { |