switch-tracking.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. #include <sys/time.h>
  2. #include <sys/prctl.h>
  3. #include <time.h>
  4. #include <stdlib.h>
  5. #include "parse-events.h"
  6. #include "evlist.h"
  7. #include "evsel.h"
  8. #include "thread_map.h"
  9. #include "cpumap.h"
  10. #include "tests.h"
  11. static int spin_sleep(void)
  12. {
  13. struct timeval start, now, diff, maxtime;
  14. struct timespec ts;
  15. int err, i;
  16. maxtime.tv_sec = 0;
  17. maxtime.tv_usec = 50000;
  18. err = gettimeofday(&start, NULL);
  19. if (err)
  20. return err;
  21. /* Spin for 50ms */
  22. while (1) {
  23. for (i = 0; i < 1000; i++)
  24. barrier();
  25. err = gettimeofday(&now, NULL);
  26. if (err)
  27. return err;
  28. timersub(&now, &start, &diff);
  29. if (timercmp(&diff, &maxtime, > /* For checkpatch */))
  30. break;
  31. }
  32. ts.tv_nsec = 50 * 1000 * 1000;
  33. ts.tv_sec = 0;
  34. /* Sleep for 50ms */
  35. err = nanosleep(&ts, NULL);
  36. if (err == EINTR)
  37. err = 0;
  38. return err;
  39. }
  40. struct switch_tracking {
  41. struct perf_evsel *switch_evsel;
  42. struct perf_evsel *cycles_evsel;
  43. pid_t *tids;
  44. int nr_tids;
  45. int comm_seen[4];
  46. int cycles_before_comm_1;
  47. int cycles_between_comm_2_and_comm_3;
  48. int cycles_after_comm_4;
  49. };
  50. static int check_comm(struct switch_tracking *switch_tracking,
  51. union perf_event *event, const char *comm, int nr)
  52. {
  53. if (event->header.type == PERF_RECORD_COMM &&
  54. (pid_t)event->comm.pid == getpid() &&
  55. (pid_t)event->comm.tid == getpid() &&
  56. strcmp(event->comm.comm, comm) == 0) {
  57. if (switch_tracking->comm_seen[nr]) {
  58. pr_debug("Duplicate comm event\n");
  59. return -1;
  60. }
  61. switch_tracking->comm_seen[nr] = 1;
  62. pr_debug3("comm event: %s nr: %d\n", event->comm.comm, nr);
  63. return 1;
  64. }
  65. return 0;
  66. }
  67. static int check_cpu(struct switch_tracking *switch_tracking, int cpu)
  68. {
  69. int i, nr = cpu + 1;
  70. if (cpu < 0)
  71. return -1;
  72. if (!switch_tracking->tids) {
  73. switch_tracking->tids = calloc(nr, sizeof(pid_t));
  74. if (!switch_tracking->tids)
  75. return -1;
  76. for (i = 0; i < nr; i++)
  77. switch_tracking->tids[i] = -1;
  78. switch_tracking->nr_tids = nr;
  79. return 0;
  80. }
  81. if (cpu >= switch_tracking->nr_tids) {
  82. void *addr;
  83. addr = realloc(switch_tracking->tids, nr * sizeof(pid_t));
  84. if (!addr)
  85. return -1;
  86. switch_tracking->tids = addr;
  87. for (i = switch_tracking->nr_tids; i < nr; i++)
  88. switch_tracking->tids[i] = -1;
  89. switch_tracking->nr_tids = nr;
  90. return 0;
  91. }
  92. return 0;
  93. }
  94. static int process_sample_event(struct perf_evlist *evlist,
  95. union perf_event *event,
  96. struct switch_tracking *switch_tracking)
  97. {
  98. struct perf_sample sample;
  99. struct perf_evsel *evsel;
  100. pid_t next_tid, prev_tid;
  101. int cpu, err;
  102. if (perf_evlist__parse_sample(evlist, event, &sample)) {
  103. pr_debug("perf_evlist__parse_sample failed\n");
  104. return -1;
  105. }
  106. evsel = perf_evlist__id2evsel(evlist, sample.id);
  107. if (evsel == switch_tracking->switch_evsel) {
  108. next_tid = perf_evsel__intval(evsel, &sample, "next_pid");
  109. prev_tid = perf_evsel__intval(evsel, &sample, "prev_pid");
  110. cpu = sample.cpu;
  111. pr_debug3("sched_switch: cpu: %d prev_tid %d next_tid %d\n",
  112. cpu, prev_tid, next_tid);
  113. err = check_cpu(switch_tracking, cpu);
  114. if (err)
  115. return err;
  116. /*
  117. * Check for no missing sched_switch events i.e. that the
  118. * evsel->system_wide flag has worked.
  119. */
  120. if (switch_tracking->tids[cpu] != -1 &&
  121. switch_tracking->tids[cpu] != prev_tid) {
  122. pr_debug("Missing sched_switch events\n");
  123. return -1;
  124. }
  125. switch_tracking->tids[cpu] = next_tid;
  126. }
  127. if (evsel == switch_tracking->cycles_evsel) {
  128. pr_debug3("cycles event\n");
  129. if (!switch_tracking->comm_seen[0])
  130. switch_tracking->cycles_before_comm_1 = 1;
  131. if (switch_tracking->comm_seen[1] &&
  132. !switch_tracking->comm_seen[2])
  133. switch_tracking->cycles_between_comm_2_and_comm_3 = 1;
  134. if (switch_tracking->comm_seen[3])
  135. switch_tracking->cycles_after_comm_4 = 1;
  136. }
  137. return 0;
  138. }
  139. static int process_event(struct perf_evlist *evlist, union perf_event *event,
  140. struct switch_tracking *switch_tracking)
  141. {
  142. if (event->header.type == PERF_RECORD_SAMPLE)
  143. return process_sample_event(evlist, event, switch_tracking);
  144. if (event->header.type == PERF_RECORD_COMM) {
  145. int err, done = 0;
  146. err = check_comm(switch_tracking, event, "Test COMM 1", 0);
  147. if (err < 0)
  148. return -1;
  149. done += err;
  150. err = check_comm(switch_tracking, event, "Test COMM 2", 1);
  151. if (err < 0)
  152. return -1;
  153. done += err;
  154. err = check_comm(switch_tracking, event, "Test COMM 3", 2);
  155. if (err < 0)
  156. return -1;
  157. done += err;
  158. err = check_comm(switch_tracking, event, "Test COMM 4", 3);
  159. if (err < 0)
  160. return -1;
  161. done += err;
  162. if (done != 1) {
  163. pr_debug("Unexpected comm event\n");
  164. return -1;
  165. }
  166. }
  167. return 0;
  168. }
  169. struct event_node {
  170. struct list_head list;
  171. union perf_event *event;
  172. u64 event_time;
  173. };
  174. static int add_event(struct perf_evlist *evlist, struct list_head *events,
  175. union perf_event *event)
  176. {
  177. struct perf_sample sample;
  178. struct event_node *node;
  179. node = malloc(sizeof(struct event_node));
  180. if (!node) {
  181. pr_debug("malloc failed\n");
  182. return -1;
  183. }
  184. node->event = event;
  185. list_add(&node->list, events);
  186. if (perf_evlist__parse_sample(evlist, event, &sample)) {
  187. pr_debug("perf_evlist__parse_sample failed\n");
  188. return -1;
  189. }
  190. if (!sample.time) {
  191. pr_debug("event with no time\n");
  192. return -1;
  193. }
  194. node->event_time = sample.time;
  195. return 0;
  196. }
  197. static void free_event_nodes(struct list_head *events)
  198. {
  199. struct event_node *node;
  200. while (!list_empty(events)) {
  201. node = list_entry(events->next, struct event_node, list);
  202. list_del(&node->list);
  203. free(node);
  204. }
  205. }
  206. static int compar(const void *a, const void *b)
  207. {
  208. const struct event_node *nodea = a;
  209. const struct event_node *nodeb = b;
  210. s64 cmp = nodea->event_time - nodeb->event_time;
  211. return cmp;
  212. }
  213. static int process_events(struct perf_evlist *evlist,
  214. struct switch_tracking *switch_tracking)
  215. {
  216. union perf_event *event;
  217. unsigned pos, cnt = 0;
  218. LIST_HEAD(events);
  219. struct event_node *events_array, *node;
  220. int i, ret;
  221. for (i = 0; i < evlist->nr_mmaps; i++) {
  222. while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
  223. cnt += 1;
  224. ret = add_event(evlist, &events, event);
  225. perf_evlist__mmap_consume(evlist, i);
  226. if (ret < 0)
  227. goto out_free_nodes;
  228. }
  229. }
  230. events_array = calloc(cnt, sizeof(struct event_node));
  231. if (!events_array) {
  232. pr_debug("calloc failed\n");
  233. ret = -1;
  234. goto out_free_nodes;
  235. }
  236. pos = 0;
  237. list_for_each_entry(node, &events, list)
  238. events_array[pos++] = *node;
  239. qsort(events_array, cnt, sizeof(struct event_node), compar);
  240. for (pos = 0; pos < cnt; pos++) {
  241. ret = process_event(evlist, events_array[pos].event,
  242. switch_tracking);
  243. if (ret < 0)
  244. goto out_free;
  245. }
  246. ret = 0;
  247. out_free:
  248. pr_debug("%u events recorded\n", cnt);
  249. free(events_array);
  250. out_free_nodes:
  251. free_event_nodes(&events);
  252. return ret;
  253. }
  254. /**
  255. * test__switch_tracking - test using sched_switch and tracking events.
  256. *
  257. * This function implements a test that checks that sched_switch events and
  258. * tracking events can be recorded for a workload (current process) using the
  259. * evsel->system_wide and evsel->tracking flags (respectively) with other events
  260. * sometimes enabled or disabled.
  261. */
  262. int test__switch_tracking(void)
  263. {
  264. const char *sched_switch = "sched:sched_switch";
  265. struct switch_tracking switch_tracking = { .tids = NULL, };
  266. struct record_opts opts = {
  267. .mmap_pages = UINT_MAX,
  268. .user_freq = UINT_MAX,
  269. .user_interval = ULLONG_MAX,
  270. .freq = 4000,
  271. .target = {
  272. .uses_mmap = true,
  273. },
  274. };
  275. struct thread_map *threads = NULL;
  276. struct cpu_map *cpus = NULL;
  277. struct perf_evlist *evlist = NULL;
  278. struct perf_evsel *evsel, *cpu_clocks_evsel, *cycles_evsel;
  279. struct perf_evsel *switch_evsel, *tracking_evsel;
  280. const char *comm;
  281. int err = -1;
  282. threads = thread_map__new(-1, getpid(), UINT_MAX);
  283. if (!threads) {
  284. pr_debug("thread_map__new failed!\n");
  285. goto out_err;
  286. }
  287. cpus = cpu_map__new(NULL);
  288. if (!cpus) {
  289. pr_debug("cpu_map__new failed!\n");
  290. goto out_err;
  291. }
  292. evlist = perf_evlist__new();
  293. if (!evlist) {
  294. pr_debug("perf_evlist__new failed!\n");
  295. goto out_err;
  296. }
  297. perf_evlist__set_maps(evlist, cpus, threads);
  298. /* First event */
  299. err = parse_events(evlist, "cpu-clock:u", NULL);
  300. if (err) {
  301. pr_debug("Failed to parse event dummy:u\n");
  302. goto out_err;
  303. }
  304. cpu_clocks_evsel = perf_evlist__last(evlist);
  305. /* Second event */
  306. err = parse_events(evlist, "cycles:u", NULL);
  307. if (err) {
  308. pr_debug("Failed to parse event cycles:u\n");
  309. goto out_err;
  310. }
  311. cycles_evsel = perf_evlist__last(evlist);
  312. /* Third event */
  313. if (!perf_evlist__can_select_event(evlist, sched_switch)) {
  314. pr_debug("No sched_switch\n");
  315. err = 0;
  316. goto out;
  317. }
  318. err = parse_events(evlist, sched_switch, NULL);
  319. if (err) {
  320. pr_debug("Failed to parse event %s\n", sched_switch);
  321. goto out_err;
  322. }
  323. switch_evsel = perf_evlist__last(evlist);
  324. perf_evsel__set_sample_bit(switch_evsel, CPU);
  325. perf_evsel__set_sample_bit(switch_evsel, TIME);
  326. switch_evsel->system_wide = true;
  327. switch_evsel->no_aux_samples = true;
  328. switch_evsel->immediate = true;
  329. /* Test moving an event to the front */
  330. if (cycles_evsel == perf_evlist__first(evlist)) {
  331. pr_debug("cycles event already at front");
  332. goto out_err;
  333. }
  334. perf_evlist__to_front(evlist, cycles_evsel);
  335. if (cycles_evsel != perf_evlist__first(evlist)) {
  336. pr_debug("Failed to move cycles event to front");
  337. goto out_err;
  338. }
  339. perf_evsel__set_sample_bit(cycles_evsel, CPU);
  340. perf_evsel__set_sample_bit(cycles_evsel, TIME);
  341. /* Fourth event */
  342. err = parse_events(evlist, "dummy:u", NULL);
  343. if (err) {
  344. pr_debug("Failed to parse event dummy:u\n");
  345. goto out_err;
  346. }
  347. tracking_evsel = perf_evlist__last(evlist);
  348. perf_evlist__set_tracking_event(evlist, tracking_evsel);
  349. tracking_evsel->attr.freq = 0;
  350. tracking_evsel->attr.sample_period = 1;
  351. perf_evsel__set_sample_bit(tracking_evsel, TIME);
  352. /* Config events */
  353. perf_evlist__config(evlist, &opts);
  354. /* Check moved event is still at the front */
  355. if (cycles_evsel != perf_evlist__first(evlist)) {
  356. pr_debug("Front event no longer at front");
  357. goto out_err;
  358. }
  359. /* Check tracking event is tracking */
  360. if (!tracking_evsel->attr.mmap || !tracking_evsel->attr.comm) {
  361. pr_debug("Tracking event not tracking\n");
  362. goto out_err;
  363. }
  364. /* Check non-tracking events are not tracking */
  365. evlist__for_each(evlist, evsel) {
  366. if (evsel != tracking_evsel) {
  367. if (evsel->attr.mmap || evsel->attr.comm) {
  368. pr_debug("Non-tracking event is tracking\n");
  369. goto out_err;
  370. }
  371. }
  372. }
  373. if (perf_evlist__open(evlist) < 0) {
  374. pr_debug("Not supported\n");
  375. err = 0;
  376. goto out;
  377. }
  378. err = perf_evlist__mmap(evlist, UINT_MAX, false);
  379. if (err) {
  380. pr_debug("perf_evlist__mmap failed!\n");
  381. goto out_err;
  382. }
  383. perf_evlist__enable(evlist);
  384. err = perf_evlist__disable_event(evlist, cpu_clocks_evsel);
  385. if (err) {
  386. pr_debug("perf_evlist__disable_event failed!\n");
  387. goto out_err;
  388. }
  389. err = spin_sleep();
  390. if (err) {
  391. pr_debug("spin_sleep failed!\n");
  392. goto out_err;
  393. }
  394. comm = "Test COMM 1";
  395. err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
  396. if (err) {
  397. pr_debug("PR_SET_NAME failed!\n");
  398. goto out_err;
  399. }
  400. err = perf_evlist__disable_event(evlist, cycles_evsel);
  401. if (err) {
  402. pr_debug("perf_evlist__disable_event failed!\n");
  403. goto out_err;
  404. }
  405. comm = "Test COMM 2";
  406. err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
  407. if (err) {
  408. pr_debug("PR_SET_NAME failed!\n");
  409. goto out_err;
  410. }
  411. err = spin_sleep();
  412. if (err) {
  413. pr_debug("spin_sleep failed!\n");
  414. goto out_err;
  415. }
  416. comm = "Test COMM 3";
  417. err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
  418. if (err) {
  419. pr_debug("PR_SET_NAME failed!\n");
  420. goto out_err;
  421. }
  422. err = perf_evlist__enable_event(evlist, cycles_evsel);
  423. if (err) {
  424. pr_debug("perf_evlist__disable_event failed!\n");
  425. goto out_err;
  426. }
  427. comm = "Test COMM 4";
  428. err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
  429. if (err) {
  430. pr_debug("PR_SET_NAME failed!\n");
  431. goto out_err;
  432. }
  433. err = spin_sleep();
  434. if (err) {
  435. pr_debug("spin_sleep failed!\n");
  436. goto out_err;
  437. }
  438. perf_evlist__disable(evlist);
  439. switch_tracking.switch_evsel = switch_evsel;
  440. switch_tracking.cycles_evsel = cycles_evsel;
  441. err = process_events(evlist, &switch_tracking);
  442. zfree(&switch_tracking.tids);
  443. if (err)
  444. goto out_err;
  445. /* Check all 4 comm events were seen i.e. that evsel->tracking works */
  446. if (!switch_tracking.comm_seen[0] || !switch_tracking.comm_seen[1] ||
  447. !switch_tracking.comm_seen[2] || !switch_tracking.comm_seen[3]) {
  448. pr_debug("Missing comm events\n");
  449. goto out_err;
  450. }
  451. /* Check cycles event got enabled */
  452. if (!switch_tracking.cycles_before_comm_1) {
  453. pr_debug("Missing cycles events\n");
  454. goto out_err;
  455. }
  456. /* Check cycles event got disabled */
  457. if (switch_tracking.cycles_between_comm_2_and_comm_3) {
  458. pr_debug("cycles events even though event was disabled\n");
  459. goto out_err;
  460. }
  461. /* Check cycles event got enabled again */
  462. if (!switch_tracking.cycles_after_comm_4) {
  463. pr_debug("Missing cycles events\n");
  464. goto out_err;
  465. }
  466. out:
  467. if (evlist) {
  468. perf_evlist__disable(evlist);
  469. perf_evlist__delete(evlist);
  470. } else {
  471. cpu_map__put(cpus);
  472. thread_map__put(threads);
  473. }
  474. return err;
  475. out_err:
  476. err = -1;
  477. goto out;
  478. }