builtin-diff.c 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364
  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 "util/debug.h"
  10. #include "util/event.h"
  11. #include "util/hist.h"
  12. #include "util/evsel.h"
  13. #include "util/evlist.h"
  14. #include "util/session.h"
  15. #include "util/tool.h"
  16. #include "util/sort.h"
  17. #include "util/symbol.h"
  18. #include "util/util.h"
  19. #include "util/data.h"
  20. #include "util/config.h"
  21. #include <errno.h>
  22. #include <inttypes.h>
  23. #include <stdlib.h>
  24. #include <math.h>
  25. /* Diff command specific HPP columns. */
  26. enum {
  27. PERF_HPP_DIFF__BASELINE,
  28. PERF_HPP_DIFF__PERIOD,
  29. PERF_HPP_DIFF__PERIOD_BASELINE,
  30. PERF_HPP_DIFF__DELTA,
  31. PERF_HPP_DIFF__RATIO,
  32. PERF_HPP_DIFF__WEIGHTED_DIFF,
  33. PERF_HPP_DIFF__FORMULA,
  34. PERF_HPP_DIFF__DELTA_ABS,
  35. PERF_HPP_DIFF__MAX_INDEX
  36. };
  37. struct diff_hpp_fmt {
  38. struct perf_hpp_fmt fmt;
  39. int idx;
  40. char *header;
  41. int header_width;
  42. };
  43. struct data__file {
  44. struct perf_session *session;
  45. struct perf_data data;
  46. int idx;
  47. struct hists *hists;
  48. struct diff_hpp_fmt fmt[PERF_HPP_DIFF__MAX_INDEX];
  49. };
  50. static struct data__file *data__files;
  51. static int data__files_cnt;
  52. #define data__for_each_file_start(i, d, s) \
  53. for (i = s, d = &data__files[s]; \
  54. i < data__files_cnt; \
  55. i++, d = &data__files[i])
  56. #define data__for_each_file(i, d) data__for_each_file_start(i, d, 0)
  57. #define data__for_each_file_new(i, d) data__for_each_file_start(i, d, 1)
  58. static bool force;
  59. static bool show_period;
  60. static bool show_formula;
  61. static bool show_baseline_only;
  62. static unsigned int sort_compute = 1;
  63. static s64 compute_wdiff_w1;
  64. static s64 compute_wdiff_w2;
  65. enum {
  66. COMPUTE_DELTA,
  67. COMPUTE_RATIO,
  68. COMPUTE_WEIGHTED_DIFF,
  69. COMPUTE_DELTA_ABS,
  70. COMPUTE_MAX,
  71. };
  72. const char *compute_names[COMPUTE_MAX] = {
  73. [COMPUTE_DELTA] = "delta",
  74. [COMPUTE_DELTA_ABS] = "delta-abs",
  75. [COMPUTE_RATIO] = "ratio",
  76. [COMPUTE_WEIGHTED_DIFF] = "wdiff",
  77. };
  78. static int compute = COMPUTE_DELTA_ABS;
  79. static int compute_2_hpp[COMPUTE_MAX] = {
  80. [COMPUTE_DELTA] = PERF_HPP_DIFF__DELTA,
  81. [COMPUTE_DELTA_ABS] = PERF_HPP_DIFF__DELTA_ABS,
  82. [COMPUTE_RATIO] = PERF_HPP_DIFF__RATIO,
  83. [COMPUTE_WEIGHTED_DIFF] = PERF_HPP_DIFF__WEIGHTED_DIFF,
  84. };
  85. #define MAX_COL_WIDTH 70
  86. static struct header_column {
  87. const char *name;
  88. int width;
  89. } columns[PERF_HPP_DIFF__MAX_INDEX] = {
  90. [PERF_HPP_DIFF__BASELINE] = {
  91. .name = "Baseline",
  92. },
  93. [PERF_HPP_DIFF__PERIOD] = {
  94. .name = "Period",
  95. .width = 14,
  96. },
  97. [PERF_HPP_DIFF__PERIOD_BASELINE] = {
  98. .name = "Base period",
  99. .width = 14,
  100. },
  101. [PERF_HPP_DIFF__DELTA] = {
  102. .name = "Delta",
  103. .width = 7,
  104. },
  105. [PERF_HPP_DIFF__DELTA_ABS] = {
  106. .name = "Delta Abs",
  107. .width = 7,
  108. },
  109. [PERF_HPP_DIFF__RATIO] = {
  110. .name = "Ratio",
  111. .width = 14,
  112. },
  113. [PERF_HPP_DIFF__WEIGHTED_DIFF] = {
  114. .name = "Weighted diff",
  115. .width = 14,
  116. },
  117. [PERF_HPP_DIFF__FORMULA] = {
  118. .name = "Formula",
  119. .width = MAX_COL_WIDTH,
  120. }
  121. };
  122. static int setup_compute_opt_wdiff(char *opt)
  123. {
  124. char *w1_str = opt;
  125. char *w2_str;
  126. int ret = -EINVAL;
  127. if (!opt)
  128. goto out;
  129. w2_str = strchr(opt, ',');
  130. if (!w2_str)
  131. goto out;
  132. *w2_str++ = 0x0;
  133. if (!*w2_str)
  134. goto out;
  135. compute_wdiff_w1 = strtol(w1_str, NULL, 10);
  136. compute_wdiff_w2 = strtol(w2_str, NULL, 10);
  137. if (!compute_wdiff_w1 || !compute_wdiff_w2)
  138. goto out;
  139. pr_debug("compute wdiff w1(%" PRId64 ") w2(%" PRId64 ")\n",
  140. compute_wdiff_w1, compute_wdiff_w2);
  141. ret = 0;
  142. out:
  143. if (ret)
  144. pr_err("Failed: wrong weight data, use 'wdiff:w1,w2'\n");
  145. return ret;
  146. }
  147. static int setup_compute_opt(char *opt)
  148. {
  149. if (compute == COMPUTE_WEIGHTED_DIFF)
  150. return setup_compute_opt_wdiff(opt);
  151. if (opt) {
  152. pr_err("Failed: extra option specified '%s'", opt);
  153. return -EINVAL;
  154. }
  155. return 0;
  156. }
  157. static int setup_compute(const struct option *opt, const char *str,
  158. int unset __maybe_unused)
  159. {
  160. int *cp = (int *) opt->value;
  161. char *cstr = (char *) str;
  162. char buf[50];
  163. unsigned i;
  164. char *option;
  165. if (!str) {
  166. *cp = COMPUTE_DELTA;
  167. return 0;
  168. }
  169. option = strchr(str, ':');
  170. if (option) {
  171. unsigned len = option++ - str;
  172. /*
  173. * The str data are not writeable, so we need
  174. * to use another buffer.
  175. */
  176. /* No option value is longer. */
  177. if (len >= sizeof(buf))
  178. return -EINVAL;
  179. strncpy(buf, str, len);
  180. buf[len] = 0x0;
  181. cstr = buf;
  182. }
  183. for (i = 0; i < COMPUTE_MAX; i++)
  184. if (!strcmp(cstr, compute_names[i])) {
  185. *cp = i;
  186. return setup_compute_opt(option);
  187. }
  188. pr_err("Failed: '%s' is not computation method "
  189. "(use 'delta','ratio' or 'wdiff')\n", str);
  190. return -EINVAL;
  191. }
  192. static double period_percent(struct hist_entry *he, u64 period)
  193. {
  194. u64 total = hists__total_period(he->hists);
  195. return (period * 100.0) / total;
  196. }
  197. static double compute_delta(struct hist_entry *he, struct hist_entry *pair)
  198. {
  199. double old_percent = period_percent(he, he->stat.period);
  200. double new_percent = period_percent(pair, pair->stat.period);
  201. pair->diff.period_ratio_delta = new_percent - old_percent;
  202. pair->diff.computed = true;
  203. return pair->diff.period_ratio_delta;
  204. }
  205. static double compute_ratio(struct hist_entry *he, struct hist_entry *pair)
  206. {
  207. double old_period = he->stat.period ?: 1;
  208. double new_period = pair->stat.period;
  209. pair->diff.computed = true;
  210. pair->diff.period_ratio = new_period / old_period;
  211. return pair->diff.period_ratio;
  212. }
  213. static s64 compute_wdiff(struct hist_entry *he, struct hist_entry *pair)
  214. {
  215. u64 old_period = he->stat.period;
  216. u64 new_period = pair->stat.period;
  217. pair->diff.computed = true;
  218. pair->diff.wdiff = new_period * compute_wdiff_w2 -
  219. old_period * compute_wdiff_w1;
  220. return pair->diff.wdiff;
  221. }
  222. static int formula_delta(struct hist_entry *he, struct hist_entry *pair,
  223. char *buf, size_t size)
  224. {
  225. u64 he_total = he->hists->stats.total_period;
  226. u64 pair_total = pair->hists->stats.total_period;
  227. if (symbol_conf.filter_relative) {
  228. he_total = he->hists->stats.total_non_filtered_period;
  229. pair_total = pair->hists->stats.total_non_filtered_period;
  230. }
  231. return scnprintf(buf, size,
  232. "(%" PRIu64 " * 100 / %" PRIu64 ") - "
  233. "(%" PRIu64 " * 100 / %" PRIu64 ")",
  234. pair->stat.period, pair_total,
  235. he->stat.period, he_total);
  236. }
  237. static int formula_ratio(struct hist_entry *he, struct hist_entry *pair,
  238. char *buf, size_t size)
  239. {
  240. double old_period = he->stat.period;
  241. double new_period = pair->stat.period;
  242. return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period);
  243. }
  244. static int formula_wdiff(struct hist_entry *he, struct hist_entry *pair,
  245. char *buf, size_t size)
  246. {
  247. u64 old_period = he->stat.period;
  248. u64 new_period = pair->stat.period;
  249. return scnprintf(buf, size,
  250. "(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")",
  251. new_period, compute_wdiff_w2, old_period, compute_wdiff_w1);
  252. }
  253. static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair,
  254. char *buf, size_t size)
  255. {
  256. switch (compute) {
  257. case COMPUTE_DELTA:
  258. case COMPUTE_DELTA_ABS:
  259. return formula_delta(he, pair, buf, size);
  260. case COMPUTE_RATIO:
  261. return formula_ratio(he, pair, buf, size);
  262. case COMPUTE_WEIGHTED_DIFF:
  263. return formula_wdiff(he, pair, buf, size);
  264. default:
  265. BUG_ON(1);
  266. }
  267. return -1;
  268. }
  269. static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
  270. union perf_event *event,
  271. struct perf_sample *sample,
  272. struct perf_evsel *evsel,
  273. struct machine *machine)
  274. {
  275. struct addr_location al;
  276. struct hists *hists = evsel__hists(evsel);
  277. int ret = -1;
  278. if (machine__resolve(machine, &al, sample) < 0) {
  279. pr_warning("problem processing %d event, skipping it.\n",
  280. event->header.type);
  281. return -1;
  282. }
  283. if (!hists__add_entry(hists, &al, NULL, NULL, NULL, sample, true)) {
  284. pr_warning("problem incrementing symbol period, skipping event\n");
  285. goto out_put;
  286. }
  287. /*
  288. * The total_period is updated here before going to the output
  289. * tree since normally only the baseline hists will call
  290. * hists__output_resort() and precompute needs the total
  291. * period in order to sort entries by percentage delta.
  292. */
  293. hists->stats.total_period += sample->period;
  294. if (!al.filtered)
  295. hists->stats.total_non_filtered_period += sample->period;
  296. ret = 0;
  297. out_put:
  298. addr_location__put(&al);
  299. return ret;
  300. }
  301. static struct perf_tool tool = {
  302. .sample = diff__process_sample_event,
  303. .mmap = perf_event__process_mmap,
  304. .mmap2 = perf_event__process_mmap2,
  305. .comm = perf_event__process_comm,
  306. .exit = perf_event__process_exit,
  307. .fork = perf_event__process_fork,
  308. .lost = perf_event__process_lost,
  309. .namespaces = perf_event__process_namespaces,
  310. .ordered_events = true,
  311. .ordering_requires_timestamps = true,
  312. };
  313. static struct perf_evsel *evsel_match(struct perf_evsel *evsel,
  314. struct perf_evlist *evlist)
  315. {
  316. struct perf_evsel *e;
  317. evlist__for_each_entry(evlist, e) {
  318. if (perf_evsel__match2(evsel, e))
  319. return e;
  320. }
  321. return NULL;
  322. }
  323. static void perf_evlist__collapse_resort(struct perf_evlist *evlist)
  324. {
  325. struct perf_evsel *evsel;
  326. evlist__for_each_entry(evlist, evsel) {
  327. struct hists *hists = evsel__hists(evsel);
  328. hists__collapse_resort(hists, NULL);
  329. }
  330. }
  331. static struct data__file *fmt_to_data_file(struct perf_hpp_fmt *fmt)
  332. {
  333. struct diff_hpp_fmt *dfmt = container_of(fmt, struct diff_hpp_fmt, fmt);
  334. void *ptr = dfmt - dfmt->idx;
  335. struct data__file *d = container_of(ptr, struct data__file, fmt);
  336. return d;
  337. }
  338. static struct hist_entry*
  339. get_pair_data(struct hist_entry *he, struct data__file *d)
  340. {
  341. if (hist_entry__has_pairs(he)) {
  342. struct hist_entry *pair;
  343. list_for_each_entry(pair, &he->pairs.head, pairs.node)
  344. if (pair->hists == d->hists)
  345. return pair;
  346. }
  347. return NULL;
  348. }
  349. static struct hist_entry*
  350. get_pair_fmt(struct hist_entry *he, struct diff_hpp_fmt *dfmt)
  351. {
  352. struct data__file *d = fmt_to_data_file(&dfmt->fmt);
  353. return get_pair_data(he, d);
  354. }
  355. static void hists__baseline_only(struct hists *hists)
  356. {
  357. struct rb_root *root;
  358. struct rb_node *next;
  359. if (hists__has(hists, need_collapse))
  360. root = &hists->entries_collapsed;
  361. else
  362. root = hists->entries_in;
  363. next = rb_first(root);
  364. while (next != NULL) {
  365. struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node_in);
  366. next = rb_next(&he->rb_node_in);
  367. if (!hist_entry__next_pair(he)) {
  368. rb_erase(&he->rb_node_in, root);
  369. hist_entry__delete(he);
  370. }
  371. }
  372. }
  373. static void hists__precompute(struct hists *hists)
  374. {
  375. struct rb_root *root;
  376. struct rb_node *next;
  377. if (hists__has(hists, need_collapse))
  378. root = &hists->entries_collapsed;
  379. else
  380. root = hists->entries_in;
  381. next = rb_first(root);
  382. while (next != NULL) {
  383. struct hist_entry *he, *pair;
  384. struct data__file *d;
  385. int i;
  386. he = rb_entry(next, struct hist_entry, rb_node_in);
  387. next = rb_next(&he->rb_node_in);
  388. data__for_each_file_new(i, d) {
  389. pair = get_pair_data(he, d);
  390. if (!pair)
  391. continue;
  392. switch (compute) {
  393. case COMPUTE_DELTA:
  394. case COMPUTE_DELTA_ABS:
  395. compute_delta(he, pair);
  396. break;
  397. case COMPUTE_RATIO:
  398. compute_ratio(he, pair);
  399. break;
  400. case COMPUTE_WEIGHTED_DIFF:
  401. compute_wdiff(he, pair);
  402. break;
  403. default:
  404. BUG_ON(1);
  405. }
  406. }
  407. }
  408. }
  409. static int64_t cmp_doubles(double l, double r)
  410. {
  411. if (l > r)
  412. return -1;
  413. else if (l < r)
  414. return 1;
  415. else
  416. return 0;
  417. }
  418. static int64_t
  419. __hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
  420. int c)
  421. {
  422. switch (c) {
  423. case COMPUTE_DELTA:
  424. {
  425. double l = left->diff.period_ratio_delta;
  426. double r = right->diff.period_ratio_delta;
  427. return cmp_doubles(l, r);
  428. }
  429. case COMPUTE_DELTA_ABS:
  430. {
  431. double l = fabs(left->diff.period_ratio_delta);
  432. double r = fabs(right->diff.period_ratio_delta);
  433. return cmp_doubles(l, r);
  434. }
  435. case COMPUTE_RATIO:
  436. {
  437. double l = left->diff.period_ratio;
  438. double r = right->diff.period_ratio;
  439. return cmp_doubles(l, r);
  440. }
  441. case COMPUTE_WEIGHTED_DIFF:
  442. {
  443. s64 l = left->diff.wdiff;
  444. s64 r = right->diff.wdiff;
  445. return r - l;
  446. }
  447. default:
  448. BUG_ON(1);
  449. }
  450. return 0;
  451. }
  452. static int64_t
  453. hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
  454. int c, int sort_idx)
  455. {
  456. bool pairs_left = hist_entry__has_pairs(left);
  457. bool pairs_right = hist_entry__has_pairs(right);
  458. struct hist_entry *p_right, *p_left;
  459. if (!pairs_left && !pairs_right)
  460. return 0;
  461. if (!pairs_left || !pairs_right)
  462. return pairs_left ? -1 : 1;
  463. p_left = get_pair_data(left, &data__files[sort_idx]);
  464. p_right = get_pair_data(right, &data__files[sort_idx]);
  465. if (!p_left && !p_right)
  466. return 0;
  467. if (!p_left || !p_right)
  468. return p_left ? -1 : 1;
  469. /*
  470. * We have 2 entries of same kind, let's
  471. * make the data comparison.
  472. */
  473. return __hist_entry__cmp_compute(p_left, p_right, c);
  474. }
  475. static int64_t
  476. hist_entry__cmp_compute_idx(struct hist_entry *left, struct hist_entry *right,
  477. int c, int sort_idx)
  478. {
  479. struct hist_entry *p_right, *p_left;
  480. p_left = get_pair_data(left, &data__files[sort_idx]);
  481. p_right = get_pair_data(right, &data__files[sort_idx]);
  482. if (!p_left && !p_right)
  483. return 0;
  484. if (!p_left || !p_right)
  485. return p_left ? -1 : 1;
  486. if (c != COMPUTE_DELTA && c != COMPUTE_DELTA_ABS) {
  487. /*
  488. * The delta can be computed without the baseline, but
  489. * others are not. Put those entries which have no
  490. * values below.
  491. */
  492. if (left->dummy && right->dummy)
  493. return 0;
  494. if (left->dummy || right->dummy)
  495. return left->dummy ? 1 : -1;
  496. }
  497. return __hist_entry__cmp_compute(p_left, p_right, c);
  498. }
  499. static int64_t
  500. hist_entry__cmp_nop(struct perf_hpp_fmt *fmt __maybe_unused,
  501. struct hist_entry *left __maybe_unused,
  502. struct hist_entry *right __maybe_unused)
  503. {
  504. return 0;
  505. }
  506. static int64_t
  507. hist_entry__cmp_baseline(struct perf_hpp_fmt *fmt __maybe_unused,
  508. struct hist_entry *left, struct hist_entry *right)
  509. {
  510. if (left->stat.period == right->stat.period)
  511. return 0;
  512. return left->stat.period > right->stat.period ? 1 : -1;
  513. }
  514. static int64_t
  515. hist_entry__cmp_delta(struct perf_hpp_fmt *fmt,
  516. struct hist_entry *left, struct hist_entry *right)
  517. {
  518. struct data__file *d = fmt_to_data_file(fmt);
  519. return hist_entry__cmp_compute(right, left, COMPUTE_DELTA, d->idx);
  520. }
  521. static int64_t
  522. hist_entry__cmp_delta_abs(struct perf_hpp_fmt *fmt,
  523. struct hist_entry *left, struct hist_entry *right)
  524. {
  525. struct data__file *d = fmt_to_data_file(fmt);
  526. return hist_entry__cmp_compute(right, left, COMPUTE_DELTA_ABS, d->idx);
  527. }
  528. static int64_t
  529. hist_entry__cmp_ratio(struct perf_hpp_fmt *fmt,
  530. struct hist_entry *left, struct hist_entry *right)
  531. {
  532. struct data__file *d = fmt_to_data_file(fmt);
  533. return hist_entry__cmp_compute(right, left, COMPUTE_RATIO, d->idx);
  534. }
  535. static int64_t
  536. hist_entry__cmp_wdiff(struct perf_hpp_fmt *fmt,
  537. struct hist_entry *left, struct hist_entry *right)
  538. {
  539. struct data__file *d = fmt_to_data_file(fmt);
  540. return hist_entry__cmp_compute(right, left, COMPUTE_WEIGHTED_DIFF, d->idx);
  541. }
  542. static int64_t
  543. hist_entry__cmp_delta_idx(struct perf_hpp_fmt *fmt __maybe_unused,
  544. struct hist_entry *left, struct hist_entry *right)
  545. {
  546. return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA,
  547. sort_compute);
  548. }
  549. static int64_t
  550. hist_entry__cmp_delta_abs_idx(struct perf_hpp_fmt *fmt __maybe_unused,
  551. struct hist_entry *left, struct hist_entry *right)
  552. {
  553. return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA_ABS,
  554. sort_compute);
  555. }
  556. static int64_t
  557. hist_entry__cmp_ratio_idx(struct perf_hpp_fmt *fmt __maybe_unused,
  558. struct hist_entry *left, struct hist_entry *right)
  559. {
  560. return hist_entry__cmp_compute_idx(right, left, COMPUTE_RATIO,
  561. sort_compute);
  562. }
  563. static int64_t
  564. hist_entry__cmp_wdiff_idx(struct perf_hpp_fmt *fmt __maybe_unused,
  565. struct hist_entry *left, struct hist_entry *right)
  566. {
  567. return hist_entry__cmp_compute_idx(right, left, COMPUTE_WEIGHTED_DIFF,
  568. sort_compute);
  569. }
  570. static void hists__process(struct hists *hists)
  571. {
  572. if (show_baseline_only)
  573. hists__baseline_only(hists);
  574. hists__precompute(hists);
  575. hists__output_resort(hists, NULL);
  576. hists__fprintf(hists, !quiet, 0, 0, 0, stdout,
  577. !symbol_conf.use_callchain);
  578. }
  579. static void data__fprintf(void)
  580. {
  581. struct data__file *d;
  582. int i;
  583. fprintf(stdout, "# Data files:\n");
  584. data__for_each_file(i, d)
  585. fprintf(stdout, "# [%d] %s %s\n",
  586. d->idx, d->data.file.path,
  587. !d->idx ? "(Baseline)" : "");
  588. fprintf(stdout, "#\n");
  589. }
  590. static void data_process(void)
  591. {
  592. struct perf_evlist *evlist_base = data__files[0].session->evlist;
  593. struct perf_evsel *evsel_base;
  594. bool first = true;
  595. evlist__for_each_entry(evlist_base, evsel_base) {
  596. struct hists *hists_base = evsel__hists(evsel_base);
  597. struct data__file *d;
  598. int i;
  599. data__for_each_file_new(i, d) {
  600. struct perf_evlist *evlist = d->session->evlist;
  601. struct perf_evsel *evsel;
  602. struct hists *hists;
  603. evsel = evsel_match(evsel_base, evlist);
  604. if (!evsel)
  605. continue;
  606. hists = evsel__hists(evsel);
  607. d->hists = hists;
  608. hists__match(hists_base, hists);
  609. if (!show_baseline_only)
  610. hists__link(hists_base, hists);
  611. }
  612. if (!quiet) {
  613. fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n",
  614. perf_evsel__name(evsel_base));
  615. }
  616. first = false;
  617. if (verbose > 0 || ((data__files_cnt > 2) && !quiet))
  618. data__fprintf();
  619. /* Don't sort callchain for perf diff */
  620. perf_evsel__reset_sample_bit(evsel_base, CALLCHAIN);
  621. hists__process(hists_base);
  622. }
  623. }
  624. static void data__free(struct data__file *d)
  625. {
  626. int col;
  627. for (col = 0; col < PERF_HPP_DIFF__MAX_INDEX; col++) {
  628. struct diff_hpp_fmt *fmt = &d->fmt[col];
  629. zfree(&fmt->header);
  630. }
  631. }
  632. static int __cmd_diff(void)
  633. {
  634. struct data__file *d;
  635. int ret = -EINVAL, i;
  636. data__for_each_file(i, d) {
  637. d->session = perf_session__new(&d->data, false, &tool);
  638. if (!d->session) {
  639. pr_err("Failed to open %s\n", d->data.file.path);
  640. ret = -1;
  641. goto out_delete;
  642. }
  643. ret = perf_session__process_events(d->session);
  644. if (ret) {
  645. pr_err("Failed to process %s\n", d->data.file.path);
  646. goto out_delete;
  647. }
  648. perf_evlist__collapse_resort(d->session->evlist);
  649. }
  650. data_process();
  651. out_delete:
  652. data__for_each_file(i, d) {
  653. perf_session__delete(d->session);
  654. data__free(d);
  655. }
  656. free(data__files);
  657. return ret;
  658. }
  659. static const char * const diff_usage[] = {
  660. "perf diff [<options>] [old_file] [new_file]",
  661. NULL,
  662. };
  663. static const struct option options[] = {
  664. OPT_INCR('v', "verbose", &verbose,
  665. "be more verbose (show symbol address, etc)"),
  666. OPT_BOOLEAN('q', "quiet", &quiet, "Do not show any message"),
  667. OPT_BOOLEAN('b', "baseline-only", &show_baseline_only,
  668. "Show only items with match in baseline"),
  669. OPT_CALLBACK('c', "compute", &compute,
  670. "delta,delta-abs,ratio,wdiff:w1,w2 (default delta-abs)",
  671. "Entries differential computation selection",
  672. setup_compute),
  673. OPT_BOOLEAN('p', "period", &show_period,
  674. "Show period values."),
  675. OPT_BOOLEAN('F', "formula", &show_formula,
  676. "Show formula."),
  677. OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
  678. "dump raw trace in ASCII"),
  679. OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
  680. OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
  681. "file", "kallsyms pathname"),
  682. OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
  683. "load module symbols - WARNING: use only with -k and LIVE kernel"),
  684. OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
  685. "only consider symbols in these dsos"),
  686. OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
  687. "only consider symbols in these comms"),
  688. OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
  689. "only consider these symbols"),
  690. OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
  691. "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..."
  692. " Please refer the man page for the complete list."),
  693. OPT_STRING_NOEMPTY('t', "field-separator", &symbol_conf.field_sep, "separator",
  694. "separator for columns, no spaces will be added between "
  695. "columns '.' is reserved."),
  696. OPT_CALLBACK(0, "symfs", NULL, "directory",
  697. "Look for files with symbols relative to this directory",
  698. symbol__config_symfs),
  699. OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."),
  700. OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
  701. "How to display percentage of filtered entries", parse_filter_percentage),
  702. OPT_END()
  703. };
  704. static double baseline_percent(struct hist_entry *he)
  705. {
  706. u64 total = hists__total_period(he->hists);
  707. return 100.0 * he->stat.period / total;
  708. }
  709. static int hpp__color_baseline(struct perf_hpp_fmt *fmt,
  710. struct perf_hpp *hpp, struct hist_entry *he)
  711. {
  712. struct diff_hpp_fmt *dfmt =
  713. container_of(fmt, struct diff_hpp_fmt, fmt);
  714. double percent = baseline_percent(he);
  715. char pfmt[20] = " ";
  716. if (!he->dummy) {
  717. scnprintf(pfmt, 20, "%%%d.2f%%%%", dfmt->header_width - 1);
  718. return percent_color_snprintf(hpp->buf, hpp->size,
  719. pfmt, percent);
  720. } else
  721. return scnprintf(hpp->buf, hpp->size, "%*s",
  722. dfmt->header_width, pfmt);
  723. }
  724. static int hpp__entry_baseline(struct hist_entry *he, char *buf, size_t size)
  725. {
  726. double percent = baseline_percent(he);
  727. const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%";
  728. int ret = 0;
  729. if (!he->dummy)
  730. ret = scnprintf(buf, size, fmt, percent);
  731. return ret;
  732. }
  733. static int __hpp__color_compare(struct perf_hpp_fmt *fmt,
  734. struct perf_hpp *hpp, struct hist_entry *he,
  735. int comparison_method)
  736. {
  737. struct diff_hpp_fmt *dfmt =
  738. container_of(fmt, struct diff_hpp_fmt, fmt);
  739. struct hist_entry *pair = get_pair_fmt(he, dfmt);
  740. double diff;
  741. s64 wdiff;
  742. char pfmt[20] = " ";
  743. if (!pair)
  744. goto no_print;
  745. switch (comparison_method) {
  746. case COMPUTE_DELTA:
  747. if (pair->diff.computed)
  748. diff = pair->diff.period_ratio_delta;
  749. else
  750. diff = compute_delta(he, pair);
  751. scnprintf(pfmt, 20, "%%%+d.2f%%%%", dfmt->header_width - 1);
  752. return percent_color_snprintf(hpp->buf, hpp->size,
  753. pfmt, diff);
  754. case COMPUTE_RATIO:
  755. if (he->dummy)
  756. goto dummy_print;
  757. if (pair->diff.computed)
  758. diff = pair->diff.period_ratio;
  759. else
  760. diff = compute_ratio(he, pair);
  761. scnprintf(pfmt, 20, "%%%d.6f", dfmt->header_width);
  762. return value_color_snprintf(hpp->buf, hpp->size,
  763. pfmt, diff);
  764. case COMPUTE_WEIGHTED_DIFF:
  765. if (he->dummy)
  766. goto dummy_print;
  767. if (pair->diff.computed)
  768. wdiff = pair->diff.wdiff;
  769. else
  770. wdiff = compute_wdiff(he, pair);
  771. scnprintf(pfmt, 20, "%%14ld", dfmt->header_width);
  772. return color_snprintf(hpp->buf, hpp->size,
  773. get_percent_color(wdiff),
  774. pfmt, wdiff);
  775. default:
  776. BUG_ON(1);
  777. }
  778. dummy_print:
  779. return scnprintf(hpp->buf, hpp->size, "%*s",
  780. dfmt->header_width, "N/A");
  781. no_print:
  782. return scnprintf(hpp->buf, hpp->size, "%*s",
  783. dfmt->header_width, pfmt);
  784. }
  785. static int hpp__color_delta(struct perf_hpp_fmt *fmt,
  786. struct perf_hpp *hpp, struct hist_entry *he)
  787. {
  788. return __hpp__color_compare(fmt, hpp, he, COMPUTE_DELTA);
  789. }
  790. static int hpp__color_ratio(struct perf_hpp_fmt *fmt,
  791. struct perf_hpp *hpp, struct hist_entry *he)
  792. {
  793. return __hpp__color_compare(fmt, hpp, he, COMPUTE_RATIO);
  794. }
  795. static int hpp__color_wdiff(struct perf_hpp_fmt *fmt,
  796. struct perf_hpp *hpp, struct hist_entry *he)
  797. {
  798. return __hpp__color_compare(fmt, hpp, he, COMPUTE_WEIGHTED_DIFF);
  799. }
  800. static void
  801. hpp__entry_unpair(struct hist_entry *he, int idx, char *buf, size_t size)
  802. {
  803. switch (idx) {
  804. case PERF_HPP_DIFF__PERIOD_BASELINE:
  805. scnprintf(buf, size, "%" PRIu64, he->stat.period);
  806. break;
  807. default:
  808. break;
  809. }
  810. }
  811. static void
  812. hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair,
  813. int idx, char *buf, size_t size)
  814. {
  815. double diff;
  816. double ratio;
  817. s64 wdiff;
  818. switch (idx) {
  819. case PERF_HPP_DIFF__DELTA:
  820. case PERF_HPP_DIFF__DELTA_ABS:
  821. if (pair->diff.computed)
  822. diff = pair->diff.period_ratio_delta;
  823. else
  824. diff = compute_delta(he, pair);
  825. scnprintf(buf, size, "%+4.2F%%", diff);
  826. break;
  827. case PERF_HPP_DIFF__RATIO:
  828. /* No point for ratio number if we are dummy.. */
  829. if (he->dummy) {
  830. scnprintf(buf, size, "N/A");
  831. break;
  832. }
  833. if (pair->diff.computed)
  834. ratio = pair->diff.period_ratio;
  835. else
  836. ratio = compute_ratio(he, pair);
  837. if (ratio > 0.0)
  838. scnprintf(buf, size, "%14.6F", ratio);
  839. break;
  840. case PERF_HPP_DIFF__WEIGHTED_DIFF:
  841. /* No point for wdiff number if we are dummy.. */
  842. if (he->dummy) {
  843. scnprintf(buf, size, "N/A");
  844. break;
  845. }
  846. if (pair->diff.computed)
  847. wdiff = pair->diff.wdiff;
  848. else
  849. wdiff = compute_wdiff(he, pair);
  850. if (wdiff != 0)
  851. scnprintf(buf, size, "%14ld", wdiff);
  852. break;
  853. case PERF_HPP_DIFF__FORMULA:
  854. formula_fprintf(he, pair, buf, size);
  855. break;
  856. case PERF_HPP_DIFF__PERIOD:
  857. scnprintf(buf, size, "%" PRIu64, pair->stat.period);
  858. break;
  859. default:
  860. BUG_ON(1);
  861. };
  862. }
  863. static void
  864. __hpp__entry_global(struct hist_entry *he, struct diff_hpp_fmt *dfmt,
  865. char *buf, size_t size)
  866. {
  867. struct hist_entry *pair = get_pair_fmt(he, dfmt);
  868. int idx = dfmt->idx;
  869. /* baseline is special */
  870. if (idx == PERF_HPP_DIFF__BASELINE)
  871. hpp__entry_baseline(he, buf, size);
  872. else {
  873. if (pair)
  874. hpp__entry_pair(he, pair, idx, buf, size);
  875. else
  876. hpp__entry_unpair(he, idx, buf, size);
  877. }
  878. }
  879. static int hpp__entry_global(struct perf_hpp_fmt *_fmt, struct perf_hpp *hpp,
  880. struct hist_entry *he)
  881. {
  882. struct diff_hpp_fmt *dfmt =
  883. container_of(_fmt, struct diff_hpp_fmt, fmt);
  884. char buf[MAX_COL_WIDTH] = " ";
  885. __hpp__entry_global(he, dfmt, buf, MAX_COL_WIDTH);
  886. if (symbol_conf.field_sep)
  887. return scnprintf(hpp->buf, hpp->size, "%s", buf);
  888. else
  889. return scnprintf(hpp->buf, hpp->size, "%*s",
  890. dfmt->header_width, buf);
  891. }
  892. static int hpp__header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
  893. struct hists *hists __maybe_unused,
  894. int line __maybe_unused,
  895. int *span __maybe_unused)
  896. {
  897. struct diff_hpp_fmt *dfmt =
  898. container_of(fmt, struct diff_hpp_fmt, fmt);
  899. BUG_ON(!dfmt->header);
  900. return scnprintf(hpp->buf, hpp->size, dfmt->header);
  901. }
  902. static int hpp__width(struct perf_hpp_fmt *fmt,
  903. struct perf_hpp *hpp __maybe_unused,
  904. struct hists *hists __maybe_unused)
  905. {
  906. struct diff_hpp_fmt *dfmt =
  907. container_of(fmt, struct diff_hpp_fmt, fmt);
  908. BUG_ON(dfmt->header_width <= 0);
  909. return dfmt->header_width;
  910. }
  911. static void init_header(struct data__file *d, struct diff_hpp_fmt *dfmt)
  912. {
  913. #define MAX_HEADER_NAME 100
  914. char buf_indent[MAX_HEADER_NAME];
  915. char buf[MAX_HEADER_NAME];
  916. const char *header = NULL;
  917. int width = 0;
  918. BUG_ON(dfmt->idx >= PERF_HPP_DIFF__MAX_INDEX);
  919. header = columns[dfmt->idx].name;
  920. width = columns[dfmt->idx].width;
  921. /* Only our defined HPP fmts should appear here. */
  922. BUG_ON(!header);
  923. if (data__files_cnt > 2)
  924. scnprintf(buf, MAX_HEADER_NAME, "%s/%d", header, d->idx);
  925. #define NAME (data__files_cnt > 2 ? buf : header)
  926. dfmt->header_width = width;
  927. width = (int) strlen(NAME);
  928. if (dfmt->header_width < width)
  929. dfmt->header_width = width;
  930. scnprintf(buf_indent, MAX_HEADER_NAME, "%*s",
  931. dfmt->header_width, NAME);
  932. dfmt->header = strdup(buf_indent);
  933. #undef MAX_HEADER_NAME
  934. #undef NAME
  935. }
  936. static void data__hpp_register(struct data__file *d, int idx)
  937. {
  938. struct diff_hpp_fmt *dfmt = &d->fmt[idx];
  939. struct perf_hpp_fmt *fmt = &dfmt->fmt;
  940. dfmt->idx = idx;
  941. fmt->header = hpp__header;
  942. fmt->width = hpp__width;
  943. fmt->entry = hpp__entry_global;
  944. fmt->cmp = hist_entry__cmp_nop;
  945. fmt->collapse = hist_entry__cmp_nop;
  946. /* TODO more colors */
  947. switch (idx) {
  948. case PERF_HPP_DIFF__BASELINE:
  949. fmt->color = hpp__color_baseline;
  950. fmt->sort = hist_entry__cmp_baseline;
  951. break;
  952. case PERF_HPP_DIFF__DELTA:
  953. fmt->color = hpp__color_delta;
  954. fmt->sort = hist_entry__cmp_delta;
  955. break;
  956. case PERF_HPP_DIFF__RATIO:
  957. fmt->color = hpp__color_ratio;
  958. fmt->sort = hist_entry__cmp_ratio;
  959. break;
  960. case PERF_HPP_DIFF__WEIGHTED_DIFF:
  961. fmt->color = hpp__color_wdiff;
  962. fmt->sort = hist_entry__cmp_wdiff;
  963. break;
  964. case PERF_HPP_DIFF__DELTA_ABS:
  965. fmt->color = hpp__color_delta;
  966. fmt->sort = hist_entry__cmp_delta_abs;
  967. break;
  968. default:
  969. fmt->sort = hist_entry__cmp_nop;
  970. break;
  971. }
  972. init_header(d, dfmt);
  973. perf_hpp__column_register(fmt);
  974. perf_hpp__register_sort_field(fmt);
  975. }
  976. static int ui_init(void)
  977. {
  978. struct data__file *d;
  979. struct perf_hpp_fmt *fmt;
  980. int i;
  981. data__for_each_file(i, d) {
  982. /*
  983. * Baseline or compute realted columns:
  984. *
  985. * PERF_HPP_DIFF__BASELINE
  986. * PERF_HPP_DIFF__DELTA
  987. * PERF_HPP_DIFF__RATIO
  988. * PERF_HPP_DIFF__WEIGHTED_DIFF
  989. */
  990. data__hpp_register(d, i ? compute_2_hpp[compute] :
  991. PERF_HPP_DIFF__BASELINE);
  992. /*
  993. * And the rest:
  994. *
  995. * PERF_HPP_DIFF__FORMULA
  996. * PERF_HPP_DIFF__PERIOD
  997. * PERF_HPP_DIFF__PERIOD_BASELINE
  998. */
  999. if (show_formula && i)
  1000. data__hpp_register(d, PERF_HPP_DIFF__FORMULA);
  1001. if (show_period)
  1002. data__hpp_register(d, i ? PERF_HPP_DIFF__PERIOD :
  1003. PERF_HPP_DIFF__PERIOD_BASELINE);
  1004. }
  1005. if (!sort_compute)
  1006. return 0;
  1007. /*
  1008. * Prepend an fmt to sort on columns at 'sort_compute' first.
  1009. * This fmt is added only to the sort list but not to the
  1010. * output fields list.
  1011. *
  1012. * Note that this column (data) can be compared twice - one
  1013. * for this 'sort_compute' fmt and another for the normal
  1014. * diff_hpp_fmt. But it shouldn't a problem as most entries
  1015. * will be sorted out by first try or baseline and comparing
  1016. * is not a costly operation.
  1017. */
  1018. fmt = zalloc(sizeof(*fmt));
  1019. if (fmt == NULL) {
  1020. pr_err("Memory allocation failed\n");
  1021. return -1;
  1022. }
  1023. fmt->cmp = hist_entry__cmp_nop;
  1024. fmt->collapse = hist_entry__cmp_nop;
  1025. switch (compute) {
  1026. case COMPUTE_DELTA:
  1027. fmt->sort = hist_entry__cmp_delta_idx;
  1028. break;
  1029. case COMPUTE_RATIO:
  1030. fmt->sort = hist_entry__cmp_ratio_idx;
  1031. break;
  1032. case COMPUTE_WEIGHTED_DIFF:
  1033. fmt->sort = hist_entry__cmp_wdiff_idx;
  1034. break;
  1035. case COMPUTE_DELTA_ABS:
  1036. fmt->sort = hist_entry__cmp_delta_abs_idx;
  1037. break;
  1038. default:
  1039. BUG_ON(1);
  1040. }
  1041. perf_hpp__prepend_sort_field(fmt);
  1042. return 0;
  1043. }
  1044. static int data_init(int argc, const char **argv)
  1045. {
  1046. struct data__file *d;
  1047. static const char *defaults[] = {
  1048. "perf.data.old",
  1049. "perf.data",
  1050. };
  1051. bool use_default = true;
  1052. int i;
  1053. data__files_cnt = 2;
  1054. if (argc) {
  1055. if (argc == 1)
  1056. defaults[1] = argv[0];
  1057. else {
  1058. data__files_cnt = argc;
  1059. use_default = false;
  1060. }
  1061. } else if (perf_guest) {
  1062. defaults[0] = "perf.data.host";
  1063. defaults[1] = "perf.data.guest";
  1064. }
  1065. if (sort_compute >= (unsigned int) data__files_cnt) {
  1066. pr_err("Order option out of limit.\n");
  1067. return -EINVAL;
  1068. }
  1069. data__files = zalloc(sizeof(*data__files) * data__files_cnt);
  1070. if (!data__files)
  1071. return -ENOMEM;
  1072. data__for_each_file(i, d) {
  1073. struct perf_data *data = &d->data;
  1074. data->file.path = use_default ? defaults[i] : argv[i];
  1075. data->mode = PERF_DATA_MODE_READ,
  1076. data->force = force,
  1077. d->idx = i;
  1078. }
  1079. return 0;
  1080. }
  1081. static int diff__config(const char *var, const char *value,
  1082. void *cb __maybe_unused)
  1083. {
  1084. if (!strcmp(var, "diff.order")) {
  1085. int ret;
  1086. if (perf_config_int(&ret, var, value) < 0)
  1087. return -1;
  1088. sort_compute = ret;
  1089. return 0;
  1090. }
  1091. if (!strcmp(var, "diff.compute")) {
  1092. if (!strcmp(value, "delta")) {
  1093. compute = COMPUTE_DELTA;
  1094. } else if (!strcmp(value, "delta-abs")) {
  1095. compute = COMPUTE_DELTA_ABS;
  1096. } else if (!strcmp(value, "ratio")) {
  1097. compute = COMPUTE_RATIO;
  1098. } else if (!strcmp(value, "wdiff")) {
  1099. compute = COMPUTE_WEIGHTED_DIFF;
  1100. } else {
  1101. pr_err("Invalid compute method: %s\n", value);
  1102. return -1;
  1103. }
  1104. }
  1105. return 0;
  1106. }
  1107. int cmd_diff(int argc, const char **argv)
  1108. {
  1109. int ret = hists__init();
  1110. if (ret < 0)
  1111. return ret;
  1112. perf_config(diff__config, NULL);
  1113. argc = parse_options(argc, argv, options, diff_usage, 0);
  1114. if (quiet)
  1115. perf_quiet_option();
  1116. if (symbol__init(NULL) < 0)
  1117. return -1;
  1118. if (data_init(argc, argv) < 0)
  1119. return -1;
  1120. if (ui_init() < 0)
  1121. return -1;
  1122. sort__mode = SORT_MODE__DIFF;
  1123. if (setup_sorting(NULL) < 0)
  1124. usage_with_options(diff_usage, options);
  1125. setup_pager();
  1126. sort__setup_elide(NULL);
  1127. return __cmd_diff();
  1128. }