Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull perf tooling updates from Thomas Gleixner: "A set of perf improvements and fixes: perf db-export: - Improvements in how COMM details are exported to databases for post processing and use in the sql-viewer.py UI. - Export switch events to the database. BPF: - Bump rlimit(MEMLOCK) for 'perf test bpf' and 'perf trace', just like selftests/bpf/bpf_rlimit.h do, which makes errors due to exhaustion of this limit, which are kinda cryptic (EPERM sometimes) less frequent. perf version: - Fix segfault due to missing OPT_END(), noticed on PowerPC. perf vendor events: - Add JSON files for IBM s/390 machine type 8561. perf cs-etm (ARM): - Fix two cases of error returns not bing done properly: Invalid ERR_PTR() use and loss of propagation error codes" * 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (28 commits) perf version: Fix segfault due to missing OPT_END() perf vendor events s390: Add JSON files for machine type 8561 perf cs-etm: Return errcode in cs_etm__process_auxtrace_info() perf cs-etm: Remove errnoeous ERR_PTR() usage in cs_etm__process_auxtrace_info perf scripts python: export-to-postgresql.py: Export switch events perf scripts python: export-to-sqlite.py: Export switch events perf db-export: Export switch events perf db-export: Factor out db_export__threads() perf script: Add scripting operation process_switch() perf scripts python: exported-sql-viewer.py: Use new 'has_calls' column perf scripts python: exported-sql-viewer.py: Remove redundant semi-colons perf scripts python: export-to-postgresql.py: Add has_calls column to comms table perf scripts python: export-to-sqlite.py: Add has_calls column to comms table perf db-export: Also export thread's current comm perf db-export: Factor out db_export__comm() perf scripts python: export-to-postgresql.py: Export comm details perf scripts python: export-to-sqlite.py: Export comm details perf db-export: Export comm details perf db-export: Fix a white space issue in db_export__sample() perf db-export: Move export__comm_thread into db_export__sample() ...
This commit is contained in:
@@ -20,6 +20,7 @@ perf-y += parse-events.o
|
||||
perf-y += perf_regs.o
|
||||
perf-y += path.o
|
||||
perf-y += print_binary.o
|
||||
perf-y += rlimit.o
|
||||
perf-y += argv_split.o
|
||||
perf-y += rbtree.o
|
||||
perf-y += libstring.o
|
||||
|
@@ -2460,7 +2460,7 @@ int cs_etm__process_auxtrace_info(union perf_event *event,
|
||||
|
||||
/* Something went wrong, no need to continue */
|
||||
if (!inode) {
|
||||
err = PTR_ERR(inode);
|
||||
err = -ENOMEM;
|
||||
goto err_free_metadata;
|
||||
}
|
||||
|
||||
@@ -2517,8 +2517,10 @@ int cs_etm__process_auxtrace_info(union perf_event *event,
|
||||
session->auxtrace = &etm->auxtrace;
|
||||
|
||||
etm->unknown_thread = thread__new(999999999, 999999999);
|
||||
if (!etm->unknown_thread)
|
||||
if (!etm->unknown_thread) {
|
||||
err = -ENOMEM;
|
||||
goto err_free_queues;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize list node so that at thread__zput() we can avoid
|
||||
@@ -2530,8 +2532,10 @@ int cs_etm__process_auxtrace_info(union perf_event *event,
|
||||
if (err)
|
||||
goto err_delete_thread;
|
||||
|
||||
if (thread__init_map_groups(etm->unknown_thread, etm->machine))
|
||||
if (thread__init_map_groups(etm->unknown_thread, etm->machine)) {
|
||||
err = -ENOMEM;
|
||||
goto err_delete_thread;
|
||||
}
|
||||
|
||||
if (dump_trace) {
|
||||
cs_etm__print_auxtrace_info(auxtrace_info->priv, num_cpu);
|
||||
@@ -2575,5 +2579,5 @@ err_free_traceid_list:
|
||||
err_free_hdr:
|
||||
zfree(&hdr);
|
||||
|
||||
return -EINVAL;
|
||||
return err;
|
||||
}
|
||||
|
@@ -20,70 +20,14 @@
|
||||
#include "db-export.h"
|
||||
#include <linux/zalloc.h>
|
||||
|
||||
struct deferred_export {
|
||||
struct list_head node;
|
||||
struct comm *comm;
|
||||
};
|
||||
|
||||
static int db_export__deferred(struct db_export *dbe)
|
||||
{
|
||||
struct deferred_export *de;
|
||||
int err;
|
||||
|
||||
while (!list_empty(&dbe->deferred)) {
|
||||
de = list_entry(dbe->deferred.next, struct deferred_export,
|
||||
node);
|
||||
err = dbe->export_comm(dbe, de->comm);
|
||||
list_del_init(&de->node);
|
||||
free(de);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void db_export__free_deferred(struct db_export *dbe)
|
||||
{
|
||||
struct deferred_export *de;
|
||||
|
||||
while (!list_empty(&dbe->deferred)) {
|
||||
de = list_entry(dbe->deferred.next, struct deferred_export,
|
||||
node);
|
||||
list_del_init(&de->node);
|
||||
free(de);
|
||||
}
|
||||
}
|
||||
|
||||
static int db_export__defer_comm(struct db_export *dbe, struct comm *comm)
|
||||
{
|
||||
struct deferred_export *de;
|
||||
|
||||
de = zalloc(sizeof(struct deferred_export));
|
||||
if (!de)
|
||||
return -ENOMEM;
|
||||
|
||||
de->comm = comm;
|
||||
list_add_tail(&de->node, &dbe->deferred);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int db_export__init(struct db_export *dbe)
|
||||
{
|
||||
memset(dbe, 0, sizeof(struct db_export));
|
||||
INIT_LIST_HEAD(&dbe->deferred);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int db_export__flush(struct db_export *dbe)
|
||||
{
|
||||
return db_export__deferred(dbe);
|
||||
}
|
||||
|
||||
void db_export__exit(struct db_export *dbe)
|
||||
{
|
||||
db_export__free_deferred(dbe);
|
||||
call_return_processor__free(dbe->crp);
|
||||
dbe->crp = NULL;
|
||||
}
|
||||
@@ -115,71 +59,73 @@ int db_export__machine(struct db_export *dbe, struct machine *machine)
|
||||
}
|
||||
|
||||
int db_export__thread(struct db_export *dbe, struct thread *thread,
|
||||
struct machine *machine, struct comm *comm)
|
||||
struct machine *machine, struct thread *main_thread)
|
||||
{
|
||||
struct thread *main_thread;
|
||||
u64 main_thread_db_id = 0;
|
||||
int err;
|
||||
|
||||
if (thread->db_id)
|
||||
return 0;
|
||||
|
||||
thread->db_id = ++dbe->thread_last_db_id;
|
||||
|
||||
if (thread->pid_ != -1) {
|
||||
if (thread->pid_ == thread->tid) {
|
||||
main_thread = thread;
|
||||
} else {
|
||||
main_thread = machine__findnew_thread(machine,
|
||||
thread->pid_,
|
||||
thread->pid_);
|
||||
if (!main_thread)
|
||||
return -ENOMEM;
|
||||
err = db_export__thread(dbe, main_thread, machine,
|
||||
comm);
|
||||
if (err)
|
||||
goto out_put;
|
||||
if (comm) {
|
||||
err = db_export__comm_thread(dbe, comm, thread);
|
||||
if (err)
|
||||
goto out_put;
|
||||
}
|
||||
}
|
||||
if (main_thread)
|
||||
main_thread_db_id = main_thread->db_id;
|
||||
if (main_thread != thread)
|
||||
thread__put(main_thread);
|
||||
}
|
||||
|
||||
if (dbe->export_thread)
|
||||
return dbe->export_thread(dbe, thread, main_thread_db_id,
|
||||
machine);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
out_put:
|
||||
thread__put(main_thread);
|
||||
return err;
|
||||
static int __db_export__comm(struct db_export *dbe, struct comm *comm,
|
||||
struct thread *thread)
|
||||
{
|
||||
comm->db_id = ++dbe->comm_last_db_id;
|
||||
|
||||
if (dbe->export_comm)
|
||||
return dbe->export_comm(dbe, comm, thread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int db_export__comm(struct db_export *dbe, struct comm *comm,
|
||||
struct thread *main_thread)
|
||||
struct thread *thread)
|
||||
{
|
||||
if (comm->db_id)
|
||||
return 0;
|
||||
|
||||
return __db_export__comm(dbe, comm, thread);
|
||||
}
|
||||
|
||||
/*
|
||||
* Export the "exec" comm. The "exec" comm is the program / application command
|
||||
* name at the time it first executes. It is used to group threads for the same
|
||||
* program. Note that the main thread pid (or thread group id tgid) cannot be
|
||||
* used because it does not change when a new program is exec'ed.
|
||||
*/
|
||||
int db_export__exec_comm(struct db_export *dbe, struct comm *comm,
|
||||
struct thread *main_thread)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (comm->db_id)
|
||||
return 0;
|
||||
|
||||
comm->db_id = ++dbe->comm_last_db_id;
|
||||
|
||||
if (dbe->export_comm) {
|
||||
if (main_thread->comm_set)
|
||||
err = dbe->export_comm(dbe, comm);
|
||||
else
|
||||
err = db_export__defer_comm(dbe, comm);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
err = __db_export__comm(dbe, comm, main_thread);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Record the main thread for this comm. Note that the main thread can
|
||||
* have many "exec" comms because there will be a new one every time it
|
||||
* exec's. An "exec" comm however will only ever have 1 main thread.
|
||||
* That is different to any other threads for that same program because
|
||||
* exec() will effectively kill them, so the relationship between the
|
||||
* "exec" comm and non-main threads is 1-to-1. That is why
|
||||
* db_export__comm_thread() is called here for the main thread, but it
|
||||
* is called for non-main threads when they are exported.
|
||||
*/
|
||||
return db_export__comm_thread(dbe, comm, main_thread);
|
||||
}
|
||||
|
||||
@@ -340,11 +286,65 @@ int db_export__branch_type(struct db_export *dbe, u32 branch_type,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int db_export__threads(struct db_export *dbe, struct thread *thread,
|
||||
struct thread *main_thread,
|
||||
struct machine *machine, struct comm **comm_ptr)
|
||||
{
|
||||
struct comm *comm = NULL;
|
||||
struct comm *curr_comm;
|
||||
int err;
|
||||
|
||||
if (main_thread) {
|
||||
/*
|
||||
* A thread has a reference to the main thread, so export the
|
||||
* main thread first.
|
||||
*/
|
||||
err = db_export__thread(dbe, main_thread, machine, main_thread);
|
||||
if (err)
|
||||
return err;
|
||||
/*
|
||||
* Export comm before exporting the non-main thread because
|
||||
* db_export__comm_thread() can be called further below.
|
||||
*/
|
||||
comm = machine__thread_exec_comm(machine, main_thread);
|
||||
if (comm) {
|
||||
err = db_export__exec_comm(dbe, comm, main_thread);
|
||||
if (err)
|
||||
return err;
|
||||
*comm_ptr = comm;
|
||||
}
|
||||
}
|
||||
|
||||
if (thread != main_thread) {
|
||||
/*
|
||||
* For a non-main thread, db_export__comm_thread() must be
|
||||
* called only if thread has not previously been exported.
|
||||
*/
|
||||
bool export_comm_thread = comm && !thread->db_id;
|
||||
|
||||
err = db_export__thread(dbe, thread, machine, main_thread);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (export_comm_thread) {
|
||||
err = db_export__comm_thread(dbe, comm, thread);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
curr_comm = thread__comm(thread);
|
||||
if (curr_comm)
|
||||
return db_export__comm(dbe, curr_comm, thread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int db_export__sample(struct db_export *dbe, union perf_event *event,
|
||||
struct perf_sample *sample, struct perf_evsel *evsel,
|
||||
struct addr_location *al)
|
||||
{
|
||||
struct thread* thread = al->thread;
|
||||
struct thread *thread = al->thread;
|
||||
struct export_sample es = {
|
||||
.event = event,
|
||||
.sample = sample,
|
||||
@@ -364,19 +364,13 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,
|
||||
return err;
|
||||
|
||||
main_thread = thread__main_thread(al->machine, thread);
|
||||
if (main_thread)
|
||||
comm = machine__thread_exec_comm(al->machine, main_thread);
|
||||
|
||||
err = db_export__thread(dbe, thread, al->machine, comm);
|
||||
err = db_export__threads(dbe, thread, main_thread, al->machine, &comm);
|
||||
if (err)
|
||||
goto out_put;
|
||||
|
||||
if (comm) {
|
||||
err = db_export__comm(dbe, comm, main_thread);
|
||||
if (err)
|
||||
goto out_put;
|
||||
if (comm)
|
||||
es.comm_db_id = comm->db_id;
|
||||
}
|
||||
|
||||
es.db_id = ++dbe->sample_last_db_id;
|
||||
|
||||
@@ -525,3 +519,92 @@ int db_export__call_return(struct db_export *dbe, struct call_return *cr,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int db_export__pid_tid(struct db_export *dbe, struct machine *machine,
|
||||
pid_t pid, pid_t tid, u64 *db_id,
|
||||
struct comm **comm_ptr, bool *is_idle)
|
||||
{
|
||||
struct thread *thread = machine__find_thread(machine, pid, tid);
|
||||
struct thread *main_thread;
|
||||
int err = 0;
|
||||
|
||||
if (!thread || !thread->comm_set)
|
||||
goto out_put;
|
||||
|
||||
*is_idle = !thread->pid_ && !thread->tid;
|
||||
|
||||
main_thread = thread__main_thread(machine, thread);
|
||||
|
||||
err = db_export__threads(dbe, thread, main_thread, machine, comm_ptr);
|
||||
|
||||
*db_id = thread->db_id;
|
||||
|
||||
thread__put(main_thread);
|
||||
out_put:
|
||||
thread__put(thread);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int db_export__switch(struct db_export *dbe, union perf_event *event,
|
||||
struct perf_sample *sample, struct machine *machine)
|
||||
{
|
||||
bool out = event->header.misc & PERF_RECORD_MISC_SWITCH_OUT;
|
||||
bool out_preempt = out &&
|
||||
(event->header.misc & PERF_RECORD_MISC_SWITCH_OUT_PREEMPT);
|
||||
int flags = out | (out_preempt << 1);
|
||||
bool is_idle_a = false, is_idle_b = false;
|
||||
u64 th_a_id = 0, th_b_id = 0;
|
||||
u64 comm_out_id, comm_in_id;
|
||||
struct comm *comm_a = NULL;
|
||||
struct comm *comm_b = NULL;
|
||||
u64 th_out_id, th_in_id;
|
||||
u64 db_id;
|
||||
int err;
|
||||
|
||||
err = db_export__machine(dbe, machine);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = db_export__pid_tid(dbe, machine, sample->pid, sample->tid,
|
||||
&th_a_id, &comm_a, &is_idle_a);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (event->header.type == PERF_RECORD_SWITCH_CPU_WIDE) {
|
||||
pid_t pid = event->context_switch.next_prev_pid;
|
||||
pid_t tid = event->context_switch.next_prev_tid;
|
||||
|
||||
err = db_export__pid_tid(dbe, machine, pid, tid, &th_b_id,
|
||||
&comm_b, &is_idle_b);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do not export if both threads are unknown (i.e. not being traced),
|
||||
* or one is unknown and the other is the idle task.
|
||||
*/
|
||||
if ((!th_a_id || is_idle_a) && (!th_b_id || is_idle_b))
|
||||
return 0;
|
||||
|
||||
db_id = ++dbe->context_switch_last_db_id;
|
||||
|
||||
if (out) {
|
||||
th_out_id = th_a_id;
|
||||
th_in_id = th_b_id;
|
||||
comm_out_id = comm_a ? comm_a->db_id : 0;
|
||||
comm_in_id = comm_b ? comm_b->db_id : 0;
|
||||
} else {
|
||||
th_out_id = th_b_id;
|
||||
th_in_id = th_a_id;
|
||||
comm_out_id = comm_b ? comm_b->db_id : 0;
|
||||
comm_in_id = comm_a ? comm_a->db_id : 0;
|
||||
}
|
||||
|
||||
if (dbe->export_context_switch)
|
||||
return dbe->export_context_switch(dbe, db_id, machine, sample,
|
||||
th_out_id, comm_out_id,
|
||||
th_in_id, comm_in_id, flags);
|
||||
return 0;
|
||||
}
|
||||
|
@@ -43,7 +43,8 @@ struct db_export {
|
||||
int (*export_machine)(struct db_export *dbe, struct machine *machine);
|
||||
int (*export_thread)(struct db_export *dbe, struct thread *thread,
|
||||
u64 main_thread_db_id, struct machine *machine);
|
||||
int (*export_comm)(struct db_export *dbe, struct comm *comm);
|
||||
int (*export_comm)(struct db_export *dbe, struct comm *comm,
|
||||
struct thread *thread);
|
||||
int (*export_comm_thread)(struct db_export *dbe, u64 db_id,
|
||||
struct comm *comm, struct thread *thread);
|
||||
int (*export_dso)(struct db_export *dbe, struct dso *dso,
|
||||
@@ -56,6 +57,11 @@ struct db_export {
|
||||
int (*export_call_path)(struct db_export *dbe, struct call_path *cp);
|
||||
int (*export_call_return)(struct db_export *dbe,
|
||||
struct call_return *cr);
|
||||
int (*export_context_switch)(struct db_export *dbe, u64 db_id,
|
||||
struct machine *machine,
|
||||
struct perf_sample *sample,
|
||||
u64 th_out_id, u64 comm_out_id,
|
||||
u64 th_in_id, u64 comm_in_id, int flags);
|
||||
struct call_return_processor *crp;
|
||||
struct call_path_root *cpr;
|
||||
u64 evsel_last_db_id;
|
||||
@@ -68,18 +74,19 @@ struct db_export {
|
||||
u64 sample_last_db_id;
|
||||
u64 call_path_last_db_id;
|
||||
u64 call_return_last_db_id;
|
||||
struct list_head deferred;
|
||||
u64 context_switch_last_db_id;
|
||||
};
|
||||
|
||||
int db_export__init(struct db_export *dbe);
|
||||
int db_export__flush(struct db_export *dbe);
|
||||
void db_export__exit(struct db_export *dbe);
|
||||
int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel);
|
||||
int db_export__machine(struct db_export *dbe, struct machine *machine);
|
||||
int db_export__thread(struct db_export *dbe, struct thread *thread,
|
||||
struct machine *machine, struct comm *comm);
|
||||
struct machine *machine, struct thread *main_thread);
|
||||
int db_export__comm(struct db_export *dbe, struct comm *comm,
|
||||
struct thread *main_thread);
|
||||
struct thread *thread);
|
||||
int db_export__exec_comm(struct db_export *dbe, struct comm *comm,
|
||||
struct thread *main_thread);
|
||||
int db_export__comm_thread(struct db_export *dbe, struct comm *comm,
|
||||
struct thread *thread);
|
||||
int db_export__dso(struct db_export *dbe, struct dso *dso,
|
||||
@@ -97,5 +104,7 @@ int db_export__branch_types(struct db_export *dbe);
|
||||
int db_export__call_path(struct db_export *dbe, struct call_path *cp);
|
||||
int db_export__call_return(struct db_export *dbe, struct call_return *cr,
|
||||
u64 *parent_db_id);
|
||||
int db_export__switch(struct db_export *dbe, union perf_event *event,
|
||||
struct perf_sample *sample, struct machine *machine);
|
||||
|
||||
#endif
|
||||
|
29
tools/perf/util/rlimit.c
Normal file
29
tools/perf/util/rlimit.c
Normal file
@@ -0,0 +1,29 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1 */
|
||||
|
||||
#include "util/debug.h"
|
||||
#include "util/rlimit.h"
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
/*
|
||||
* Bump the memlock so that we can get bpf maps of a reasonable size,
|
||||
* like the ones used with 'perf trace' and with 'perf test bpf',
|
||||
* improve this to some specific request if needed.
|
||||
*/
|
||||
void rlimit__bump_memlock(void)
|
||||
{
|
||||
struct rlimit rlim;
|
||||
|
||||
if (getrlimit(RLIMIT_MEMLOCK, &rlim) == 0) {
|
||||
rlim.rlim_cur *= 4;
|
||||
rlim.rlim_max *= 4;
|
||||
|
||||
if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0) {
|
||||
rlim.rlim_cur /= 2;
|
||||
rlim.rlim_max /= 2;
|
||||
|
||||
if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0)
|
||||
pr_debug("Couldn't bump rlimit(MEMLOCK), failures may take place when creating BPF maps, etc\n");
|
||||
}
|
||||
}
|
||||
}
|
6
tools/perf/util/rlimit.h
Normal file
6
tools/perf/util/rlimit.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef __PERF_RLIMIT_H_
|
||||
#define __PERF_RLIMIT_H_
|
||||
/* SPDX-License-Identifier: LGPL-2.1 */
|
||||
|
||||
void rlimit__bump_memlock(void);
|
||||
#endif // __PERF_RLIMIT_H_
|
@@ -113,6 +113,7 @@ struct tables {
|
||||
PyObject *call_path_handler;
|
||||
PyObject *call_return_handler;
|
||||
PyObject *synth_handler;
|
||||
PyObject *context_switch_handler;
|
||||
bool db_export_mode;
|
||||
};
|
||||
|
||||
@@ -1011,15 +1012,19 @@ static int python_export_thread(struct db_export *dbe, struct thread *thread,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int python_export_comm(struct db_export *dbe, struct comm *comm)
|
||||
static int python_export_comm(struct db_export *dbe, struct comm *comm,
|
||||
struct thread *thread)
|
||||
{
|
||||
struct tables *tables = container_of(dbe, struct tables, dbe);
|
||||
PyObject *t;
|
||||
|
||||
t = tuple_new(2);
|
||||
t = tuple_new(5);
|
||||
|
||||
tuple_set_u64(t, 0, comm->db_id);
|
||||
tuple_set_string(t, 1, comm__str(comm));
|
||||
tuple_set_u64(t, 2, thread->db_id);
|
||||
tuple_set_u64(t, 3, comm->start);
|
||||
tuple_set_s32(t, 4, comm->exec);
|
||||
|
||||
call_object(tables->comm_handler, t, "comm_table");
|
||||
|
||||
@@ -1233,6 +1238,34 @@ static int python_export_call_return(struct db_export *dbe,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int python_export_context_switch(struct db_export *dbe, u64 db_id,
|
||||
struct machine *machine,
|
||||
struct perf_sample *sample,
|
||||
u64 th_out_id, u64 comm_out_id,
|
||||
u64 th_in_id, u64 comm_in_id, int flags)
|
||||
{
|
||||
struct tables *tables = container_of(dbe, struct tables, dbe);
|
||||
PyObject *t;
|
||||
|
||||
t = tuple_new(9);
|
||||
|
||||
tuple_set_u64(t, 0, db_id);
|
||||
tuple_set_u64(t, 1, machine->db_id);
|
||||
tuple_set_u64(t, 2, sample->time);
|
||||
tuple_set_s32(t, 3, sample->cpu);
|
||||
tuple_set_u64(t, 4, th_out_id);
|
||||
tuple_set_u64(t, 5, comm_out_id);
|
||||
tuple_set_u64(t, 6, th_in_id);
|
||||
tuple_set_u64(t, 7, comm_in_id);
|
||||
tuple_set_s32(t, 8, flags);
|
||||
|
||||
call_object(tables->context_switch_handler, t, "context_switch");
|
||||
|
||||
Py_DECREF(t);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int python_process_call_return(struct call_return *cr, u64 *parent_db_id,
|
||||
void *data)
|
||||
{
|
||||
@@ -1296,6 +1329,16 @@ static void python_process_event(union perf_event *event,
|
||||
}
|
||||
}
|
||||
|
||||
static void python_process_switch(union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct tables *tables = &tables_global;
|
||||
|
||||
if (tables->db_export_mode)
|
||||
db_export__switch(&tables->dbe, event, sample, machine);
|
||||
}
|
||||
|
||||
static void get_handler_name(char *str, size_t size,
|
||||
struct perf_evsel *evsel)
|
||||
{
|
||||
@@ -1511,6 +1554,7 @@ static void set_table_handlers(struct tables *tables)
|
||||
SET_TABLE_HANDLER(sample);
|
||||
SET_TABLE_HANDLER(call_path);
|
||||
SET_TABLE_HANDLER(call_return);
|
||||
SET_TABLE_HANDLER(context_switch);
|
||||
|
||||
/*
|
||||
* Synthesized events are samples but with architecture-specific data
|
||||
@@ -1620,9 +1664,7 @@ error:
|
||||
|
||||
static int python_flush_script(void)
|
||||
{
|
||||
struct tables *tables = &tables_global;
|
||||
|
||||
return db_export__flush(&tables->dbe);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1831,6 +1873,7 @@ struct scripting_ops python_scripting_ops = {
|
||||
.flush_script = python_flush_script,
|
||||
.stop_script = python_stop_script,
|
||||
.process_event = python_process_event,
|
||||
.process_switch = python_process_switch,
|
||||
.process_stat = python_process_stat,
|
||||
.process_stat_interval = python_process_stat_interval,
|
||||
.generate_script = python_generate_script,
|
||||
|
@@ -81,6 +81,9 @@ struct scripting_ops {
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct addr_location *al);
|
||||
void (*process_switch)(union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
void (*process_stat)(struct perf_stat_config *config,
|
||||
struct perf_evsel *evsel, u64 tstamp);
|
||||
void (*process_stat_interval)(u64 tstamp);
|
||||
|
Reference in New Issue
Block a user