xref: /linux/tools/perf/util/sideband_evlist.c (revision 473f6c8f437b049f8ec015d57cd59bb983b1d85c)
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