builtin-diff.c 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * builtin-diff.c
  4. *
  5. * Builtin diff command: Analyze two perf.data input files, look up and read
  6. * DSOs and symbol information, sort them and produce a diff.
  7. */
  8. #include "builtin.h"
  9. #include "perf.h"
  10. #include "util/debug.h"
  11. #include "util/event.h"
  12. #include "util/hist.h"
  13. #include "util/evsel.h"
  14. #include "util/evlist.h"
  15. #include "util/session.h"
  16. #include "util/tool.h"
  17. #include "util/sort.h"
  18. #include "util/srcline.h"
  19. #include "util/symbol.h"
  20. #include "util/data.h"
  21. #include "util/config.h"
  22. #include "util/time-utils.h"
  23. #include "util/annotate.h"
  24. #include "util/map.h"
  25. #include "util/spark.h"
  26. #include "util/block-info.h"
  27. #include "util/stream.h"
  28. #include "util/util.h"
  29. #include <linux/err.h>
  30. #include <linux/zalloc.h>
  31. #include <subcmd/pager.h>
  32. #include <subcmd/parse-options.h>
  33. #include <errno.h>
  34. #include <inttypes.h>
  35. #include <stdlib.h>
  36. #include <math.h>
  37. struct perf_diff {
  38. struct perf_tool tool;
  39. const char *time_str;
  40. struct perf_time_interval *ptime_range;
  41. int range_size;
  42. int range_num;
  43. bool has_br_stack;
  44. bool stream;
  45. };
  46. /* Diff command specific HPP columns. */
  47. enum {
  48. PERF_HPP_DIFF__BASELINE,
  49. PERF_HPP_DIFF__PERIOD,
  50. PERF_HPP_DIFF__PERIOD_BASELINE,
  51. PERF_HPP_DIFF__DELTA,
  52. PERF_HPP_DIFF__RATIO,
  53. PERF_HPP_DIFF__WEIGHTED_DIFF,
  54. PERF_HPP_DIFF__FORMULA,
  55. PERF_HPP_DIFF__DELTA_ABS,
  56. PERF_HPP_DIFF__CYCLES,
  57. PERF_HPP_DIFF__CYCLES_HIST,
  58. PERF_HPP_DIFF__MAX_INDEX
  59. };
  60. struct diff_hpp_fmt {
  61. struct perf_hpp_fmt fmt;
  62. int idx;
  63. char *header;
  64. int header_width;
  65. };
  66. struct data__file {
  67. struct perf_session *session;
  68. struct perf_data data;
  69. int idx;
  70. struct hists *hists;
  71. struct evlist_streams *evlist_streams;
  72. struct diff_hpp_fmt fmt[PERF_HPP_DIFF__MAX_INDEX];
  73. };
  74. static struct data__file *data__files;
  75. static int data__files_cnt;
  76. #define data__for_each_file_start(i, d, s) \
  77. for (i = s, d = &data__files[s]; \
  78. i < data__files_cnt; \
  79. i++, d = &data__files[i])
  80. #define data__for_each_file(i, d) data__for_each_file_start(i, d, 0)
  81. #define data__for_each_file_new(i, d) data__for_each_file_start(i, d, 1)
  82. static bool force;
  83. static bool show_period;
  84. static bool show_formula;
  85. static bool show_baseline_only;
  86. static bool cycles_hist;
  87. static unsigned int sort_compute = 1;
  88. static s64 compute_wdiff_w1;
  89. static s64 compute_wdiff_w2;
  90. static const char *cpu_list;
  91. static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
  92. enum {
  93. COMPUTE_DELTA,
  94. COMPUTE_RATIO,
  95. COMPUTE_WEIGHTED_DIFF,
  96. COMPUTE_DELTA_ABS,
  97. COMPUTE_CYCLES,
  98. COMPUTE_MAX,
  99. COMPUTE_STREAM, /* After COMPUTE_MAX to avoid use current compute arrays */
  100. };
  101. const char *compute_names[COMPUTE_MAX] = {
  102. [COMPUTE_DELTA] = "delta",
  103. [COMPUTE_DELTA_ABS] = "delta-abs",
  104. [COMPUTE_RATIO] = "ratio",
  105. [COMPUTE_WEIGHTED_DIFF] = "wdiff",
  106. [COMPUTE_CYCLES] = "cycles",
  107. };
  108. static int compute = COMPUTE_DELTA_ABS;
  109. static int compute_2_hpp[COMPUTE_MAX] = {
  110. [COMPUTE_DELTA] = PERF_HPP_DIFF__DELTA,
  111. [COMPUTE_DELTA_ABS] = PERF_HPP_DIFF__DELTA_ABS,
  112. [COMPUTE_RATIO] = PERF_HPP_DIFF__RATIO,
  113. [COMPUTE_WEIGHTED_DIFF] = PERF_HPP_DIFF__WEIGHTED_DIFF,
  114. [COMPUTE_CYCLES] = PERF_HPP_DIFF__CYCLES,
  115. };
  116. #define MAX_COL_WIDTH 70
  117. static struct header_column {
  118. const char *name;
  119. int width;
  120. } columns[PERF_HPP_DIFF__MAX_INDEX] = {
  121. [PERF_HPP_DIFF__BASELINE] = {
  122. .name = "Baseline",
  123. },
  124. [PERF_HPP_DIFF__PERIOD] = {
  125. .name = "Period",
  126. .width = 14,
  127. },
  128. [PERF_HPP_DIFF__PERIOD_BASELINE] = {
  129. .name = "Base period",
  130. .width = 14,
  131. },
  132. [PERF_HPP_DIFF__DELTA] = {
  133. .name = "Delta",
  134. .width = 7,
  135. },
  136. [PERF_HPP_DIFF__DELTA_ABS] = {
  137. .name = "Delta Abs",
  138. .width = 7,
  139. },
  140. [PERF_HPP_DIFF__RATIO] = {
  141. .name = "Ratio",
  142. .width = 14,
  143. },
  144. [PERF_HPP_DIFF__WEIGHTED_DIFF] = {
  145. .name = "Weighted diff",
  146. .width = 14,
  147. },
  148. [PERF_HPP_DIFF__FORMULA] = {
  149. .name = "Formula",
  150. .width = MAX_COL_WIDTH,
  151. },
  152. [PERF_HPP_DIFF__CYCLES] = {
  153. .name = "[Program Block Range] Cycles Diff",
  154. .width = 70,
  155. },
  156. [PERF_HPP_DIFF__CYCLES_HIST] = {
  157. .name = "stddev/Hist",
  158. .width = NUM_SPARKS + 9,
  159. }
  160. };
  161. static int setup_compute_opt_wdiff(const char *opt)
  162. {
  163. const char *w1_str = opt, *w2_str;
  164. int ret = -EINVAL;
  165. if (!opt)
  166. goto out;
  167. w2_str = strchr(opt, ',');
  168. if (!w2_str)
  169. goto out;
  170. if (!*++w2_str)
  171. goto out;
  172. compute_wdiff_w1 = strtol(w1_str, NULL, 10);
  173. compute_wdiff_w2 = strtol(w2_str, NULL, 10);
  174. if (!compute_wdiff_w1 || !compute_wdiff_w2)
  175. goto out;
  176. pr_debug("compute wdiff w1(%" PRId64 ") w2(%" PRId64 ")\n",
  177. compute_wdiff_w1, compute_wdiff_w2);
  178. ret = 0;
  179. out:
  180. if (ret)
  181. pr_err("Failed: wrong weight data, use 'wdiff:w1,w2'\n");
  182. return ret;
  183. }
  184. static int setup_compute_opt(const char *opt)
  185. {
  186. if (compute == COMPUTE_WEIGHTED_DIFF)
  187. return setup_compute_opt_wdiff(opt);
  188. if (opt) {
  189. pr_err("Failed: extra option specified '%s'", opt);
  190. return -EINVAL;
  191. }
  192. return 0;
  193. }
  194. static int setup_compute(const struct option *opt, const char *str,
  195. int unset __maybe_unused)
  196. {
  197. int *cp = (int *) opt->value;
  198. char *cstr = (char *) str;
  199. char buf[50];
  200. unsigned i;
  201. const char *option;
  202. if (!str) {
  203. *cp = COMPUTE_DELTA;
  204. return 0;
  205. }
  206. option = strchr(str, ':');
  207. if (option) {
  208. unsigned len = option++ - str;
  209. /*
  210. * The str data are not writeable, so we need
  211. * to use another buffer.
  212. */
  213. /* No option value is longer. */
  214. if (len >= sizeof(buf))
  215. return -EINVAL;
  216. strncpy(buf, str, len);
  217. buf[len] = 0x0;
  218. cstr = buf;
  219. }
  220. for (i = 0; i < COMPUTE_MAX; i++)
  221. if (!strcmp(cstr, compute_names[i])) {
  222. *cp = i;
  223. return setup_compute_opt(option);
  224. }
  225. pr_err("Failed: '%s' is not computation method "
  226. "(use 'delta','ratio' or 'wdiff')\n", str);
  227. return -EINVAL;
  228. }
  229. static double period_percent(struct hist_entry *he, u64 period)
  230. {
  231. u64 total = hists__total_period(he->hists);
  232. return (period * 100.0) / total;
  233. }
  234. static double compute_delta(struct hist_entry *he, struct hist_entry *pair)
  235. {
  236. double old_percent = period_percent(he, he->stat.period);
  237. double new_percent = period_percent(pair, pair->stat.period);
  238. pair->diff.period_ratio_delta = new_percent - old_percent;
  239. pair->diff.computed = true;
  240. return pair->diff.period_ratio_delta;
  241. }
  242. static double compute_ratio(struct hist_entry *he, struct hist_entry *pair)
  243. {
  244. double old_period = he->stat.period ?: 1;
  245. double new_period = pair->stat.period;
  246. pair->diff.computed = true;
  247. pair->diff.period_ratio = new_period / old_period;
  248. return pair->diff.period_ratio;
  249. }
  250. static s64 compute_wdiff(struct hist_entry *he, struct hist_entry *pair)
  251. {
  252. u64 old_period = he->stat.period;
  253. u64 new_period = pair->stat.period;
  254. pair->diff.computed = true;
  255. pair->diff.wdiff = new_period * compute_wdiff_w2 -
  256. old_period * compute_wdiff_w1;
  257. return pair->diff.wdiff;
  258. }
  259. static int formula_delta(struct hist_entry *he, struct hist_entry *pair,
  260. char *buf, size_t size)
  261. {
  262. u64 he_total = he->hists->stats.total_period;
  263. u64 pair_total = pair->hists->stats.total_period;
  264. if (symbol_conf.filter_relative) {
  265. he_total = he->hists->stats.total_non_filtered_period;
  266. pair_total = pair->hists->stats.total_non_filtered_period;
  267. }
  268. return scnprintf(buf, size,
  269. "(%" PRIu64 " * 100 / %" PRIu64 ") - "
  270. "(%" PRIu64 " * 100 / %" PRIu64 ")",
  271. pair->stat.period, pair_total,
  272. he->stat.period, he_total);
  273. }
  274. static int formula_ratio(struct hist_entry *he, struct hist_entry *pair,
  275. char *buf, size_t size)
  276. {
  277. double old_period = he->stat.period;
  278. double new_period = pair->stat.period;
  279. return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period);
  280. }
  281. static int formula_wdiff(struct hist_entry *he, struct hist_entry *pair,
  282. char *buf, size_t size)
  283. {
  284. u64 old_period = he->stat.period;
  285. u64 new_period = pair->stat.period;
  286. return scnprintf(buf, size,
  287. "(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")",
  288. new_period, compute_wdiff_w2, old_period, compute_wdiff_w1);
  289. }
  290. static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair,
  291. char *buf, size_t size)
  292. {
  293. switch (compute) {
  294. case COMPUTE_DELTA:
  295. case COMPUTE_DELTA_ABS:
  296. return formula_delta(he, pair, buf, size);
  297. case COMPUTE_RATIO:
  298. return formula_ratio(he, pair, buf, size);
  299. case COMPUTE_WEIGHTED_DIFF:
  300. return formula_wdiff(he, pair, buf, size);
  301. default:
  302. BUG_ON(1);
  303. }
  304. return -1;
  305. }
  306. static void *block_hist_zalloc(size_t size)
  307. {
  308. struct block_hist *bh;
  309. bh = zalloc(size + sizeof(*bh));
  310. if (!bh)
  311. return NULL;
  312. return &bh->he;
  313. }
  314. static void block_hist_free(void *he)
  315. {
  316. struct block_hist *bh;
  317. bh = container_of(he, struct block_hist, he);
  318. hists__delete_entries(&bh->block_hists);
  319. free(bh);
  320. }
  321. struct hist_entry_ops block_hist_ops = {
  322. .new = block_hist_zalloc,
  323. .free = block_hist_free,
  324. };
  325. static int diff__process_sample_event(const struct perf_tool *tool,
  326. union perf_event *event,
  327. struct perf_sample *sample,
  328. struct evsel *evsel,
  329. struct machine *machine)
  330. {
  331. struct perf_diff *pdiff = container_of(tool, struct perf_diff, tool);
  332. struct addr_location al;
  333. struct hists *hists = evsel__hists(evsel);
  334. struct hist_entry_iter iter = {
  335. .evsel = evsel,
  336. .sample = sample,
  337. .ops = &hist_iter_normal,
  338. };
  339. int ret = -1;
  340. if (perf_time__ranges_skip_sample(pdiff->ptime_range, pdiff->range_num,
  341. sample->time)) {
  342. return 0;
  343. }
  344. addr_location__init(&al);
  345. if (machine__resolve(machine, &al, sample) < 0) {
  346. pr_warning("problem processing %d event, skipping it.\n",
  347. event->header.type);
  348. ret = -1;
  349. goto out;
  350. }
  351. if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) {
  352. ret = 0;
  353. goto out;
  354. }
  355. switch (compute) {
  356. case COMPUTE_CYCLES:
  357. if (!hists__add_entry_ops(hists, &block_hist_ops, &al, NULL,
  358. NULL, NULL, NULL, sample, true)) {
  359. pr_warning("problem incrementing symbol period, "
  360. "skipping event\n");
  361. goto out;
  362. }
  363. hist__account_cycles(sample->branch_stack, &al, sample,
  364. false, NULL, evsel);
  365. break;
  366. case COMPUTE_STREAM:
  367. if (hist_entry_iter__add(&iter, &al, PERF_MAX_STACK_DEPTH,
  368. NULL)) {
  369. pr_debug("problem adding hist entry, skipping event\n");
  370. goto out;
  371. }
  372. break;
  373. default:
  374. if (!hists__add_entry(hists, &al, NULL, NULL, NULL, NULL, sample,
  375. true)) {
  376. pr_warning("problem incrementing symbol period, "
  377. "skipping event\n");
  378. goto out;
  379. }
  380. }
  381. /*
  382. * The total_period is updated here before going to the output
  383. * tree since normally only the baseline hists will call
  384. * hists__output_resort() and precompute needs the total
  385. * period in order to sort entries by percentage delta.
  386. */
  387. hists->stats.total_period += sample->period;
  388. if (!al.filtered)
  389. hists->stats.total_non_filtered_period += sample->period;
  390. ret = 0;
  391. out:
  392. addr_location__exit(&al);
  393. return ret;
  394. }
  395. static struct perf_diff pdiff;
  396. static struct evsel *evsel_match(struct evsel *evsel, struct evlist *evlist)
  397. {
  398. struct evsel *e;
  399. evlist__for_each_entry(evlist, e) {
  400. if ((evsel->core.attr.type == e->core.attr.type) &&
  401. (evsel->core.attr.config == e->core.attr.config))
  402. return e;
  403. }
  404. return NULL;
  405. }
  406. static void evlist__collapse_resort(struct evlist *evlist)
  407. {
  408. struct evsel *evsel;
  409. evlist__for_each_entry(evlist, evsel) {
  410. struct hists *hists = evsel__hists(evsel);
  411. hists__collapse_resort(hists, NULL);
  412. }
  413. }
  414. static struct data__file *fmt_to_data_file(struct perf_hpp_fmt *fmt)
  415. {
  416. struct diff_hpp_fmt *dfmt = container_of(fmt, struct diff_hpp_fmt, fmt);
  417. void *ptr = dfmt - dfmt->idx;
  418. struct data__file *d = container_of(ptr, struct data__file, fmt);
  419. return d;
  420. }
  421. static struct hist_entry*
  422. get_pair_data(struct hist_entry *he, struct data__file *d)
  423. {
  424. if (hist_entry__has_pairs(he)) {
  425. struct hist_entry *pair;
  426. list_for_each_entry(pair, &he->pairs.head, pairs.node)
  427. if (pair->hists == d->hists)
  428. return pair;
  429. }
  430. return NULL;
  431. }
  432. static struct hist_entry*
  433. get_pair_fmt(struct hist_entry *he, struct diff_hpp_fmt *dfmt)
  434. {
  435. struct data__file *d = fmt_to_data_file(&dfmt->fmt);
  436. return get_pair_data(he, d);
  437. }
  438. static void hists__baseline_only(struct hists *hists)
  439. {
  440. struct rb_root_cached *root;
  441. struct rb_node *next;
  442. if (hists__has(hists, need_collapse))
  443. root = &hists->entries_collapsed;
  444. else
  445. root = hists->entries_in;
  446. next = rb_first_cached(root);
  447. while (next != NULL) {
  448. struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node_in);
  449. next = rb_next(&he->rb_node_in);
  450. if (!hist_entry__next_pair(he)) {
  451. rb_erase_cached(&he->rb_node_in, root);
  452. hist_entry__delete(he);
  453. }
  454. }
  455. }
  456. static int64_t block_cycles_diff_cmp(struct hist_entry *left,
  457. struct hist_entry *right)
  458. {
  459. bool pairs_left = hist_entry__has_pairs(left);
  460. bool pairs_right = hist_entry__has_pairs(right);
  461. s64 l, r;
  462. if (!pairs_left && !pairs_right)
  463. return 0;
  464. l = llabs(left->diff.cycles);
  465. r = llabs(right->diff.cycles);
  466. return r - l;
  467. }
  468. static int64_t block_sort(struct perf_hpp_fmt *fmt __maybe_unused,
  469. struct hist_entry *left, struct hist_entry *right)
  470. {
  471. return block_cycles_diff_cmp(right, left);
  472. }
  473. static void init_block_hist(struct block_hist *bh)
  474. {
  475. __hists__init(&bh->block_hists, &bh->block_list);
  476. perf_hpp_list__init(&bh->block_list);
  477. INIT_LIST_HEAD(&bh->block_fmt.list);
  478. INIT_LIST_HEAD(&bh->block_fmt.sort_list);
  479. bh->block_fmt.cmp = block_info__cmp;
  480. bh->block_fmt.sort = block_sort;
  481. perf_hpp_list__register_sort_field(&bh->block_list,
  482. &bh->block_fmt);
  483. bh->valid = true;
  484. }
  485. static struct hist_entry *get_block_pair(struct hist_entry *he,
  486. struct hists *hists_pair)
  487. {
  488. struct rb_root_cached *root = hists_pair->entries_in;
  489. struct rb_node *next = rb_first_cached(root);
  490. int64_t cmp;
  491. while (next != NULL) {
  492. struct hist_entry *he_pair = rb_entry(next, struct hist_entry,
  493. rb_node_in);
  494. next = rb_next(&he_pair->rb_node_in);
  495. cmp = __block_info__cmp(he_pair, he);
  496. if (!cmp)
  497. return he_pair;
  498. }
  499. return NULL;
  500. }
  501. static void init_spark_values(unsigned long *svals, int num)
  502. {
  503. for (int i = 0; i < num; i++)
  504. svals[i] = 0;
  505. }
  506. static void update_spark_value(unsigned long *svals, int num,
  507. struct stats *stats, u64 val)
  508. {
  509. int n = stats->n;
  510. if (n < num)
  511. svals[n] = val;
  512. }
  513. static void compute_cycles_diff(struct hist_entry *he,
  514. struct hist_entry *pair)
  515. {
  516. pair->diff.computed = true;
  517. if (pair->block_info->num && he->block_info->num) {
  518. pair->diff.cycles =
  519. pair->block_info->cycles_aggr / pair->block_info->num_aggr -
  520. he->block_info->cycles_aggr / he->block_info->num_aggr;
  521. if (!cycles_hist)
  522. return;
  523. init_stats(&pair->diff.stats);
  524. init_spark_values(pair->diff.svals, NUM_SPARKS);
  525. for (int i = 0; i < pair->block_info->num; i++) {
  526. u64 val;
  527. if (i >= he->block_info->num || i >= NUM_SPARKS)
  528. break;
  529. val = llabs(pair->block_info->cycles_spark[i] -
  530. he->block_info->cycles_spark[i]);
  531. update_spark_value(pair->diff.svals, NUM_SPARKS,
  532. &pair->diff.stats, val);
  533. update_stats(&pair->diff.stats, val);
  534. }
  535. }
  536. }
  537. static void block_hists_match(struct hists *hists_base,
  538. struct hists *hists_pair)
  539. {
  540. struct rb_root_cached *root = hists_base->entries_in;
  541. struct rb_node *next = rb_first_cached(root);
  542. while (next != NULL) {
  543. struct hist_entry *he = rb_entry(next, struct hist_entry,
  544. rb_node_in);
  545. struct hist_entry *pair = get_block_pair(he, hists_pair);
  546. next = rb_next(&he->rb_node_in);
  547. if (pair) {
  548. hist_entry__add_pair(pair, he);
  549. compute_cycles_diff(he, pair);
  550. }
  551. }
  552. }
  553. static void hists__precompute(struct hists *hists)
  554. {
  555. struct rb_root_cached *root;
  556. struct rb_node *next;
  557. if (hists__has(hists, need_collapse))
  558. root = &hists->entries_collapsed;
  559. else
  560. root = hists->entries_in;
  561. next = rb_first_cached(root);
  562. while (next != NULL) {
  563. struct block_hist *bh, *pair_bh;
  564. struct hist_entry *he, *pair;
  565. struct data__file *d;
  566. int i;
  567. he = rb_entry(next, struct hist_entry, rb_node_in);
  568. next = rb_next(&he->rb_node_in);
  569. if (compute == COMPUTE_CYCLES) {
  570. bh = container_of(he, struct block_hist, he);
  571. init_block_hist(bh);
  572. block_info__process_sym(he, bh, NULL, 0, 0);
  573. }
  574. data__for_each_file_new(i, d) {
  575. pair = get_pair_data(he, d);
  576. if (!pair)
  577. continue;
  578. switch (compute) {
  579. case COMPUTE_DELTA:
  580. case COMPUTE_DELTA_ABS:
  581. compute_delta(he, pair);
  582. break;
  583. case COMPUTE_RATIO:
  584. compute_ratio(he, pair);
  585. break;
  586. case COMPUTE_WEIGHTED_DIFF:
  587. compute_wdiff(he, pair);
  588. break;
  589. case COMPUTE_CYCLES:
  590. pair_bh = container_of(pair, struct block_hist,
  591. he);
  592. init_block_hist(pair_bh);
  593. block_info__process_sym(pair, pair_bh, NULL, 0, 0);
  594. bh = container_of(he, struct block_hist, he);
  595. if (bh->valid && pair_bh->valid) {
  596. block_hists_match(&bh->block_hists,
  597. &pair_bh->block_hists);
  598. hists__output_resort(&pair_bh->block_hists,
  599. NULL);
  600. }
  601. break;
  602. default:
  603. BUG_ON(1);
  604. }
  605. }
  606. }
  607. }
  608. static int64_t cmp_doubles(double l, double r)
  609. {
  610. if (l > r)
  611. return -1;
  612. else if (l < r)
  613. return 1;
  614. else
  615. return 0;
  616. }
  617. static int64_t
  618. __hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
  619. int c)
  620. {
  621. switch (c) {
  622. case COMPUTE_DELTA:
  623. {
  624. double l = left->diff.period_ratio_delta;
  625. double r = right->diff.period_ratio_delta;
  626. return cmp_doubles(l, r);
  627. }
  628. case COMPUTE_DELTA_ABS:
  629. {
  630. double l = fabs(left->diff.period_ratio_delta);
  631. double r = fabs(right->diff.period_ratio_delta);
  632. return cmp_doubles(l, r);
  633. }
  634. case COMPUTE_RATIO:
  635. {
  636. double l = left->diff.period_ratio;
  637. double r = right->diff.period_ratio;
  638. return cmp_doubles(l, r);
  639. }
  640. case COMPUTE_WEIGHTED_DIFF:
  641. {
  642. s64 l = left->diff.wdiff;
  643. s64 r = right->diff.wdiff;
  644. return r - l;
  645. }
  646. default:
  647. BUG_ON(1);
  648. }
  649. return 0;
  650. }
  651. static int64_t
  652. hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
  653. int c, int sort_idx)
  654. {
  655. bool pairs_left = hist_entry__has_pairs(left);
  656. bool pairs_right = hist_entry__has_pairs(right);
  657. struct hist_entry *p_right, *p_left;
  658. if (!pairs_left && !pairs_right)
  659. return 0;
  660. if (!pairs_left || !pairs_right)
  661. return pairs_left ? -1 : 1;
  662. p_left = get_pair_data(left, &data__files[sort_idx]);
  663. p_right = get_pair_data(right, &data__files[sort_idx]);
  664. if (!p_left && !p_right)
  665. return 0;
  666. if (!p_left || !p_right)
  667. return p_left ? -1 : 1;
  668. /*
  669. * We have 2 entries of same kind, let's
  670. * make the data comparison.
  671. */
  672. return __hist_entry__cmp_compute(p_left, p_right, c);
  673. }
  674. static int64_t
  675. hist_entry__cmp_compute_idx(struct hist_entry *left, struct hist_entry *right,
  676. int c, int sort_idx)
  677. {
  678. struct hist_entry *p_right, *p_left;
  679. p_left = get_pair_data(left, &data__files[sort_idx]);
  680. p_right = get_pair_data(right, &data__files[sort_idx]);
  681. if (!p_left && !p_right)
  682. return 0;
  683. if (!p_left || !p_right)
  684. return p_left ? -1 : 1;
  685. if (c != COMPUTE_DELTA && c != COMPUTE_DELTA_ABS) {
  686. /*
  687. * The delta can be computed without the baseline, but
  688. * others are not. Put those entries which have no
  689. * values below.
  690. */
  691. if (left->dummy && right->dummy)
  692. return 0;
  693. if (left->dummy || right->dummy)
  694. return left->dummy ? 1 : -1;
  695. }
  696. return __hist_entry__cmp_compute(p_left, p_right, c);
  697. }
  698. static int64_t
  699. hist_entry__cmp_nop(struct perf_hpp_fmt *fmt __maybe_unused,
  700. struct hist_entry *left __maybe_unused,
  701. struct hist_entry *right __maybe_unused)
  702. {
  703. return 0;
  704. }
  705. static int64_t
  706. hist_entry__cmp_baseline(struct perf_hpp_fmt *fmt __maybe_unused,
  707. struct hist_entry *left, struct hist_entry *right)
  708. {
  709. if (left->stat.period == right->stat.period)
  710. return 0;
  711. return left->stat.period > right->stat.period ? 1 : -1;
  712. }
  713. static int64_t
  714. hist_entry__cmp_delta(struct perf_hpp_fmt *fmt,
  715. struct hist_entry *left, struct hist_entry *right)
  716. {
  717. struct data__file *d = fmt_to_data_file(fmt);
  718. return hist_entry__cmp_compute(right, left, COMPUTE_DELTA, d->idx);
  719. }
  720. static int64_t
  721. hist_entry__cmp_delta_abs(struct perf_hpp_fmt *fmt,
  722. struct hist_entry *left, struct hist_entry *right)
  723. {
  724. struct data__file *d = fmt_to_data_file(fmt);
  725. return hist_entry__cmp_compute(right, left, COMPUTE_DELTA_ABS, d->idx);
  726. }
  727. static int64_t
  728. hist_entry__cmp_ratio(struct perf_hpp_fmt *fmt,
  729. struct hist_entry *left, struct hist_entry *right)
  730. {
  731. struct data__file *d = fmt_to_data_file(fmt);
  732. return hist_entry__cmp_compute(right, left, COMPUTE_RATIO, d->idx);
  733. }
  734. static int64_t
  735. hist_entry__cmp_wdiff(struct perf_hpp_fmt *fmt,
  736. struct hist_entry *left, struct hist_entry *right)
  737. {
  738. struct data__file *d = fmt_to_data_file(fmt);
  739. return hist_entry__cmp_compute(right, left, COMPUTE_WEIGHTED_DIFF, d->idx);
  740. }
  741. static int64_t
  742. hist_entry__cmp_delta_idx(struct perf_hpp_fmt *fmt __maybe_unused,
  743. struct hist_entry *left, struct hist_entry *right)
  744. {
  745. return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA,
  746. sort_compute);
  747. }
  748. static int64_t
  749. hist_entry__cmp_delta_abs_idx(struct perf_hpp_fmt *fmt __maybe_unused,
  750. struct hist_entry *left, struct hist_entry *right)
  751. {
  752. return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA_ABS,
  753. sort_compute);
  754. }
  755. static int64_t
  756. hist_entry__cmp_ratio_idx(struct perf_hpp_fmt *fmt __maybe_unused,
  757. struct hist_entry *left, struct hist_entry *right)
  758. {
  759. return hist_entry__cmp_compute_idx(right, left, COMPUTE_RATIO,
  760. sort_compute);
  761. }
  762. static int64_t
  763. hist_entry__cmp_wdiff_idx(struct perf_hpp_fmt *fmt __maybe_unused,
  764. struct hist_entry *left, struct hist_entry *right)
  765. {
  766. return hist_entry__cmp_compute_idx(right, left, COMPUTE_WEIGHTED_DIFF,
  767. sort_compute);
  768. }
  769. static void hists__process(struct hists *hists)
  770. {
  771. if (show_baseline_only)
  772. hists__baseline_only(hists);
  773. hists__precompute(hists);
  774. hists__output_resort(hists, NULL);
  775. if (compute == COMPUTE_CYCLES)
  776. symbol_conf.report_block = true;
  777. hists__fprintf(hists, !quiet, 0, 0, 0, stdout,
  778. !symbol_conf.use_callchain);
  779. }
  780. static void data__fprintf(void)
  781. {
  782. struct data__file *d;
  783. int i;
  784. fprintf(stdout, "# Data files:\n");
  785. data__for_each_file(i, d)
  786. fprintf(stdout, "# [%d] %s %s\n",
  787. d->idx, d->data.path,
  788. !d->idx ? "(Baseline)" : "");
  789. fprintf(stdout, "#\n");
  790. }
  791. static void data_process(void)
  792. {
  793. struct evlist *evlist_base = data__files[0].session->evlist;
  794. struct evsel *evsel_base;
  795. bool first = true;
  796. evlist__for_each_entry(evlist_base, evsel_base) {
  797. struct hists *hists_base = evsel__hists(evsel_base);
  798. struct data__file *d;
  799. int i;
  800. data__for_each_file_new(i, d) {
  801. struct evlist *evlist = d->session->evlist;
  802. struct evsel *evsel;
  803. struct hists *hists;
  804. evsel = evsel_match(evsel_base, evlist);
  805. if (!evsel)
  806. continue;
  807. hists = evsel__hists(evsel);
  808. d->hists = hists;
  809. hists__match(hists_base, hists);
  810. if (!show_baseline_only)
  811. hists__link(hists_base, hists);
  812. }
  813. if (!quiet) {
  814. fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n",
  815. evsel__name(evsel_base));
  816. }
  817. first = false;
  818. if (verbose > 0 || ((data__files_cnt > 2) && !quiet))
  819. data__fprintf();
  820. /* Don't sort callchain for perf diff */
  821. evsel__reset_sample_bit(evsel_base, CALLCHAIN);
  822. hists__process(hists_base);
  823. }
  824. }
  825. static int process_base_stream(struct data__file *data_base,
  826. struct data__file *data_pair,
  827. const char *title __maybe_unused)
  828. {
  829. struct evlist *evlist_base = data_base->session->evlist;
  830. struct evlist *evlist_pair = data_pair->session->evlist;
  831. struct evsel *evsel_base, *evsel_pair;
  832. struct evsel_streams *es_base, *es_pair;
  833. evlist__for_each_entry(evlist_base, evsel_base) {
  834. evsel_pair = evsel_match(evsel_base, evlist_pair);
  835. if (!evsel_pair)
  836. continue;
  837. es_base = evsel_streams__entry(data_base->evlist_streams,
  838. evsel_base);
  839. if (!es_base)
  840. return -1;
  841. es_pair = evsel_streams__entry(data_pair->evlist_streams,
  842. evsel_pair);
  843. if (!es_pair)
  844. return -1;
  845. evsel_streams__match(es_base, es_pair);
  846. evsel_streams__report(es_base, es_pair);
  847. }
  848. return 0;
  849. }
  850. static void stream_process(void)
  851. {
  852. /*
  853. * Stream comparison only supports two data files.
  854. * perf.data.old and perf.data. data__files[0] is perf.data.old,
  855. * data__files[1] is perf.data.
  856. */
  857. process_base_stream(&data__files[0], &data__files[1],
  858. "# Output based on old perf data:\n#\n");
  859. }
  860. static void data__free(struct data__file *d)
  861. {
  862. int col;
  863. if (d->evlist_streams)
  864. evlist_streams__delete(d->evlist_streams);
  865. for (col = 0; col < PERF_HPP_DIFF__MAX_INDEX; col++) {
  866. struct diff_hpp_fmt *fmt = &d->fmt[col];
  867. zfree(&fmt->header);
  868. }
  869. }
  870. static int abstime_str_dup(char **pstr)
  871. {
  872. char *str = NULL;
  873. if (pdiff.time_str && strchr(pdiff.time_str, ':')) {
  874. str = strdup(pdiff.time_str);
  875. if (!str)
  876. return -ENOMEM;
  877. }
  878. *pstr = str;
  879. return 0;
  880. }
  881. static int parse_absolute_time(struct data__file *d, char **pstr)
  882. {
  883. char *p = *pstr;
  884. int ret;
  885. /*
  886. * Absolute timestamp for one file has the format: a.b,c.d
  887. * For multiple files, the format is: a.b,c.d:a.b,c.d
  888. */
  889. p = strchr(*pstr, ':');
  890. if (p) {
  891. if (p == *pstr) {
  892. pr_err("Invalid time string\n");
  893. return -EINVAL;
  894. }
  895. *p = 0;
  896. p++;
  897. if (*p == 0) {
  898. pr_err("Invalid time string\n");
  899. return -EINVAL;
  900. }
  901. }
  902. ret = perf_time__parse_for_ranges(*pstr, d->session,
  903. &pdiff.ptime_range,
  904. &pdiff.range_size,
  905. &pdiff.range_num);
  906. if (ret < 0)
  907. return ret;
  908. if (!p || *p == 0)
  909. *pstr = NULL;
  910. else
  911. *pstr = p;
  912. return ret;
  913. }
  914. static int parse_percent_time(struct data__file *d)
  915. {
  916. int ret;
  917. ret = perf_time__parse_for_ranges(pdiff.time_str, d->session,
  918. &pdiff.ptime_range,
  919. &pdiff.range_size,
  920. &pdiff.range_num);
  921. return ret;
  922. }
  923. static int parse_time_str(struct data__file *d, char *abstime_ostr,
  924. char **pabstime_tmp)
  925. {
  926. int ret = 0;
  927. if (abstime_ostr)
  928. ret = parse_absolute_time(d, pabstime_tmp);
  929. else if (pdiff.time_str)
  930. ret = parse_percent_time(d);
  931. return ret;
  932. }
  933. static int check_file_brstack(void)
  934. {
  935. struct data__file *d;
  936. bool has_br_stack;
  937. int i;
  938. data__for_each_file(i, d) {
  939. d->session = perf_session__new(&d->data, &pdiff.tool);
  940. if (IS_ERR(d->session)) {
  941. pr_err("Failed to open %s\n", d->data.path);
  942. return PTR_ERR(d->session);
  943. }
  944. has_br_stack = perf_header__has_feat(&d->session->header,
  945. HEADER_BRANCH_STACK);
  946. perf_session__delete(d->session);
  947. if (!has_br_stack)
  948. return 0;
  949. }
  950. /* Set only all files having branch stacks */
  951. pdiff.has_br_stack = true;
  952. return 0;
  953. }
  954. static int __cmd_diff(void)
  955. {
  956. struct data__file *d;
  957. int ret, i;
  958. char *abstime_ostr, *abstime_tmp;
  959. ret = abstime_str_dup(&abstime_ostr);
  960. if (ret)
  961. return ret;
  962. abstime_tmp = abstime_ostr;
  963. ret = -EINVAL;
  964. data__for_each_file(i, d) {
  965. d->session = perf_session__new(&d->data, &pdiff.tool);
  966. if (IS_ERR(d->session)) {
  967. ret = PTR_ERR(d->session);
  968. pr_err("Failed to open %s\n", d->data.path);
  969. goto out_delete;
  970. }
  971. if (pdiff.time_str) {
  972. ret = parse_time_str(d, abstime_ostr, &abstime_tmp);
  973. if (ret < 0)
  974. goto out_delete;
  975. }
  976. if (cpu_list) {
  977. ret = perf_session__cpu_bitmap(d->session, cpu_list,
  978. cpu_bitmap);
  979. if (ret < 0)
  980. goto out_delete;
  981. }
  982. ret = perf_session__process_events(d->session);
  983. if (ret) {
  984. pr_err("Failed to process %s\n", d->data.path);
  985. goto out_delete;
  986. }
  987. evlist__collapse_resort(d->session->evlist);
  988. if (pdiff.ptime_range)
  989. zfree(&pdiff.ptime_range);
  990. if (compute == COMPUTE_STREAM) {
  991. d->evlist_streams = evlist__create_streams(
  992. d->session->evlist, 5);
  993. if (!d->evlist_streams) {
  994. ret = -ENOMEM;
  995. goto out_delete;
  996. }
  997. }
  998. }
  999. if (compute == COMPUTE_STREAM)
  1000. stream_process();
  1001. else
  1002. data_process();
  1003. out_delete:
  1004. data__for_each_file(i, d) {
  1005. if (!IS_ERR(d->session))
  1006. perf_session__delete(d->session);
  1007. data__free(d);
  1008. }
  1009. free(data__files);
  1010. if (pdiff.ptime_range)
  1011. zfree(&pdiff.ptime_range);
  1012. if (abstime_ostr)
  1013. free(abstime_ostr);
  1014. return ret;
  1015. }
  1016. static const char * const diff_usage[] = {
  1017. "perf diff [<options>] [old_file] [new_file]",
  1018. NULL,
  1019. };
  1020. static const struct option options[] = {
  1021. OPT_INCR('v', "verbose", &verbose,
  1022. "be more verbose (show symbol address, etc)"),
  1023. OPT_BOOLEAN('q', "quiet", &quiet, "Do not show any warnings or messages"),
  1024. OPT_BOOLEAN('b', "baseline-only", &show_baseline_only,
  1025. "Show only items with match in baseline"),
  1026. OPT_CALLBACK('c', "compute", &compute,
  1027. "delta,delta-abs,ratio,wdiff:w1,w2 (default delta-abs),cycles",
  1028. "Entries differential computation selection",
  1029. setup_compute),
  1030. OPT_BOOLEAN('p', "period", &show_period,
  1031. "Show period values."),
  1032. OPT_BOOLEAN('F', "formula", &show_formula,
  1033. "Show formula."),
  1034. OPT_BOOLEAN(0, "cycles-hist", &cycles_hist,
  1035. "Show cycles histogram and standard deviation "
  1036. "- WARNING: use only with -c cycles."),
  1037. OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
  1038. "dump raw trace in ASCII"),
  1039. OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
  1040. OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
  1041. "file", "kallsyms pathname"),
  1042. OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
  1043. "load module symbols - WARNING: use only with -k and LIVE kernel"),
  1044. OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
  1045. "only consider symbols in these dsos"),
  1046. OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
  1047. "only consider symbols in these comms"),
  1048. OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
  1049. "only consider these symbols"),
  1050. OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
  1051. "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..."
  1052. " Please refer the man page for the complete list."),
  1053. OPT_STRING_NOEMPTY('t', "field-separator", &symbol_conf.field_sep, "separator",
  1054. "separator for columns, no spaces will be added between "
  1055. "columns '.' is reserved."),
  1056. OPT_CALLBACK(0, "symfs", NULL, "directory",
  1057. "Look for files with symbols relative to this directory",
  1058. symbol__config_symfs),
  1059. OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."),
  1060. OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
  1061. "How to display percentage of filtered entries", parse_filter_percentage),
  1062. OPT_STRING(0, "time", &pdiff.time_str, "str",
  1063. "Time span (time percent or absolute timestamp)"),
  1064. OPT_STRING(0, "cpu", &cpu_list, "cpu", "list of cpus to profile"),
  1065. OPT_STRING(0, "pid", &symbol_conf.pid_list_str, "pid[,pid...]",
  1066. "only consider symbols in these pids"),
  1067. OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]",
  1068. "only consider symbols in these tids"),
  1069. OPT_BOOLEAN(0, "stream", &pdiff.stream,
  1070. "Enable hot streams comparison."),
  1071. OPT_END()
  1072. };
  1073. static double baseline_percent(struct hist_entry *he)
  1074. {
  1075. u64 total = hists__total_period(he->hists);
  1076. return 100.0 * he->stat.period / total;
  1077. }
  1078. static int hpp__color_baseline(struct perf_hpp_fmt *fmt,
  1079. struct perf_hpp *hpp, struct hist_entry *he)
  1080. {
  1081. struct diff_hpp_fmt *dfmt =
  1082. container_of(fmt, struct diff_hpp_fmt, fmt);
  1083. double percent = baseline_percent(he);
  1084. char pfmt[20] = " ";
  1085. if (!he->dummy) {
  1086. scnprintf(pfmt, 20, "%%%d.2f%%%%", dfmt->header_width - 1);
  1087. return percent_color_snprintf(hpp->buf, hpp->size,
  1088. pfmt, percent);
  1089. } else
  1090. return scnprintf(hpp->buf, hpp->size, "%*s",
  1091. dfmt->header_width, pfmt);
  1092. }
  1093. static int hpp__entry_baseline(struct hist_entry *he, char *buf, size_t size)
  1094. {
  1095. double percent = baseline_percent(he);
  1096. const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%";
  1097. int ret = 0;
  1098. if (!he->dummy)
  1099. ret = scnprintf(buf, size, fmt, percent);
  1100. return ret;
  1101. }
  1102. static int cycles_printf(struct hist_entry *he, struct hist_entry *pair,
  1103. struct perf_hpp *hpp, int width)
  1104. {
  1105. struct block_hist *bh = container_of(he, struct block_hist, he);
  1106. struct block_hist *bh_pair = container_of(pair, struct block_hist, he);
  1107. struct hist_entry *block_he;
  1108. struct block_info *bi;
  1109. char buf[128];
  1110. char *start_line, *end_line;
  1111. block_he = hists__get_entry(&bh_pair->block_hists, bh->block_idx);
  1112. if (!block_he) {
  1113. hpp->skip = true;
  1114. return 0;
  1115. }
  1116. /*
  1117. * Avoid printing the warning "addr2line_init failed for ..."
  1118. */
  1119. symbol_conf.disable_add2line_warn = true;
  1120. bi = block_he->block_info;
  1121. start_line = map__srcline(he->ms.map, bi->sym->start + bi->start,
  1122. he->ms.sym);
  1123. end_line = map__srcline(he->ms.map, bi->sym->start + bi->end,
  1124. he->ms.sym);
  1125. if (start_line != SRCLINE_UNKNOWN &&
  1126. end_line != SRCLINE_UNKNOWN) {
  1127. scnprintf(buf, sizeof(buf), "[%s -> %s] %4ld",
  1128. start_line, end_line, block_he->diff.cycles);
  1129. } else {
  1130. scnprintf(buf, sizeof(buf), "[%7lx -> %7lx] %4ld",
  1131. bi->start, bi->end, block_he->diff.cycles);
  1132. }
  1133. zfree_srcline(&start_line);
  1134. zfree_srcline(&end_line);
  1135. return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
  1136. }
  1137. static int __hpp__color_compare(struct perf_hpp_fmt *fmt,
  1138. struct perf_hpp *hpp, struct hist_entry *he,
  1139. int comparison_method)
  1140. {
  1141. struct diff_hpp_fmt *dfmt =
  1142. container_of(fmt, struct diff_hpp_fmt, fmt);
  1143. struct hist_entry *pair = get_pair_fmt(he, dfmt);
  1144. double diff;
  1145. s64 wdiff;
  1146. char pfmt[20] = " ";
  1147. if (!pair) {
  1148. if (comparison_method == COMPUTE_CYCLES) {
  1149. struct block_hist *bh;
  1150. bh = container_of(he, struct block_hist, he);
  1151. if (bh->block_idx)
  1152. hpp->skip = true;
  1153. }
  1154. goto no_print;
  1155. }
  1156. switch (comparison_method) {
  1157. case COMPUTE_DELTA:
  1158. if (pair->diff.computed)
  1159. diff = pair->diff.period_ratio_delta;
  1160. else
  1161. diff = compute_delta(he, pair);
  1162. scnprintf(pfmt, 20, "%%%+d.2f%%%%", dfmt->header_width - 1);
  1163. return percent_color_snprintf(hpp->buf, hpp->size,
  1164. pfmt, diff);
  1165. case COMPUTE_RATIO:
  1166. if (he->dummy)
  1167. goto dummy_print;
  1168. if (pair->diff.computed)
  1169. diff = pair->diff.period_ratio;
  1170. else
  1171. diff = compute_ratio(he, pair);
  1172. scnprintf(pfmt, 20, "%%%d.6f", dfmt->header_width);
  1173. return value_color_snprintf(hpp->buf, hpp->size,
  1174. pfmt, diff);
  1175. case COMPUTE_WEIGHTED_DIFF:
  1176. if (he->dummy)
  1177. goto dummy_print;
  1178. if (pair->diff.computed)
  1179. wdiff = pair->diff.wdiff;
  1180. else
  1181. wdiff = compute_wdiff(he, pair);
  1182. scnprintf(pfmt, 20, "%%14ld", dfmt->header_width);
  1183. return color_snprintf(hpp->buf, hpp->size,
  1184. get_percent_color(wdiff),
  1185. pfmt, wdiff);
  1186. case COMPUTE_CYCLES:
  1187. return cycles_printf(he, pair, hpp, dfmt->header_width);
  1188. default:
  1189. BUG_ON(1);
  1190. }
  1191. dummy_print:
  1192. return scnprintf(hpp->buf, hpp->size, "%*s",
  1193. dfmt->header_width, "N/A");
  1194. no_print:
  1195. return scnprintf(hpp->buf, hpp->size, "%*s",
  1196. dfmt->header_width, pfmt);
  1197. }
  1198. static int hpp__color_delta(struct perf_hpp_fmt *fmt,
  1199. struct perf_hpp *hpp, struct hist_entry *he)
  1200. {
  1201. return __hpp__color_compare(fmt, hpp, he, COMPUTE_DELTA);
  1202. }
  1203. static int hpp__color_ratio(struct perf_hpp_fmt *fmt,
  1204. struct perf_hpp *hpp, struct hist_entry *he)
  1205. {
  1206. return __hpp__color_compare(fmt, hpp, he, COMPUTE_RATIO);
  1207. }
  1208. static int hpp__color_wdiff(struct perf_hpp_fmt *fmt,
  1209. struct perf_hpp *hpp, struct hist_entry *he)
  1210. {
  1211. return __hpp__color_compare(fmt, hpp, he, COMPUTE_WEIGHTED_DIFF);
  1212. }
  1213. static int hpp__color_cycles(struct perf_hpp_fmt *fmt,
  1214. struct perf_hpp *hpp, struct hist_entry *he)
  1215. {
  1216. return __hpp__color_compare(fmt, hpp, he, COMPUTE_CYCLES);
  1217. }
  1218. static int all_zero(unsigned long *vals, int len)
  1219. {
  1220. int i;
  1221. for (i = 0; i < len; i++)
  1222. if (vals[i] != 0)
  1223. return 0;
  1224. return 1;
  1225. }
  1226. static int print_cycles_spark(char *bf, int size, unsigned long *svals, u64 n)
  1227. {
  1228. int printed;
  1229. if (n <= 1)
  1230. return 0;
  1231. if (n > NUM_SPARKS)
  1232. n = NUM_SPARKS;
  1233. if (all_zero(svals, n))
  1234. return 0;
  1235. printed = print_spark(bf, size, svals, n);
  1236. printed += scnprintf(bf + printed, size - printed, " ");
  1237. return printed;
  1238. }
  1239. static int hpp__color_cycles_hist(struct perf_hpp_fmt *fmt,
  1240. struct perf_hpp *hpp, struct hist_entry *he)
  1241. {
  1242. struct diff_hpp_fmt *dfmt =
  1243. container_of(fmt, struct diff_hpp_fmt, fmt);
  1244. struct hist_entry *pair = get_pair_fmt(he, dfmt);
  1245. struct block_hist *bh = container_of(he, struct block_hist, he);
  1246. struct block_hist *bh_pair;
  1247. struct hist_entry *block_he;
  1248. char spark[32], buf[128];
  1249. double r;
  1250. int ret, pad;
  1251. if (!pair) {
  1252. if (bh->block_idx)
  1253. hpp->skip = true;
  1254. goto no_print;
  1255. }
  1256. bh_pair = container_of(pair, struct block_hist, he);
  1257. block_he = hists__get_entry(&bh_pair->block_hists, bh->block_idx);
  1258. if (!block_he) {
  1259. hpp->skip = true;
  1260. goto no_print;
  1261. }
  1262. ret = print_cycles_spark(spark, sizeof(spark), block_he->diff.svals,
  1263. block_he->diff.stats.n);
  1264. r = rel_stddev_stats(stddev_stats(&block_he->diff.stats),
  1265. avg_stats(&block_he->diff.stats));
  1266. if (ret) {
  1267. /*
  1268. * Padding spaces if number of sparks less than NUM_SPARKS
  1269. * otherwise the output is not aligned.
  1270. */
  1271. pad = NUM_SPARKS - ((ret - 1) / 3);
  1272. scnprintf(buf, sizeof(buf), "%s%5.1f%% %s", "\u00B1", r, spark);
  1273. ret = scnprintf(hpp->buf, hpp->size, "%*s",
  1274. dfmt->header_width, buf);
  1275. if (pad) {
  1276. ret += scnprintf(hpp->buf + ret, hpp->size - ret,
  1277. "%-*s", pad, " ");
  1278. }
  1279. return ret;
  1280. }
  1281. no_print:
  1282. return scnprintf(hpp->buf, hpp->size, "%*s",
  1283. dfmt->header_width, " ");
  1284. }
  1285. static void
  1286. hpp__entry_unpair(struct hist_entry *he, int idx, char *buf, size_t size)
  1287. {
  1288. switch (idx) {
  1289. case PERF_HPP_DIFF__PERIOD_BASELINE:
  1290. scnprintf(buf, size, "%" PRIu64, he->stat.period);
  1291. break;
  1292. default:
  1293. break;
  1294. }
  1295. }
  1296. static void
  1297. hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair,
  1298. int idx, char *buf, size_t size)
  1299. {
  1300. double diff;
  1301. double ratio;
  1302. s64 wdiff;
  1303. switch (idx) {
  1304. case PERF_HPP_DIFF__DELTA:
  1305. case PERF_HPP_DIFF__DELTA_ABS:
  1306. if (pair->diff.computed)
  1307. diff = pair->diff.period_ratio_delta;
  1308. else
  1309. diff = compute_delta(he, pair);
  1310. scnprintf(buf, size, "%+4.2F%%", diff);
  1311. break;
  1312. case PERF_HPP_DIFF__RATIO:
  1313. /* No point for ratio number if we are dummy.. */
  1314. if (he->dummy) {
  1315. scnprintf(buf, size, "N/A");
  1316. break;
  1317. }
  1318. if (pair->diff.computed)
  1319. ratio = pair->diff.period_ratio;
  1320. else
  1321. ratio = compute_ratio(he, pair);
  1322. if (ratio > 0.0)
  1323. scnprintf(buf, size, "%14.6F", ratio);
  1324. break;
  1325. case PERF_HPP_DIFF__WEIGHTED_DIFF:
  1326. /* No point for wdiff number if we are dummy.. */
  1327. if (he->dummy) {
  1328. scnprintf(buf, size, "N/A");
  1329. break;
  1330. }
  1331. if (pair->diff.computed)
  1332. wdiff = pair->diff.wdiff;
  1333. else
  1334. wdiff = compute_wdiff(he, pair);
  1335. if (wdiff != 0)
  1336. scnprintf(buf, size, "%14ld", wdiff);
  1337. break;
  1338. case PERF_HPP_DIFF__FORMULA:
  1339. formula_fprintf(he, pair, buf, size);
  1340. break;
  1341. case PERF_HPP_DIFF__PERIOD:
  1342. scnprintf(buf, size, "%" PRIu64, pair->stat.period);
  1343. break;
  1344. default:
  1345. BUG_ON(1);
  1346. }
  1347. }
  1348. static void
  1349. __hpp__entry_global(struct hist_entry *he, struct diff_hpp_fmt *dfmt,
  1350. char *buf, size_t size)
  1351. {
  1352. struct hist_entry *pair = get_pair_fmt(he, dfmt);
  1353. int idx = dfmt->idx;
  1354. /* baseline is special */
  1355. if (idx == PERF_HPP_DIFF__BASELINE)
  1356. hpp__entry_baseline(he, buf, size);
  1357. else {
  1358. if (pair)
  1359. hpp__entry_pair(he, pair, idx, buf, size);
  1360. else
  1361. hpp__entry_unpair(he, idx, buf, size);
  1362. }
  1363. }
  1364. static int hpp__entry_global(struct perf_hpp_fmt *_fmt, struct perf_hpp *hpp,
  1365. struct hist_entry *he)
  1366. {
  1367. struct diff_hpp_fmt *dfmt =
  1368. container_of(_fmt, struct diff_hpp_fmt, fmt);
  1369. char buf[MAX_COL_WIDTH] = " ";
  1370. __hpp__entry_global(he, dfmt, buf, MAX_COL_WIDTH);
  1371. if (symbol_conf.field_sep)
  1372. return scnprintf(hpp->buf, hpp->size, "%s", buf);
  1373. else
  1374. return scnprintf(hpp->buf, hpp->size, "%*s",
  1375. dfmt->header_width, buf);
  1376. }
  1377. static int hpp__header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
  1378. struct hists *hists __maybe_unused,
  1379. int line __maybe_unused,
  1380. int *span __maybe_unused)
  1381. {
  1382. struct diff_hpp_fmt *dfmt =
  1383. container_of(fmt, struct diff_hpp_fmt, fmt);
  1384. BUG_ON(!dfmt->header);
  1385. return scnprintf(hpp->buf, hpp->size, dfmt->header);
  1386. }
  1387. static int hpp__width(struct perf_hpp_fmt *fmt,
  1388. struct perf_hpp *hpp __maybe_unused,
  1389. struct hists *hists __maybe_unused)
  1390. {
  1391. struct diff_hpp_fmt *dfmt =
  1392. container_of(fmt, struct diff_hpp_fmt, fmt);
  1393. BUG_ON(dfmt->header_width <= 0);
  1394. return dfmt->header_width;
  1395. }
  1396. static void init_header(struct data__file *d, struct diff_hpp_fmt *dfmt)
  1397. {
  1398. #define MAX_HEADER_NAME 100
  1399. char buf_indent[MAX_HEADER_NAME];
  1400. char buf[MAX_HEADER_NAME];
  1401. const char *header = NULL;
  1402. int width = 0;
  1403. BUG_ON(dfmt->idx >= PERF_HPP_DIFF__MAX_INDEX);
  1404. header = columns[dfmt->idx].name;
  1405. width = columns[dfmt->idx].width;
  1406. /* Only our defined HPP fmts should appear here. */
  1407. BUG_ON(!header);
  1408. if (data__files_cnt > 2)
  1409. scnprintf(buf, MAX_HEADER_NAME, "%s/%d", header, d->idx);
  1410. #define NAME (data__files_cnt > 2 ? buf : header)
  1411. dfmt->header_width = width;
  1412. width = (int) strlen(NAME);
  1413. if (dfmt->header_width < width)
  1414. dfmt->header_width = width;
  1415. scnprintf(buf_indent, MAX_HEADER_NAME, "%*s",
  1416. dfmt->header_width, NAME);
  1417. dfmt->header = strdup(buf_indent);
  1418. #undef MAX_HEADER_NAME
  1419. #undef NAME
  1420. }
  1421. static void data__hpp_register(struct data__file *d, int idx)
  1422. {
  1423. struct diff_hpp_fmt *dfmt = &d->fmt[idx];
  1424. struct perf_hpp_fmt *fmt = &dfmt->fmt;
  1425. dfmt->idx = idx;
  1426. fmt->header = hpp__header;
  1427. fmt->width = hpp__width;
  1428. fmt->entry = hpp__entry_global;
  1429. fmt->cmp = hist_entry__cmp_nop;
  1430. fmt->collapse = hist_entry__cmp_nop;
  1431. /* TODO more colors */
  1432. switch (idx) {
  1433. case PERF_HPP_DIFF__BASELINE:
  1434. fmt->color = hpp__color_baseline;
  1435. fmt->sort = hist_entry__cmp_baseline;
  1436. break;
  1437. case PERF_HPP_DIFF__DELTA:
  1438. fmt->color = hpp__color_delta;
  1439. fmt->sort = hist_entry__cmp_delta;
  1440. break;
  1441. case PERF_HPP_DIFF__RATIO:
  1442. fmt->color = hpp__color_ratio;
  1443. fmt->sort = hist_entry__cmp_ratio;
  1444. break;
  1445. case PERF_HPP_DIFF__WEIGHTED_DIFF:
  1446. fmt->color = hpp__color_wdiff;
  1447. fmt->sort = hist_entry__cmp_wdiff;
  1448. break;
  1449. case PERF_HPP_DIFF__DELTA_ABS:
  1450. fmt->color = hpp__color_delta;
  1451. fmt->sort = hist_entry__cmp_delta_abs;
  1452. break;
  1453. case PERF_HPP_DIFF__CYCLES:
  1454. fmt->color = hpp__color_cycles;
  1455. fmt->sort = hist_entry__cmp_nop;
  1456. break;
  1457. case PERF_HPP_DIFF__CYCLES_HIST:
  1458. fmt->color = hpp__color_cycles_hist;
  1459. fmt->sort = hist_entry__cmp_nop;
  1460. break;
  1461. default:
  1462. fmt->sort = hist_entry__cmp_nop;
  1463. break;
  1464. }
  1465. init_header(d, dfmt);
  1466. perf_hpp__column_register(fmt);
  1467. perf_hpp__register_sort_field(fmt);
  1468. }
  1469. static int ui_init(void)
  1470. {
  1471. struct data__file *d;
  1472. struct perf_hpp_fmt *fmt;
  1473. int i;
  1474. data__for_each_file(i, d) {
  1475. /*
  1476. * Baseline or compute related columns:
  1477. *
  1478. * PERF_HPP_DIFF__BASELINE
  1479. * PERF_HPP_DIFF__DELTA
  1480. * PERF_HPP_DIFF__RATIO
  1481. * PERF_HPP_DIFF__WEIGHTED_DIFF
  1482. * PERF_HPP_DIFF__CYCLES
  1483. */
  1484. data__hpp_register(d, i ? compute_2_hpp[compute] :
  1485. PERF_HPP_DIFF__BASELINE);
  1486. if (cycles_hist && i)
  1487. data__hpp_register(d, PERF_HPP_DIFF__CYCLES_HIST);
  1488. /*
  1489. * And the rest:
  1490. *
  1491. * PERF_HPP_DIFF__FORMULA
  1492. * PERF_HPP_DIFF__PERIOD
  1493. * PERF_HPP_DIFF__PERIOD_BASELINE
  1494. */
  1495. if (show_formula && i)
  1496. data__hpp_register(d, PERF_HPP_DIFF__FORMULA);
  1497. if (show_period)
  1498. data__hpp_register(d, i ? PERF_HPP_DIFF__PERIOD :
  1499. PERF_HPP_DIFF__PERIOD_BASELINE);
  1500. }
  1501. if (!sort_compute)
  1502. return 0;
  1503. /*
  1504. * Prepend an fmt to sort on columns at 'sort_compute' first.
  1505. * This fmt is added only to the sort list but not to the
  1506. * output fields list.
  1507. *
  1508. * Note that this column (data) can be compared twice - one
  1509. * for this 'sort_compute' fmt and another for the normal
  1510. * diff_hpp_fmt. But it shouldn't a problem as most entries
  1511. * will be sorted out by first try or baseline and comparing
  1512. * is not a costly operation.
  1513. */
  1514. fmt = zalloc(sizeof(*fmt));
  1515. if (fmt == NULL) {
  1516. pr_err("Memory allocation failed\n");
  1517. return -1;
  1518. }
  1519. fmt->cmp = hist_entry__cmp_nop;
  1520. fmt->collapse = hist_entry__cmp_nop;
  1521. switch (compute) {
  1522. case COMPUTE_DELTA:
  1523. fmt->sort = hist_entry__cmp_delta_idx;
  1524. break;
  1525. case COMPUTE_RATIO:
  1526. fmt->sort = hist_entry__cmp_ratio_idx;
  1527. break;
  1528. case COMPUTE_WEIGHTED_DIFF:
  1529. fmt->sort = hist_entry__cmp_wdiff_idx;
  1530. break;
  1531. case COMPUTE_DELTA_ABS:
  1532. fmt->sort = hist_entry__cmp_delta_abs_idx;
  1533. break;
  1534. case COMPUTE_CYCLES:
  1535. /*
  1536. * Should set since 'fmt->sort' is called without
  1537. * checking valid during sorting
  1538. */
  1539. fmt->sort = hist_entry__cmp_nop;
  1540. break;
  1541. default:
  1542. BUG_ON(1);
  1543. }
  1544. perf_hpp__prepend_sort_field(fmt);
  1545. return 0;
  1546. }
  1547. static int data_init(int argc, const char **argv)
  1548. {
  1549. struct data__file *d;
  1550. static const char *defaults[] = {
  1551. "perf.data.old",
  1552. "perf.data",
  1553. };
  1554. bool use_default = true;
  1555. int i;
  1556. data__files_cnt = 2;
  1557. if (argc) {
  1558. if (argc == 1)
  1559. defaults[1] = argv[0];
  1560. else {
  1561. data__files_cnt = argc;
  1562. use_default = false;
  1563. }
  1564. } else if (perf_guest) {
  1565. defaults[0] = "perf.data.host";
  1566. defaults[1] = "perf.data.guest";
  1567. }
  1568. if (sort_compute >= (unsigned int) data__files_cnt) {
  1569. pr_err("Order option out of limit.\n");
  1570. return -EINVAL;
  1571. }
  1572. data__files = zalloc(sizeof(*data__files) * data__files_cnt);
  1573. if (!data__files)
  1574. return -ENOMEM;
  1575. data__for_each_file(i, d) {
  1576. struct perf_data *data = &d->data;
  1577. data->path = use_default ? defaults[i] : argv[i];
  1578. data->mode = PERF_DATA_MODE_READ;
  1579. data->force = force;
  1580. d->idx = i;
  1581. }
  1582. return 0;
  1583. }
  1584. static int diff__config(const char *var, const char *value,
  1585. void *cb __maybe_unused)
  1586. {
  1587. if (!strcmp(var, "diff.order")) {
  1588. int ret;
  1589. if (perf_config_int(&ret, var, value) < 0)
  1590. return -1;
  1591. sort_compute = ret;
  1592. return 0;
  1593. }
  1594. if (!strcmp(var, "diff.compute")) {
  1595. if (!strcmp(value, "delta")) {
  1596. compute = COMPUTE_DELTA;
  1597. } else if (!strcmp(value, "delta-abs")) {
  1598. compute = COMPUTE_DELTA_ABS;
  1599. } else if (!strcmp(value, "ratio")) {
  1600. compute = COMPUTE_RATIO;
  1601. } else if (!strcmp(value, "wdiff")) {
  1602. compute = COMPUTE_WEIGHTED_DIFF;
  1603. } else {
  1604. pr_err("Invalid compute method: %s\n", value);
  1605. return -1;
  1606. }
  1607. }
  1608. return 0;
  1609. }
  1610. int cmd_diff(int argc, const char **argv)
  1611. {
  1612. int ret = hists__init();
  1613. if (ret < 0)
  1614. return ret;
  1615. perf_tool__init(&pdiff.tool, /*ordered_events=*/true);
  1616. pdiff.tool.sample = diff__process_sample_event;
  1617. pdiff.tool.mmap = perf_event__process_mmap;
  1618. pdiff.tool.mmap2 = perf_event__process_mmap2;
  1619. pdiff.tool.comm = perf_event__process_comm;
  1620. pdiff.tool.exit = perf_event__process_exit;
  1621. pdiff.tool.fork = perf_event__process_fork;
  1622. pdiff.tool.lost = perf_event__process_lost;
  1623. pdiff.tool.namespaces = perf_event__process_namespaces;
  1624. pdiff.tool.cgroup = perf_event__process_cgroup;
  1625. pdiff.tool.ordering_requires_timestamps = true;
  1626. perf_config(diff__config, NULL);
  1627. argc = parse_options(argc, argv, options, diff_usage, 0);
  1628. if (quiet)
  1629. perf_quiet_option();
  1630. if (cycles_hist && (compute != COMPUTE_CYCLES))
  1631. usage_with_options(diff_usage, options);
  1632. if (pdiff.stream)
  1633. compute = COMPUTE_STREAM;
  1634. symbol__annotation_init();
  1635. if (symbol__init(NULL) < 0)
  1636. return -1;
  1637. if (data_init(argc, argv) < 0)
  1638. return -1;
  1639. if (check_file_brstack() < 0)
  1640. return -1;
  1641. if ((compute == COMPUTE_CYCLES || compute == COMPUTE_STREAM)
  1642. && !pdiff.has_br_stack) {
  1643. return -1;
  1644. }
  1645. if (compute == COMPUTE_STREAM) {
  1646. symbol_conf.show_branchflag_count = true;
  1647. symbol_conf.disable_add2line_warn = true;
  1648. callchain_param.mode = CHAIN_FLAT;
  1649. callchain_param.key = CCKEY_SRCLINE;
  1650. callchain_param.branch_callstack = 1;
  1651. symbol_conf.use_callchain = true;
  1652. callchain_register_param(&callchain_param);
  1653. sort_order = "srcline,symbol,dso";
  1654. } else {
  1655. if (ui_init() < 0)
  1656. return -1;
  1657. sort__mode = SORT_MODE__DIFF;
  1658. }
  1659. if (setup_sorting(/*evlist=*/NULL, perf_session__env(data__files[0].session)) < 0)
  1660. usage_with_options(diff_usage, options);
  1661. setup_pager();
  1662. sort__setup_elide(NULL);
  1663. return __cmd_diff();
  1664. }