xref: /aosp_15_r20/external/bcc/src/cc/perf_reader.c (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1 /*
2  * Copyright (c) 2015 PLUMgrid, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <inttypes.h>
18 #include <poll.h>
19 #include <stdio.h>
20 #include <stdint.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <syscall.h>
24 #include <sys/ioctl.h>
25 #include <sys/mman.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28 #include <linux/types.h>
29 #include <linux/perf_event.h>
30 
31 #include "libbpf.h"
32 #include "perf_reader.h"
33 
34 enum {
35   RB_NOT_USED = 0, // ring buffer not usd
36   RB_USED_IN_MUNMAP = 1, // used in munmap
37   RB_USED_IN_READ = 2, // used in read
38 };
39 
40 struct perf_reader {
41   perf_reader_raw_cb raw_cb;
42   perf_reader_lost_cb lost_cb;
43   void *cb_cookie; // to be returned in the cb
44   void *buf; // for keeping segmented data
45   size_t buf_size;
46   void *base;
47   int rb_use_state;
48   pid_t rb_read_tid;
49   int page_size;
50   int page_cnt;
51   int fd;
52 };
53 
perf_reader_new(perf_reader_raw_cb raw_cb,perf_reader_lost_cb lost_cb,void * cb_cookie,int page_cnt)54 struct perf_reader * perf_reader_new(perf_reader_raw_cb raw_cb,
55                                      perf_reader_lost_cb lost_cb,
56                                      void *cb_cookie, int page_cnt) {
57   struct perf_reader *reader = calloc(1, sizeof(struct perf_reader));
58   if (!reader)
59     return NULL;
60   reader->raw_cb = raw_cb;
61   reader->lost_cb = lost_cb;
62   reader->cb_cookie = cb_cookie;
63   reader->fd = -1;
64   reader->page_size = getpagesize();
65   reader->page_cnt = page_cnt;
66   return reader;
67 }
68 
perf_reader_free(void * ptr)69 void perf_reader_free(void *ptr) {
70   if (ptr) {
71     struct perf_reader *reader = ptr;
72     pid_t tid = syscall(__NR_gettid);
73     while (!__sync_bool_compare_and_swap(&reader->rb_use_state, RB_NOT_USED, RB_USED_IN_MUNMAP)) {
74       // If the same thread, it is called from call back handler, no locking needed
75       if (tid == reader->rb_read_tid)
76         break;
77     }
78     munmap(reader->base, reader->page_size * (reader->page_cnt + 1));
79     if (reader->fd >= 0) {
80       ioctl(reader->fd, PERF_EVENT_IOC_DISABLE, 0);
81       close(reader->fd);
82     }
83     free(reader->buf);
84     free(ptr);
85   }
86 }
87 
perf_reader_mmap(struct perf_reader * reader)88 int perf_reader_mmap(struct perf_reader *reader) {
89   int mmap_size = reader->page_size * (reader->page_cnt + 1);
90 
91   if (reader->fd < 0) {
92     fprintf(stderr, "%s: reader fd is not set\n", __FUNCTION__);
93     return -1;
94   }
95 
96   reader->base = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, reader->fd, 0);
97   if (reader->base == MAP_FAILED) {
98     perror("mmap");
99     return -1;
100   }
101 
102   return 0;
103 }
104 
105 struct perf_sample_trace_common {
106   uint16_t id;
107   uint8_t flags;
108   uint8_t preempt_count;
109   int pid;
110 };
111 
112 struct perf_sample_trace_kprobe {
113   struct perf_sample_trace_common common;
114   uint64_t ip;
115 };
116 
parse_sw(struct perf_reader * reader,void * data,int size)117 static void parse_sw(struct perf_reader *reader, void *data, int size) {
118   uint8_t *ptr = data;
119   struct perf_event_header *header = (void *)data;
120 
121   struct {
122       uint32_t size;
123       char data[0];
124   } *raw = NULL;
125 
126   ptr += sizeof(*header);
127   if (ptr > (uint8_t *)data + size) {
128     fprintf(stderr, "%s: corrupt sample header\n", __FUNCTION__);
129     return;
130   }
131 
132   raw = (void *)ptr;
133   ptr += sizeof(raw->size) + raw->size;
134   if (ptr > (uint8_t *)data + size) {
135     fprintf(stderr, "%s: corrupt raw sample\n", __FUNCTION__);
136     return;
137   }
138 
139   // sanity check
140   if (ptr != (uint8_t *)data + size) {
141     fprintf(stderr, "%s: extra data at end of sample\n", __FUNCTION__);
142     return;
143   }
144 
145   if (reader->raw_cb)
146     reader->raw_cb(reader->cb_cookie, raw->data, raw->size);
147 }
148 
read_data_head(volatile struct perf_event_mmap_page * perf_header)149 static uint64_t read_data_head(volatile struct perf_event_mmap_page *perf_header) {
150   uint64_t data_head = perf_header->data_head;
151   asm volatile("" ::: "memory");
152   return data_head;
153 }
154 
write_data_tail(volatile struct perf_event_mmap_page * perf_header,uint64_t data_tail)155 static void write_data_tail(volatile struct perf_event_mmap_page *perf_header, uint64_t data_tail) {
156   asm volatile("" ::: "memory");
157   perf_header->data_tail = data_tail;
158 }
159 
perf_reader_event_read(struct perf_reader * reader)160 void perf_reader_event_read(struct perf_reader *reader) {
161   volatile struct perf_event_mmap_page *perf_header = reader->base;
162   uint64_t buffer_size = (uint64_t)reader->page_size * reader->page_cnt;
163   uint64_t data_head;
164   uint8_t *base = (uint8_t *)reader->base + reader->page_size;
165   uint8_t *sentinel = (uint8_t *)reader->base + buffer_size + reader->page_size;
166   uint8_t *begin, *end;
167 
168   reader->rb_read_tid = syscall(__NR_gettid);
169   if (!__sync_bool_compare_and_swap(&reader->rb_use_state, RB_NOT_USED, RB_USED_IN_READ))
170     return;
171 
172   // Consume all the events on this ring, calling the cb function for each one.
173   // The message may fall on the ring boundary, in which case copy the message
174   // into a malloced buffer.
175   for (data_head = read_data_head(perf_header); perf_header->data_tail != data_head;
176       data_head = read_data_head(perf_header)) {
177     uint64_t data_tail = perf_header->data_tail;
178     uint8_t *ptr;
179 
180     begin = base + data_tail % buffer_size;
181     // event header is u64, won't wrap
182     struct perf_event_header *e = (void *)begin;
183     ptr = begin;
184     end = base + (data_tail + e->size) % buffer_size;
185     if (end < begin) {
186       // perf event wraps around the ring, make a contiguous copy
187       reader->buf = realloc(reader->buf, e->size);
188       size_t len = sentinel - begin;
189       memcpy(reader->buf, begin, len);
190       memcpy((void *)((unsigned long)reader->buf + len), base, e->size - len);
191       ptr = reader->buf;
192     }
193 
194     if (e->type == PERF_RECORD_LOST) {
195       /*
196        * struct {
197        *    struct perf_event_header    header;
198        *    u64                id;
199        *    u64                lost;
200        *    struct sample_id        sample_id;
201        * };
202        */
203       uint64_t lost = *(uint64_t *)(ptr + sizeof(*e) + sizeof(uint64_t));
204       if (reader->lost_cb) {
205         reader->lost_cb(reader->cb_cookie, lost);
206       } else {
207         fprintf(stderr, "Possibly lost %" PRIu64 " samples\n", lost);
208       }
209     } else if (e->type == PERF_RECORD_SAMPLE) {
210       parse_sw(reader, ptr, e->size);
211     } else {
212       fprintf(stderr, "%s: unknown sample type %d\n", __FUNCTION__, e->type);
213     }
214 
215     write_data_tail(perf_header, perf_header->data_tail + e->size);
216   }
217   reader->rb_use_state = RB_NOT_USED;
218   __sync_synchronize();
219   reader->rb_read_tid = 0;
220 }
221 
perf_reader_poll(int num_readers,struct perf_reader ** readers,int timeout)222 int perf_reader_poll(int num_readers, struct perf_reader **readers, int timeout) {
223   struct pollfd pfds[num_readers];
224   int i;
225 
226   for (i = 0; i <num_readers; ++i) {
227     pfds[i].fd = readers[i]->fd;
228     pfds[i].events = POLLIN;
229   }
230 
231   if (poll(pfds, num_readers, timeout) > 0) {
232     for (i = 0; i < num_readers; ++i) {
233       if (pfds[i].revents & POLLIN)
234         perf_reader_event_read(readers[i]);
235     }
236   }
237   return 0;
238 }
239 
perf_reader_consume(int num_readers,struct perf_reader ** readers)240 int perf_reader_consume(int num_readers, struct perf_reader **readers) {
241   int i;
242   for (i = 0; i < num_readers; ++i) {
243     perf_reader_event_read(readers[i]);
244   }
245   return 0;
246 }
247 
perf_reader_set_fd(struct perf_reader * reader,int fd)248 void perf_reader_set_fd(struct perf_reader *reader, int fd) {
249   reader->fd = fd;
250 }
251 
perf_reader_fd(struct perf_reader * reader)252 int perf_reader_fd(struct perf_reader *reader) {
253   return reader->fd;
254 }
255