Skip to content

Commit 76193a9

Browse files
liu-song-6acmel
authored andcommitted
perf, bpf: Introduce PERF_RECORD_KSYMBOL
For better performance analysis of dynamically JITed and loaded kernel functions, such as BPF programs, this patch introduces PERF_RECORD_KSYMBOL, a new perf_event_type that exposes kernel symbol register/unregister information to user space. The following data structure is used for PERF_RECORD_KSYMBOL. /* * struct { * struct perf_event_header header; * u64 addr; * u32 len; * u16 ksym_type; * u16 flags; * char name[]; * struct sample_id sample_id; * }; */ Signed-off-by: Song Liu <[email protected]> Reviewed-by: Arnaldo Carvalho de Melo <[email protected]> Tested-by: Arnaldo Carvalho de Melo <[email protected]> Acked-by: Peter Zijlstra <[email protected]> Cc: Alexei Starovoitov <[email protected]> Cc: Daniel Borkmann <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: [email protected] Cc: [email protected] Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
1 parent 5620196 commit 76193a9

File tree

3 files changed

+130
-2
lines changed

3 files changed

+130
-2
lines changed

include/linux/perf_event.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,10 @@ static inline void perf_event_task_sched_out(struct task_struct *prev,
11221122
}
11231123

11241124
extern void perf_event_mmap(struct vm_area_struct *vma);
1125+
1126+
extern void perf_event_ksymbol(u16 ksym_type, u64 addr, u32 len,
1127+
bool unregister, const char *sym);
1128+
11251129
extern struct perf_guest_info_callbacks *perf_guest_cbs;
11261130
extern int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks);
11271131
extern int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks);
@@ -1342,6 +1346,10 @@ static inline int perf_unregister_guest_info_callbacks
13421346
(struct perf_guest_info_callbacks *callbacks) { return 0; }
13431347

13441348
static inline void perf_event_mmap(struct vm_area_struct *vma) { }
1349+
1350+
typedef int (perf_ksymbol_get_name_f)(char *name, int name_len, void *data);
1351+
static inline void perf_event_ksymbol(u16 ksym_type, u64 addr, u32 len,
1352+
bool unregister, const char *sym) { }
13451353
static inline void perf_event_exec(void) { }
13461354
static inline void perf_event_comm(struct task_struct *tsk, bool exec) { }
13471355
static inline void perf_event_namespaces(struct task_struct *tsk) { }

