doc: Add flags for JUL and python agent compiling
[lttng-ust.git] / liblttng-ust / lttng-context-perf-counters.c
CommitLineData
d58d1454
MD
1/*
2 * lttng-context-perf-counters.c
3 *
4 * LTTng UST performance monitoring counters (perf-counters) integration.
5 *
6 * Copyright (C) 2009-2014 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; only
11 * version 2.1 of the License.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#include <sys/types.h>
24#include <unistd.h>
25#include <string.h>
26#include <stdlib.h>
27#include <stdio.h>
649fb6b3 28#include <stdbool.h>
d58d1454
MD
29#include <sys/mman.h>
30#include <sys/syscall.h>
31#include <linux/perf_event.h>
32#include <lttng/ust-events.h>
33#include <lttng/ust-tracer.h>
34#include <lttng/ringbuffer-config.h>
35#include <urcu/system.h>
36#include <urcu/arch.h>
37#include <urcu/rculist.h>
38#include <helper.h>
39#include <urcu/ref.h>
40#include <usterr-signal-safe.h>
41#include <signal.h>
42#include "lttng-tracer-core.h"
43
44/*
45 * We use a global perf counter key and iterate on per-thread RCU lists
46 * of fields in the fast path, even though this is not strictly speaking
47 * what would provide the best fast-path complexity, to ensure teardown
48 * of sessions vs thread exit is handled racelessly.
49 *
50 * Updates and traversals of thread_list are protected by UST lock.
51 * Updates to rcu_field_list are protected by UST lock.
52 */
53
54struct lttng_perf_counter_thread_field {
55 struct lttng_perf_counter_field *field; /* Back reference */
56 struct perf_event_mmap_page *pc;
57 struct cds_list_head thread_field_node; /* Per-field list of thread fields (node) */
58 struct cds_list_head rcu_field_node; /* RCU per-thread list of fields (node) */
b9389e6e 59 int fd; /* Perf FD */
d58d1454
MD
60};
61
62struct lttng_perf_counter_thread {
63 struct cds_list_head rcu_field_list; /* RCU per-thread list of fields */
64};
65
66struct lttng_perf_counter_field {
67 struct perf_event_attr attr;
68 struct cds_list_head thread_field_list; /* Per-field list of thread fields */
69};
70
71static pthread_key_t perf_counter_key;
72
73static
53569322 74size_t perf_counter_get_size(struct lttng_ctx_field *field, size_t offset)
d58d1454
MD
75{
76 size_t size = 0;
77
78 size += lib_ring_buffer_align(offset, lttng_alignof(uint64_t));
79 size += sizeof(uint64_t);
80 return size;
81}
82
83#if defined(__x86_64__) || defined(__i386__)
84
85static
86uint64_t rdpmc(unsigned int counter)
87{
88 unsigned int low, high;
89
90 asm volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (counter));
91
92 return low | ((uint64_t) high) << 32;
93}
94
b9389e6e
JD
95static bool arch_perf_use_read(void)
96{
97 return false;
98}
99
d58d1454 100static
d286ad50
JD
101uint64_t read_perf_counter(
102 struct lttng_perf_counter_thread_field *thread_field)
d58d1454
MD
103{
104 uint32_t seq, idx;
105 uint64_t count;
d286ad50 106 struct perf_event_mmap_page *pc = thread_field->pc;
d58d1454
MD
107
108 if (caa_unlikely(!pc))
109 return 0;
110
111 do {
112 seq = CMM_LOAD_SHARED(pc->lock);
113 cmm_barrier();
114
115 idx = pc->index;
116 if (idx)
117 count = pc->offset + rdpmc(idx - 1);
118 else
119 count = 0;
120
121 cmm_barrier();
122 } while (CMM_LOAD_SHARED(pc->lock) != seq);
123
124 return count;
125}
126
d286ad50
JD
127#elif defined (__ARM_ARCH_7A__)
128
129static bool arch_perf_use_read(void)
130{
131 return true;
132}
133
134static
135uint64_t read_perf_counter(
136 struct lttng_perf_counter_thread_field *thread_field)
137{
138 uint64_t count;
139
140 if (caa_unlikely(thread_field->fd < 0))
141 return 0;
142
143 if (caa_unlikely(read(thread_field->fd, &count, sizeof(count))
144 < sizeof(count)))
145 return 0;
146
147 return count;
148}
149
150#else /* defined(__x86_64__) || defined(__i386__) || defined(__ARM_ARCH_7A__) */
151
152#error "Perf event counters are only supported on x86 and ARMv7 so far."
153
154#endif /* #else defined(__x86_64__) || defined(__i386__) || defined(__ARM_ARCH_7A__) */
155
d58d1454
MD
156static
157int sys_perf_event_open(struct perf_event_attr *attr,
158 pid_t pid, int cpu, int group_fd,
159 unsigned long flags)
160{
161 return syscall(SYS_perf_event_open, attr, pid, cpu,
162 group_fd, flags);
163}
164
165static
b9389e6e 166int open_perf_fd(struct perf_event_attr *attr)
d58d1454 167{
b9389e6e 168 int fd;
d58d1454
MD
169
170 fd = sys_perf_event_open(attr, 0, -1, -1, 0);
171 if (fd < 0)
b9389e6e
JD
172 return -1;
173
174 return fd;
175}
176
d286ad50
JD
177static
178void close_perf_fd(int fd)
179{
180 int ret;
181
182 if (fd < 0)
183 return;
184
185 ret = close(fd);
186 if (ret) {
187 perror("Error closing LTTng-UST perf memory mapping FD");
188 }
189}
190
b9389e6e
JD
191static
192struct perf_event_mmap_page *setup_perf(
193 struct lttng_perf_counter_thread_field *thread_field)
194{
195 void *perf_addr;
d58d1454
MD
196
197 perf_addr = mmap(NULL, sizeof(struct perf_event_mmap_page),
b9389e6e 198 PROT_READ, MAP_SHARED, thread_field->fd, 0);
d58d1454 199 if (perf_addr == MAP_FAILED)
b9389e6e
JD
200 perf_addr = NULL;
201
202 if (!arch_perf_use_read()) {
203 close_perf_fd(thread_field->fd);
204 thread_field->fd = -1;
6c2125af 205 }
b9389e6e 206
d58d1454
MD
207 return perf_addr;
208}
209
210static
211void unmap_perf_page(struct perf_event_mmap_page *pc)
212{
213 int ret;
214
215 if (!pc)
216 return;
217 ret = munmap(pc, sizeof(struct perf_event_mmap_page));
218 if (ret < 0) {
219 PERROR("Error in munmap");
220 abort();
221 }
222}
223
224static
225struct lttng_perf_counter_thread *alloc_perf_counter_thread(void)
226{
227 struct lttng_perf_counter_thread *perf_thread;
228 sigset_t newmask, oldmask;
229 int ret;
230
231 ret = sigfillset(&newmask);
232 if (ret)
233 abort();
234 ret = pthread_sigmask(SIG_BLOCK, &newmask, &oldmask);
235 if (ret)
236 abort();
237 /* Check again with signals disabled */
238 perf_thread = pthread_getspecific(perf_counter_key);
239 if (perf_thread)
240 goto skip;
241 perf_thread = zmalloc(sizeof(*perf_thread));
242 if (!perf_thread)
243 abort();
244 CDS_INIT_LIST_HEAD(&perf_thread->rcu_field_list);
245 ret = pthread_setspecific(perf_counter_key, perf_thread);
246 if (ret)
247 abort();
248skip:
249 ret = pthread_sigmask(SIG_SETMASK, &oldmask, NULL);
250 if (ret)
251 abort();
252 return perf_thread;
253}
254
255static
256struct lttng_perf_counter_thread_field *
257 add_thread_field(struct lttng_perf_counter_field *perf_field,
258 struct lttng_perf_counter_thread *perf_thread)
259{
260 struct lttng_perf_counter_thread_field *thread_field;
261 sigset_t newmask, oldmask;
262 int ret;
263
264 ret = sigfillset(&newmask);
265 if (ret)
266 abort();
267 ret = pthread_sigmask(SIG_BLOCK, &newmask, &oldmask);
268 if (ret)
269 abort();
270 /* Check again with signals disabled */
271 cds_list_for_each_entry_rcu(thread_field, &perf_thread->rcu_field_list,
272 rcu_field_node) {
273 if (thread_field->field == perf_field)
274 goto skip;
275 }
276 thread_field = zmalloc(sizeof(*thread_field));
277 if (!thread_field)
278 abort();
279 thread_field->field = perf_field;
b9389e6e
JD
280 thread_field->fd = open_perf_fd(&perf_field->attr);
281 if (thread_field->fd >= 0)
282 thread_field->pc = setup_perf(thread_field);
283 /*
284 * Note: thread_field->pc can be NULL if setup_perf() fails.
285 * Also, thread_field->fd can be -1 if open_perf_fd() fails.
286 */
d58d1454
MD
287 ust_lock_nocheck();
288 cds_list_add_rcu(&thread_field->rcu_field_node,
289 &perf_thread->rcu_field_list);
290 cds_list_add(&thread_field->thread_field_node,
291 &perf_field->thread_field_list);
292 ust_unlock();
293skip:
294 ret = pthread_sigmask(SIG_SETMASK, &oldmask, NULL);
295 if (ret)
296 abort();
297 return thread_field;
298}
299
300static
301struct lttng_perf_counter_thread_field *
302 get_thread_field(struct lttng_perf_counter_field *field)
303{
304 struct lttng_perf_counter_thread *perf_thread;
305 struct lttng_perf_counter_thread_field *thread_field;
306
307 perf_thread = pthread_getspecific(perf_counter_key);
308 if (!perf_thread)
309 perf_thread = alloc_perf_counter_thread();
310 cds_list_for_each_entry_rcu(thread_field, &perf_thread->rcu_field_list,
311 rcu_field_node) {
312 if (thread_field->field == field)
313 return thread_field;
314 }
315 /* perf_counter_thread_field not found, need to add one */
316 return add_thread_field(field, perf_thread);
317}
318
319static
320uint64_t wrapper_perf_counter_read(struct lttng_ctx_field *field)
321{
322 struct lttng_perf_counter_field *perf_field;
323 struct lttng_perf_counter_thread_field *perf_thread_field;
324
325 perf_field = field->u.perf_counter;
326 perf_thread_field = get_thread_field(perf_field);
d286ad50 327 return read_perf_counter(perf_thread_field);
d58d1454
MD
328}
329
330static
331void perf_counter_record(struct lttng_ctx_field *field,
332 struct lttng_ust_lib_ring_buffer_ctx *ctx,
333 struct lttng_channel *chan)
334{
335 uint64_t value;
336
337 value = wrapper_perf_counter_read(field);
338 lib_ring_buffer_align_ctx(ctx, lttng_alignof(value));
339 chan->ops->event_write(ctx, &value, sizeof(value));
340}
341
342static
343void perf_counter_get_value(struct lttng_ctx_field *field,
53569322 344 struct lttng_ctx_value *value)
d58d1454
MD
345{
346 uint64_t v;
347
348 v = wrapper_perf_counter_read(field);
53569322 349 value->u.s64 = v;
d58d1454
MD
350}
351
352/* Called with UST lock held */
353static
354void lttng_destroy_perf_thread_field(
355 struct lttng_perf_counter_thread_field *thread_field)
356{
b9389e6e 357 close_perf_fd(thread_field->fd);
d58d1454
MD
358 unmap_perf_page(thread_field->pc);
359 cds_list_del_rcu(&thread_field->rcu_field_node);
360 cds_list_del(&thread_field->thread_field_node);
361 free(thread_field);
362}
363
364static
365void lttng_destroy_perf_thread_key(void *_key)
366{
367 struct lttng_perf_counter_thread *perf_thread = _key;
368 struct lttng_perf_counter_thread_field *pos, *p;
369
370 ust_lock_nocheck();
371 cds_list_for_each_entry_safe(pos, p, &perf_thread->rcu_field_list,
372 rcu_field_node)
373 lttng_destroy_perf_thread_field(pos);
374 ust_unlock();
375 free(perf_thread);
376}
377
378/* Called with UST lock held */
379static
380void lttng_destroy_perf_counter_field(struct lttng_ctx_field *field)
381{
382 struct lttng_perf_counter_field *perf_field;
383 struct lttng_perf_counter_thread_field *pos, *p;
384
385 free((char *) field->event_field.name);
386 perf_field = field->u.perf_counter;
387 /*
388 * This put is performed when no threads can concurrently
389 * perform a "get" concurrently, thanks to urcu-bp grace
390 * period.
391 */
392 cds_list_for_each_entry_safe(pos, p, &perf_field->thread_field_list,
393 thread_field_node)
394 lttng_destroy_perf_thread_field(pos);
395 free(perf_field);
396}
397
d286ad50
JD
398#ifdef __ARM_ARCH_7A__
399
400static
401int perf_get_exclude_kernel(void)
402{
403 return 0;
404}
405
406#else /* __ARM_ARCH_7A__ */
407
408static
409int perf_get_exclude_kernel(void)
410{
411 return 1;
412}
413
414#endif /* __ARM_ARCH_7A__ */
415
d58d1454
MD
416/* Called with UST lock held */
417int lttng_add_perf_counter_to_ctx(uint32_t type,
418 uint64_t config,
419 const char *name,
420 struct lttng_ctx **ctx)
421{
422 struct lttng_ctx_field *field;
423 struct lttng_perf_counter_field *perf_field;
d58d1454
MD
424 char *name_alloc;
425 int ret;
426
427 name_alloc = strdup(name);
428 if (!name_alloc) {
429 ret = -ENOMEM;
430 goto name_alloc_error;
431 }
432 perf_field = zmalloc(sizeof(*perf_field));
433 if (!perf_field) {
434 ret = -ENOMEM;
435 goto perf_field_alloc_error;
436 }
437 field = lttng_append_context(ctx);
438 if (!field) {
439 ret = -ENOMEM;
440 goto append_context_error;
441 }
442 if (lttng_find_context(*ctx, name_alloc)) {
443 ret = -EEXIST;
444 goto find_error;
445 }
446
447 field->destroy = lttng_destroy_perf_counter_field;
448
449 field->event_field.name = name_alloc;
450 field->event_field.type.atype = atype_integer;
451 field->event_field.type.u.basic.integer.size =
452 sizeof(uint64_t) * CHAR_BIT;
453 field->event_field.type.u.basic.integer.alignment =
454 lttng_alignof(uint64_t) * CHAR_BIT;
455 field->event_field.type.u.basic.integer.signedness =
456 lttng_is_signed_type(uint64_t);
457 field->event_field.type.u.basic.integer.reverse_byte_order = 0;
458 field->event_field.type.u.basic.integer.base = 10;
459 field->event_field.type.u.basic.integer.encoding = lttng_encode_none;
460 field->get_size = perf_counter_get_size;
461 field->record = perf_counter_record;
462 field->get_value = perf_counter_get_value;
463
464 perf_field->attr.type = type;
465 perf_field->attr.config = config;
d286ad50 466 perf_field->attr.exclude_kernel = perf_get_exclude_kernel();
d58d1454
MD
467 CDS_INIT_LIST_HEAD(&perf_field->thread_field_list);
468 field->u.perf_counter = perf_field;
469
470 /* Ensure that this perf counter can be used in this process. */
b9389e6e
JD
471 ret = open_perf_fd(&perf_field->attr);
472 if (ret < 0) {
d58d1454
MD
473 ret = -ENODEV;
474 goto setup_error;
475 }
b9389e6e 476 close_perf_fd(ret);
d58d1454
MD
477
478 /*
479 * Contexts can only be added before tracing is started, so we
480 * don't have to synchronize against concurrent threads using
481 * the field here.
482 */
483
b2cc986a 484 lttng_context_update(*ctx);
d58d1454
MD
485 return 0;
486
487setup_error:
488find_error:
489 lttng_remove_context_field(ctx, field);
490append_context_error:
491 free(perf_field);
492perf_field_alloc_error:
493 free(name_alloc);
494name_alloc_error:
495 return ret;
496}
497
498int lttng_perf_counter_init(void)
499{
500 int ret;
501
502 ret = pthread_key_create(&perf_counter_key,
503 lttng_destroy_perf_thread_key);
504 if (ret)
505 ret = -ret;
506 return ret;
507}
508
509void lttng_perf_counter_exit(void)
510{
511 int ret;
512
513 ret = pthread_key_delete(perf_counter_key);
514 if (ret) {
515 errno = ret;
516 PERROR("Error in pthread_key_delete");
517 }
518}
This page took 0.044725 seconds and 4 git commands to generate.