stat-shadow.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <errno.h>
  3. #include <math.h>
  4. #include <stdio.h>
  5. #include "evsel.h"
  6. #include "stat.h"
  7. #include "color.h"
  8. #include "debug.h"
  9. #include "pmu.h"
  10. #include "rblist.h"
  11. #include "evlist.h"
  12. #include "expr.h"
  13. #include "metricgroup.h"
  14. #include "cgroup.h"
  15. #include "units.h"
  16. #include <linux/zalloc.h>
  17. #include "iostat.h"
  18. #include "util/hashmap.h"
  19. #include "tool_pmu.h"
  20. static bool tool_pmu__is_time_event(const struct perf_stat_config *config,
  21. const struct evsel *evsel, int *tool_aggr_idx)
  22. {
  23. enum tool_pmu_event event = evsel__tool_event(evsel);
  24. int aggr_idx;
  25. if (event != TOOL_PMU__EVENT_DURATION_TIME &&
  26. event != TOOL_PMU__EVENT_USER_TIME &&
  27. event != TOOL_PMU__EVENT_SYSTEM_TIME)
  28. return false;
  29. if (config) {
  30. cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) {
  31. if (config->aggr_map->map[aggr_idx].cpu.cpu == 0) {
  32. *tool_aggr_idx = aggr_idx;
  33. return true;
  34. }
  35. }
  36. pr_debug("Unexpected CPU0 missing in aggregation for tool event.\n");
  37. }
  38. *tool_aggr_idx = 0; /* Assume the first aggregation index works. */
  39. return true;
  40. }
  41. static int prepare_metric(struct perf_stat_config *config,
  42. const struct metric_expr *mexp,
  43. const struct evsel *evsel,
  44. struct expr_parse_ctx *pctx,
  45. int aggr_idx)
  46. {
  47. struct evsel * const *metric_events = mexp->metric_events;
  48. struct metric_ref *metric_refs = mexp->metric_refs;
  49. int i;
  50. for (i = 0; metric_events[i]; i++) {
  51. int source_count = 0, tool_aggr_idx;
  52. bool is_tool_time =
  53. tool_pmu__is_time_event(config, metric_events[i], &tool_aggr_idx);
  54. struct perf_stat_evsel *ps = metric_events[i]->stats;
  55. char *n;
  56. double val;
  57. /*
  58. * If there are multiple uncore PMUs and we're not reading the
  59. * leader's stats, determine the stats for the appropriate
  60. * uncore PMU.
  61. */
  62. if (evsel && evsel->metric_leader &&
  63. evsel->pmu != evsel->metric_leader->pmu &&
  64. mexp->metric_events[i]->pmu == evsel->metric_leader->pmu) {
  65. struct evsel *pos;
  66. evlist__for_each_entry(evsel->evlist, pos) {
  67. if (pos->pmu != evsel->pmu)
  68. continue;
  69. if (pos->metric_leader != mexp->metric_events[i])
  70. continue;
  71. ps = pos->stats;
  72. source_count = 1;
  73. break;
  74. }
  75. }
  76. /* Time events are always on CPU0, the first aggregation index. */
  77. if (!ps || !metric_events[i]->supported) {
  78. /*
  79. * Not supported events will have a count of 0, which
  80. * can be confusing in a metric. Explicitly set the
  81. * value to NAN. Not counted events (enable time of 0)
  82. * are read as 0.
  83. */
  84. val = NAN;
  85. source_count = 0;
  86. } else {
  87. struct perf_stat_aggr *aggr =
  88. &ps->aggr[is_tool_time ? tool_aggr_idx : aggr_idx];
  89. if (aggr->counts.run == 0) {
  90. val = NAN;
  91. source_count = 0;
  92. } else {
  93. val = aggr->counts.val;
  94. if (is_tool_time) {
  95. /* Convert time event nanoseconds to seconds. */
  96. val *= 1e-9;
  97. }
  98. if (!source_count)
  99. source_count = evsel__source_count(metric_events[i]);
  100. }
  101. }
  102. n = strdup(evsel__metric_id(metric_events[i]));
  103. if (!n)
  104. return -ENOMEM;
  105. expr__add_id_val_source_count(pctx, n, val, source_count);
  106. }
  107. for (int j = 0; metric_refs && metric_refs[j].metric_name; j++) {
  108. int ret = expr__add_ref(pctx, &metric_refs[j]);
  109. if (ret)
  110. return ret;
  111. }
  112. return i;
  113. }
  114. static void generic_metric(struct perf_stat_config *config,
  115. struct metric_expr *mexp,
  116. struct evsel *evsel,
  117. int aggr_idx,
  118. struct perf_stat_output_ctx *out)
  119. {
  120. print_metric_t print_metric = out->print_metric;
  121. const char *metric_name = mexp->metric_name;
  122. const char *metric_expr = mexp->metric_expr;
  123. const char *metric_threshold = mexp->metric_threshold;
  124. const char *metric_unit = mexp->metric_unit;
  125. struct evsel * const *metric_events = mexp->metric_events;
  126. int runtime = mexp->runtime;
  127. struct expr_parse_ctx *pctx;
  128. double ratio, scale, threshold;
  129. int i;
  130. void *ctxp = out->ctx;
  131. enum metric_threshold_classify thresh = METRIC_THRESHOLD_UNKNOWN;
  132. pctx = expr__ctx_new();
  133. if (!pctx)
  134. return;
  135. if (config->user_requested_cpu_list)
  136. pctx->sctx.user_requested_cpu_list = strdup(config->user_requested_cpu_list);
  137. pctx->sctx.runtime = runtime;
  138. pctx->sctx.system_wide = config->system_wide;
  139. i = prepare_metric(config, mexp, evsel, pctx, aggr_idx);
  140. if (i < 0) {
  141. expr__ctx_free(pctx);
  142. return;
  143. }
  144. if (!metric_events[i]) {
  145. if (expr__parse(&ratio, pctx, metric_expr) == 0) {
  146. char *unit;
  147. char metric_bf[128];
  148. if (metric_threshold &&
  149. expr__parse(&threshold, pctx, metric_threshold) == 0 &&
  150. !isnan(threshold)) {
  151. thresh = fpclassify(threshold) == FP_ZERO
  152. ? METRIC_THRESHOLD_GOOD : METRIC_THRESHOLD_BAD;
  153. }
  154. if (metric_unit && metric_name) {
  155. if (perf_pmu__convert_scale(metric_unit,
  156. &unit, &scale) >= 0) {
  157. ratio *= scale;
  158. }
  159. if (strstr(metric_expr, "?"))
  160. scnprintf(metric_bf, sizeof(metric_bf),
  161. "%s %s_%d", unit, metric_name, runtime);
  162. else
  163. scnprintf(metric_bf, sizeof(metric_bf),
  164. "%s %s", unit, metric_name);
  165. print_metric(config, ctxp, thresh, "%8.1f",
  166. metric_bf, ratio);
  167. } else {
  168. print_metric(config, ctxp, thresh, "%8.2f",
  169. metric_name ?
  170. metric_name :
  171. out->force_header ? evsel->name : "",
  172. ratio);
  173. }
  174. } else {
  175. print_metric(config, ctxp, thresh, /*fmt=*/NULL,
  176. out->force_header ?
  177. (metric_name ?: evsel->name) : "", 0);
  178. }
  179. } else {
  180. print_metric(config, ctxp, thresh, /*fmt=*/NULL,
  181. out->force_header ?
  182. (metric_name ?: evsel->name) : "", 0);
  183. }
  184. expr__ctx_free(pctx);
  185. }
  186. double test_generic_metric(struct metric_expr *mexp, int aggr_idx)
  187. {
  188. struct expr_parse_ctx *pctx;
  189. double ratio = 0.0;
  190. pctx = expr__ctx_new();
  191. if (!pctx)
  192. return NAN;
  193. if (prepare_metric(/*config=*/NULL, mexp, /*evsel=*/NULL, pctx, aggr_idx) < 0)
  194. goto out;
  195. if (expr__parse(&ratio, pctx, mexp->metric_expr))
  196. ratio = 0.0;
  197. out:
  198. expr__ctx_free(pctx);
  199. return ratio;
  200. }
  201. static void perf_stat__print_metricgroup_header(struct perf_stat_config *config,
  202. struct evsel *evsel,
  203. void *ctxp,
  204. const char *name,
  205. struct perf_stat_output_ctx *out)
  206. {
  207. bool need_full_name = perf_pmus__num_core_pmus() > 1;
  208. static const char *last_name;
  209. static const struct perf_pmu *last_pmu;
  210. char full_name[64];
  211. /*
  212. * A metricgroup may have several metric events,
  213. * e.g.,TopdownL1 on e-core of ADL.
  214. * The name has been output by the first metric
  215. * event. Only align with other metics from
  216. * different metric events.
  217. */
  218. if (last_name && !strcmp(last_name, name) && last_pmu == evsel->pmu) {
  219. out->print_metricgroup_header(config, ctxp, NULL);
  220. return;
  221. }
  222. if (need_full_name && evsel->pmu)
  223. scnprintf(full_name, sizeof(full_name), "%s (%s)", name, evsel->pmu->name);
  224. else
  225. scnprintf(full_name, sizeof(full_name), "%s", name);
  226. out->print_metricgroup_header(config, ctxp, full_name);
  227. last_name = name;
  228. last_pmu = evsel->pmu;
  229. }
  230. /**
  231. * perf_stat__print_shadow_stats_metricgroup - Print out metrics associated with the evsel
  232. * For the non-default, all metrics associated
  233. * with the evsel are printed.
  234. * For the default mode, only the metrics from
  235. * the same metricgroup and the name of the
  236. * metricgroup are printed. To print the metrics
  237. * from the next metricgroup (if available),
  238. * invoke the function with correspoinding
  239. * metric_expr.
  240. */
  241. void *perf_stat__print_shadow_stats_metricgroup(struct perf_stat_config *config,
  242. struct evsel *evsel,
  243. int aggr_idx,
  244. int *num,
  245. void *from,
  246. struct perf_stat_output_ctx *out)
  247. {
  248. struct metric_event *me;
  249. struct metric_expr *mexp = from;
  250. void *ctxp = out->ctx;
  251. bool header_printed = false;
  252. const char *name = NULL;
  253. struct rblist *metric_events = &evsel->evlist->metric_events;
  254. me = metricgroup__lookup(metric_events, evsel, false);
  255. if (me == NULL)
  256. return NULL;
  257. if (!mexp)
  258. mexp = list_first_entry(&me->head, typeof(*mexp), nd);
  259. list_for_each_entry_from(mexp, &me->head, nd) {
  260. /* Print the display name of the Default metricgroup */
  261. if (!config->metric_only && me->is_default) {
  262. if (!name)
  263. name = mexp->default_metricgroup_name;
  264. /*
  265. * Two or more metricgroup may share the same metric
  266. * event, e.g., TopdownL1 and TopdownL2 on SPR.
  267. * Return and print the prefix, e.g., noise, running
  268. * for the next metricgroup.
  269. */
  270. if (strcmp(name, mexp->default_metricgroup_name))
  271. return (void *)mexp;
  272. /* Only print the name of the metricgroup once */
  273. if (!header_printed && !evsel->default_show_events) {
  274. header_printed = true;
  275. perf_stat__print_metricgroup_header(config, evsel, ctxp,
  276. name, out);
  277. }
  278. }
  279. if ((*num)++ > 0 && out->new_line)
  280. out->new_line(config, ctxp);
  281. generic_metric(config, mexp, evsel, aggr_idx, out);
  282. }
  283. return NULL;
  284. }
  285. void perf_stat__print_shadow_stats(struct perf_stat_config *config,
  286. struct evsel *evsel,
  287. int aggr_idx,
  288. struct perf_stat_output_ctx *out)
  289. {
  290. print_metric_t print_metric = out->print_metric;
  291. void *ctxp = out->ctx;
  292. int num = 0;
  293. if (config->iostat_run)
  294. iostat_print_metric(config, evsel, out);
  295. perf_stat__print_shadow_stats_metricgroup(config, evsel, aggr_idx,
  296. &num, NULL, out);
  297. if (num == 0) {
  298. print_metric(config, ctxp, METRIC_THRESHOLD_UNKNOWN,
  299. /*fmt=*/NULL, /*unit=*/NULL, 0);
  300. }
  301. }
  302. /**
  303. * perf_stat__skip_metric_event - Skip the evsel in the Default metricgroup,
  304. * if it's not running or not the metric event.
  305. */
  306. bool perf_stat__skip_metric_event(struct evsel *evsel)
  307. {
  308. if (!evsel->default_metricgroup)
  309. return false;
  310. return !metricgroup__lookup(&evsel->evlist->metric_events, evsel, false);
  311. }