diff options
Diffstat (limited to 'tools/perf/util/hist.c')
-rw-r--r-- | tools/perf/util/hist.c | 843 |
1 files changed, 751 insertions, 92 deletions
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index c226303e3da0..290b3cbf6877 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -131,6 +131,8 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) | |||
131 | symlen = unresolved_col_width + 4 + 2; | 131 | symlen = unresolved_col_width + 4 + 2; |
132 | hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, | 132 | hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, |
133 | symlen); | 133 | symlen); |
134 | hists__new_col_len(hists, HISTC_MEM_DCACHELINE, | ||
135 | symlen); | ||
134 | } | 136 | } |
135 | 137 | ||
136 | if (h->mem_info->iaddr.sym) { | 138 | if (h->mem_info->iaddr.sym) { |
@@ -177,6 +179,9 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) | |||
177 | if (h->transaction) | 179 | if (h->transaction) |
178 | hists__new_col_len(hists, HISTC_TRANSACTION, | 180 | hists__new_col_len(hists, HISTC_TRANSACTION, |
179 | hist_entry__transaction_len()); | 181 | hist_entry__transaction_len()); |
182 | |||
183 | if (h->trace_output) | ||
184 | hists__new_col_len(hists, HISTC_TRACE, strlen(h->trace_output)); | ||
180 | } | 185 | } |
181 | 186 | ||
182 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) | 187 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) |
@@ -243,6 +248,8 @@ static void he_stat__decay(struct he_stat *he_stat) | |||
243 | /* XXX need decay for weight too? */ | 248 | /* XXX need decay for weight too? */ |
244 | } | 249 | } |
245 | 250 | ||
251 | static void hists__delete_entry(struct hists *hists, struct hist_entry *he); | ||
252 | |||
246 | static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) | 253 | static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) |
247 | { | 254 | { |
248 | u64 prev_period = he->stat.period; | 255 | u64 prev_period = he->stat.period; |
@@ -258,21 +265,45 @@ static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) | |||
258 | 265 | ||
259 | diff = prev_period - he->stat.period; | 266 | diff = prev_period - he->stat.period; |
260 | 267 | ||
261 | hists->stats.total_period -= diff; | 268 | if (!he->depth) { |
262 | if (!he->filtered) | 269 | hists->stats.total_period -= diff; |
263 | hists->stats.total_non_filtered_period -= diff; | 270 | if (!he->filtered) |
271 | hists->stats.total_non_filtered_period -= diff; | ||
272 | } | ||
273 | |||
274 | if (!he->leaf) { | ||
275 | struct hist_entry *child; | ||
276 | struct rb_node *node = rb_first(&he->hroot_out); | ||
277 | while (node) { | ||
278 | child = rb_entry(node, struct hist_entry, rb_node); | ||
279 | node = rb_next(node); | ||
280 | |||
281 | if (hists__decay_entry(hists, child)) | ||
282 | hists__delete_entry(hists, child); | ||
283 | } | ||
284 | } | ||
264 | 285 | ||
265 | return he->stat.period == 0; | 286 | return he->stat.period == 0; |
266 | } | 287 | } |
267 | 288 | ||
268 | static void hists__delete_entry(struct hists *hists, struct hist_entry *he) | 289 | static void hists__delete_entry(struct hists *hists, struct hist_entry *he) |
269 | { | 290 | { |
270 | rb_erase(&he->rb_node, &hists->entries); | 291 | struct rb_root *root_in; |
292 | struct rb_root *root_out; | ||
271 | 293 | ||
272 | if (sort__need_collapse) | 294 | if (he->parent_he) { |
273 | rb_erase(&he->rb_node_in, &hists->entries_collapsed); | 295 | root_in = &he->parent_he->hroot_in; |
274 | else | 296 | root_out = &he->parent_he->hroot_out; |
275 | rb_erase(&he->rb_node_in, hists->entries_in); | 297 | } else { |
298 | if (sort__need_collapse) | ||
299 | root_in = &hists->entries_collapsed; | ||
300 | else | ||
301 | root_in = hists->entries_in; | ||
302 | root_out = &hists->entries; | ||
303 | } | ||
304 | |||
305 | rb_erase(&he->rb_node_in, root_in); | ||
306 | rb_erase(&he->rb_node, root_out); | ||
276 | 307 | ||
277 | --hists->nr_entries; | 308 | --hists->nr_entries; |
278 | if (!he->filtered) | 309 | if (!he->filtered) |
@@ -391,6 +422,9 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template, | |||
391 | } | 422 | } |
392 | INIT_LIST_HEAD(&he->pairs.node); | 423 | INIT_LIST_HEAD(&he->pairs.node); |
393 | thread__get(he->thread); | 424 | thread__get(he->thread); |
425 | |||
426 | if (!symbol_conf.report_hierarchy) | ||
427 | he->leaf = true; | ||
394 | } | 428 | } |
395 | 429 | ||
396 | return he; | 430 | return he; |
@@ -403,6 +437,16 @@ static u8 symbol__parent_filter(const struct symbol *parent) | |||
403 | return 0; | 437 | return 0; |
404 | } | 438 | } |
405 | 439 | ||
440 | static void hist_entry__add_callchain_period(struct hist_entry *he, u64 period) | ||
441 | { | ||
442 | if (!symbol_conf.use_callchain) | ||
443 | return; | ||
444 | |||
445 | he->hists->callchain_period += period; | ||
446 | if (!he->filtered) | ||
447 | he->hists->callchain_non_filtered_period += period; | ||
448 | } | ||
449 | |||
406 | static struct hist_entry *hists__findnew_entry(struct hists *hists, | 450 | static struct hist_entry *hists__findnew_entry(struct hists *hists, |
407 | struct hist_entry *entry, | 451 | struct hist_entry *entry, |
408 | struct addr_location *al, | 452 | struct addr_location *al, |
@@ -430,8 +474,10 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists, | |||
430 | cmp = hist_entry__cmp(he, entry); | 474 | cmp = hist_entry__cmp(he, entry); |
431 | 475 | ||
432 | if (!cmp) { | 476 | if (!cmp) { |
433 | if (sample_self) | 477 | if (sample_self) { |
434 | he_stat__add_period(&he->stat, period, weight); | 478 | he_stat__add_period(&he->stat, period, weight); |
479 | hist_entry__add_callchain_period(he, period); | ||
480 | } | ||
435 | if (symbol_conf.cumulate_callchain) | 481 | if (symbol_conf.cumulate_callchain) |
436 | he_stat__add_period(he->stat_acc, period, weight); | 482 | he_stat__add_period(he->stat_acc, period, weight); |
437 | 483 | ||
@@ -464,6 +510,8 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists, | |||
464 | if (!he) | 510 | if (!he) |
465 | return NULL; | 511 | return NULL; |
466 | 512 | ||
513 | if (sample_self) | ||
514 | hist_entry__add_callchain_period(he, period); | ||
467 | hists->nr_entries++; | 515 | hists->nr_entries++; |
468 | 516 | ||
469 | rb_link_node(&he->rb_node_in, parent, p); | 517 | rb_link_node(&he->rb_node_in, parent, p); |
@@ -949,10 +997,15 @@ out: | |||
949 | int64_t | 997 | int64_t |
950 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) | 998 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) |
951 | { | 999 | { |
1000 | struct hists *hists = left->hists; | ||
952 | struct perf_hpp_fmt *fmt; | 1001 | struct perf_hpp_fmt *fmt; |
953 | int64_t cmp = 0; | 1002 | int64_t cmp = 0; |
954 | 1003 | ||
955 | perf_hpp__for_each_sort_list(fmt) { | 1004 | hists__for_each_sort_list(hists, fmt) { |
1005 | if (perf_hpp__is_dynamic_entry(fmt) && | ||
1006 | !perf_hpp__defined_dynamic_entry(fmt, hists)) | ||
1007 | continue; | ||
1008 | |||
956 | cmp = fmt->cmp(fmt, left, right); | 1009 | cmp = fmt->cmp(fmt, left, right); |
957 | if (cmp) | 1010 | if (cmp) |
958 | break; | 1011 | break; |
@@ -964,10 +1017,15 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) | |||
964 | int64_t | 1017 | int64_t |
965 | hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | 1018 | hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) |
966 | { | 1019 | { |
1020 | struct hists *hists = left->hists; | ||
967 | struct perf_hpp_fmt *fmt; | 1021 | struct perf_hpp_fmt *fmt; |
968 | int64_t cmp = 0; | 1022 | int64_t cmp = 0; |
969 | 1023 | ||
970 | perf_hpp__for_each_sort_list(fmt) { | 1024 | hists__for_each_sort_list(hists, fmt) { |
1025 | if (perf_hpp__is_dynamic_entry(fmt) && | ||
1026 | !perf_hpp__defined_dynamic_entry(fmt, hists)) | ||
1027 | continue; | ||
1028 | |||
971 | cmp = fmt->collapse(fmt, left, right); | 1029 | cmp = fmt->collapse(fmt, left, right); |
972 | if (cmp) | 1030 | if (cmp) |
973 | break; | 1031 | break; |
@@ -1004,17 +1062,250 @@ void hist_entry__delete(struct hist_entry *he) | |||
1004 | } | 1062 | } |
1005 | 1063 | ||
1006 | /* | 1064 | /* |
1065 | * If this is not the last column, then we need to pad it according to the | ||
1066 | * pre-calculated max lenght for this column, otherwise don't bother adding | ||
1067 | * spaces because that would break viewing this with, for instance, 'less', | ||
1068 | * that would show tons of trailing spaces when a long C++ demangled method | ||
1069 | * names is sampled. | ||
1070 | */ | ||
1071 | int hist_entry__snprintf_alignment(struct hist_entry *he, struct perf_hpp *hpp, | ||
1072 | struct perf_hpp_fmt *fmt, int printed) | ||
1073 | { | ||
1074 | if (!list_is_last(&fmt->list, &he->hists->hpp_list->fields)) { | ||
1075 | const int width = fmt->width(fmt, hpp, hists_to_evsel(he->hists)); | ||
1076 | if (printed < width) { | ||
1077 | advance_hpp(hpp, printed); | ||
1078 | printed = scnprintf(hpp->buf, hpp->size, "%-*s", width - printed, " "); | ||
1079 | } | ||
1080 | } | ||
1081 | |||
1082 | return printed; | ||
1083 | } | ||
1084 | |||
1085 | /* | ||
1007 | * collapse the histogram | 1086 | * collapse the histogram |
1008 | */ | 1087 | */ |
1009 | 1088 | ||
1010 | bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, | 1089 | static void hists__apply_filters(struct hists *hists, struct hist_entry *he); |
1011 | struct rb_root *root, struct hist_entry *he) | 1090 | static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *he, |
1091 | enum hist_filter type); | ||
1092 | |||
1093 | typedef bool (*fmt_chk_fn)(struct perf_hpp_fmt *fmt); | ||
1094 | |||
1095 | static bool check_thread_entry(struct perf_hpp_fmt *fmt) | ||
1096 | { | ||
1097 | return perf_hpp__is_thread_entry(fmt) || perf_hpp__is_comm_entry(fmt); | ||
1098 | } | ||
1099 | |||
1100 | static void hist_entry__check_and_remove_filter(struct hist_entry *he, | ||
1101 | enum hist_filter type, | ||
1102 | fmt_chk_fn check) | ||
1103 | { | ||
1104 | struct perf_hpp_fmt *fmt; | ||
1105 | bool type_match = false; | ||
1106 | struct hist_entry *parent = he->parent_he; | ||
1107 | |||
1108 | switch (type) { | ||
1109 | case HIST_FILTER__THREAD: | ||
1110 | if (symbol_conf.comm_list == NULL && | ||
1111 | symbol_conf.pid_list == NULL && | ||
1112 | symbol_conf.tid_list == NULL) | ||
1113 | return; | ||
1114 | break; | ||
1115 | case HIST_FILTER__DSO: | ||
1116 | if (symbol_conf.dso_list == NULL) | ||
1117 | return; | ||
1118 | break; | ||
1119 | case HIST_FILTER__SYMBOL: | ||
1120 | if (symbol_conf.sym_list == NULL) | ||
1121 | return; | ||
1122 | break; | ||
1123 | case HIST_FILTER__PARENT: | ||
1124 | case HIST_FILTER__GUEST: | ||
1125 | case HIST_FILTER__HOST: | ||
1126 | case HIST_FILTER__SOCKET: | ||
1127 | default: | ||
1128 | return; | ||
1129 | } | ||
1130 | |||
1131 | /* if it's filtered by own fmt, it has to have filter bits */ | ||
1132 | perf_hpp_list__for_each_format(he->hpp_list, fmt) { | ||
1133 | if (check(fmt)) { | ||
1134 | type_match = true; | ||
1135 | break; | ||
1136 | } | ||
1137 | } | ||
1138 | |||
1139 | if (type_match) { | ||
1140 | /* | ||
1141 | * If the filter is for current level entry, propagate | ||
1142 | * filter marker to parents. The marker bit was | ||
1143 | * already set by default so it only needs to clear | ||
1144 | * non-filtered entries. | ||
1145 | */ | ||
1146 | if (!(he->filtered & (1 << type))) { | ||
1147 | while (parent) { | ||
1148 | parent->filtered &= ~(1 << type); | ||
1149 | parent = parent->parent_he; | ||
1150 | } | ||
1151 | } | ||
1152 | } else { | ||
1153 | /* | ||
1154 | * If current entry doesn't have matching formats, set | ||
1155 | * filter marker for upper level entries. it will be | ||
1156 | * cleared if its lower level entries is not filtered. | ||
1157 | * | ||
1158 | * For lower-level entries, it inherits parent's | ||
1159 | * filter bit so that lower level entries of a | ||
1160 | * non-filtered entry won't set the filter marker. | ||
1161 | */ | ||
1162 | if (parent == NULL) | ||
1163 | he->filtered |= (1 << type); | ||
1164 | else | ||
1165 | he->filtered |= (parent->filtered & (1 << type)); | ||
1166 | } | ||
1167 | } | ||
1168 | |||
1169 | static void hist_entry__apply_hierarchy_filters(struct hist_entry *he) | ||
1170 | { | ||
1171 | hist_entry__check_and_remove_filter(he, HIST_FILTER__THREAD, | ||
1172 | check_thread_entry); | ||
1173 | |||
1174 | hist_entry__check_and_remove_filter(he, HIST_FILTER__DSO, | ||
1175 | perf_hpp__is_dso_entry); | ||
1176 | |||
1177 | hist_entry__check_and_remove_filter(he, HIST_FILTER__SYMBOL, | ||
1178 | perf_hpp__is_sym_entry); | ||
1179 | |||
1180 | hists__apply_filters(he->hists, he); | ||
1181 | } | ||
1182 | |||
1183 | static struct hist_entry *hierarchy_insert_entry(struct hists *hists, | ||
1184 | struct rb_root *root, | ||
1185 | struct hist_entry *he, | ||
1186 | struct hist_entry *parent_he, | ||
1187 | struct perf_hpp_list *hpp_list) | ||
1188 | { | ||
1189 | struct rb_node **p = &root->rb_node; | ||
1190 | struct rb_node *parent = NULL; | ||
1191 | struct hist_entry *iter, *new; | ||
1192 | struct perf_hpp_fmt *fmt; | ||
1193 | int64_t cmp; | ||
1194 | |||
1195 | while (*p != NULL) { | ||
1196 | parent = *p; | ||
1197 | iter = rb_entry(parent, struct hist_entry, rb_node_in); | ||
1198 | |||
1199 | cmp = 0; | ||
1200 | perf_hpp_list__for_each_sort_list(hpp_list, fmt) { | ||
1201 | cmp = fmt->collapse(fmt, iter, he); | ||
1202 | if (cmp) | ||
1203 | break; | ||
1204 | } | ||
1205 | |||
1206 | if (!cmp) { | ||
1207 | he_stat__add_stat(&iter->stat, &he->stat); | ||
1208 | return iter; | ||
1209 | } | ||
1210 | |||
1211 | if (cmp < 0) | ||
1212 | p = &parent->rb_left; | ||
1213 | else | ||
1214 | p = &parent->rb_right; | ||
1215 | } | ||
1216 | |||
1217 | new = hist_entry__new(he, true); | ||
1218 | if (new == NULL) | ||
1219 | return NULL; | ||
1220 | |||
1221 | hists->nr_entries++; | ||
1222 | |||
1223 | /* save related format list for output */ | ||
1224 | new->hpp_list = hpp_list; | ||
1225 | new->parent_he = parent_he; | ||
1226 | |||
1227 | hist_entry__apply_hierarchy_filters(new); | ||
1228 | |||
1229 | /* some fields are now passed to 'new' */ | ||
1230 | perf_hpp_list__for_each_sort_list(hpp_list, fmt) { | ||
1231 | if (perf_hpp__is_trace_entry(fmt) || perf_hpp__is_dynamic_entry(fmt)) | ||
1232 | he->trace_output = NULL; | ||
1233 | else | ||
1234 | new->trace_output = NULL; | ||
1235 | |||
1236 | if (perf_hpp__is_srcline_entry(fmt)) | ||
1237 | he->srcline = NULL; | ||
1238 | else | ||
1239 | new->srcline = NULL; | ||
1240 | |||
1241 | if (perf_hpp__is_srcfile_entry(fmt)) | ||
1242 | he->srcfile = NULL; | ||
1243 | else | ||
1244 | new->srcfile = NULL; | ||
1245 | } | ||
1246 | |||
1247 | rb_link_node(&new->rb_node_in, parent, p); | ||
1248 | rb_insert_color(&new->rb_node_in, root); | ||
1249 | return new; | ||
1250 | } | ||
1251 | |||
1252 | static int hists__hierarchy_insert_entry(struct hists *hists, | ||
1253 | struct rb_root *root, | ||
1254 | struct hist_entry *he) | ||
1255 | { | ||
1256 | struct perf_hpp_list_node *node; | ||
1257 | struct hist_entry *new_he = NULL; | ||
1258 | struct hist_entry *parent = NULL; | ||
1259 | int depth = 0; | ||
1260 | int ret = 0; | ||
1261 | |||
1262 | list_for_each_entry(node, &hists->hpp_formats, list) { | ||
1263 | /* skip period (overhead) and elided columns */ | ||
1264 | if (node->level == 0 || node->skip) | ||
1265 | continue; | ||
1266 | |||
1267 | /* insert copy of 'he' for each fmt into the hierarchy */ | ||
1268 | new_he = hierarchy_insert_entry(hists, root, he, parent, &node->hpp); | ||
1269 | if (new_he == NULL) { | ||
1270 | ret = -1; | ||
1271 | break; | ||
1272 | } | ||
1273 | |||
1274 | root = &new_he->hroot_in; | ||
1275 | new_he->depth = depth++; | ||
1276 | parent = new_he; | ||
1277 | } | ||
1278 | |||
1279 | if (new_he) { | ||
1280 | new_he->leaf = true; | ||
1281 | |||
1282 | if (symbol_conf.use_callchain) { | ||
1283 | callchain_cursor_reset(&callchain_cursor); | ||
1284 | if (callchain_merge(&callchain_cursor, | ||
1285 | new_he->callchain, | ||
1286 | he->callchain) < 0) | ||
1287 | ret = -1; | ||
1288 | } | ||
1289 | } | ||
1290 | |||
1291 | /* 'he' is no longer used */ | ||
1292 | hist_entry__delete(he); | ||
1293 | |||
1294 | /* return 0 (or -1) since it already applied filters */ | ||
1295 | return ret; | ||
1296 | } | ||
1297 | |||
1298 | int hists__collapse_insert_entry(struct hists *hists, struct rb_root *root, | ||
1299 | struct hist_entry *he) | ||
1012 | { | 1300 | { |
1013 | struct rb_node **p = &root->rb_node; | 1301 | struct rb_node **p = &root->rb_node; |
1014 | struct rb_node *parent = NULL; | 1302 | struct rb_node *parent = NULL; |
1015 | struct hist_entry *iter; | 1303 | struct hist_entry *iter; |
1016 | int64_t cmp; | 1304 | int64_t cmp; |
1017 | 1305 | ||
1306 | if (symbol_conf.report_hierarchy) | ||
1307 | return hists__hierarchy_insert_entry(hists, root, he); | ||
1308 | |||
1018 | while (*p != NULL) { | 1309 | while (*p != NULL) { |
1019 | parent = *p; | 1310 | parent = *p; |
1020 | iter = rb_entry(parent, struct hist_entry, rb_node_in); | 1311 | iter = rb_entry(parent, struct hist_entry, rb_node_in); |
@@ -1022,18 +1313,21 @@ bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, | |||
1022 | cmp = hist_entry__collapse(iter, he); | 1313 | cmp = hist_entry__collapse(iter, he); |
1023 | 1314 | ||
1024 | if (!cmp) { | 1315 | if (!cmp) { |
1316 | int ret = 0; | ||
1317 | |||
1025 | he_stat__add_stat(&iter->stat, &he->stat); | 1318 | he_stat__add_stat(&iter->stat, &he->stat); |
1026 | if (symbol_conf.cumulate_callchain) | 1319 | if (symbol_conf.cumulate_callchain) |
1027 | he_stat__add_stat(iter->stat_acc, he->stat_acc); | 1320 | he_stat__add_stat(iter->stat_acc, he->stat_acc); |
1028 | 1321 | ||
1029 | if (symbol_conf.use_callchain) { | 1322 | if (symbol_conf.use_callchain) { |
1030 | callchain_cursor_reset(&callchain_cursor); | 1323 | callchain_cursor_reset(&callchain_cursor); |
1031 | callchain_merge(&callchain_cursor, | 1324 | if (callchain_merge(&callchain_cursor, |
1032 | iter->callchain, | 1325 | iter->callchain, |
1033 | he->callchain); | 1326 | he->callchain) < 0) |
1327 | ret = -1; | ||
1034 | } | 1328 | } |
1035 | hist_entry__delete(he); | 1329 | hist_entry__delete(he); |
1036 | return false; | 1330 | return ret; |
1037 | } | 1331 | } |
1038 | 1332 | ||
1039 | if (cmp < 0) | 1333 | if (cmp < 0) |
@@ -1045,7 +1339,7 @@ bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, | |||
1045 | 1339 | ||
1046 | rb_link_node(&he->rb_node_in, parent, p); | 1340 | rb_link_node(&he->rb_node_in, parent, p); |
1047 | rb_insert_color(&he->rb_node_in, root); | 1341 | rb_insert_color(&he->rb_node_in, root); |
1048 | return true; | 1342 | return 1; |
1049 | } | 1343 | } |
1050 | 1344 | ||
1051 | struct rb_root *hists__get_rotate_entries_in(struct hists *hists) | 1345 | struct rb_root *hists__get_rotate_entries_in(struct hists *hists) |
@@ -1071,14 +1365,15 @@ static void hists__apply_filters(struct hists *hists, struct hist_entry *he) | |||
1071 | hists__filter_entry_by_socket(hists, he); | 1365 | hists__filter_entry_by_socket(hists, he); |
1072 | } | 1366 | } |
1073 | 1367 | ||
1074 | void hists__collapse_resort(struct hists *hists, struct ui_progress *prog) | 1368 | int hists__collapse_resort(struct hists *hists, struct ui_progress *prog) |
1075 | { | 1369 | { |
1076 | struct rb_root *root; | 1370 | struct rb_root *root; |
1077 | struct rb_node *next; | 1371 | struct rb_node *next; |
1078 | struct hist_entry *n; | 1372 | struct hist_entry *n; |
1373 | int ret; | ||
1079 | 1374 | ||
1080 | if (!sort__need_collapse) | 1375 | if (!sort__need_collapse) |
1081 | return; | 1376 | return 0; |
1082 | 1377 | ||
1083 | hists->nr_entries = 0; | 1378 | hists->nr_entries = 0; |
1084 | 1379 | ||
@@ -1093,7 +1388,11 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog) | |||
1093 | next = rb_next(&n->rb_node_in); | 1388 | next = rb_next(&n->rb_node_in); |
1094 | 1389 | ||
1095 | rb_erase(&n->rb_node_in, root); | 1390 | rb_erase(&n->rb_node_in, root); |
1096 | if (hists__collapse_insert_entry(hists, &hists->entries_collapsed, n)) { | 1391 | ret = hists__collapse_insert_entry(hists, &hists->entries_collapsed, n); |
1392 | if (ret < 0) | ||
1393 | return -1; | ||
1394 | |||
1395 | if (ret) { | ||
1097 | /* | 1396 | /* |
1098 | * If it wasn't combined with one of the entries already | 1397 | * If it wasn't combined with one of the entries already |
1099 | * collapsed, we need to apply the filters that may have | 1398 | * collapsed, we need to apply the filters that may have |
@@ -1104,14 +1403,16 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog) | |||
1104 | if (prog) | 1403 | if (prog) |
1105 | ui_progress__update(prog, 1); | 1404 | ui_progress__update(prog, 1); |
1106 | } | 1405 | } |
1406 | return 0; | ||
1107 | } | 1407 | } |
1108 | 1408 | ||
1109 | static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b) | 1409 | static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b) |
1110 | { | 1410 | { |
1411 | struct hists *hists = a->hists; | ||
1111 | struct perf_hpp_fmt *fmt; | 1412 | struct perf_hpp_fmt *fmt; |
1112 | int64_t cmp = 0; | 1413 | int64_t cmp = 0; |
1113 | 1414 | ||
1114 | perf_hpp__for_each_sort_list(fmt) { | 1415 | hists__for_each_sort_list(hists, fmt) { |
1115 | if (perf_hpp__should_skip(fmt, a->hists)) | 1416 | if (perf_hpp__should_skip(fmt, a->hists)) |
1116 | continue; | 1417 | continue; |
1117 | 1418 | ||
@@ -1152,6 +1453,113 @@ void hists__inc_stats(struct hists *hists, struct hist_entry *h) | |||
1152 | hists->stats.total_period += h->stat.period; | 1453 | hists->stats.total_period += h->stat.period; |
1153 | } | 1454 | } |
1154 | 1455 | ||
1456 | static void hierarchy_recalc_total_periods(struct hists *hists) | ||
1457 | { | ||
1458 | struct rb_node *node; | ||
1459 | struct hist_entry *he; | ||
1460 | |||
1461 | node = rb_first(&hists->entries); | ||
1462 | |||
1463 | hists->stats.total_period = 0; | ||
1464 | hists->stats.total_non_filtered_period = 0; | ||
1465 | |||
1466 | /* | ||
1467 | * recalculate total period using top-level entries only | ||
1468 | * since lower level entries only see non-filtered entries | ||
1469 | * but upper level entries have sum of both entries. | ||
1470 | */ | ||
1471 | while (node) { | ||
1472 | he = rb_entry(node, struct hist_entry, rb_node); | ||
1473 | node = rb_next(node); | ||
1474 | |||
1475 | hists->stats.total_period += he->stat.period; | ||
1476 | if (!he->filtered) | ||
1477 | hists->stats.total_non_filtered_period += he->stat.period; | ||
1478 | } | ||
1479 | } | ||
1480 | |||
1481 | static void hierarchy_insert_output_entry(struct rb_root *root, | ||
1482 | struct hist_entry *he) | ||
1483 | { | ||
1484 | struct rb_node **p = &root->rb_node; | ||
1485 | struct rb_node *parent = NULL; | ||
1486 | struct hist_entry *iter; | ||
1487 | struct perf_hpp_fmt *fmt; | ||
1488 | |||
1489 | while (*p != NULL) { | ||
1490 | parent = *p; | ||
1491 | iter = rb_entry(parent, struct hist_entry, rb_node); | ||
1492 | |||
1493 | if (hist_entry__sort(he, iter) > 0) | ||
1494 | p = &parent->rb_left; | ||
1495 | else | ||
1496 | p = &parent->rb_right; | ||
1497 | } | ||
1498 | |||
1499 | rb_link_node(&he->rb_node, parent, p); | ||
1500 | rb_insert_color(&he->rb_node, root); | ||
1501 | |||
1502 | /* update column width of dynamic entry */ | ||
1503 | perf_hpp_list__for_each_sort_list(he->hpp_list, fmt) { | ||
1504 | if (perf_hpp__is_dynamic_entry(fmt)) | ||
1505 | fmt->sort(fmt, he, NULL); | ||
1506 | } | ||
1507 | } | ||
1508 | |||
1509 | static void hists__hierarchy_output_resort(struct hists *hists, | ||
1510 | struct ui_progress *prog, | ||
1511 | struct rb_root *root_in, | ||
1512 | struct rb_root *root_out, | ||
1513 | u64 min_callchain_hits, | ||
1514 | bool use_callchain) | ||
1515 | { | ||
1516 | struct rb_node *node; | ||
1517 | struct hist_entry *he; | ||
1518 | |||
1519 | *root_out = RB_ROOT; | ||
1520 | node = rb_first(root_in); | ||
1521 | |||
1522 | while (node) { | ||
1523 | he = rb_entry(node, struct hist_entry, rb_node_in); | ||
1524 | node = rb_next(node); | ||
1525 | |||
1526 | hierarchy_insert_output_entry(root_out, he); | ||
1527 | |||
1528 | if (prog) | ||
1529 | ui_progress__update(prog, 1); | ||
1530 | |||
1531 | if (!he->leaf) { | ||
1532 | hists__hierarchy_output_resort(hists, prog, | ||
1533 | &he->hroot_in, | ||
1534 | &he->hroot_out, | ||
1535 | min_callchain_hits, | ||
1536 | use_callchain); | ||
1537 | hists->nr_entries++; | ||
1538 | if (!he->filtered) { | ||
1539 | hists->nr_non_filtered_entries++; | ||
1540 | hists__calc_col_len(hists, he); | ||
1541 | } | ||
1542 | |||
1543 | continue; | ||
1544 | } | ||
1545 | |||
1546 | if (!use_callchain) | ||
1547 | continue; | ||
1548 | |||
1549 | if (callchain_param.mode == CHAIN_GRAPH_REL) { | ||
1550 | u64 total = he->stat.period; | ||
1551 | |||
1552 | if (symbol_conf.cumulate_callchain) | ||
1553 | total = he->stat_acc->period; | ||
1554 | |||
1555 | min_callchain_hits = total * (callchain_param.min_percent / 100); | ||
1556 | } | ||
1557 | |||
1558 | callchain_param.sort(&he->sorted_chain, he->callchain, | ||
1559 | min_callchain_hits, &callchain_param); | ||
1560 | } | ||
1561 | } | ||
1562 | |||
1155 | static void __hists__insert_output_entry(struct rb_root *entries, | 1563 | static void __hists__insert_output_entry(struct rb_root *entries, |
1156 | struct hist_entry *he, | 1564 | struct hist_entry *he, |
1157 | u64 min_callchain_hits, | 1565 | u64 min_callchain_hits, |
@@ -1160,10 +1568,20 @@ static void __hists__insert_output_entry(struct rb_root *entries, | |||
1160 | struct rb_node **p = &entries->rb_node; | 1568 | struct rb_node **p = &entries->rb_node; |
1161 | struct rb_node *parent = NULL; | 1569 | struct rb_node *parent = NULL; |
1162 | struct hist_entry *iter; | 1570 | struct hist_entry *iter; |
1571 | struct perf_hpp_fmt *fmt; | ||
1572 | |||
1573 | if (use_callchain) { | ||
1574 | if (callchain_param.mode == CHAIN_GRAPH_REL) { | ||
1575 | u64 total = he->stat.period; | ||
1163 | 1576 | ||
1164 | if (use_callchain) | 1577 | if (symbol_conf.cumulate_callchain) |
1578 | total = he->stat_acc->period; | ||
1579 | |||
1580 | min_callchain_hits = total * (callchain_param.min_percent / 100); | ||
1581 | } | ||
1165 | callchain_param.sort(&he->sorted_chain, he->callchain, | 1582 | callchain_param.sort(&he->sorted_chain, he->callchain, |
1166 | min_callchain_hits, &callchain_param); | 1583 | min_callchain_hits, &callchain_param); |
1584 | } | ||
1167 | 1585 | ||
1168 | while (*p != NULL) { | 1586 | while (*p != NULL) { |
1169 | parent = *p; | 1587 | parent = *p; |
@@ -1177,23 +1595,41 @@ static void __hists__insert_output_entry(struct rb_root *entries, | |||
1177 | 1595 | ||
1178 | rb_link_node(&he->rb_node, parent, p); | 1596 | rb_link_node(&he->rb_node, parent, p); |
1179 | rb_insert_color(&he->rb_node, entries); | 1597 | rb_insert_color(&he->rb_node, entries); |
1598 | |||
1599 | perf_hpp_list__for_each_sort_list(&perf_hpp_list, fmt) { | ||
1600 | if (perf_hpp__is_dynamic_entry(fmt) && | ||
1601 | perf_hpp__defined_dynamic_entry(fmt, he->hists)) | ||
1602 | fmt->sort(fmt, he, NULL); /* update column width */ | ||
1603 | } | ||
1180 | } | 1604 | } |
1181 | 1605 | ||
1182 | void hists__output_resort(struct hists *hists, struct ui_progress *prog) | 1606 | static void output_resort(struct hists *hists, struct ui_progress *prog, |
1607 | bool use_callchain) | ||
1183 | { | 1608 | { |
1184 | struct rb_root *root; | 1609 | struct rb_root *root; |
1185 | struct rb_node *next; | 1610 | struct rb_node *next; |
1186 | struct hist_entry *n; | 1611 | struct hist_entry *n; |
1612 | u64 callchain_total; | ||
1187 | u64 min_callchain_hits; | 1613 | u64 min_callchain_hits; |
1188 | struct perf_evsel *evsel = hists_to_evsel(hists); | ||
1189 | bool use_callchain; | ||
1190 | 1614 | ||
1191 | if (evsel && symbol_conf.use_callchain && !symbol_conf.show_ref_callgraph) | 1615 | callchain_total = hists->callchain_period; |
1192 | use_callchain = evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN; | 1616 | if (symbol_conf.filter_relative) |
1193 | else | 1617 | callchain_total = hists->callchain_non_filtered_period; |
1194 | use_callchain = symbol_conf.use_callchain; | ||
1195 | 1618 | ||
1196 | min_callchain_hits = hists->stats.total_period * (callchain_param.min_percent / 100); | 1619 | min_callchain_hits = callchain_total * (callchain_param.min_percent / 100); |
1620 | |||
1621 | hists__reset_stats(hists); | ||
1622 | hists__reset_col_len(hists); | ||
1623 | |||
1624 | if (symbol_conf.report_hierarchy) { | ||
1625 | hists__hierarchy_output_resort(hists, prog, | ||
1626 | &hists->entries_collapsed, | ||
1627 | &hists->entries, | ||
1628 | min_callchain_hits, | ||
1629 | use_callchain); | ||
1630 | hierarchy_recalc_total_periods(hists); | ||
1631 | return; | ||
1632 | } | ||
1197 | 1633 | ||
1198 | if (sort__need_collapse) | 1634 | if (sort__need_collapse) |
1199 | root = &hists->entries_collapsed; | 1635 | root = &hists->entries_collapsed; |
@@ -1203,9 +1639,6 @@ void hists__output_resort(struct hists *hists, struct ui_progress *prog) | |||
1203 | next = rb_first(root); | 1639 | next = rb_first(root); |
1204 | hists->entries = RB_ROOT; | 1640 | hists->entries = RB_ROOT; |
1205 | 1641 | ||
1206 | hists__reset_stats(hists); | ||
1207 | hists__reset_col_len(hists); | ||
1208 | |||
1209 | while (next) { | 1642 | while (next) { |
1210 | n = rb_entry(next, struct hist_entry, rb_node_in); | 1643 | n = rb_entry(next, struct hist_entry, rb_node_in); |
1211 | next = rb_next(&n->rb_node_in); | 1644 | next = rb_next(&n->rb_node_in); |
@@ -1221,15 +1654,136 @@ void hists__output_resort(struct hists *hists, struct ui_progress *prog) | |||
1221 | } | 1654 | } |
1222 | } | 1655 | } |
1223 | 1656 | ||
1657 | void perf_evsel__output_resort(struct perf_evsel *evsel, struct ui_progress *prog) | ||
1658 | { | ||
1659 | bool use_callchain; | ||
1660 | |||
1661 | if (evsel && symbol_conf.use_callchain && !symbol_conf.show_ref_callgraph) | ||
1662 | use_callchain = evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN; | ||
1663 | else | ||
1664 | use_callchain = symbol_conf.use_callchain; | ||
1665 | |||
1666 | output_resort(evsel__hists(evsel), prog, use_callchain); | ||
1667 | } | ||
1668 | |||
1669 | void hists__output_resort(struct hists *hists, struct ui_progress *prog) | ||
1670 | { | ||
1671 | output_resort(hists, prog, symbol_conf.use_callchain); | ||
1672 | } | ||
1673 | |||
1674 | static bool can_goto_child(struct hist_entry *he, enum hierarchy_move_dir hmd) | ||
1675 | { | ||
1676 | if (he->leaf || hmd == HMD_FORCE_SIBLING) | ||
1677 | return false; | ||
1678 | |||
1679 | if (he->unfolded || hmd == HMD_FORCE_CHILD) | ||
1680 | return true; | ||
1681 | |||
1682 | return false; | ||
1683 | } | ||
1684 | |||
1685 | struct rb_node *rb_hierarchy_last(struct rb_node *node) | ||
1686 | { | ||
1687 | struct hist_entry *he = rb_entry(node, struct hist_entry, rb_node); | ||
1688 | |||
1689 | while (can_goto_child(he, HMD_NORMAL)) { | ||
1690 | node = rb_last(&he->hroot_out); | ||
1691 | he = rb_entry(node, struct hist_entry, rb_node); | ||
1692 | } | ||
1693 | return node; | ||
1694 | } | ||
1695 | |||
1696 | struct rb_node *__rb_hierarchy_next(struct rb_node *node, enum hierarchy_move_dir hmd) | ||
1697 | { | ||
1698 | struct hist_entry *he = rb_entry(node, struct hist_entry, rb_node); | ||
1699 | |||
1700 | if (can_goto_child(he, hmd)) | ||
1701 | node = rb_first(&he->hroot_out); | ||
1702 | else | ||
1703 | node = rb_next(node); | ||
1704 | |||
1705 | while (node == NULL) { | ||
1706 | he = he->parent_he; | ||
1707 | if (he == NULL) | ||
1708 | break; | ||
1709 | |||
1710 | node = rb_next(&he->rb_node); | ||
1711 | } | ||
1712 | return node; | ||
1713 | } | ||
1714 | |||
1715 | struct rb_node *rb_hierarchy_prev(struct rb_node *node) | ||
1716 | { | ||
1717 | struct hist_entry *he = rb_entry(node, struct hist_entry, rb_node); | ||
1718 | |||
1719 | node = rb_prev(node); | ||
1720 | if (node) | ||
1721 | return rb_hierarchy_last(node); | ||
1722 | |||
1723 | he = he->parent_he; | ||
1724 | if (he == NULL) | ||
1725 | return NULL; | ||
1726 | |||
1727 | return &he->rb_node; | ||
1728 | } | ||
1729 | |||
1730 | bool hist_entry__has_hierarchy_children(struct hist_entry *he, float limit) | ||
1731 | { | ||
1732 | struct rb_node *node; | ||
1733 | struct hist_entry *child; | ||
1734 | float percent; | ||
1735 | |||
1736 | if (he->leaf) | ||
1737 | return false; | ||
1738 | |||
1739 | node = rb_first(&he->hroot_out); | ||
1740 | child = rb_entry(node, struct hist_entry, rb_node); | ||
1741 | |||
1742 | while (node && child->filtered) { | ||
1743 | node = rb_next(node); | ||
1744 | child = rb_entry(node, struct hist_entry, rb_node); | ||
1745 | } | ||
1746 | |||
1747 | if (node) | ||
1748 | percent = hist_entry__get_percent_limit(child); | ||
1749 | else | ||
1750 | percent = 0; | ||
1751 | |||
1752 | return node && percent >= limit; | ||
1753 | } | ||
1754 | |||
1224 | static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h, | 1755 | static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h, |
1225 | enum hist_filter filter) | 1756 | enum hist_filter filter) |
1226 | { | 1757 | { |
1227 | h->filtered &= ~(1 << filter); | 1758 | h->filtered &= ~(1 << filter); |
1759 | |||
1760 | if (symbol_conf.report_hierarchy) { | ||
1761 | struct hist_entry *parent = h->parent_he; | ||
1762 | |||
1763 | while (parent) { | ||
1764 | he_stat__add_stat(&parent->stat, &h->stat); | ||
1765 | |||
1766 | parent->filtered &= ~(1 << filter); | ||
1767 | |||
1768 | if (parent->filtered) | ||
1769 | goto next; | ||
1770 | |||
1771 | /* force fold unfiltered entry for simplicity */ | ||
1772 | parent->unfolded = false; | ||
1773 | parent->has_no_entry = false; | ||
1774 | parent->row_offset = 0; | ||
1775 | parent->nr_rows = 0; | ||
1776 | next: | ||
1777 | parent = parent->parent_he; | ||
1778 | } | ||
1779 | } | ||
1780 | |||
1228 | if (h->filtered) | 1781 | if (h->filtered) |
1229 | return; | 1782 | return; |
1230 | 1783 | ||
1231 | /* force fold unfiltered entry for simplicity */ | 1784 | /* force fold unfiltered entry for simplicity */ |
1232 | h->unfolded = false; | 1785 | h->unfolded = false; |
1786 | h->has_no_entry = false; | ||
1233 | h->row_offset = 0; | 1787 | h->row_offset = 0; |
1234 | h->nr_rows = 0; | 1788 | h->nr_rows = 0; |
1235 | 1789 | ||
@@ -1252,28 +1806,6 @@ static bool hists__filter_entry_by_dso(struct hists *hists, | |||
1252 | return false; | 1806 | return false; |
1253 | } | 1807 | } |
1254 | 1808 | ||
1255 | void hists__filter_by_dso(struct hists *hists) | ||
1256 | { | ||
1257 | struct rb_node *nd; | ||
1258 | |||
1259 | hists->stats.nr_non_filtered_samples = 0; | ||
1260 | |||
1261 | hists__reset_filter_stats(hists); | ||
1262 | hists__reset_col_len(hists); | ||
1263 | |||
1264 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | ||
1265 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
1266 | |||
1267 | if (symbol_conf.exclude_other && !h->parent) | ||
1268 | continue; | ||
1269 | |||
1270 | if (hists__filter_entry_by_dso(hists, h)) | ||
1271 | continue; | ||
1272 | |||
1273 | hists__remove_entry_filter(hists, h, HIST_FILTER__DSO); | ||
1274 | } | ||
1275 | } | ||
1276 | |||
1277 | static bool hists__filter_entry_by_thread(struct hists *hists, | 1809 | static bool hists__filter_entry_by_thread(struct hists *hists, |
1278 | struct hist_entry *he) | 1810 | struct hist_entry *he) |
1279 | { | 1811 | { |
@@ -1286,25 +1818,6 @@ static bool hists__filter_entry_by_thread(struct hists *hists, | |||
1286 | return false; | 1818 | return false; |
1287 | } | 1819 | } |
1288 | 1820 | ||
1289 | void hists__filter_by_thread(struct hists *hists) | ||
1290 | { | ||
1291 | struct rb_node *nd; | ||
1292 | |||
1293 | hists->stats.nr_non_filtered_samples = 0; | ||
1294 | |||
1295 | hists__reset_filter_stats(hists); | ||
1296 | hists__reset_col_len(hists); | ||
1297 | |||
1298 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | ||
1299 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
1300 | |||
1301 | if (hists__filter_entry_by_thread(hists, h)) | ||
1302 | continue; | ||
1303 | |||
1304 | hists__remove_entry_filter(hists, h, HIST_FILTER__THREAD); | ||
1305 | } | ||
1306 | } | ||
1307 | |||
1308 | static bool hists__filter_entry_by_symbol(struct hists *hists, | 1821 | static bool hists__filter_entry_by_symbol(struct hists *hists, |
1309 | struct hist_entry *he) | 1822 | struct hist_entry *he) |
1310 | { | 1823 | { |
@@ -1318,7 +1831,21 @@ static bool hists__filter_entry_by_symbol(struct hists *hists, | |||
1318 | return false; | 1831 | return false; |
1319 | } | 1832 | } |
1320 | 1833 | ||
1321 | void hists__filter_by_symbol(struct hists *hists) | 1834 | static bool hists__filter_entry_by_socket(struct hists *hists, |
1835 | struct hist_entry *he) | ||
1836 | { | ||
1837 | if ((hists->socket_filter > -1) && | ||
1838 | (he->socket != hists->socket_filter)) { | ||
1839 | he->filtered |= (1 << HIST_FILTER__SOCKET); | ||
1840 | return true; | ||
1841 | } | ||
1842 | |||
1843 | return false; | ||
1844 | } | ||
1845 | |||
1846 | typedef bool (*filter_fn_t)(struct hists *hists, struct hist_entry *he); | ||
1847 | |||
1848 | static void hists__filter_by_type(struct hists *hists, int type, filter_fn_t filter) | ||
1322 | { | 1849 | { |
1323 | struct rb_node *nd; | 1850 | struct rb_node *nd; |
1324 | 1851 | ||
@@ -1330,42 +1857,155 @@ void hists__filter_by_symbol(struct hists *hists) | |||
1330 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | 1857 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
1331 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 1858 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
1332 | 1859 | ||
1333 | if (hists__filter_entry_by_symbol(hists, h)) | 1860 | if (filter(hists, h)) |
1334 | continue; | 1861 | continue; |
1335 | 1862 | ||
1336 | hists__remove_entry_filter(hists, h, HIST_FILTER__SYMBOL); | 1863 | hists__remove_entry_filter(hists, h, type); |
1337 | } | 1864 | } |
1338 | } | 1865 | } |
1339 | 1866 | ||
1340 | static bool hists__filter_entry_by_socket(struct hists *hists, | 1867 | static void resort_filtered_entry(struct rb_root *root, struct hist_entry *he) |
1341 | struct hist_entry *he) | ||
1342 | { | 1868 | { |
1343 | if ((hists->socket_filter > -1) && | 1869 | struct rb_node **p = &root->rb_node; |
1344 | (he->socket != hists->socket_filter)) { | 1870 | struct rb_node *parent = NULL; |
1345 | he->filtered |= (1 << HIST_FILTER__SOCKET); | 1871 | struct hist_entry *iter; |
1346 | return true; | 1872 | struct rb_root new_root = RB_ROOT; |
1873 | struct rb_node *nd; | ||
1874 | |||
1875 | while (*p != NULL) { | ||
1876 | parent = *p; | ||
1877 | iter = rb_entry(parent, struct hist_entry, rb_node); | ||
1878 | |||
1879 | if (hist_entry__sort(he, iter) > 0) | ||
1880 | p = &(*p)->rb_left; | ||
1881 | else | ||
1882 | p = &(*p)->rb_right; | ||
1347 | } | 1883 | } |
1348 | 1884 | ||
1349 | return false; | 1885 | rb_link_node(&he->rb_node, parent, p); |
1886 | rb_insert_color(&he->rb_node, root); | ||
1887 | |||
1888 | if (he->leaf || he->filtered) | ||
1889 | return; | ||
1890 | |||
1891 | nd = rb_first(&he->hroot_out); | ||
1892 | while (nd) { | ||
1893 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
1894 | |||
1895 | nd = rb_next(nd); | ||
1896 | rb_erase(&h->rb_node, &he->hroot_out); | ||
1897 | |||
1898 | resort_filtered_entry(&new_root, h); | ||
1899 | } | ||
1900 | |||
1901 | he->hroot_out = new_root; | ||
1350 | } | 1902 | } |
1351 | 1903 | ||
1352 | void hists__filter_by_socket(struct hists *hists) | 1904 | static void hists__filter_hierarchy(struct hists *hists, int type, const void *arg) |
1353 | { | 1905 | { |
1354 | struct rb_node *nd; | 1906 | struct rb_node *nd; |
1907 | struct rb_root new_root = RB_ROOT; | ||
1355 | 1908 | ||
1356 | hists->stats.nr_non_filtered_samples = 0; | 1909 | hists->stats.nr_non_filtered_samples = 0; |
1357 | 1910 | ||
1358 | hists__reset_filter_stats(hists); | 1911 | hists__reset_filter_stats(hists); |
1359 | hists__reset_col_len(hists); | 1912 | hists__reset_col_len(hists); |
1360 | 1913 | ||
1361 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | 1914 | nd = rb_first(&hists->entries); |
1915 | while (nd) { | ||
1362 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 1916 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
1917 | int ret; | ||
1363 | 1918 | ||
1364 | if (hists__filter_entry_by_socket(hists, h)) | 1919 | ret = hist_entry__filter(h, type, arg); |
1365 | continue; | ||
1366 | 1920 | ||
1367 | hists__remove_entry_filter(hists, h, HIST_FILTER__SOCKET); | 1921 | /* |
1922 | * case 1. non-matching type | ||
1923 | * zero out the period, set filter marker and move to child | ||
1924 | */ | ||
1925 | if (ret < 0) { | ||
1926 | memset(&h->stat, 0, sizeof(h->stat)); | ||
1927 | h->filtered |= (1 << type); | ||
1928 | |||
1929 | nd = __rb_hierarchy_next(&h->rb_node, HMD_FORCE_CHILD); | ||
1930 | } | ||
1931 | /* | ||
1932 | * case 2. matched type (filter out) | ||
1933 | * set filter marker and move to next | ||
1934 | */ | ||
1935 | else if (ret == 1) { | ||
1936 | h->filtered |= (1 << type); | ||
1937 | |||
1938 | nd = __rb_hierarchy_next(&h->rb_node, HMD_FORCE_SIBLING); | ||
1939 | } | ||
1940 | /* | ||
1941 | * case 3. ok (not filtered) | ||
1942 | * add period to hists and parents, erase the filter marker | ||
1943 | * and move to next sibling | ||
1944 | */ | ||
1945 | else { | ||
1946 | hists__remove_entry_filter(hists, h, type); | ||
1947 | |||
1948 | nd = __rb_hierarchy_next(&h->rb_node, HMD_FORCE_SIBLING); | ||
1949 | } | ||
1950 | } | ||
1951 | |||
1952 | hierarchy_recalc_total_periods(hists); | ||
1953 | |||
1954 | /* | ||
1955 | * resort output after applying a new filter since filter in a lower | ||
1956 | * hierarchy can change periods in a upper hierarchy. | ||
1957 | */ | ||
1958 | nd = rb_first(&hists->entries); | ||
1959 | while (nd) { | ||
1960 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
1961 | |||
1962 | nd = rb_next(nd); | ||
1963 | rb_erase(&h->rb_node, &hists->entries); | ||
1964 | |||
1965 | resort_filtered_entry(&new_root, h); | ||
1368 | } | 1966 | } |
1967 | |||
1968 | hists->entries = new_root; | ||
1969 | } | ||
1970 | |||
1971 | void hists__filter_by_thread(struct hists *hists) | ||
1972 | { | ||
1973 | if (symbol_conf.report_hierarchy) | ||
1974 | hists__filter_hierarchy(hists, HIST_FILTER__THREAD, | ||
1975 | hists->thread_filter); | ||
1976 | else | ||
1977 | hists__filter_by_type(hists, HIST_FILTER__THREAD, | ||
1978 | hists__filter_entry_by_thread); | ||
1979 | } | ||
1980 | |||
1981 | void hists__filter_by_dso(struct hists *hists) | ||
1982 | { | ||
1983 | if (symbol_conf.report_hierarchy) | ||
1984 | hists__filter_hierarchy(hists, HIST_FILTER__DSO, | ||
1985 | hists->dso_filter); | ||
1986 | else | ||
1987 | hists__filter_by_type(hists, HIST_FILTER__DSO, | ||
1988 | hists__filter_entry_by_dso); | ||
1989 | } | ||
1990 | |||
1991 | void hists__filter_by_symbol(struct hists *hists) | ||
1992 | { | ||
1993 | if (symbol_conf.report_hierarchy) | ||
1994 | hists__filter_hierarchy(hists, HIST_FILTER__SYMBOL, | ||
1995 | hists->symbol_filter_str); | ||
1996 | else | ||
1997 | hists__filter_by_type(hists, HIST_FILTER__SYMBOL, | ||
1998 | hists__filter_entry_by_symbol); | ||
1999 | } | ||
2000 | |||
2001 | void hists__filter_by_socket(struct hists *hists) | ||
2002 | { | ||
2003 | if (symbol_conf.report_hierarchy) | ||
2004 | hists__filter_hierarchy(hists, HIST_FILTER__SOCKET, | ||
2005 | &hists->socket_filter); | ||
2006 | else | ||
2007 | hists__filter_by_type(hists, HIST_FILTER__SOCKET, | ||
2008 | hists__filter_entry_by_socket); | ||
1369 | } | 2009 | } |
1370 | 2010 | ||
1371 | void events_stats__inc(struct events_stats *stats, u32 type) | 2011 | void events_stats__inc(struct events_stats *stats, u32 type) |
@@ -1583,7 +2223,7 @@ int perf_hist_config(const char *var, const char *value) | |||
1583 | return 0; | 2223 | return 0; |
1584 | } | 2224 | } |
1585 | 2225 | ||
1586 | int __hists__init(struct hists *hists) | 2226 | int __hists__init(struct hists *hists, struct perf_hpp_list *hpp_list) |
1587 | { | 2227 | { |
1588 | memset(hists, 0, sizeof(*hists)); | 2228 | memset(hists, 0, sizeof(*hists)); |
1589 | hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT; | 2229 | hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT; |
@@ -1592,6 +2232,8 @@ int __hists__init(struct hists *hists) | |||
1592 | hists->entries = RB_ROOT; | 2232 | hists->entries = RB_ROOT; |
1593 | pthread_mutex_init(&hists->lock, NULL); | 2233 | pthread_mutex_init(&hists->lock, NULL); |
1594 | hists->socket_filter = -1; | 2234 | hists->socket_filter = -1; |
2235 | hists->hpp_list = hpp_list; | ||
2236 | INIT_LIST_HEAD(&hists->hpp_formats); | ||
1595 | return 0; | 2237 | return 0; |
1596 | } | 2238 | } |
1597 | 2239 | ||
@@ -1620,15 +2262,26 @@ static void hists__delete_all_entries(struct hists *hists) | |||
1620 | static void hists_evsel__exit(struct perf_evsel *evsel) | 2262 | static void hists_evsel__exit(struct perf_evsel *evsel) |
1621 | { | 2263 | { |
1622 | struct hists *hists = evsel__hists(evsel); | 2264 | struct hists *hists = evsel__hists(evsel); |
2265 | struct perf_hpp_fmt *fmt, *pos; | ||
2266 | struct perf_hpp_list_node *node, *tmp; | ||
1623 | 2267 | ||
1624 | hists__delete_all_entries(hists); | 2268 | hists__delete_all_entries(hists); |
2269 | |||
2270 | list_for_each_entry_safe(node, tmp, &hists->hpp_formats, list) { | ||
2271 | perf_hpp_list__for_each_format_safe(&node->hpp, fmt, pos) { | ||
2272 | list_del(&fmt->list); | ||
2273 | free(fmt); | ||
2274 | } | ||
2275 | list_del(&node->list); | ||
2276 | free(node); | ||
2277 | } | ||
1625 | } | 2278 | } |
1626 | 2279 | ||
1627 | static int hists_evsel__init(struct perf_evsel *evsel) | 2280 | static int hists_evsel__init(struct perf_evsel *evsel) |
1628 | { | 2281 | { |
1629 | struct hists *hists = evsel__hists(evsel); | 2282 | struct hists *hists = evsel__hists(evsel); |
1630 | 2283 | ||
1631 | __hists__init(hists); | 2284 | __hists__init(hists, &perf_hpp_list); |
1632 | return 0; | 2285 | return 0; |
1633 | } | 2286 | } |
1634 | 2287 | ||
@@ -1647,3 +2300,9 @@ int hists__init(void) | |||
1647 | 2300 | ||
1648 | return err; | 2301 | return err; |
1649 | } | 2302 | } |
2303 | |||
2304 | void perf_hpp_list__init(struct perf_hpp_list *list) | ||
2305 | { | ||
2306 | INIT_LIST_HEAD(&list->fields); | ||
2307 | INIT_LIST_HEAD(&list->sorts); | ||
2308 | } | ||