include/uapi/linux/perf_event.h

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,8 @@ struct perf_event_attr {
372372
context_switch : 1, /* context switch data */
373373
write_backward : 1, /* Write ring buffer from end to beginning */
374374
namespaces : 1, /* include namespaces data */
375-
__reserved_1 : 35;
375+
ksymbol : 1, /* include ksymbol events */
376+
__reserved_1 : 34;
376377

377378
union {
378379
__u32 wakeup_events; /* wakeup every n events */
@@ -963,9 +964,32 @@ enum perf_event_type {
963964
*/
964965
PERF_RECORD_NAMESPACES = 16,
965966

967+
/*
968+
* Record ksymbol register/unregister events:
969+
*
970+
* struct {
971+
* struct perf_event_header header;
972+
* u64 addr;
973+
* u32 len;
974+
* u16 ksym_type;
975+
* u16 flags;
976+
* char name[];
977+
* struct sample_id sample_id;
978+
* };
979+
*/
980+
PERF_RECORD_KSYMBOL = 17,
981+
966982
PERF_RECORD_MAX, /* non-ABI */
967983
};
968984

985+
enum perf_record_ksymbol_type {
986+
PERF_RECORD_KSYMBOL_TYPE_UNKNOWN = 0,
987+
PERF_RECORD_KSYMBOL_TYPE_BPF = 1,
988+
PERF_RECORD_KSYMBOL_TYPE_MAX /* non-ABI */
989+
};
990+
991+
#define PERF_RECORD_KSYMBOL_FLAGS_UNREGISTER (1 << 0)
992+
969993
#define PERF_MAX_STACK_DEPTH 127
970994
#define PERF_MAX_CONTEXTS_PER_STACK 8
971995

kernel/events/core.c

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@ static atomic_t nr_namespaces_events __read_mostly;
385385
static atomic_t nr_task_events __read_mostly;
386386
static atomic_t nr_freq_events __read_mostly;
387387
static atomic_t nr_switch_events __read_mostly;
388+
static atomic_t nr_ksymbol_events __read_mostly;
388389

389390
static LIST_HEAD(pmus);
390391
static DEFINE_MUTEX(pmus_lock);
@@ -4235,7 +4236,7 @@ static bool is_sb_event(struct perf_event *event)
42354236

42364237
if (attr->mmap || attr->mmap_data || attr->mmap2 ||
42374238
attr->comm || attr->comm_exec ||
4238-
attr->task ||
4239+
attr->task || attr->ksymbol ||
42394240
attr->context_switch)
42404241
return true;
42414242
return false;
@@ -4305,6 +4306,8 @@ static void unaccount_event(struct perf_event *event)
43054306
dec = true;
43064307
if (has_branch_stack(event))
43074308
dec = true;
4309+
if (event->attr.ksymbol)
4310+
atomic_dec(&nr_ksymbol_events);
43084311

43094312
if (dec) {
43104313
if (!atomic_add_unless(&perf_sched_count, -1, 1))
@@ -7653,6 +7656,97 @@ static void perf_log_throttle(struct perf_event *event, int enable)
76537656
perf_output_end(&handle);
76547657
}
76557658

7659+
/*
7660+
* ksymbol register/unregister tracking
7661+
*/
7662+
7663+
struct perf_ksymbol_event {
7664+
const char *name;
7665+
int name_len;
7666+
struct {
7667+
struct perf_event_header header;
7668+
u64 addr;
7669+
u32 len;
7670+
u16 ksym_type;
7671+
u16 flags;
7672+
} event_id;
7673+
};
7674+
7675+
static int perf_event_ksymbol_match(struct perf_event *event)
7676+
{
7677+
return event->attr.ksymbol;
7678+
}
7679+
7680+
static void perf_event_ksymbol_output(struct perf_event *event, void *data)
7681+
{
7682+
struct perf_ksymbol_event *ksymbol_event = data;
7683+
struct perf_output_handle handle;
7684+
struct perf_sample_data sample;
7685+
int ret;
7686+
7687+
if (!perf_event_ksymbol_match(event))
7688+
return;
7689+
7690+
perf_event_header__init_id(&ksymbol_event->event_id.header,
7691+
&sample, event);
7692+
ret = perf_output_begin(&handle, event,
7693+
ksymbol_event->event_id.header.size);
7694+
if (ret)
7695+
return;
7696+
7697+
perf_output_put(&handle, ksymbol_event->event_id);
7698+
__output_copy(&handle, ksymbol_event->name, ksymbol_event->name_len);
7699+
perf_event__output_id_sample(event, &handle, &sample);
7700+
7701+
perf_output_end(&handle);
7702+
}
7703+
7704+
void perf_event_ksymbol(u16 ksym_type, u64 addr, u32 len, bool unregister,
7705+
const char *sym)
7706+
{
7707+
struct perf_ksymbol_event ksymbol_event;
7708+
char name[KSYM_NAME_LEN];
7709+
u16 flags = 0;
7710+
int name_len;
7711+
7712+
if (!atomic_read(&nr_ksymbol_events))
7713+
return;
7714+
7715+
if (ksym_type >= PERF_RECORD_KSYMBOL_TYPE_MAX ||
7716+
ksym_type == PERF_RECORD_KSYMBOL_TYPE_UNKNOWN)
7717+
goto err;
7718+
7719+
strlcpy(name, sym, KSYM_NAME_LEN);
7720+
name_len = strlen(name) + 1;
7721+
while (!IS_ALIGNED(name_len, sizeof(u64)))
7722+
name[name_len++] = '\0';
7723+
BUILD_BUG_ON(KSYM_NAME_LEN % sizeof(u64));
7724+
7725+
if (unregister)
7726+
flags |= PERF_RECORD_KSYMBOL_FLAGS_UNREGISTER;
7727+
7728+
ksymbol_event = (struct perf_ksymbol_event){
7729+
.name = name,
7730+
.name_len = name_len,
7731+
.event_id = {
7732+
.header = {
7733+
.type = PERF_RECORD_KSYMBOL,
7734+
.size = sizeof(ksymbol_event.event_id) +
7735+
name_len,
7736+
},
7737+
.addr = addr,
7738+
.len = len,
7739+
.ksym_type = ksym_type,
7740+
.flags = flags,
7741+
},
7742+
};
7743+
7744+
perf_iterate_sb(perf_event_ksymbol_output, &ksymbol_event, NULL);
7745+
return;
7746+
err:
7747+
WARN_ONCE(1, "%s: Invalid KSYMBOL type 0x%x\n", __func__, ksym_type);
7748+
}
7749+
76567750
void perf_event_itrace_started(struct perf_event *event)
76577751
{
76587752
event->attach_state |= PERF_ATTACH_ITRACE;
@@ -9912,6 +10006,8 @@ static void account_event(struct perf_event *event)
991210006
inc = true;
991310007
if (is_cgroup_event(event))
991410008
inc = true;
10009+
if (event->attr.ksymbol)
10010+
atomic_inc(&nr_ksymbol_events);
991510011

991610012
if (inc) {
991710013
/*

0 commit comments

Comments
 (0)