Commit e0c5c5e3 authored by Thomas Gleixner's avatar Thomas Gleixner

Merge tag 'perf-core-for-mingo-5.3-20190715' of...

Merge tag 'perf-core-for-mingo-5.3-20190715' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/urgent

Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:

 perf db-export:
  Adrian Hunter:
  - 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:
  Arnaldo Carvalho de Melo:
  - 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:
  Ravi Bangoria:
  - Fix segfault due to missing OPT_END(), noticed on PowerPC.

 perf vendor events:
  Thomas Richter:
  - Add JSON files for IBM s/390 machine type 8561.

 perf cs-etm (ARM):
  YueHaibing:
  - Fix two cases of error returns not bing done properly: Invalid ERR_PTR() use
    and loss of propagation error codes.
parents e4557c1a 916c31ff
...@@ -2289,6 +2289,12 @@ static int process_switch_event(struct perf_tool *tool, ...@@ -2289,6 +2289,12 @@ static int process_switch_event(struct perf_tool *tool,
if (perf_event__process_switch(tool, event, sample, machine) < 0) if (perf_event__process_switch(tool, event, sample, machine) < 0)
return -1; return -1;
if (scripting_ops && scripting_ops->process_switch)
scripting_ops->process_switch(event, sample, machine);
if (!script->show_switch_events)
return 0;
thread = machine__findnew_thread(machine, sample->pid, thread = machine__findnew_thread(machine, sample->pid,
sample->tid); sample->tid);
if (thread == NULL) { if (thread == NULL) {
...@@ -2467,7 +2473,7 @@ static int __cmd_script(struct perf_script *script) ...@@ -2467,7 +2473,7 @@ static int __cmd_script(struct perf_script *script)
script->tool.mmap = process_mmap_event; script->tool.mmap = process_mmap_event;
script->tool.mmap2 = process_mmap2_event; script->tool.mmap2 = process_mmap2_event;
} }
if (script->show_switch_events) if (script->show_switch_events || (scripting_ops && scripting_ops->process_switch))
script->tool.context_switch = process_switch_event; script->tool.context_switch = process_switch_event;
if (script->show_namespace_events) if (script->show_namespace_events)
script->tool.namespaces = process_namespaces_event; script->tool.namespaces = process_namespaces_event;
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <api/fs/tracing_path.h> #include <api/fs/tracing_path.h>
#include <bpf/bpf.h> #include <bpf/bpf.h>
#include "util/bpf_map.h" #include "util/bpf_map.h"
#include "util/rlimit.h"
#include "builtin.h" #include "builtin.h"
#include "util/cgroup.h" #include "util/cgroup.h"
#include "util/color.h" #include "util/color.h"
...@@ -3864,6 +3865,15 @@ int cmd_trace(int argc, const char **argv) ...@@ -3864,6 +3865,15 @@ int cmd_trace(int argc, const char **argv)
goto out; goto out;
} }
/*
* Parsing .perfconfig may entail creating a BPF event, that may need
* to create BPF maps, so bump RLIM_MEMLOCK as the default 64K setting
* is too small. This affects just this process, not touching the
* global setting. If it fails we'll get something in 'perf trace -v'
* to help diagnose the problem.
*/
rlimit__bump_memlock();
err = perf_config(trace__config, &trace); err = perf_config(trace__config, &trace);
if (err) if (err)
goto out; goto out;
......
...@@ -19,6 +19,7 @@ static struct version version; ...@@ -19,6 +19,7 @@ static struct version version;
static struct option version_options[] = { static struct option version_options[] = {
OPT_BOOLEAN(0, "build-options", &version.build_options, OPT_BOOLEAN(0, "build-options", &version.build_options,
"display the build options"), "display the build options"),
OPT_END(),
}; };
static const char * const version_usage[] = { static const char * const version_usage[] = {
......
[
{
"Unit": "CPU-M-CF",
"EventCode": "0",
"EventName": "CPU_CYCLES",
"BriefDescription": "CPU Cycles",
"PublicDescription": "Cycle Count"
},
{
"Unit": "CPU-M-CF",
"EventCode": "1",
"EventName": "INSTRUCTIONS",
"BriefDescription": "Instructions",
"PublicDescription": "Instruction Count"
},
{
"Unit": "CPU-M-CF",
"EventCode": "2",
"EventName": "L1I_DIR_WRITES",
"BriefDescription": "L1I Directory Writes",
"PublicDescription": "Level-1 I-Cache Directory Write Count"
},
{
"Unit": "CPU-M-CF",
"EventCode": "3",
"EventName": "L1I_PENALTY_CYCLES",
"BriefDescription": "L1I Penalty Cycles",
"PublicDescription": "Level-1 I-Cache Penalty Cycle Count"
},
{
"Unit": "CPU-M-CF",
"EventCode": "4",
"EventName": "L1D_DIR_WRITES",
"BriefDescription": "L1D Directory Writes",
"PublicDescription": "Level-1 D-Cache Directory Write Count"
},
{
"Unit": "CPU-M-CF",
"EventCode": "5",
"EventName": "L1D_PENALTY_CYCLES",
"BriefDescription": "L1D Penalty Cycles",
"PublicDescription": "Level-1 D-Cache Penalty Cycle Count"
},
{
"Unit": "CPU-M-CF",
"EventCode": "32",
"EventName": "PROBLEM_STATE_CPU_CYCLES",
"BriefDescription": "Problem-State CPU Cycles",
"PublicDescription": "Problem-State Cycle Count"
},
{
"Unit": "CPU-M-CF",
"EventCode": "33",
"EventName": "PROBLEM_STATE_INSTRUCTIONS",
"BriefDescription": "Problem-State Instructions",
"PublicDescription": "Problem-State Instruction Count"
},
]
[
{
"Unit": "CPU-M-CF",
"EventCode": "64",
"EventName": "PRNG_FUNCTIONS",
"BriefDescription": "PRNG Functions",
"PublicDescription": "Total number of the PRNG functions issued by the CPU"
},
{
"Unit": "CPU-M-CF",
"EventCode": "65",
"EventName": "PRNG_CYCLES",
"BriefDescription": "PRNG Cycles",
"PublicDescription": "Total number of CPU cycles when the DEA/AES coprocessor is busy performing PRNG functions issued by the CPU"
},
{
"Unit": "CPU-M-CF",
"EventCode": "66",
"EventName": "PRNG_BLOCKED_FUNCTIONS",
"BriefDescription": "PRNG Blocked Functions",
"PublicDescription": "Total number of the PRNG functions that are issued by the CPU and are blocked because the DEA/AES coprocessor is busy performing a function issued by another CPU"
},
{
"Unit": "CPU-M-CF",
"EventCode": "67",
"EventName": "PRNG_BLOCKED_CYCLES",
"BriefDescription": "PRNG Blocked Cycles",
"PublicDescription": "Total number of CPU cycles blocked for the PRNG functions issued by the CPU because the DEA/AES coprocessor is busy performing a function issued by another CPU"
},
{
"Unit": "CPU-M-CF",
"EventCode": "68",
"EventName": "SHA_FUNCTIONS",
"BriefDescription": "SHA Functions",
"PublicDescription": "Total number of SHA functions issued by the CPU"
},
{
"Unit": "CPU-M-CF",
"EventCode": "69",
"EventName": "SHA_CYCLES",
"BriefDescription": "SHA Cycles",
"PublicDescription": "Total number of CPU cycles when the SHA coprocessor is busy performing the SHA functions issued by the CPU"
},
{
"Unit": "CPU-M-CF",
"EventCode": "70",
"EventName": "SHA_BLOCKED_FUNCTIONS",
"BriefDescription": "SHA Blocked Functions",
"PublicDescription": "Total number of the SHA functions that are issued by the CPU and are blocked because the SHA coprocessor is busy performing a function issued by another CPU"
},
{
"Unit": "CPU-M-CF",
"EventCode": "71",
"EventName": "SHA_BLOCKED_CYCLES",
"BriefDescription": "SHA Bloced Cycles",
"PublicDescription": "Total number of CPU cycles blocked for the SHA functions issued by the CPU because the SHA coprocessor is busy performing a function issued by another CPU"
},
{
"Unit": "CPU-M-CF",
"EventCode": "72",
"EventName": "DEA_FUNCTIONS",
"BriefDescription": "DEA Functions",
"PublicDescription": "Total number of the DEA functions issued by the CPU"
},
{
"Unit": "CPU-M-CF",
"EventCode": "73",
"EventName": "DEA_CYCLES",
"BriefDescription": "DEA Cycles",
"PublicDescription": "Total number of CPU cycles when the DEA/AES coprocessor is busy performing the DEA functions issued by the CPU"
},
{
"Unit": "CPU-M-CF",
"EventCode": "74",
"EventName": "DEA_BLOCKED_FUNCTIONS",
"BriefDescription": "DEA Blocked Functions",
"PublicDescription": "Total number of the DEA functions that are issued by the CPU and are blocked because the DEA/AES coprocessor is busy performing a function issued by another CPU"
},
{
"Unit": "CPU-M-CF",
"EventCode": "75",
"EventName": "DEA_BLOCKED_CYCLES",
"BriefDescription": "DEA Blocked Cycles",
"PublicDescription": "Total number of CPU cycles blocked for the DEA functions issued by the CPU because the DEA/AES coprocessor is busy performing a function issued by another CPU"
},
{
"Unit": "CPU-M-CF",
"EventCode": "76",
"EventName": "AES_FUNCTIONS",
"BriefDescription": "AES Functions",
"PublicDescription": "Total number of AES functions issued by the CPU"
},
{
"Unit": "CPU-M-CF",
"EventCode": "77",
"EventName": "AES_CYCLES",
"BriefDescription": "AES Cycles",
"PublicDescription": "Total number of CPU cycles when the DEA/AES coprocessor is busy performing the AES functions issued by the CPU"
},
{
"Unit": "CPU-M-CF",
"EventCode": "78",
"EventName": "AES_BLOCKED_FUNCTIONS",
"BriefDescription": "AES Blocked Functions",
"PublicDescription": "Total number of AES functions that are issued by the CPU and are blocked because the DEA/AES coprocessor is busy performing a function issued by another CPU"
},
{
"Unit": "CPU-M-CF",
"EventCode": "79",
"EventName": "AES_BLOCKED_CYCLES",
"BriefDescription": "AES Blocked Cycles",
"PublicDescription": "Total number of CPU cycles blocked for the AES functions issued by the CPU because the DEA/AES coprocessor is busy performing a function issued by another CPU"
},
]
[
{
"Unit": "CPU-M-CF",
"EventCode": "80",
"EventName": "ECC_FUNCTION_COUNT",
"BriefDescription": "ECC Function Count",
"PublicDescription": "Long ECC function Count"
},
{
"Unit": "CPU-M-CF",
"EventCode": "81",
"EventName": "ECC_CYCLES_COUNT",
"BriefDescription": "ECC Cycles Count",
"PublicDescription": "Long ECC Function cycles count"
},
{
"Unit": "CPU-M-CF",
"EventCode": "82",
"EventName": "ECC_BLOCKED_FUNCTION_COUNT",
"BriefDescription": "Ecc Blocked Function Count",
"PublicDescription": "Long ECC blocked function count"
},
{
"Unit": "CPU-M-CF",
"EventCode": "83",
"EventName": "ECC_BLOCKED_CYCLES_COUNT",
"BriefDescription": "ECC Blocked Cycles Count",
"PublicDescription": "Long ECC blocked cycles count"
},
]
This diff is collapsed.
...@@ -4,3 +4,4 @@ Family-model,Version,Filename,EventType ...@@ -4,3 +4,4 @@ Family-model,Version,Filename,EventType
^IBM.282[78].*[13]\.[1-5].[[:xdigit:]]+$,1,cf_zec12,core ^IBM.282[78].*[13]\.[1-5].[[:xdigit:]]+$,1,cf_zec12,core
^IBM.296[45].*[13]\.[1-5].[[:xdigit:]]+$,1,cf_z13,core ^IBM.296[45].*[13]\.[1-5].[[:xdigit:]]+$,1,cf_z13,core
^IBM.390[67].*[13]\.[1-5].[[:xdigit:]]+$,3,cf_z14,core ^IBM.390[67].*[13]\.[1-5].[[:xdigit:]]+$,3,cf_z14,core
^IBM.856[12].*3\.6.[[:xdigit:]]+$,3,cf_m8561,core
...@@ -353,7 +353,10 @@ do_query(query, 'CREATE TABLE threads (' ...@@ -353,7 +353,10 @@ do_query(query, 'CREATE TABLE threads ('
'tid integer)') 'tid integer)')
do_query(query, 'CREATE TABLE comms (' do_query(query, 'CREATE TABLE comms ('
'id bigint NOT NULL,' 'id bigint NOT NULL,'
'comm varchar(16))') 'comm varchar(16),'
'c_thread_id bigint,'
'c_time bigint,'
'exec_flag boolean)')
do_query(query, 'CREATE TABLE comm_threads (' do_query(query, 'CREATE TABLE comm_threads ('
'id bigint NOT NULL,' 'id bigint NOT NULL,'
'comm_id bigint,' 'comm_id bigint,'
...@@ -479,6 +482,17 @@ do_query(query, 'CREATE TABLE pwrx (' ...@@ -479,6 +482,17 @@ do_query(query, 'CREATE TABLE pwrx ('
'last_cstate integer,' 'last_cstate integer,'
'wake_reason integer)') 'wake_reason integer)')
do_query(query, 'CREATE TABLE context_switches ('
'id bigint NOT NULL,'
'machine_id bigint,'
'time bigint,'
'cpu integer,'
'thread_out_id bigint,'
'comm_out_id bigint,'
'thread_in_id bigint,'
'comm_in_id bigint,'
'flags integer)')
do_query(query, 'CREATE VIEW machines_view AS ' do_query(query, 'CREATE VIEW machines_view AS '
'SELECT ' 'SELECT '
'id,' 'id,'
...@@ -692,6 +706,29 @@ do_query(query, 'CREATE VIEW power_events_view AS ' ...@@ -692,6 +706,29 @@ do_query(query, 'CREATE VIEW power_events_view AS '
' INNER JOIN selected_events ON selected_events.id = samples.evsel_id' ' INNER JOIN selected_events ON selected_events.id = samples.evsel_id'
' ORDER BY samples.id') ' ORDER BY samples.id')
do_query(query, 'CREATE VIEW context_switches_view AS '
'SELECT '
'context_switches.id,'
'context_switches.machine_id,'
'context_switches.time,'
'context_switches.cpu,'
'th_out.pid AS pid_out,'
'th_out.tid AS tid_out,'
'comm_out.comm AS comm_out,'
'th_in.pid AS pid_in,'
'th_in.tid AS tid_in,'
'comm_in.comm AS comm_in,'
'CASE WHEN context_switches.flags = 0 THEN \'in\''
' WHEN context_switches.flags = 1 THEN \'out\''
' WHEN context_switches.flags = 3 THEN \'out preempt\''
' ELSE CAST ( context_switches.flags AS VARCHAR(11) )'
'END AS flags'
' FROM context_switches'
' INNER JOIN threads AS th_out ON th_out.id = context_switches.thread_out_id'
' INNER JOIN threads AS th_in ON th_in.id = context_switches.thread_in_id'
' INNER JOIN comms AS comm_out ON comm_out.id = context_switches.comm_out_id'
' INNER JOIN comms AS comm_in ON comm_in.id = context_switches.comm_in_id')
file_header = struct.pack("!11sii", b"PGCOPY\n\377\r\n\0", 0, 0) file_header = struct.pack("!11sii", b"PGCOPY\n\377\r\n\0", 0, 0)
file_trailer = b"\377\377" file_trailer = b"\377\377"
...@@ -756,6 +793,7 @@ mwait_file = open_output_file("mwait_table.bin") ...@@ -756,6 +793,7 @@ mwait_file = open_output_file("mwait_table.bin")
pwre_file = open_output_file("pwre_table.bin") pwre_file = open_output_file("pwre_table.bin")
exstop_file = open_output_file("exstop_table.bin") exstop_file = open_output_file("exstop_table.bin")
pwrx_file = open_output_file("pwrx_table.bin") pwrx_file = open_output_file("pwrx_table.bin")
context_switches_file = open_output_file("context_switches_table.bin")
def trace_begin(): def trace_begin():
printdate("Writing to intermediate files...") printdate("Writing to intermediate files...")
...@@ -763,7 +801,7 @@ def trace_begin(): ...@@ -763,7 +801,7 @@ def trace_begin():
evsel_table(0, "unknown") evsel_table(0, "unknown")
machine_table(0, 0, "unknown") machine_table(0, 0, "unknown")
thread_table(0, 0, 0, -1, -1) thread_table(0, 0, 0, -1, -1)
comm_table(0, "unknown") comm_table(0, "unknown", 0, 0, 0)
dso_table(0, 0, "unknown", "unknown", "") dso_table(0, 0, "unknown", "unknown", "")
symbol_table(0, 0, 0, 0, 0, "unknown") symbol_table(0, 0, 0, 0, 0, "unknown")
sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
...@@ -804,6 +842,7 @@ def trace_end(): ...@@ -804,6 +842,7 @@ def trace_end():
copy_output_file(pwre_file, "pwre") copy_output_file(pwre_file, "pwre")
copy_output_file(exstop_file, "exstop") copy_output_file(exstop_file, "exstop")
copy_output_file(pwrx_file, "pwrx") copy_output_file(pwrx_file, "pwrx")
copy_output_file(context_switches_file, "context_switches")
printdate("Removing intermediate files...") printdate("Removing intermediate files...")
remove_output_file(evsel_file) remove_output_file(evsel_file)
...@@ -825,6 +864,7 @@ def trace_end(): ...@@ -825,6 +864,7 @@ def trace_end():
remove_output_file(pwre_file) remove_output_file(pwre_file)
remove_output_file(exstop_file) remove_output_file(exstop_file)
remove_output_file(pwrx_file) remove_output_file(pwrx_file)
remove_output_file(context_switches_file)
os.rmdir(output_dir_name) os.rmdir(output_dir_name)
printdate("Adding primary keys") printdate("Adding primary keys")
do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)') do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)')
...@@ -846,11 +886,14 @@ def trace_end(): ...@@ -846,11 +886,14 @@ def trace_end():
do_query(query, 'ALTER TABLE pwre ADD PRIMARY KEY (id)') do_query(query, 'ALTER TABLE pwre ADD PRIMARY KEY (id)')
do_query(query, 'ALTER TABLE exstop ADD PRIMARY KEY (id)') do_query(query, 'ALTER TABLE exstop ADD PRIMARY KEY (id)')
do_query(query, 'ALTER TABLE pwrx ADD PRIMARY KEY (id)') do_query(query, 'ALTER TABLE pwrx ADD PRIMARY KEY (id)')
do_query(query, 'ALTER TABLE context_switches ADD PRIMARY KEY (id)')
printdate("Adding foreign keys") printdate("Adding foreign keys")
do_query(query, 'ALTER TABLE threads ' do_query(query, 'ALTER TABLE threads '
'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id),' 'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id),'
'ADD CONSTRAINT processfk FOREIGN KEY (process_id) REFERENCES threads (id)') 'ADD CONSTRAINT processfk FOREIGN KEY (process_id) REFERENCES threads (id)')
do_query(query, 'ALTER TABLE comms '
'ADD CONSTRAINT threadfk FOREIGN KEY (c_thread_id) REFERENCES threads (id)')
do_query(query, 'ALTER TABLE comm_threads ' do_query(query, 'ALTER TABLE comm_threads '
'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),' 'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),'
'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id)') 'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id)')
...@@ -881,6 +924,8 @@ def trace_end(): ...@@ -881,6 +924,8 @@ def trace_end():
'ADD CONSTRAINT parent_call_pathfk FOREIGN KEY (parent_call_path_id) REFERENCES call_paths (id)') 'ADD CONSTRAINT parent_call_pathfk FOREIGN KEY (parent_call_path_id) REFERENCES call_paths (id)')
do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)') do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)')
do_query(query, 'CREATE INDEX pid_idx ON calls (parent_id)') do_query(query, 'CREATE INDEX pid_idx ON calls (parent_id)')
do_query(query, 'ALTER TABLE comms ADD has_calls boolean')
do_query(query, 'UPDATE comms SET has_calls = TRUE WHERE comms.id IN (SELECT DISTINCT comm_id FROM calls)')
do_query(query, 'ALTER TABLE ptwrite ' do_query(query, 'ALTER TABLE ptwrite '
'ADD CONSTRAINT idfk FOREIGN KEY (id) REFERENCES samples (id)') 'ADD CONSTRAINT idfk FOREIGN KEY (id) REFERENCES samples (id)')
do_query(query, 'ALTER TABLE cbr ' do_query(query, 'ALTER TABLE cbr '
...@@ -893,6 +938,12 @@ def trace_end(): ...@@ -893,6 +938,12 @@ def trace_end():
'ADD CONSTRAINT idfk FOREIGN KEY (id) REFERENCES samples (id)') 'ADD CONSTRAINT idfk FOREIGN KEY (id) REFERENCES samples (id)')
do_query(query, 'ALTER TABLE pwrx ' do_query(query, 'ALTER TABLE pwrx '
'ADD CONSTRAINT idfk FOREIGN KEY (id) REFERENCES samples (id)') 'ADD CONSTRAINT idfk FOREIGN KEY (id) REFERENCES samples (id)')
do_query(query, 'ALTER TABLE context_switches '
'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id),'
'ADD CONSTRAINT toutfk FOREIGN KEY (thread_out_id) REFERENCES threads (id),'
'ADD CONSTRAINT tinfk FOREIGN KEY (thread_in_id) REFERENCES threads (id),'
'ADD CONSTRAINT coutfk FOREIGN KEY (comm_out_id) REFERENCES comms (id),'
'ADD CONSTRAINT cinfk FOREIGN KEY (comm_in_id) REFERENCES comms (id)')
printdate("Dropping unused tables") printdate("Dropping unused tables")
if is_table_empty("ptwrite"): if is_table_empty("ptwrite"):
...@@ -905,6 +956,8 @@ def trace_end(): ...@@ -905,6 +956,8 @@ def trace_end():
drop("pwrx") drop("pwrx")
if is_table_empty("cbr"): if is_table_empty("cbr"):
drop("cbr") drop("cbr")
if is_table_empty("context_switches"):
drop("context_switches")
if (unhandled_count): if (unhandled_count):
printdate("Warning: ", unhandled_count, " unhandled events") printdate("Warning: ", unhandled_count, " unhandled events")
...@@ -935,11 +988,11 @@ def thread_table(thread_id, machine_id, process_id, pid, tid, *x): ...@@ -935,11 +988,11 @@ def thread_table(thread_id, machine_id, process_id, pid, tid, *x):
value = struct.pack("!hiqiqiqiiii", 5, 8, thread_id, 8, machine_id, 8, process_id, 4, pid, 4, tid) value = struct.pack("!hiqiqiqiiii", 5, 8, thread_id, 8, machine_id, 8, process_id, 4, pid, 4, tid)
thread_file.write(value) thread_file.write(value)
def comm_table(comm_id, comm_str, *x): def comm_table(comm_id, comm_str, thread_id, time, exec_flag, *x):
comm_str = toserverstr(comm_str) comm_str = toserverstr(comm_str)
n = len(comm_str) n = len(comm_str)
fmt = "!hiqi" + str(n) + "s" fmt = "!hiqi" + str(n) + "s" + "iqiqiB"
value = struct.pack(fmt, 2, 8, comm_id, n, comm_str) value = struct.pack(fmt, 5, 8, comm_id, n, comm_str, 8, thread_id, 8, time, 1, exec_flag)
comm_file.write(value) comm_file.write(value)
def comm_thread_table(comm_thread_id, comm_id, thread_id, *x): def comm_thread_table(comm_thread_id, comm_id, thread_id, *x):
...@@ -1051,3 +1104,8 @@ def synth_data(id, config, raw_buf, *x): ...@@ -1051,3 +1104,8 @@ def synth_data(id, config, raw_buf, *x):
pwrx(id, raw_buf) pwrx(id, raw_buf)
elif config == 5: elif config == 5:
cbr(id, raw_buf) cbr(id, raw_buf)
def context_switch_table(id, machine_id, time, cpu, thread_out_id, comm_out_id, thread_in_id, comm_in_id, flags, *x):
fmt = "!hiqiqiqiiiqiqiqiqii"
value = struct.pack(fmt, 9, 8, id, 8, machine_id, 8, time, 4, cpu, 8, thread_out_id, 8, comm_out_id, 8, thread_in_id, 8, comm_in_id, 4, flags)
context_switches_file.write(value)
...@@ -177,7 +177,10 @@ do_query(query, 'CREATE TABLE threads (' ...@@ -177,7 +177,10 @@ do_query(query, 'CREATE TABLE threads ('
'tid integer)') 'tid integer)')
do_query(query, 'CREATE TABLE comms (' do_query(query, 'CREATE TABLE comms ('
'id integer NOT NULL PRIMARY KEY,' 'id integer NOT NULL PRIMARY KEY,'
'comm varchar(16))') 'comm varchar(16),'
'c_thread_id bigint,'
'c_time bigint,'
'exec_flag boolean)')
do_query(query, 'CREATE TABLE comm_threads (' do_query(query, 'CREATE TABLE comm_threads ('
'id integer NOT NULL PRIMARY KEY,' 'id integer NOT NULL PRIMARY KEY,'
'comm_id bigint,' 'comm_id bigint,'
...@@ -303,6 +306,17 @@ do_query(query, 'CREATE TABLE pwrx (' ...@@ -303,6 +306,17 @@ do_query(query, 'CREATE TABLE pwrx ('
'last_cstate integer,' 'last_cstate integer,'
'wake_reason integer)') 'wake_reason integer)')
do_query(query, 'CREATE TABLE context_switches ('
'id integer NOT NULL PRIMARY KEY,'
'machine_id bigint,'
'time bigint,'
'cpu integer,'
'thread_out_id bigint,'
'comm_out_id bigint,'
'thread_in_id bigint,'
'comm_in_id bigint,'
'flags integer)')
# printf was added to sqlite in version 3.8.3 # printf was added to sqlite in version 3.8.3
sqlite_has_printf = False sqlite_has_printf = False
try: try:
...@@ -527,6 +541,29 @@ do_query(query, 'CREATE VIEW power_events_view AS ' ...@@ -527,6 +541,29 @@ do_query(query, 'CREATE VIEW power_events_view AS '
' INNER JOIN selected_events ON selected_events.id = evsel_id' ' INNER JOIN selected_events ON selected_events.id = evsel_id'
' WHERE selected_events.name IN (\'cbr\',\'mwait\',\'exstop\',\'pwre\',\'pwrx\')') ' WHERE selected_events.name IN (\'cbr\',\'mwait\',\'exstop\',\'pwre\',\'pwrx\')')
do_query(query, 'CREATE VIEW context_switches_view AS '
'SELECT '
'context_switches.id,'
'context_switches.machine_id,'
'context_switches.time,'
'context_switches.cpu,'
'th_out.pid AS pid_out,'
'th_out.tid AS tid_out,'
'comm_out.comm AS comm_out,'
'th_in.pid AS pid_in,'
'th_in.tid AS tid_in,'
'comm_in.comm AS comm_in,'
'CASE WHEN context_switches.flags = 0 THEN \'in\''
' WHEN context_switches.flags = 1 THEN \'out\''
' WHEN context_switches.flags = 3 THEN \'out preempt\''
' ELSE context_switches.flags '
'END AS flags'
' FROM context_switches'
' INNER JOIN threads AS th_out ON th_out.id = context_switches.thread_out_id'
' INNER JOIN threads AS th_in ON th_in.id = context_switches.thread_in_id'
' INNER JOIN comms AS comm_out ON comm_out.id = context_switches.comm_out_id'
' INNER JOIN comms AS comm_in ON comm_in.id = context_switches.comm_in_id')
do_query(query, 'END TRANSACTION') do_query(query, 'END TRANSACTION')
evsel_query = QSqlQuery(db) evsel_query = QSqlQuery(db)
...@@ -536,7 +573,7 @@ machine_query.prepare("INSERT INTO machines VALUES (?, ?, ?)") ...@@ -536,7 +573,7 @@ machine_query.prepare("INSERT INTO machines VALUES (?, ?, ?)")
thread_query = QSqlQuery(db) thread_query = QSqlQuery(db)
thread_query.prepare("INSERT INTO threads VALUES (?, ?, ?, ?, ?)") thread_query.prepare("INSERT INTO threads VALUES (?, ?, ?, ?, ?)")
comm_query = QSqlQuery(db) comm_query = QSqlQuery(db)
comm_query.prepare("INSERT INTO comms VALUES (?, ?)") comm_query.prepare("INSERT INTO comms VALUES (?, ?, ?, ?, ?)")
comm_thread_query = QSqlQuery(db) comm_thread_query = QSqlQuery(db)
comm_thread_query.prepare("INSERT INTO comm_threads VALUES (?, ?, ?)") comm_thread_query.prepare("INSERT INTO comm_threads VALUES (?, ?, ?)")
dso_query = QSqlQuery(db) dso_query = QSqlQuery(db)
...@@ -568,6 +605,8 @@ exstop_query = QSqlQuery(db) ...@@ -568,6 +605,8 @@ exstop_query = QSqlQuery(db)
exstop_query.prepare("INSERT INTO exstop VALUES (?, ?)") exstop_query.prepare("INSERT INTO exstop VALUES (?, ?)")
pwrx_query = QSqlQuery(db) pwrx_query = QSqlQuery(db)
pwrx_query.prepare("INSERT INTO pwrx VALUES (?, ?, ?, ?)") pwrx_query.prepare("INSERT INTO pwrx VALUES (?, ?, ?, ?)")
context_switch_query = QSqlQuery(db)
context_switch_query.prepare("INSERT INTO context_switches VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)")
def trace_begin(): def trace_begin():
printdate("Writing records...") printdate("Writing records...")
...@@ -576,7 +615,7 @@ def trace_begin(): ...@@ -576,7 +615,7 @@ def trace_begin():
evsel_table(0, "unknown") evsel_table(0, "unknown")
machine_table(0, 0, "unknown") machine_table(0, 0, "unknown")
thread_table(0, 0, 0, -1, -1) thread_table(0, 0, 0, -1, -1)
comm_table(0, "unknown") comm_table(0, "unknown", 0, 0, 0)
dso_table(0, 0, "unknown", "unknown", "") dso_table(0, 0, "unknown", "unknown", "")
symbol_table(0, 0, 0, 0, 0, "unknown") symbol_table(0, 0, 0, 0, 0, "unknown")
sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
...@@ -603,6 +642,8 @@ def trace_end(): ...@@ -603,6 +642,8 @@ def trace_end():
if perf_db_export_calls: if perf_db_export_calls:
do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)') do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)')
do_query(query, 'CREATE INDEX pid_idx ON calls (parent_id)') do_query(query, 'CREATE INDEX pid_idx ON calls (parent_id)')
do_query(query, 'ALTER TABLE comms ADD has_calls boolean')
do_query(query, 'UPDATE comms SET has_calls = 1 WHERE comms.id IN (SELECT DISTINCT comm_id FROM calls)')
printdate("Dropping unused tables") printdate("Dropping unused tables")
if is_table_empty("ptwrite"): if is_table_empty("ptwrite"):
...@@ -615,6 +656,8 @@ def trace_end(): ...@@ -615,6 +656,8 @@ def trace_end():
drop("pwrx") drop("pwrx")
if is_table_empty("cbr"): if is_table_empty("cbr"):
drop("cbr") drop("cbr")
if is_table_empty("context_switches"):
drop("context_switches")
if (unhandled_count): if (unhandled_count):
printdate("Warning: ", unhandled_count, " unhandled events") printdate("Warning: ", unhandled_count, " unhandled events")
...@@ -642,7 +685,7 @@ def thread_table(*x): ...@@ -642,7 +685,7 @@ def thread_table(*x):
bind_exec(thread_query, 5, x) bind_exec(thread_query, 5, x)
def comm_table(*x): def comm_table(*x):
bind_exec(comm_query, 2, x) bind_exec(comm_query, 5, x)
def comm_thread_table(*x): def comm_thread_table(*x):
bind_exec(comm_thread_query, 3, x) bind_exec(comm_thread_query, 3, x)
...@@ -748,3 +791,6 @@ def synth_data(id, config, raw_buf, *x): ...@@ -748,3 +791,6 @@ def synth_data(id, config, raw_buf, *x):
pwrx(id, raw_buf) pwrx(id, raw_buf)
elif config == 5: elif config == 5:
cbr(id, raw_buf) cbr(id, raw_buf)
def context_switch_table(*x):
bind_exec(context_switch_query, 9, x)
...@@ -392,7 +392,7 @@ class FindBar(): ...@@ -392,7 +392,7 @@ class FindBar():
self.hbox.addWidget(self.close_button) self.hbox.addWidget(self.close_button)
self.bar = QWidget() self.bar = QWidget()
self.bar.setLayout(self.hbox); self.bar.setLayout(self.hbox)
self.bar.hide() self.bar.hide()
def Widget(self): def Widget(self):
...@@ -470,7 +470,7 @@ class CallGraphLevelItemBase(object): ...@@ -470,7 +470,7 @@ class CallGraphLevelItemBase(object):
self.params = params self.params = params
self.row = row self.row = row
self.parent_item = parent_item self.parent_item = parent_item
self.query_done = False; self.query_done = False
self.child_count = 0 self.child_count = 0
self.child_items = [] self.child_items = []
if parent_item: if parent_item:
...@@ -517,7 +517,7 @@ class CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): ...@@ -517,7 +517,7 @@ class CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase):
self.time = time self.time = time
def Select(self): def Select(self):
self.query_done = True; self.query_done = True
query = QSqlQuery(self.glb.db) query = QSqlQuery(self.glb.db)
if self.params.have_ipc: if self.params.have_ipc:
ipc_str = ", SUM(insn_count), SUM(cyc_count)" ipc_str = ", SUM(insn_count), SUM(cyc_count)"
...@@ -604,7 +604,7 @@ class CallGraphLevelOneItem(CallGraphLevelItemBase): ...@@ -604,7 +604,7 @@ class CallGraphLevelOneItem(CallGraphLevelItemBase):
self.dbid = comm_id self.dbid = comm_id
def Select(self): def Select(self):
self.query_done = True; self.query_done = True
query = QSqlQuery(self.glb.db) query = QSqlQuery(self.glb.db)
QueryExec(query, "SELECT thread_id, pid, tid" QueryExec(query, "SELECT thread_id, pid, tid"
" FROM comm_threads" " FROM comm_threads"
...@@ -622,9 +622,12 @@ class CallGraphRootItem(CallGraphLevelItemBase): ...@@ -622,9 +622,12 @@ class CallGraphRootItem(CallGraphLevelItemBase):
def __init__(self, glb, params): def __init__(self, glb, params):
super(CallGraphRootItem, self).__init__(glb, params, 0, None) super(CallGraphRootItem, self).__init__(glb, params, 0, None)
self.dbid = 0 self.dbid = 0
self.query_done = True; self.query_done = True
if_has_calls = ""
if IsSelectable(glb.db, "comms", columns = "has_calls"):
if_has_calls = " WHERE has_calls = TRUE"
query = QSqlQuery(glb.db) query = QSqlQuery(glb.db)
QueryExec(query, "SELECT id, comm FROM comms") QueryExec(query, "SELECT id, comm FROM comms" + if_has_calls)
while query.next(): while query.next():
if not query.value(0): if not query.value(0):
continue continue
...@@ -793,7 +796,7 @@ class CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase): ...@@ -793,7 +796,7 @@ class CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase):
self.time = time self.time = time
def Select(self): def Select(self):
self.query_done = True; self.query_done = True
if self.calls_id == 0: if self.calls_id == 0:
comm_thread = " AND comm_id = " + str(self.comm_id) + " AND thread_id = " + str(self.thread_id) comm_thread = " AND comm_id = " + str(self.comm_id) + " AND thread_id = " + str(self.thread_id)
else: else:
...@@ -881,7 +884,7 @@ class CallTreeLevelOneItem(CallGraphLevelItemBase): ...@@ -881,7 +884,7 @@ class CallTreeLevelOneItem(CallGraphLevelItemBase):
self.dbid = comm_id self.dbid = comm_id
def Select(self): def Select(self):
self.query_done = True; self.query_done = True
query = QSqlQuery(self.glb.db) query = QSqlQuery(self.glb.db)
QueryExec(query, "SELECT thread_id, pid, tid" QueryExec(query, "SELECT thread_id, pid, tid"
" FROM comm_threads" " FROM comm_threads"
...@@ -899,9 +902,12 @@ class CallTreeRootItem(CallGraphLevelItemBase): ...@@ -899,9 +902,12 @@ class CallTreeRootItem(CallGraphLevelItemBase):
def __init__(self, glb, params): def __init__(self, glb, params):
super(CallTreeRootItem, self).__init__(glb, params, 0, None) super(CallTreeRootItem, self).__init__(glb, params, 0, None)
self.dbid = 0 self.dbid = 0
self.query_done = True; self.query_done = True
if_has_calls = ""
if IsSelectable(glb.db, "comms", columns = "has_calls"):
if_has_calls = " WHERE has_calls = TRUE"
query = QSqlQuery(glb.db) query = QSqlQuery(glb.db)
QueryExec(query, "SELECT id, comm FROM comms") QueryExec(query, "SELECT id, comm FROM comms" + if_has_calls)
while query.next(): while query.next():
if not query.value(0): if not query.value(0):
continue continue
...@@ -971,7 +977,7 @@ class VBox(): ...@@ -971,7 +977,7 @@ class VBox():
def __init__(self, w1, w2, w3=None): def __init__(self, w1, w2, w3=None):
self.vbox = QWidget() self.vbox = QWidget()
self.vbox.setLayout(QVBoxLayout()); self.vbox.setLayout(QVBoxLayout())
self.vbox.layout().setContentsMargins(0, 0, 0, 0) self.vbox.layout().setContentsMargins(0, 0, 0, 0)
...@@ -1391,7 +1397,7 @@ class FetchMoreRecordsBar(): ...@@ -1391,7 +1397,7 @@ class FetchMoreRecordsBar():
self.hbox.addWidget(self.close_button) self.hbox.addWidget(self.close_button)
self.bar = QWidget() self.bar = QWidget()
self.bar.setLayout(self.hbox); self.bar.setLayout(self.hbox)
self.bar.show() self.bar.show()
self.in_progress = False self.in_progress = False
...@@ -2206,7 +2212,7 @@ class ReportDialogBase(QDialog): ...@@ -2206,7 +2212,7 @@ class ReportDialogBase(QDialog):
self.vbox.addLayout(self.grid) self.vbox.addLayout(self.grid)
self.vbox.addLayout(self.hbox) self.vbox.addLayout(self.hbox)
self.setLayout(self.vbox); self.setLayout(self.vbox)
def Ok(self): def Ok(self):
vars = self.report_vars vars = self.report_vars
...@@ -3139,7 +3145,7 @@ class AboutDialog(QDialog): ...@@ -3139,7 +3145,7 @@ class AboutDialog(QDialog):
self.vbox = QVBoxLayout() self.vbox = QVBoxLayout()
self.vbox.addWidget(self.text) self.vbox.addWidget(self.text)
self.setLayout(self.vbox); self.setLayout(self.vbox)
# Font resize # Font resize
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <subcmd/parse-options.h> #include <subcmd/parse-options.h>
#include "string2.h" #include "string2.h"
#include "symbol.h" #include "symbol.h"
#include "util/rlimit.h"
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/string.h> #include <linux/string.h>
#include <subcmd/exec-cmd.h> #include <subcmd/exec-cmd.h>
...@@ -727,6 +728,11 @@ int cmd_test(int argc, const char **argv) ...@@ -727,6 +728,11 @@ int cmd_test(int argc, const char **argv)
if (skip != NULL) if (skip != NULL)
skiplist = intlist__new(skip); skiplist = intlist__new(skip);
/*
* Tests that create BPF maps, for instance, need more than the 64K
* default:
*/
rlimit__bump_memlock();
return __cmd_test(argc, argv, skiplist); return __cmd_test(argc, argv, skiplist);
} }
...@@ -20,6 +20,7 @@ perf-y += parse-events.o ...@@ -20,6 +20,7 @@ perf-y += parse-events.o
perf-y += perf_regs.o perf-y += perf_regs.o
perf-y += path.o perf-y += path.o
perf-y += print_binary.o perf-y += print_binary.o
perf-y += rlimit.o
perf-y += argv_split.o perf-y += argv_split.o
perf-y += rbtree.o perf-y += rbtree.o
perf-y += libstring.o perf-y += libstring.o
......
...@@ -2460,7 +2460,7 @@ int cs_etm__process_auxtrace_info(union perf_event *event, ...@@ -2460,7 +2460,7 @@ int cs_etm__process_auxtrace_info(union perf_event *event,
/* Something went wrong, no need to continue */ /* Something went wrong, no need to continue */
if (!inode) { if (!inode) {
err = PTR_ERR(inode); err = -ENOMEM;
goto err_free_metadata; goto err_free_metadata;
} }
...@@ -2517,8 +2517,10 @@ int cs_etm__process_auxtrace_info(union perf_event *event, ...@@ -2517,8 +2517,10 @@ int cs_etm__process_auxtrace_info(union perf_event *event,
session->auxtrace = &etm->auxtrace; session->auxtrace = &etm->auxtrace;
etm->unknown_thread = thread__new(999999999, 999999999); etm->unknown_thread = thread__new(999999999, 999999999);
if (!etm->unknown_thread) if (!etm->unknown_thread) {
err = -ENOMEM;
goto err_free_queues; goto err_free_queues;
}
/* /*
* Initialize list node so that at thread__zput() we can avoid * 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, ...@@ -2530,8 +2532,10 @@ int cs_etm__process_auxtrace_info(union perf_event *event,
if (err) if (err)
goto err_delete_thread; 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; goto err_delete_thread;
}
if (dump_trace) { if (dump_trace) {
cs_etm__print_auxtrace_info(auxtrace_info->priv, num_cpu); cs_etm__print_auxtrace_info(auxtrace_info->priv, num_cpu);
...@@ -2575,5 +2579,5 @@ int cs_etm__process_auxtrace_info(union perf_event *event, ...@@ -2575,5 +2579,5 @@ int cs_etm__process_auxtrace_info(union perf_event *event,
err_free_hdr: err_free_hdr:
zfree(&hdr); zfree(&hdr);
return -EINVAL; return err;
} }
...@@ -20,70 +20,14 @@ ...@@ -20,70 +20,14 @@
#include "db-export.h" #include "db-export.h"
#include <linux/zalloc.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) int db_export__init(struct db_export *dbe)
{ {
memset(dbe, 0, sizeof(struct db_export)); memset(dbe, 0, sizeof(struct db_export));
INIT_LIST_HEAD(&dbe->deferred);
return 0; return 0;
} }
int db_export__flush(struct db_export *dbe)
{
return db_export__deferred(dbe);
}
void db_export__exit(struct db_export *dbe) void db_export__exit(struct db_export *dbe)
{ {
db_export__free_deferred(dbe);
call_return_processor__free(dbe->crp); call_return_processor__free(dbe->crp);
dbe->crp = NULL; dbe->crp = NULL;
} }
...@@ -115,53 +59,52 @@ int db_export__machine(struct db_export *dbe, struct machine *machine) ...@@ -115,53 +59,52 @@ int db_export__machine(struct db_export *dbe, struct machine *machine)
} }
int db_export__thread(struct db_export *dbe, struct thread *thread, 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; u64 main_thread_db_id = 0;
int err;
if (thread->db_id) if (thread->db_id)
return 0; return 0;
thread->db_id = ++dbe->thread_last_db_id; thread->db_id = ++dbe->thread_last_db_id;
if (thread->pid_ != -1) { if (main_thread)
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;
}
}
main_thread_db_id = main_thread->db_id; main_thread_db_id = main_thread->db_id;
if (main_thread != thread)
thread__put(main_thread);
}
if (dbe->export_thread) if (dbe->export_thread)
return dbe->export_thread(dbe, thread, main_thread_db_id, return dbe->export_thread(dbe, thread, main_thread_db_id,
machine); machine);
return 0; return 0;
}
out_put: static int __db_export__comm(struct db_export *dbe, struct comm *comm,
thread__put(main_thread); struct thread *thread)
return err; {
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, int db_export__comm(struct db_export *dbe, struct comm *comm,
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) struct thread *main_thread)
{ {
int err; int err;
...@@ -169,17 +112,20 @@ int db_export__comm(struct db_export *dbe, struct comm *comm, ...@@ -169,17 +112,20 @@ int db_export__comm(struct db_export *dbe, struct comm *comm,
if (comm->db_id) if (comm->db_id)
return 0; return 0;
comm->db_id = ++dbe->comm_last_db_id; err = __db_export__comm(dbe, comm, main_thread);
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) if (err)
return 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); 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, ...@@ -340,11 +286,65 @@ int db_export__branch_type(struct db_export *dbe, u32 branch_type,
return 0; 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, int db_export__sample(struct db_export *dbe, union perf_event *event,
struct perf_sample *sample, struct perf_evsel *evsel, struct perf_sample *sample, struct perf_evsel *evsel,
struct addr_location *al) struct addr_location *al)
{ {
struct thread* thread = al->thread; struct thread *thread = al->thread;
struct export_sample es = { struct export_sample es = {
.event = event, .event = event,
.sample = sample, .sample = sample,
...@@ -364,19 +364,13 @@ int db_export__sample(struct db_export *dbe, union perf_event *event, ...@@ -364,19 +364,13 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,
return err; return err;
main_thread = thread__main_thread(al->machine, thread); 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) if (err)
goto out_put; goto out_put;
if (comm) { if (comm)
err = db_export__comm(dbe, comm, main_thread);
if (err)
goto out_put;
es.comm_db_id = comm->db_id; es.comm_db_id = comm->db_id;
}
es.db_id = ++dbe->sample_last_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, ...@@ -525,3 +519,92 @@ int db_export__call_return(struct db_export *dbe, struct call_return *cr,
return 0; 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 { ...@@ -43,7 +43,8 @@ struct db_export {
int (*export_machine)(struct db_export *dbe, struct machine *machine); int (*export_machine)(struct db_export *dbe, struct machine *machine);
int (*export_thread)(struct db_export *dbe, struct thread *thread, int (*export_thread)(struct db_export *dbe, struct thread *thread,
u64 main_thread_db_id, struct machine *machine); 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, int (*export_comm_thread)(struct db_export *dbe, u64 db_id,
struct comm *comm, struct thread *thread); struct comm *comm, struct thread *thread);
int (*export_dso)(struct db_export *dbe, struct dso *dso, int (*export_dso)(struct db_export *dbe, struct dso *dso,
...@@ -56,6 +57,11 @@ struct db_export { ...@@ -56,6 +57,11 @@ struct db_export {
int (*export_call_path)(struct db_export *dbe, struct call_path *cp); int (*export_call_path)(struct db_export *dbe, struct call_path *cp);
int (*export_call_return)(struct db_export *dbe, int (*export_call_return)(struct db_export *dbe,
struct call_return *cr); 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_return_processor *crp;
struct call_path_root *cpr; struct call_path_root *cpr;
u64 evsel_last_db_id; u64 evsel_last_db_id;
...@@ -68,17 +74,18 @@ struct db_export { ...@@ -68,17 +74,18 @@ struct db_export {
u64 sample_last_db_id; u64 sample_last_db_id;
u64 call_path_last_db_id; u64 call_path_last_db_id;
u64 call_return_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__init(struct db_export *dbe);
int db_export__flush(struct db_export *dbe);
void db_export__exit(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__evsel(struct db_export *dbe, struct perf_evsel *evsel);
int db_export__machine(struct db_export *dbe, struct machine *machine); int db_export__machine(struct db_export *dbe, struct machine *machine);
int db_export__thread(struct db_export *dbe, struct thread *thread, 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, int db_export__comm(struct db_export *dbe, struct comm *comm,
struct thread *thread);
int db_export__exec_comm(struct db_export *dbe, struct comm *comm,
struct thread *main_thread); struct thread *main_thread);
int db_export__comm_thread(struct db_export *dbe, struct comm *comm, int db_export__comm_thread(struct db_export *dbe, struct comm *comm,
struct thread *thread); struct thread *thread);
...@@ -97,5 +104,7 @@ int db_export__branch_types(struct db_export *dbe); ...@@ -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_path(struct db_export *dbe, struct call_path *cp);
int db_export__call_return(struct db_export *dbe, struct call_return *cr, int db_export__call_return(struct db_export *dbe, struct call_return *cr,
u64 *parent_db_id); u64 *parent_db_id);
int db_export__switch(struct db_export *dbe, union perf_event *event,
struct perf_sample *sample, struct machine *machine);
#endif #endif
/* 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");
}
}
}
#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 { ...@@ -113,6 +113,7 @@ struct tables {
PyObject *call_path_handler; PyObject *call_path_handler;
PyObject *call_return_handler; PyObject *call_return_handler;
PyObject *synth_handler; PyObject *synth_handler;
PyObject *context_switch_handler;
bool db_export_mode; bool db_export_mode;
}; };
...@@ -1011,15 +1012,19 @@ static int python_export_thread(struct db_export *dbe, struct thread *thread, ...@@ -1011,15 +1012,19 @@ static int python_export_thread(struct db_export *dbe, struct thread *thread,
return 0; 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); struct tables *tables = container_of(dbe, struct tables, dbe);
PyObject *t; PyObject *t;
t = tuple_new(2); t = tuple_new(5);
tuple_set_u64(t, 0, comm->db_id); tuple_set_u64(t, 0, comm->db_id);
tuple_set_string(t, 1, comm__str(comm)); 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"); call_object(tables->comm_handler, t, "comm_table");
...@@ -1233,6 +1238,34 @@ static int python_export_call_return(struct db_export *dbe, ...@@ -1233,6 +1238,34 @@ static int python_export_call_return(struct db_export *dbe,
return 0; 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, static int python_process_call_return(struct call_return *cr, u64 *parent_db_id,
void *data) void *data)
{ {
...@@ -1296,6 +1329,16 @@ static void python_process_event(union perf_event *event, ...@@ -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, static void get_handler_name(char *str, size_t size,
struct perf_evsel *evsel) struct perf_evsel *evsel)
{ {
...@@ -1511,6 +1554,7 @@ static void set_table_handlers(struct tables *tables) ...@@ -1511,6 +1554,7 @@ static void set_table_handlers(struct tables *tables)
SET_TABLE_HANDLER(sample); SET_TABLE_HANDLER(sample);
SET_TABLE_HANDLER(call_path); SET_TABLE_HANDLER(call_path);
SET_TABLE_HANDLER(call_return); SET_TABLE_HANDLER(call_return);
SET_TABLE_HANDLER(context_switch);
/* /*
* Synthesized events are samples but with architecture-specific data * Synthesized events are samples but with architecture-specific data
...@@ -1620,9 +1664,7 @@ static int python_start_script(const char *script, int argc, const char **argv) ...@@ -1620,9 +1664,7 @@ static int python_start_script(const char *script, int argc, const char **argv)
static int python_flush_script(void) static int python_flush_script(void)
{ {
struct tables *tables = &tables_global; return 0;
return db_export__flush(&tables->dbe);
} }
/* /*
...@@ -1831,6 +1873,7 @@ struct scripting_ops python_scripting_ops = { ...@@ -1831,6 +1873,7 @@ struct scripting_ops python_scripting_ops = {
.flush_script = python_flush_script, .flush_script = python_flush_script,
.stop_script = python_stop_script, .stop_script = python_stop_script,
.process_event = python_process_event, .process_event = python_process_event,
.process_switch = python_process_switch,
.process_stat = python_process_stat, .process_stat = python_process_stat,
.process_stat_interval = python_process_stat_interval, .process_stat_interval = python_process_stat_interval,
.generate_script = python_generate_script, .generate_script = python_generate_script,
......
...@@ -81,6 +81,9 @@ struct scripting_ops { ...@@ -81,6 +81,9 @@ struct scripting_ops {
struct perf_sample *sample, struct perf_sample *sample,
struct perf_evsel *evsel, struct perf_evsel *evsel,
struct addr_location *al); 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, void (*process_stat)(struct perf_stat_config *config,
struct perf_evsel *evsel, u64 tstamp); struct perf_evsel *evsel, u64 tstamp);
void (*process_stat_interval)(u64 tstamp); void (*process_stat_interval)(u64 tstamp);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment