/*
* builtin-record.c
*
* Builtin record command: Record the profile of a workload
* (or a CPU, or a PID) into the perf.data output file - for
* later analysis via perf report.
*/
#define _FILE_OFFSET_BITS 64
#include "builtin.h"
#include "perf.h"
#include "util/build-id.h"
#include "util/util.h"
#include "util/parse-options.h"
#include "util/parse-events.h"
#include "util/header.h"
#include "util/event.h"
#include "util/evlist.h"
#include "util/evsel.h"
#include "util/debug.h"
#include "util/session.h"
#include "util/tool.h"
#include "util/symbol.h"
#include "util/cpumap.h"
#include "util/thread_map.h"
#include <unistd.h>
#include <sched.h>
#include <sys/mman.h>
enum write_mode_t {
WRITE_FORCE,
WRITE_APPEND
};
struct perf_record {
struct perf_tool tool;
struct perf_record_opts opts;
u64 bytes_written;
const char *output_name;
struct perf_evlist *evlist;
struct perf_session *session;
const char *progname;
int output;
unsigned int page_size;
int realtime_prio;
enum write_mode_t write_mode;
bool no_buildid;
bool no_buildid_cache;
bool force;
bool file_new;
bool append_file;
long samples;
off_t post_processing_offset;
};
static void advance_output(struct perf_record *rec, size_t size)
{
rec->bytes_written += size;
}
static void write_output(struct perf_record *rec, void *buf, size_t size)
{
while (size) {
int ret = write(rec->output, buf, size);
if (ret < 0)
die("failed to write");
size -= ret;
buf += ret;
rec->bytes_written += ret;
}
}
static int process_synthesized_event(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample __used,
struct machine *machine __used)
{
struct perf_record *rec = container_of(tool, struct perf_record, tool);
write_output(rec, event, event->header.size);
return 0;
}
static void perf_record__mmap_read(struct perf_record *rec,
struct perf_mmap *md)
{
unsigned int head = perf_mmap__read_head(md);
unsigned int old = md->prev;
unsigned char *data = md->base + rec->page_size;
unsigned long size;
void *buf;
if (old == head)
return;
rec->samples++;
size = head - old;
if ((old & md->mask) + size != (head & md->mask)) {
buf = &data[old & md->mask];
size = md->mask + 1 - (old & md->mask);
old += size;
write_output(rec, buf, size);
}
buf = &data[old & md->mask];
size = head - old;
old += size;
write_output(rec, buf, size);
md->prev = old;
perf_mmap__write_tail(md, old);
}
static volatile int done = 0;
static volatile int signr = -1;
static volatile int child_finished = 0;
static void sig_handler(int sig)
{
if (sig == SIGCHLD)
child_finished = 1;
done = 1;
signr = sig;
}
static void perf_record__sig_exit(int exit_status __used, void *arg)
{
struct perf_record *rec = arg;
int status;
if (rec->evlist->workload.pid > 0) {
if (!child_finished)
kill(rec->evlist->workload.pid, SIGTERM);
wait(&status);
if (WIFSIGNALED(status))
psignal(WTERMSIG(status), rec->progname);
}
if (signr == -1 || signr == SIGUSR1)
return;
signal(signr, SIG_DFL);
kill(getpid(), signr);
}
static bool perf_evlist__equal(struct perf_evlist *evlist,
struct perf_evlist *other)
{
struct perf_evsel *pos, *pair;
if (evlist->nr_entries != other->nr_entries)
return false;
pair = list_entry(other->entries.next