mirror of https://gitee.com/openkylin/linux.git
perf evlist: Introduce {prepare,start}_workload refactored from 'perf record'
So that we can easily start a workload in other tools. Cc: David Ahern <dsahern@gmail.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Link: http://lkml.kernel.org/n/tip-zdsksd4aphu0nltg2lpwsw3x@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
0f82ebc452
commit
35b9d88ecd
|
@ -47,11 +47,9 @@ static struct perf_record_opts record_opts = {
|
||||||
static unsigned int page_size;
|
static unsigned int page_size;
|
||||||
static unsigned int mmap_pages = UINT_MAX;
|
static unsigned int mmap_pages = UINT_MAX;
|
||||||
static int output;
|
static int output;
|
||||||
static int pipe_output = 0;
|
|
||||||
static const char *output_name = NULL;
|
static const char *output_name = NULL;
|
||||||
static bool group = false;
|
static bool group = false;
|
||||||
static int realtime_prio = 0;
|
static int realtime_prio = 0;
|
||||||
static pid_t child_pid = -1;
|
|
||||||
static enum write_mode_t write_mode = WRITE_FORCE;
|
static enum write_mode_t write_mode = WRITE_FORCE;
|
||||||
static bool no_buildid = false;
|
static bool no_buildid = false;
|
||||||
static bool no_buildid_cache = false;
|
static bool no_buildid_cache = false;
|
||||||
|
@ -144,9 +142,9 @@ static void sig_atexit(void)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
if (child_pid > 0) {
|
if (evsel_list->workload.pid > 0) {
|
||||||
if (!child_finished)
|
if (!child_finished)
|
||||||
kill(child_pid, SIGTERM);
|
kill(evsel_list->workload.pid, SIGTERM);
|
||||||
|
|
||||||
wait(&status);
|
wait(&status);
|
||||||
if (WIFSIGNALED(status))
|
if (WIFSIGNALED(status))
|
||||||
|
@ -304,7 +302,7 @@ static int process_buildids(void)
|
||||||
|
|
||||||
static void atexit_header(void)
|
static void atexit_header(void)
|
||||||
{
|
{
|
||||||
if (!pipe_output) {
|
if (!record_opts.pipe_output) {
|
||||||
session->header.data_size += bytes_written;
|
session->header.data_size += bytes_written;
|
||||||
|
|
||||||
if (!no_buildid)
|
if (!no_buildid)
|
||||||
|
@ -377,9 +375,7 @@ static int __cmd_record(int argc, const char **argv)
|
||||||
int flags;
|
int flags;
|
||||||
int err;
|
int err;
|
||||||
unsigned long waking = 0;
|
unsigned long waking = 0;
|
||||||
int child_ready_pipe[2], go_pipe[2];
|
|
||||||
const bool forks = argc > 0;
|
const bool forks = argc > 0;
|
||||||
char buf;
|
|
||||||
struct machine *machine;
|
struct machine *machine;
|
||||||
|
|
||||||
progname = argv[0];
|
progname = argv[0];
|
||||||
|
@ -391,20 +387,15 @@ static int __cmd_record(int argc, const char **argv)
|
||||||
signal(SIGINT, sig_handler);
|
signal(SIGINT, sig_handler);
|
||||||
signal(SIGUSR1, sig_handler);
|
signal(SIGUSR1, sig_handler);
|
||||||
|
|
||||||
if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) {
|
|
||||||
perror("failed to create pipes");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!output_name) {
|
if (!output_name) {
|
||||||
if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode))
|
if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode))
|
||||||
pipe_output = true;
|
record_opts.pipe_output = true;
|
||||||
else
|
else
|
||||||
output_name = "perf.data";
|
output_name = "perf.data";
|
||||||
}
|
}
|
||||||
if (output_name) {
|
if (output_name) {
|
||||||
if (!strcmp(output_name, "-"))
|
if (!strcmp(output_name, "-"))
|
||||||
pipe_output = true;
|
record_opts.pipe_output = true;
|
||||||
else if (!stat(output_name, &st) && st.st_size) {
|
else if (!stat(output_name, &st) && st.st_size) {
|
||||||
if (write_mode == WRITE_FORCE) {
|
if (write_mode == WRITE_FORCE) {
|
||||||
char oldname[PATH_MAX];
|
char oldname[PATH_MAX];
|
||||||
|
@ -424,7 +415,7 @@ static int __cmd_record(int argc, const char **argv)
|
||||||
else
|
else
|
||||||
flags |= O_TRUNC;
|
flags |= O_TRUNC;
|
||||||
|
|
||||||
if (pipe_output)
|
if (record_opts.pipe_output)
|
||||||
output = STDOUT_FILENO;
|
output = STDOUT_FILENO;
|
||||||
else
|
else
|
||||||
output = open(output_name, flags, S_IRUSR | S_IWUSR);
|
output = open(output_name, flags, S_IRUSR | S_IWUSR);
|
||||||
|
@ -470,57 +461,11 @@ static int __cmd_record(int argc, const char **argv)
|
||||||
mmap_pages = (512 * 1024) / page_size;
|
mmap_pages = (512 * 1024) / page_size;
|
||||||
|
|
||||||
if (forks) {
|
if (forks) {
|
||||||
child_pid = fork();
|
err = perf_evlist__prepare_workload(evsel_list, &record_opts, argv);
|
||||||
if (child_pid < 0) {
|
if (err < 0) {
|
||||||
perror("failed to fork");
|
pr_err("Couldn't run the workload!\n");
|
||||||
exit(-1);
|
goto out_delete_session;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!child_pid) {
|
|
||||||
if (pipe_output)
|
|
||||||
dup2(2, 1);
|
|
||||||
close(child_ready_pipe[0]);
|
|
||||||
close(go_pipe[1]);
|
|
||||||
fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Do a dummy execvp to get the PLT entry resolved,
|
|
||||||
* so we avoid the resolver overhead on the real
|
|
||||||
* execvp call.
|
|
||||||
*/
|
|
||||||
execvp("", (char **)argv);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Tell the parent we're ready to go
|
|
||||||
*/
|
|
||||||
close(child_ready_pipe[1]);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Wait until the parent tells us to go.
|
|
||||||
*/
|
|
||||||
if (read(go_pipe[0], &buf, 1) == -1)
|
|
||||||
perror("unable to read pipe");
|
|
||||||
|
|
||||||
execvp(argv[0], (char **)argv);
|
|
||||||
|
|
||||||
perror(argv[0]);
|
|
||||||
kill(getppid(), SIGUSR1);
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!record_opts.system_wide && record_opts.target_tid == -1 && record_opts.target_pid == -1)
|
|
||||||
evsel_list->threads->map[0] = child_pid;
|
|
||||||
|
|
||||||
close(child_ready_pipe[1]);
|
|
||||||
close(go_pipe[0]);
|
|
||||||
/*
|
|
||||||
* wait for child to settle
|
|
||||||
*/
|
|
||||||
if (read(child_ready_pipe[0], &buf, 1) == -1) {
|
|
||||||
perror("unable to read pipe");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
close(child_ready_pipe[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open_counters(evsel_list);
|
open_counters(evsel_list);
|
||||||
|
@ -530,7 +475,7 @@ static int __cmd_record(int argc, const char **argv)
|
||||||
*/
|
*/
|
||||||
atexit(atexit_header);
|
atexit(atexit_header);
|
||||||
|
|
||||||
if (pipe_output) {
|
if (record_opts.pipe_output) {
|
||||||
err = perf_header__write_pipe(output);
|
err = perf_header__write_pipe(output);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
@ -543,7 +488,7 @@ static int __cmd_record(int argc, const char **argv)
|
||||||
|
|
||||||
post_processing_offset = lseek(output, 0, SEEK_CUR);
|
post_processing_offset = lseek(output, 0, SEEK_CUR);
|
||||||
|
|
||||||
if (pipe_output) {
|
if (record_opts.pipe_output) {
|
||||||
err = perf_session__synthesize_attrs(session,
|
err = perf_session__synthesize_attrs(session,
|
||||||
process_synthesized_event);
|
process_synthesized_event);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
|
@ -629,7 +574,7 @@ static int __cmd_record(int argc, const char **argv)
|
||||||
* Let the child rip
|
* Let the child rip
|
||||||
*/
|
*/
|
||||||
if (forks)
|
if (forks)
|
||||||
close(go_pipe[1]);
|
perf_evlist__start_workload(evsel_list);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int hits = samples;
|
int hits = samples;
|
||||||
|
|
|
@ -193,6 +193,7 @@ struct perf_record_opts {
|
||||||
bool no_delay;
|
bool no_delay;
|
||||||
bool no_inherit;
|
bool no_inherit;
|
||||||
bool no_samples;
|
bool no_samples;
|
||||||
|
bool pipe_output;
|
||||||
bool raw_samples;
|
bool raw_samples;
|
||||||
bool sample_address;
|
bool sample_address;
|
||||||
bool sample_time;
|
bool sample_time;
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "thread_map.h"
|
#include "thread_map.h"
|
||||||
#include "evlist.h"
|
#include "evlist.h"
|
||||||
#include "evsel.h"
|
#include "evsel.h"
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "parse-events.h"
|
#include "parse-events.h"
|
||||||
|
|
||||||
|
@ -33,6 +34,7 @@ void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus,
|
||||||
INIT_HLIST_HEAD(&evlist->heads[i]);
|
INIT_HLIST_HEAD(&evlist->heads[i]);
|
||||||
INIT_LIST_HEAD(&evlist->entries);
|
INIT_LIST_HEAD(&evlist->entries);
|
||||||
perf_evlist__set_maps(evlist, cpus, threads);
|
perf_evlist__set_maps(evlist, cpus, threads);
|
||||||
|
evlist->workload.pid = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct perf_evlist *perf_evlist__new(struct cpu_map *cpus,
|
struct perf_evlist *perf_evlist__new(struct cpu_map *cpus,
|
||||||
|
@ -674,3 +676,97 @@ int perf_evlist__open(struct perf_evlist *evlist, bool group)
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int perf_evlist__prepare_workload(struct perf_evlist *evlist,
|
||||||
|
struct perf_record_opts *opts,
|
||||||
|
const char *argv[])
|
||||||
|
{
|
||||||
|
int child_ready_pipe[2], go_pipe[2];
|
||||||
|
char bf;
|
||||||
|
|
||||||
|
if (pipe(child_ready_pipe) < 0) {
|
||||||
|
perror("failed to create 'ready' pipe");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pipe(go_pipe) < 0) {
|
||||||
|
perror("failed to create 'go' pipe");
|
||||||
|
goto out_close_ready_pipe;
|
||||||
|
}
|
||||||
|
|
||||||
|
evlist->workload.pid = fork();
|
||||||
|
if (evlist->workload.pid < 0) {
|
||||||
|
perror("failed to fork");
|
||||||
|
goto out_close_pipes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!evlist->workload.pid) {
|
||||||
|
if (opts->pipe_output)
|
||||||
|
dup2(2, 1);
|
||||||
|
|
||||||
|
close(child_ready_pipe[0]);
|
||||||
|
close(go_pipe[1]);
|
||||||
|
fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do a dummy execvp to get the PLT entry resolved,
|
||||||
|
* so we avoid the resolver overhead on the real
|
||||||
|
* execvp call.
|
||||||
|
*/
|
||||||
|
execvp("", (char **)argv);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tell the parent we're ready to go
|
||||||
|
*/
|
||||||
|
close(child_ready_pipe[1]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wait until the parent tells us to go.
|
||||||
|
*/
|
||||||
|
if (read(go_pipe[0], &bf, 1) == -1)
|
||||||
|
perror("unable to read pipe");
|
||||||
|
|
||||||
|
execvp(argv[0], (char **)argv);
|
||||||
|
|
||||||
|
perror(argv[0]);
|
||||||
|
kill(getppid(), SIGUSR1);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!opts->system_wide && opts->target_tid == -1 && opts->target_pid == -1)
|
||||||
|
evlist->threads->map[0] = evlist->workload.pid;
|
||||||
|
|
||||||
|
close(child_ready_pipe[1]);
|
||||||
|
close(go_pipe[0]);
|
||||||
|
/*
|
||||||
|
* wait for child to settle
|
||||||
|
*/
|
||||||
|
if (read(child_ready_pipe[0], &bf, 1) == -1) {
|
||||||
|
perror("unable to read pipe");
|
||||||
|
goto out_close_pipes;
|
||||||
|
}
|
||||||
|
|
||||||
|
evlist->workload.cork_fd = go_pipe[1];
|
||||||
|
close(child_ready_pipe[0]);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_close_pipes:
|
||||||
|
close(go_pipe[0]);
|
||||||
|
close(go_pipe[1]);
|
||||||
|
out_close_ready_pipe:
|
||||||
|
close(child_ready_pipe[0]);
|
||||||
|
close(child_ready_pipe[1]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int perf_evlist__start_workload(struct perf_evlist *evlist)
|
||||||
|
{
|
||||||
|
if (evlist->workload.cork_fd > 0) {
|
||||||
|
/*
|
||||||
|
* Remove the cork, let it rip!
|
||||||
|
*/
|
||||||
|
return close(evlist->workload.cork_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "../perf.h"
|
#include "../perf.h"
|
||||||
#include "event.h"
|
#include "event.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
struct pollfd;
|
struct pollfd;
|
||||||
struct thread_map;
|
struct thread_map;
|
||||||
|
@ -22,6 +23,10 @@ struct perf_evlist {
|
||||||
int nr_fds;
|
int nr_fds;
|
||||||
int nr_mmaps;
|
int nr_mmaps;
|
||||||
int mmap_len;
|
int mmap_len;
|
||||||
|
struct {
|
||||||
|
int cork_fd;
|
||||||
|
pid_t pid;
|
||||||
|
} workload;
|
||||||
bool overwrite;
|
bool overwrite;
|
||||||
union perf_event event_copy;
|
union perf_event event_copy;
|
||||||
struct perf_mmap *mmap;
|
struct perf_mmap *mmap;
|
||||||
|
@ -68,6 +73,11 @@ int perf_evlist__open(struct perf_evlist *evlist, bool group);
|
||||||
void perf_evlist__config_attrs(struct perf_evlist *evlist,
|
void perf_evlist__config_attrs(struct perf_evlist *evlist,
|
||||||
struct perf_record_opts *opts);
|
struct perf_record_opts *opts);
|
||||||
|
|
||||||
|
int perf_evlist__prepare_workload(struct perf_evlist *evlist,
|
||||||
|
struct perf_record_opts *opts,
|
||||||
|
const char *argv[]);
|
||||||
|
int perf_evlist__start_workload(struct perf_evlist *evlist);
|
||||||
|
|
||||||
int perf_evlist__alloc_mmap(struct perf_evlist *evlist);
|
int perf_evlist__alloc_mmap(struct perf_evlist *evlist);
|
||||||
int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite);
|
int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite);
|
||||||
void perf_evlist__munmap(struct perf_evlist *evlist);
|
void perf_evlist__munmap(struct perf_evlist *evlist);
|
||||||
|
|
Loading…
Reference in New Issue