perf tools: Rename perf_event_ops to perf_tool
To better reflect that it became the base class for all tools, that must be in each tool struct and where common stuff will be put. Cc: David Ahern <dsahern@gmail.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Link: http://lkml.kernel.org/n/tip-qgpc4msetqlwr8y2k7537cxe@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
743eb86865
commit
45694aa770
22 changed files with 301 additions and 277 deletions
|
|
@ -14,8 +14,9 @@
|
|||
#include <linux/kernel.h>
|
||||
#include "debug.h"
|
||||
#include "session.h"
|
||||
#include "tool.h"
|
||||
|
||||
static int build_id__mark_dso_hit(struct perf_event_ops *ops __used,
|
||||
static int build_id__mark_dso_hit(struct perf_tool *tool __used,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct perf_evsel *evsel __used,
|
||||
|
|
@ -40,7 +41,7 @@ static int build_id__mark_dso_hit(struct perf_event_ops *ops __used,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int perf_event__exit_del_thread(struct perf_event_ops *ops __used,
|
||||
static int perf_event__exit_del_thread(struct perf_tool *tool __used,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct machine *machine)
|
||||
|
|
@ -59,7 +60,7 @@ static int perf_event__exit_del_thread(struct perf_event_ops *ops __used,
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct perf_event_ops build_id__mark_dso_hit_ops = {
|
||||
struct perf_tool build_id__mark_dso_hit_ops = {
|
||||
.sample = build_id__mark_dso_hit,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.fork = perf_event__process_task,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include "session.h"
|
||||
|
||||
extern struct perf_event_ops build_id__mark_dso_hit_ops;
|
||||
extern struct perf_tool build_id__mark_dso_hit_ops;
|
||||
|
||||
char *dso__build_id_filename(struct dso *self, char *bf, size_t size);
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ static struct perf_sample synth_sample = {
|
|||
.period = 1,
|
||||
};
|
||||
|
||||
static pid_t perf_event__synthesize_comm(struct perf_event_ops *ops,
|
||||
static pid_t perf_event__synthesize_comm(struct perf_tool *tool,
|
||||
union perf_event *event, pid_t pid,
|
||||
int full, perf_event__handler_t process,
|
||||
struct machine *machine)
|
||||
|
|
@ -99,7 +99,7 @@ out_race:
|
|||
if (!full) {
|
||||
event->comm.tid = pid;
|
||||
|
||||
process(ops, event, &synth_sample, machine);
|
||||
process(tool, event, &synth_sample, machine);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ out_race:
|
|||
|
||||
event->comm.tid = pid;
|
||||
|
||||
process(ops, event, &synth_sample, machine);
|
||||
process(tool, event, &synth_sample, machine);
|
||||
}
|
||||
|
||||
closedir(tasks);
|
||||
|
|
@ -127,7 +127,7 @@ out:
|
|||
return tgid;
|
||||
}
|
||||
|
||||
static int perf_event__synthesize_mmap_events(struct perf_event_ops *ops,
|
||||
static int perf_event__synthesize_mmap_events(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
pid_t pid, pid_t tgid,
|
||||
perf_event__handler_t process,
|
||||
|
|
@ -199,7 +199,7 @@ static int perf_event__synthesize_mmap_events(struct perf_event_ops *ops,
|
|||
event->mmap.pid = tgid;
|
||||
event->mmap.tid = pid;
|
||||
|
||||
process(ops, event, &synth_sample, machine);
|
||||
process(tool, event, &synth_sample, machine);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -207,7 +207,7 @@ static int perf_event__synthesize_mmap_events(struct perf_event_ops *ops,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_modules(struct perf_event_ops *ops,
|
||||
int perf_event__synthesize_modules(struct perf_tool *tool,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine)
|
||||
{
|
||||
|
|
@ -252,7 +252,7 @@ int perf_event__synthesize_modules(struct perf_event_ops *ops,
|
|||
|
||||
memcpy(event->mmap.filename, pos->dso->long_name,
|
||||
pos->dso->long_name_len + 1);
|
||||
process(ops, event, &synth_sample, machine);
|
||||
process(tool, event, &synth_sample, machine);
|
||||
}
|
||||
|
||||
free(event);
|
||||
|
|
@ -262,18 +262,18 @@ int perf_event__synthesize_modules(struct perf_event_ops *ops,
|
|||
static int __event__synthesize_thread(union perf_event *comm_event,
|
||||
union perf_event *mmap_event,
|
||||
pid_t pid, perf_event__handler_t process,
|
||||
struct perf_event_ops *ops,
|
||||
struct perf_tool *tool,
|
||||
struct machine *machine)
|
||||
{
|
||||
pid_t tgid = perf_event__synthesize_comm(ops, comm_event, pid, 1,
|
||||
pid_t tgid = perf_event__synthesize_comm(tool, comm_event, pid, 1,
|
||||
process, machine);
|
||||
if (tgid == -1)
|
||||
return -1;
|
||||
return perf_event__synthesize_mmap_events(ops, mmap_event, pid, tgid,
|
||||
return perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid,
|
||||
process, machine);
|
||||
}
|
||||
|
||||
int perf_event__synthesize_thread_map(struct perf_event_ops *ops,
|
||||
int perf_event__synthesize_thread_map(struct perf_tool *tool,
|
||||
struct thread_map *threads,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine)
|
||||
|
|
@ -293,7 +293,7 @@ int perf_event__synthesize_thread_map(struct perf_event_ops *ops,
|
|||
for (thread = 0; thread < threads->nr; ++thread) {
|
||||
if (__event__synthesize_thread(comm_event, mmap_event,
|
||||
threads->map[thread],
|
||||
process, ops, machine)) {
|
||||
process, tool, machine)) {
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
|
|
@ -305,7 +305,7 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_threads(struct perf_event_ops *ops,
|
||||
int perf_event__synthesize_threads(struct perf_tool *tool,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine)
|
||||
{
|
||||
|
|
@ -334,7 +334,7 @@ int perf_event__synthesize_threads(struct perf_event_ops *ops,
|
|||
continue;
|
||||
|
||||
__event__synthesize_thread(comm_event, mmap_event, pid,
|
||||
process, ops, machine);
|
||||
process, tool, machine);
|
||||
}
|
||||
|
||||
closedir(proc);
|
||||
|
|
@ -369,7 +369,7 @@ static int find_symbol_cb(void *arg, const char *name, char type,
|
|||
return 1;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_kernel_mmap(struct perf_event_ops *ops,
|
||||
int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine,
|
||||
const char *symbol_name)
|
||||
|
|
@ -427,13 +427,13 @@ int perf_event__synthesize_kernel_mmap(struct perf_event_ops *ops,
|
|||
event->mmap.len = map->end - event->mmap.start;
|
||||
event->mmap.pid = machine->pid;
|
||||
|
||||
err = process(ops, event, &synth_sample, machine);
|
||||
err = process(tool, event, &synth_sample, machine);
|
||||
free(event);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int perf_event__process_comm(struct perf_event_ops *ops __used,
|
||||
int perf_event__process_comm(struct perf_tool *tool __used,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct machine *machine)
|
||||
|
|
@ -450,7 +450,7 @@ int perf_event__process_comm(struct perf_event_ops *ops __used,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int perf_event__process_lost(struct perf_event_ops *ops __used,
|
||||
int perf_event__process_lost(struct perf_tool *tool __used,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct machine *machine __used)
|
||||
|
|
@ -473,7 +473,7 @@ static void perf_event__set_kernel_mmap_len(union perf_event *event,
|
|||
maps[MAP__FUNCTION]->end = ~0ULL;
|
||||
}
|
||||
|
||||
static int perf_event__process_kernel_mmap(struct perf_event_ops *ops __used,
|
||||
static int perf_event__process_kernel_mmap(struct perf_tool *tool __used,
|
||||
union perf_event *event,
|
||||
struct machine *machine)
|
||||
{
|
||||
|
|
@ -566,7 +566,7 @@ out_problem:
|
|||
return -1;
|
||||
}
|
||||
|
||||
int perf_event__process_mmap(struct perf_event_ops *ops,
|
||||
int perf_event__process_mmap(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct machine *machine)
|
||||
|
|
@ -582,7 +582,7 @@ int perf_event__process_mmap(struct perf_event_ops *ops,
|
|||
|
||||
if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL ||
|
||||
cpumode == PERF_RECORD_MISC_KERNEL) {
|
||||
ret = perf_event__process_kernel_mmap(ops, event, machine);
|
||||
ret = perf_event__process_kernel_mmap(tool, event, machine);
|
||||
if (ret < 0)
|
||||
goto out_problem;
|
||||
return 0;
|
||||
|
|
@ -606,7 +606,7 @@ out_problem:
|
|||
return 0;
|
||||
}
|
||||
|
||||
int perf_event__process_task(struct perf_event_ops *ops __used,
|
||||
int perf_event__process_task(struct perf_tool *tool __used,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct machine *machine)
|
||||
|
|
@ -631,22 +631,22 @@ int perf_event__process_task(struct perf_event_ops *ops __used,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int perf_event__process(struct perf_event_ops *ops, union perf_event *event,
|
||||
int perf_event__process(struct perf_tool *tool, union perf_event *event,
|
||||
struct perf_sample *sample, struct machine *machine)
|
||||
{
|
||||
switch (event->header.type) {
|
||||
case PERF_RECORD_COMM:
|
||||
perf_event__process_comm(ops, event, sample, machine);
|
||||
perf_event__process_comm(tool, event, sample, machine);
|
||||
break;
|
||||
case PERF_RECORD_MMAP:
|
||||
perf_event__process_mmap(ops, event, sample, machine);
|
||||
perf_event__process_mmap(tool, event, sample, machine);
|
||||
break;
|
||||
case PERF_RECORD_FORK:
|
||||
case PERF_RECORD_EXIT:
|
||||
perf_event__process_task(ops, event, sample, machine);
|
||||
perf_event__process_task(tool, event, sample, machine);
|
||||
break;
|
||||
case PERF_RECORD_LOST:
|
||||
perf_event__process_lost(ops, event, sample, machine);
|
||||
perf_event__process_lost(tool, event, sample, machine);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,47 +141,47 @@ union perf_event {
|
|||
|
||||
void perf_event__print_totals(void);
|
||||
|
||||
struct perf_event_ops;
|
||||
struct perf_tool;
|
||||
struct thread_map;
|
||||
|
||||
typedef int (*perf_event__handler_t)(struct perf_event_ops *ops,
|
||||
typedef int (*perf_event__handler_t)(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
|
||||
int perf_event__synthesize_thread_map(struct perf_event_ops *ops,
|
||||
int perf_event__synthesize_thread_map(struct perf_tool *tool,
|
||||
struct thread_map *threads,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine);
|
||||
int perf_event__synthesize_threads(struct perf_event_ops *ops,
|
||||
int perf_event__synthesize_threads(struct perf_tool *tool,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine);
|
||||
int perf_event__synthesize_kernel_mmap(struct perf_event_ops *ops,
|
||||
int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine,
|
||||
const char *symbol_name);
|
||||
|
||||
int perf_event__synthesize_modules(struct perf_event_ops *ops,
|
||||
int perf_event__synthesize_modules(struct perf_tool *tool,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine);
|
||||
|
||||
int perf_event__process_comm(struct perf_event_ops *ops,
|
||||
int perf_event__process_comm(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
int perf_event__process_lost(struct perf_event_ops *ops,
|
||||
int perf_event__process_lost(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
int perf_event__process_mmap(struct perf_event_ops *ops,
|
||||
int perf_event__process_mmap(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
int perf_event__process_task(struct perf_event_ops *ops,
|
||||
int perf_event__process_task(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
int perf_event__process(struct perf_event_ops *ops,
|
||||
int perf_event__process(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
|
|
|
|||
|
|
@ -2070,7 +2070,7 @@ out_delete_evlist:
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_attr(struct perf_event_ops *ops,
|
||||
int perf_event__synthesize_attr(struct perf_tool *tool,
|
||||
struct perf_event_attr *attr, u16 ids, u64 *id,
|
||||
perf_event__handler_t process)
|
||||
{
|
||||
|
|
@ -2094,14 +2094,14 @@ int perf_event__synthesize_attr(struct perf_event_ops *ops,
|
|||
ev->attr.header.type = PERF_RECORD_HEADER_ATTR;
|
||||
ev->attr.header.size = size;
|
||||
|
||||
err = process(ops, ev, NULL, NULL);
|
||||
err = process(tool, ev, NULL, NULL);
|
||||
|
||||
free(ev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_attrs(struct perf_event_ops *ops,
|
||||
int perf_event__synthesize_attrs(struct perf_tool *tool,
|
||||
struct perf_session *session,
|
||||
perf_event__handler_t process)
|
||||
{
|
||||
|
|
@ -2109,7 +2109,7 @@ int perf_event__synthesize_attrs(struct perf_event_ops *ops,
|
|||
int err = 0;
|
||||
|
||||
list_for_each_entry(attr, &session->evlist->entries, node) {
|
||||
err = perf_event__synthesize_attr(ops, &attr->attr, attr->ids,
|
||||
err = perf_event__synthesize_attr(tool, &attr->attr, attr->ids,
|
||||
attr->id, process);
|
||||
if (err) {
|
||||
pr_debug("failed to create perf header attribute\n");
|
||||
|
|
@ -2157,7 +2157,7 @@ int perf_event__process_attr(union perf_event *event,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_event_type(struct perf_event_ops *ops,
|
||||
int perf_event__synthesize_event_type(struct perf_tool *tool,
|
||||
u64 event_id, char *name,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine)
|
||||
|
|
@ -2178,12 +2178,12 @@ int perf_event__synthesize_event_type(struct perf_event_ops *ops,
|
|||
ev.event_type.header.size = sizeof(ev.event_type) -
|
||||
(sizeof(ev.event_type.event_type.name) - size);
|
||||
|
||||
err = process(ops, &ev, NULL, machine);
|
||||
err = process(tool, &ev, NULL, machine);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_event_types(struct perf_event_ops *ops,
|
||||
int perf_event__synthesize_event_types(struct perf_tool *tool,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine)
|
||||
{
|
||||
|
|
@ -2193,7 +2193,7 @@ int perf_event__synthesize_event_types(struct perf_event_ops *ops,
|
|||
for (i = 0; i < event_count; i++) {
|
||||
type = &events[i];
|
||||
|
||||
err = perf_event__synthesize_event_type(ops, type->event_id,
|
||||
err = perf_event__synthesize_event_type(tool, type->event_id,
|
||||
type->name, process,
|
||||
machine);
|
||||
if (err) {
|
||||
|
|
@ -2205,7 +2205,7 @@ int perf_event__synthesize_event_types(struct perf_event_ops *ops,
|
|||
return err;
|
||||
}
|
||||
|
||||
int perf_event__process_event_type(struct perf_event_ops *ops __unused,
|
||||
int perf_event__process_event_type(struct perf_tool *tool __unused,
|
||||
union perf_event *event)
|
||||
{
|
||||
if (perf_header__push_event(event->event_type.event_type.event_id,
|
||||
|
|
@ -2215,7 +2215,7 @@ int perf_event__process_event_type(struct perf_event_ops *ops __unused,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_tracing_data(struct perf_event_ops *ops, int fd,
|
||||
int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd,
|
||||
struct perf_evlist *evlist,
|
||||
perf_event__handler_t process)
|
||||
{
|
||||
|
|
@ -2248,7 +2248,7 @@ int perf_event__synthesize_tracing_data(struct perf_event_ops *ops, int fd,
|
|||
ev.tracing_data.header.size = sizeof(ev.tracing_data);
|
||||
ev.tracing_data.size = aligned_size;
|
||||
|
||||
process(ops, &ev, NULL, NULL);
|
||||
process(tool, &ev, NULL, NULL);
|
||||
|
||||
/*
|
||||
* The put function will copy all the tracing data
|
||||
|
|
@ -2290,7 +2290,7 @@ int perf_event__process_tracing_data(union perf_event *event,
|
|||
return size_read + padding;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_build_id(struct perf_event_ops *ops,
|
||||
int perf_event__synthesize_build_id(struct perf_tool *tool,
|
||||
struct dso *pos, u16 misc,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine)
|
||||
|
|
@ -2313,12 +2313,12 @@ int perf_event__synthesize_build_id(struct perf_event_ops *ops,
|
|||
ev.build_id.header.size = sizeof(ev.build_id) + len;
|
||||
memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len);
|
||||
|
||||
err = process(ops, &ev, NULL, machine);
|
||||
err = process(tool, &ev, NULL, machine);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int perf_event__process_build_id(struct perf_event_ops *ops __used,
|
||||
int perf_event__process_build_id(struct perf_tool *tool __used,
|
||||
union perf_event *event,
|
||||
struct perf_session *session)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -97,35 +97,35 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
|
|||
const char *name, bool is_kallsyms);
|
||||
int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir);
|
||||
|
||||
int perf_event__synthesize_attr(struct perf_event_ops *ops,
|
||||
int perf_event__synthesize_attr(struct perf_tool *tool,
|
||||
struct perf_event_attr *attr, u16 ids, u64 *id,
|
||||
perf_event__handler_t process);
|
||||
int perf_event__synthesize_attrs(struct perf_event_ops *ops,
|
||||
int perf_event__synthesize_attrs(struct perf_tool *tool,
|
||||
struct perf_session *session,
|
||||
perf_event__handler_t process);
|
||||
int perf_event__process_attr(union perf_event *event, struct perf_evlist **pevlist);
|
||||
|
||||
int perf_event__synthesize_event_type(struct perf_event_ops *ops,
|
||||
int perf_event__synthesize_event_type(struct perf_tool *tool,
|
||||
u64 event_id, char *name,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine);
|
||||
int perf_event__synthesize_event_types(struct perf_event_ops *ops,
|
||||
int perf_event__synthesize_event_types(struct perf_tool *tool,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine);
|
||||
int perf_event__process_event_type(struct perf_event_ops *ops,
|
||||
int perf_event__process_event_type(struct perf_tool *tool,
|
||||
union perf_event *event);
|
||||
|
||||
int perf_event__synthesize_tracing_data(struct perf_event_ops *ops,
|
||||
int perf_event__synthesize_tracing_data(struct perf_tool *tool,
|
||||
int fd, struct perf_evlist *evlist,
|
||||
perf_event__handler_t process);
|
||||
int perf_event__process_tracing_data(union perf_event *event,
|
||||
struct perf_session *session);
|
||||
|
||||
int perf_event__synthesize_build_id(struct perf_event_ops *ops,
|
||||
int perf_event__synthesize_build_id(struct perf_tool *tool,
|
||||
struct dso *pos, u16 misc,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine);
|
||||
int perf_event__process_build_id(struct perf_event_ops *ops,
|
||||
int perf_event__process_build_id(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_session *session);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
#include "session.h"
|
||||
#include "tool.h"
|
||||
#include "sort.h"
|
||||
#include "util.h"
|
||||
#include "cpumap.h"
|
||||
|
|
@ -104,7 +105,7 @@ static void perf_session__destroy_kernel_maps(struct perf_session *self)
|
|||
|
||||
struct perf_session *perf_session__new(const char *filename, int mode,
|
||||
bool force, bool repipe,
|
||||
struct perf_event_ops *ops)
|
||||
struct perf_tool *tool)
|
||||
{
|
||||
size_t len = filename ? strlen(filename) + 1 : 0;
|
||||
struct perf_session *self = zalloc(sizeof(*self) + len);
|
||||
|
|
@ -142,10 +143,10 @@ struct perf_session *perf_session__new(const char *filename, int mode,
|
|||
goto out_delete;
|
||||
}
|
||||
|
||||
if (ops && ops->ordering_requires_timestamps &&
|
||||
ops->ordered_samples && !self->sample_id_all) {
|
||||
if (tool && tool->ordering_requires_timestamps &&
|
||||
tool->ordered_samples && !self->sample_id_all) {
|
||||
dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n");
|
||||
ops->ordered_samples = false;
|
||||
tool->ordered_samples = false;
|
||||
}
|
||||
|
||||
out:
|
||||
|
|
@ -285,7 +286,7 @@ static int process_event_synth_attr_stub(union perf_event *event __used,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int process_event_sample_stub(struct perf_event_ops *ops __used,
|
||||
static int process_event_sample_stub(struct perf_tool *tool __used,
|
||||
union perf_event *event __used,
|
||||
struct perf_sample *sample __used,
|
||||
struct perf_evsel *evsel __used,
|
||||
|
|
@ -295,7 +296,7 @@ static int process_event_sample_stub(struct perf_event_ops *ops __used,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int process_event_stub(struct perf_event_ops *ops __used,
|
||||
static int process_event_stub(struct perf_tool *tool __used,
|
||||
union perf_event *event __used,
|
||||
struct perf_sample *sample __used,
|
||||
struct machine *machine __used)
|
||||
|
|
@ -304,7 +305,7 @@ static int process_event_stub(struct perf_event_ops *ops __used,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int process_finished_round_stub(struct perf_event_ops *ops __used,
|
||||
static int process_finished_round_stub(struct perf_tool *tool __used,
|
||||
union perf_event *event __used,
|
||||
struct perf_session *perf_session __used)
|
||||
{
|
||||
|
|
@ -312,50 +313,50 @@ static int process_finished_round_stub(struct perf_event_ops *ops __used,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int process_event_type_stub(struct perf_event_ops *ops __used,
|
||||
static int process_event_type_stub(struct perf_tool *tool __used,
|
||||
union perf_event *event __used)
|
||||
{
|
||||
dump_printf(": unhandled!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_finished_round(struct perf_event_ops *ops,
|
||||
static int process_finished_round(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_session *session);
|
||||
|
||||
static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
|
||||
static void perf_tool__fill_defaults(struct perf_tool *tool)
|
||||
{
|
||||
if (handler->sample == NULL)
|
||||
handler->sample = process_event_sample_stub;
|
||||
if (handler->mmap == NULL)
|
||||
handler->mmap = process_event_stub;
|
||||
if (handler->comm == NULL)
|
||||
handler->comm = process_event_stub;
|
||||
if (handler->fork == NULL)
|
||||
handler->fork = process_event_stub;
|
||||
if (handler->exit == NULL)
|
||||
handler->exit = process_event_stub;
|
||||
if (handler->lost == NULL)
|
||||
handler->lost = perf_event__process_lost;
|
||||
if (handler->read == NULL)
|
||||
handler->read = process_event_sample_stub;
|
||||
if (handler->throttle == NULL)
|
||||
handler->throttle = process_event_stub;
|
||||
if (handler->unthrottle == NULL)
|
||||
handler->unthrottle = process_event_stub;
|
||||
if (handler->attr == NULL)
|
||||
handler->attr = process_event_synth_attr_stub;
|
||||
if (handler->event_type == NULL)
|
||||
handler->event_type = process_event_type_stub;
|
||||
if (handler->tracing_data == NULL)
|
||||
handler->tracing_data = process_event_synth_tracing_data_stub;
|
||||
if (handler->build_id == NULL)
|
||||
handler->build_id = process_finished_round_stub;
|
||||
if (handler->finished_round == NULL) {
|
||||
if (handler->ordered_samples)
|
||||
handler->finished_round = process_finished_round;
|
||||
if (tool->sample == NULL)
|
||||
tool->sample = process_event_sample_stub;
|
||||
if (tool->mmap == NULL)
|
||||
tool->mmap = process_event_stub;
|
||||
if (tool->comm == NULL)
|
||||
tool->comm = process_event_stub;
|
||||
if (tool->fork == NULL)
|
||||
tool->fork = process_event_stub;
|
||||
if (tool->exit == NULL)
|
||||
tool->exit = process_event_stub;
|
||||
if (tool->lost == NULL)
|
||||
tool->lost = perf_event__process_lost;
|
||||
if (tool->read == NULL)
|
||||
tool->read = process_event_sample_stub;
|
||||
if (tool->throttle == NULL)
|
||||
tool->throttle = process_event_stub;
|
||||
if (tool->unthrottle == NULL)
|
||||
tool->unthrottle = process_event_stub;
|
||||
if (tool->attr == NULL)
|
||||
tool->attr = process_event_synth_attr_stub;
|
||||
if (tool->event_type == NULL)
|
||||
tool->event_type = process_event_type_stub;
|
||||
if (tool->tracing_data == NULL)
|
||||
tool->tracing_data = process_event_synth_tracing_data_stub;
|
||||
if (tool->build_id == NULL)
|
||||
tool->build_id = process_finished_round_stub;
|
||||
if (tool->finished_round == NULL) {
|
||||
if (tool->ordered_samples)
|
||||
tool->finished_round = process_finished_round;
|
||||
else
|
||||
handler->finished_round = process_finished_round_stub;
|
||||
tool->finished_round = process_finished_round_stub;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -487,11 +488,11 @@ static void perf_session_free_sample_buffers(struct perf_session *session)
|
|||
static int perf_session_deliver_event(struct perf_session *session,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_event_ops *ops,
|
||||
struct perf_tool *tool,
|
||||
u64 file_offset);
|
||||
|
||||
static void flush_sample_queue(struct perf_session *s,
|
||||
struct perf_event_ops *ops)
|
||||
struct perf_tool *tool)
|
||||
{
|
||||
struct ordered_samples *os = &s->ordered_samples;
|
||||
struct list_head *head = &os->samples;
|
||||
|
|
@ -502,7 +503,7 @@ static void flush_sample_queue(struct perf_session *s,
|
|||
unsigned idx = 0, progress_next = os->nr_samples / 16;
|
||||
int ret;
|
||||
|
||||
if (!ops->ordered_samples || !limit)
|
||||
if (!tool->ordered_samples || !limit)
|
||||
return;
|
||||
|
||||
list_for_each_entry_safe(iter, tmp, head, list) {
|
||||
|
|
@ -513,7 +514,7 @@ static void flush_sample_queue(struct perf_session *s,
|
|||
if (ret)
|
||||
pr_err("Can't parse sample, err = %d\n", ret);
|
||||
else
|
||||
perf_session_deliver_event(s, iter->event, &sample, ops,
|
||||
perf_session_deliver_event(s, iter->event, &sample, tool,
|
||||
iter->file_offset);
|
||||
|
||||
os->last_flush = iter->timestamp;
|
||||
|
|
@ -575,11 +576,11 @@ static void flush_sample_queue(struct perf_session *s,
|
|||
* Flush every events below timestamp 7
|
||||
* etc...
|
||||
*/
|
||||
static int process_finished_round(struct perf_event_ops *ops,
|
||||
static int process_finished_round(struct perf_tool *tool,
|
||||
union perf_event *event __used,
|
||||
struct perf_session *session)
|
||||
{
|
||||
flush_sample_queue(session, ops);
|
||||
flush_sample_queue(session, tool);
|
||||
session->ordered_samples.next_flush = session->ordered_samples.max_timestamp;
|
||||
|
||||
return 0;
|
||||
|
|
@ -749,7 +750,7 @@ static struct machine *
|
|||
static int perf_session_deliver_event(struct perf_session *session,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_event_ops *ops,
|
||||
struct perf_tool *tool,
|
||||
u64 file_offset)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
|
|
@ -784,25 +785,25 @@ static int perf_session_deliver_event(struct perf_session *session,
|
|||
++session->hists.stats.nr_unknown_id;
|
||||
return -1;
|
||||
}
|
||||
return ops->sample(ops, event, sample, evsel, machine);
|
||||
return tool->sample(tool, event, sample, evsel, machine);
|
||||
case PERF_RECORD_MMAP:
|
||||
return ops->mmap(ops, event, sample, machine);
|
||||
return tool->mmap(tool, event, sample, machine);
|
||||
case PERF_RECORD_COMM:
|
||||
return ops->comm(ops, event, sample, machine);
|
||||
return tool->comm(tool, event, sample, machine);
|
||||
case PERF_RECORD_FORK:
|
||||
return ops->fork(ops, event, sample, machine);
|
||||
return tool->fork(tool, event, sample, machine);
|
||||
case PERF_RECORD_EXIT:
|
||||
return ops->exit(ops, event, sample, machine);
|
||||
return tool->exit(tool, event, sample, machine);
|
||||
case PERF_RECORD_LOST:
|
||||
if (ops->lost == perf_event__process_lost)
|
||||
if (tool->lost == perf_event__process_lost)
|
||||
session->hists.stats.total_lost += event->lost.lost;
|
||||
return ops->lost(ops, event, sample, machine);
|
||||
return tool->lost(tool, event, sample, machine);
|
||||
case PERF_RECORD_READ:
|
||||
return ops->read(ops, event, sample, evsel, machine);
|
||||
return tool->read(tool, event, sample, evsel, machine);
|
||||
case PERF_RECORD_THROTTLE:
|
||||
return ops->throttle(ops, event, sample, machine);
|
||||
return tool->throttle(tool, event, sample, machine);
|
||||
case PERF_RECORD_UNTHROTTLE:
|
||||
return ops->unthrottle(ops, event, sample, machine);
|
||||
return tool->unthrottle(tool, event, sample, machine);
|
||||
default:
|
||||
++session->hists.stats.nr_unknown_events;
|
||||
return -1;
|
||||
|
|
@ -826,7 +827,7 @@ static int perf_session__preprocess_sample(struct perf_session *session,
|
|||
}
|
||||
|
||||
static int perf_session__process_user_event(struct perf_session *session, union perf_event *event,
|
||||
struct perf_event_ops *ops, u64 file_offset)
|
||||
struct perf_tool *tool, u64 file_offset)
|
||||
{
|
||||
int err;
|
||||
|
||||
|
|
@ -835,20 +836,20 @@ static int perf_session__process_user_event(struct perf_session *session, union
|
|||
/* These events are processed right away */
|
||||
switch (event->header.type) {
|
||||
case PERF_RECORD_HEADER_ATTR:
|
||||
err = ops->attr(event, &session->evlist);
|
||||
err = tool->attr(event, &session->evlist);
|
||||
if (err == 0)
|
||||
perf_session__update_sample_type(session);
|
||||
return err;
|
||||
case PERF_RECORD_HEADER_EVENT_TYPE:
|
||||
return ops->event_type(ops, event);
|
||||
return tool->event_type(tool, event);
|
||||
case PERF_RECORD_HEADER_TRACING_DATA:
|
||||
/* setup for reading amidst mmap */
|
||||
lseek(session->fd, file_offset, SEEK_SET);
|
||||
return ops->tracing_data(event, session);
|
||||
return tool->tracing_data(event, session);
|
||||
case PERF_RECORD_HEADER_BUILD_ID:
|
||||
return ops->build_id(ops, event, session);
|
||||
return tool->build_id(tool, event, session);
|
||||
case PERF_RECORD_FINISHED_ROUND:
|
||||
return ops->finished_round(ops, event, session);
|
||||
return tool->finished_round(tool, event, session);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -856,7 +857,7 @@ static int perf_session__process_user_event(struct perf_session *session, union
|
|||
|
||||
static int perf_session__process_event(struct perf_session *session,
|
||||
union perf_event *event,
|
||||
struct perf_event_ops *ops,
|
||||
struct perf_tool *tool,
|
||||
u64 file_offset)
|
||||
{
|
||||
struct perf_sample sample;
|
||||
|
|
@ -872,7 +873,7 @@ static int perf_session__process_event(struct perf_session *session,
|
|||
hists__inc_nr_events(&session->hists, event->header.type);
|
||||
|
||||
if (event->header.type >= PERF_RECORD_USER_TYPE_START)
|
||||
return perf_session__process_user_event(session, event, ops, file_offset);
|
||||
return perf_session__process_user_event(session, event, tool, file_offset);
|
||||
|
||||
/*
|
||||
* For all kernel events we get the sample data
|
||||
|
|
@ -885,14 +886,14 @@ static int perf_session__process_event(struct perf_session *session,
|
|||
if (perf_session__preprocess_sample(session, event, &sample))
|
||||
return 0;
|
||||
|
||||
if (ops->ordered_samples) {
|
||||
if (tool->ordered_samples) {
|
||||
ret = perf_session_queue_event(session, event, &sample,
|
||||
file_offset);
|
||||
if (ret != -ETIME)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return perf_session_deliver_event(session, event, &sample, ops,
|
||||
return perf_session_deliver_event(session, event, &sample, tool,
|
||||
file_offset);
|
||||
}
|
||||
|
||||
|
|
@ -921,9 +922,9 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se
|
|||
}
|
||||
|
||||
static void perf_session__warn_about_errors(const struct perf_session *session,
|
||||
const struct perf_event_ops *ops)
|
||||
const struct perf_tool *tool)
|
||||
{
|
||||
if (ops->lost == perf_event__process_lost &&
|
||||
if (tool->lost == perf_event__process_lost &&
|
||||
session->hists.stats.nr_events[PERF_RECORD_LOST] != 0) {
|
||||
ui__warning("Processed %d events and lost %d chunks!\n\n"
|
||||
"Check IO/CPU overload!\n\n",
|
||||
|
|
@ -958,7 +959,7 @@ static void perf_session__warn_about_errors(const struct perf_session *session,
|
|||
volatile int session_done;
|
||||
|
||||
static int __perf_session__process_pipe_events(struct perf_session *self,
|
||||
struct perf_event_ops *ops)
|
||||
struct perf_tool *tool)
|
||||
{
|
||||
union perf_event event;
|
||||
uint32_t size;
|
||||
|
|
@ -967,7 +968,7 @@ static int __perf_session__process_pipe_events(struct perf_session *self,
|
|||
int err;
|
||||
void *p;
|
||||
|
||||
perf_event_ops__fill_defaults(ops);
|
||||
perf_tool__fill_defaults(tool);
|
||||
|
||||
head = 0;
|
||||
more:
|
||||
|
|
@ -1004,7 +1005,7 @@ more:
|
|||
}
|
||||
|
||||
if (size == 0 ||
|
||||
(skip = perf_session__process_event(self, &event, ops, head)) < 0) {
|
||||
(skip = perf_session__process_event(self, &event, tool, head)) < 0) {
|
||||
dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n",
|
||||
head, event.header.size, event.header.type);
|
||||
/*
|
||||
|
|
@ -1027,7 +1028,7 @@ more:
|
|||
done:
|
||||
err = 0;
|
||||
out_err:
|
||||
perf_session__warn_about_errors(self, ops);
|
||||
perf_session__warn_about_errors(self, tool);
|
||||
perf_session_free_sample_buffers(self);
|
||||
return err;
|
||||
}
|
||||
|
|
@ -1058,7 +1059,7 @@ fetch_mmaped_event(struct perf_session *session,
|
|||
|
||||
int __perf_session__process_events(struct perf_session *session,
|
||||
u64 data_offset, u64 data_size,
|
||||
u64 file_size, struct perf_event_ops *ops)
|
||||
u64 file_size, struct perf_tool *tool)
|
||||
{
|
||||
u64 head, page_offset, file_offset, file_pos, progress_next;
|
||||
int err, mmap_prot, mmap_flags, map_idx = 0;
|
||||
|
|
@ -1067,7 +1068,7 @@ int __perf_session__process_events(struct perf_session *session,
|
|||
union perf_event *event;
|
||||
uint32_t size;
|
||||
|
||||
perf_event_ops__fill_defaults(ops);
|
||||
perf_tool__fill_defaults(tool);
|
||||
|
||||
page_size = sysconf(_SC_PAGESIZE);
|
||||
|
||||
|
|
@ -1122,7 +1123,7 @@ more:
|
|||
size = event->header.size;
|
||||
|
||||
if (size == 0 ||
|
||||
perf_session__process_event(session, event, ops, file_pos) < 0) {
|
||||
perf_session__process_event(session, event, tool, file_pos) < 0) {
|
||||
dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n",
|
||||
file_offset + head, event->header.size,
|
||||
event->header.type);
|
||||
|
|
@ -1151,15 +1152,15 @@ more:
|
|||
err = 0;
|
||||
/* do the final flush for ordered samples */
|
||||
session->ordered_samples.next_flush = ULLONG_MAX;
|
||||
flush_sample_queue(session, ops);
|
||||
flush_sample_queue(session, tool);
|
||||
out_err:
|
||||
perf_session__warn_about_errors(session, ops);
|
||||
perf_session__warn_about_errors(session, tool);
|
||||
perf_session_free_sample_buffers(session);
|
||||
return err;
|
||||
}
|
||||
|
||||
int perf_session__process_events(struct perf_session *self,
|
||||
struct perf_event_ops *ops)
|
||||
struct perf_tool *tool)
|
||||
{
|
||||
int err;
|
||||
|
||||
|
|
@ -1170,9 +1171,9 @@ int perf_session__process_events(struct perf_session *self,
|
|||
err = __perf_session__process_events(self,
|
||||
self->header.data_offset,
|
||||
self->header.data_size,
|
||||
self->size, ops);
|
||||
self->size, tool);
|
||||
else
|
||||
err = __perf_session__process_pipe_events(self, ops);
|
||||
err = __perf_session__process_pipe_events(self, tool);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,55 +53,20 @@ struct perf_session {
|
|||
char filename[0];
|
||||
};
|
||||
|
||||
struct perf_evsel;
|
||||
struct perf_event_ops;
|
||||
|
||||
typedef int (*event_sample)(struct perf_event_ops *ops,
|
||||
union perf_event *event, struct perf_sample *sample,
|
||||
struct perf_evsel *evsel, struct machine *machine);
|
||||
typedef int (*event_op)(struct perf_event_ops *ops, union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
typedef int (*event_synth_op)(union perf_event *self,
|
||||
struct perf_session *session);
|
||||
typedef int (*event_attr_op)(union perf_event *event,
|
||||
struct perf_evlist **pevlist);
|
||||
typedef int (*event_simple_op)(struct perf_event_ops *ops,
|
||||
union perf_event *event);
|
||||
typedef int (*event_op2)(struct perf_event_ops *ops, union perf_event *event,
|
||||
struct perf_session *session);
|
||||
|
||||
struct perf_event_ops {
|
||||
event_sample sample,
|
||||
read;
|
||||
event_op mmap,
|
||||
comm,
|
||||
fork,
|
||||
exit,
|
||||
lost,
|
||||
throttle,
|
||||
unthrottle;
|
||||
event_attr_op attr;
|
||||
event_synth_op tracing_data;
|
||||
event_simple_op event_type;
|
||||
event_op2 finished_round,
|
||||
build_id;
|
||||
bool ordered_samples;
|
||||
bool ordering_requires_timestamps;
|
||||
};
|
||||
struct perf_tool;
|
||||
|
||||
struct perf_session *perf_session__new(const char *filename, int mode,
|
||||
bool force, bool repipe,
|
||||
struct perf_event_ops *ops);
|
||||
struct perf_tool *tool);
|
||||
void perf_session__delete(struct perf_session *self);
|
||||
|
||||
void perf_event_header__bswap(struct perf_event_header *self);
|
||||
|
||||
int __perf_session__process_events(struct perf_session *self,
|
||||
u64 data_offset, u64 data_size, u64 size,
|
||||
struct perf_event_ops *ops);
|
||||
struct perf_tool *tool);
|
||||
int perf_session__process_events(struct perf_session *self,
|
||||
struct perf_event_ops *event_ops);
|
||||
struct perf_tool *tool);
|
||||
|
||||
int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel *evsel,
|
||||
struct thread *thread,
|
||||
|
|
@ -142,11 +107,11 @@ struct machine *perf_session__findnew_machine(struct perf_session *self, pid_t p
|
|||
|
||||
static inline
|
||||
void perf_session__process_machines(struct perf_session *self,
|
||||
struct perf_event_ops *ops,
|
||||
struct perf_tool *tool,
|
||||
machine__process_t process)
|
||||
{
|
||||
process(&self->host_machine, ops);
|
||||
return machines__process(&self->machines, process, ops);
|
||||
process(&self->host_machine, tool);
|
||||
return machines__process(&self->machines, process, tool);
|
||||
}
|
||||
|
||||
struct thread *perf_session__findnew(struct perf_session *self, pid_t pid);
|
||||
|
|
|
|||
45
tools/perf/util/tool.h
Normal file
45
tools/perf/util/tool.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#ifndef __PERF_TOOL_H
|
||||
#define __PERF_TOOL_H
|
||||
|
||||
struct perf_session;
|
||||
struct perf_evsel;
|
||||
struct perf_tool;
|
||||
struct machine;
|
||||
|
||||
typedef int (*event_sample)(struct perf_tool *tool, union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel, struct machine *machine);
|
||||
|
||||
typedef int (*event_op)(struct perf_tool *tool, union perf_event *event,
|
||||
struct perf_sample *sample, struct machine *machine);
|
||||
|
||||
typedef int (*event_attr_op)(union perf_event *event,
|
||||
struct perf_evlist **pevlist);
|
||||
typedef int (*event_simple_op)(struct perf_tool *tool, union perf_event *event);
|
||||
|
||||
typedef int (*event_synth_op)(union perf_event *event,
|
||||
struct perf_session *session);
|
||||
|
||||
typedef int (*event_op2)(struct perf_tool *tool, union perf_event *event,
|
||||
struct perf_session *session);
|
||||
|
||||
struct perf_tool {
|
||||
event_sample sample,
|
||||
read;
|
||||
event_op mmap,
|
||||
comm,
|
||||
fork,
|
||||
exit,
|
||||
lost,
|
||||
throttle,
|
||||
unthrottle;
|
||||
event_attr_op attr;
|
||||
event_synth_op tracing_data;
|
||||
event_simple_op event_type;
|
||||
event_op2 finished_round,
|
||||
build_id;
|
||||
bool ordered_samples;
|
||||
bool ordering_requires_timestamps;
|
||||
};
|
||||
|
||||
#endif /* __PERF_TOOL_H */
|
||||
|
|
@ -1,16 +1,17 @@
|
|||
#ifndef __PERF_TOP_H
|
||||
#define __PERF_TOP_H 1
|
||||
|
||||
#include "tool.h"
|
||||
#include "types.h"
|
||||
#include "session.h"
|
||||
#include "../perf.h"
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct perf_evlist;
|
||||
struct perf_evsel;
|
||||
struct perf_session;
|
||||
|
||||
struct perf_top {
|
||||
struct perf_event_ops ops;
|
||||
struct perf_tool tool;
|
||||
struct perf_evlist *evlist;
|
||||
/*
|
||||
* Symbols will be added here in perf_event__process_sample and will
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue