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:
Linus Torvalds
2019-07-20 11:06:12 -07:00
20 changed files with 1029 additions and 142 deletions

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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
View 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
View 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_

View File

@@ -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,

View File

@@ -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);