1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include "util/debug.h" 4 #include "util/evlist.h" 5 #include "util/evsel.h" 6 #include "util/mmap.h" 7 #include "util/perf_api_probe.h" 8 #include <perf/mmap.h> 9 #include <linux/perf_event.h> 10 #include <limits.h> 11 #include <poll.h> 12 #include <pthread.h> 13 #include <sched.h> 14 #include <stdbool.h> 15 16 int evlist__add_sb_event(struct evlist *evlist, struct perf_event_attr *attr, 17 evsel__sb_cb_t cb, void *data) 18 { 19 struct evsel *evsel; 20 21 if (!attr->sample_id_all) { 22 pr_warning("enabling sample_id_all for all side band events\n"); 23 attr->sample_id_all = 1; 24 } 25 26 evsel = evsel__new_idx(attr, evlist__nr_entries(evlist)); 27 if (!evsel) 28 return -1; 29 30 evsel->side_band.cb = cb; 31 evsel->side_band.data = data; 32 evlist__add(evlist, evsel); 33 return 0; 34 } 35 36 static void *perf_evlist__poll_thread(void *arg) 37 { 38 struct evlist *evlist = arg; 39 bool draining = false; 40 int i, done = 0; 41 /* 42 * In order to read symbols from other namespaces perf to needs to call 43 * setns(2). This isn't permitted if the struct_fs has multiple users. 44 * unshare(2) the fs so that we may continue to setns into namespaces 45 * that we're observing when, for instance, reading the build-ids at 46 * the end of a 'perf record' session. 47 */ 48 unshare(CLONE_FS); 49 50 while (!done) { 51 bool got_data = false; 52 53 if (evlist__sb_thread_done(evlist)) 54 draining = true; 55 56 if (!draining) 57 evlist__poll(evlist, 1000); 58 59 /* 60 * When a thread of the monitored target exits, its per-cpu 61 * ring-buffer fd is closed and starts returning POLLHUP. Such 62 * dead fds are never requested for POLLIN, but poll() reports 63 * POLLHUP/POLLERR unconditionally, so leaving them in the 64 * pollfd array makes the following evlist__poll() return 65 * immediately forever, spinning this thread at 100% CPU. 66 * 67 * Filter them out here, mirroring what the 'perf record' main 68 * loop does after fdarray__poll(). 69 */ 70 evlist__filter_pollfd(evlist, POLLERR | POLLHUP); 71 72 for (i = 0; i < evlist__core(evlist)->nr_mmaps; i++) { 73 struct mmap *map = &evlist__mmap(evlist)[i]; 74 union perf_event *event; 75 76 if (perf_mmap__read_init(&map->core)) 77 continue; 78 while ((event = perf_mmap__read_event(&map->core)) != NULL) { 79 struct evsel *evsel = evlist__event2evsel(evlist, event); 80 81 if (evsel && evsel->side_band.cb) 82 evsel->side_band.cb(event, evsel->side_band.data); 83 else 84 pr_warning("cannot locate proper evsel for the side band event\n"); 85 86 perf_mmap__consume(&map->core); 87 got_data = true; 88 } 89 perf_mmap__read_done(&map->core); 90 } 91 92 if (draining && !got_data) 93 break; 94 } 95 return NULL; 96 } 97 98 void evlist__set_cb(struct evlist *evlist, evsel__sb_cb_t cb, void *data) 99 { 100 struct evsel *evsel; 101 102 evlist__for_each_entry(evlist, evsel) { 103 evsel->core.attr.sample_id_all = 1; 104 evsel->core.attr.watermark = 1; 105 evsel->core.attr.wakeup_watermark = 1; 106 evsel->side_band.cb = cb; 107 evsel->side_band.data = data; 108 } 109 } 110 111 int evlist__start_sb_thread(struct evlist *evlist, struct target *target) 112 { 113 struct evsel *counter; 114 115 if (!evlist) 116 return 0; 117 118 if (evlist__create_maps(evlist, target)) 119 goto out_put_evlist; 120 121 if (evlist__nr_entries(evlist) > 1) { 122 bool can_sample_identifier = perf_can_sample_identifier(); 123 124 evlist__for_each_entry(evlist, counter) 125 evsel__set_sample_id(counter, can_sample_identifier); 126 127 evlist__set_id_pos(evlist); 128 } 129 130 evlist__for_each_entry(evlist, counter) { 131 if (evsel__open(counter, evlist__core(evlist)->user_requested_cpus, 132 evlist__core(evlist)->threads) < 0) 133 goto out_put_evlist; 134 } 135 136 if (evlist__do_mmap(evlist, UINT_MAX)) 137 goto out_put_evlist; 138 139 evlist__for_each_entry(evlist, counter) { 140 if (evsel__enable(counter)) 141 goto out_put_evlist; 142 } 143 144 evlist__set_sb_thread_done(evlist, 0); 145 if (pthread_create(evlist__sb_thread_th(evlist), NULL, perf_evlist__poll_thread, evlist)) 146 goto out_put_evlist; 147 148 return 0; 149 150 out_put_evlist: 151 evlist__put(evlist); 152 evlist = NULL; 153 return -1; 154 } 155 156 void evlist__stop_sb_thread(struct evlist *evlist) 157 { 158 if (!evlist) 159 return; 160 evlist__set_sb_thread_done(evlist, 1); 161 pthread_join(*evlist__sb_thread_th(evlist), NULL); 162 evlist__put(evlist); 163 } 164