/*
* 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/symbol.h"
#include "util/cpumap.h"
#include "util/thread_map.h"
#include <unistd.h>
#include <sched.h>
#include <sys/mman.h>
#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
#define SID(e, x, y) xyarray__entry(e->id, x, y)
enum write_mode_t {
WRITE_FORCE,
WRITE_APPEND
};
static u64 user_interval = ULLONG_MAX;
static u64 default_interval = 0;
static u64 sample_type;
static struct cpu_map *cpus;
static unsigned int page_size;
static unsigned int mmap_pages = 128;
static unsigned int user_freq = UINT_MAX;
static int freq = 1000;
static int output;
static int pipe_output = 0;
static const char *output_name = NULL;
static int group = 0;
static int realtime_prio = 0;
static bool nodelay = false;
static bool raw_samples = false;
static bool sample_id_all_avail = true;
static bool system_wide = false;
static pid_t target_pid = -1;
static pid_t target_tid = -1;
static struct thread_map *threads;
static pid_t child_pid = -1;
static bool no_inherit = false;
static enum write_mode_t write_mode = WRITE_FORCE;
static bool call_graph = false;
static bool inherit_stat = false;
static bool no_samples = false;
static bool sample_address = false;
static bool sample_time = false;
static bool no_buildid = false;
static bool no_buildid_cache = false;
static struct perf_evlist *evsel_list;
static long samples = 0;
static u64 bytes_written = 0;
static int file_new = 1;
static off_t post_processing_offset;
static struct perf_session *session;
static const char *cpu_list;
static void advance_output(size_t size)
{
bytes_written += size;
}
static void write_output(void *buf, size_t size)
{
while (size) {
int ret = write(output, buf, size);
if (ret < 0)
die("failed to write");
size -= ret;
buf += ret;
bytes_written += ret;
}
}
static int process_synthesized_event(event_t *event,
struct sample_data *sample __used,
struct perf_session *self __used)
{
write_output(event, event->header.size);
return 0;
}
static void mmap_read(struct perf_mmap *md)
{
unsigned int head = perf_mmap__read_head(md);
unsigned int old = md->prev;
unsigned char *data = md->base + page_size;
unsigned long size;
void *buf;
int diff;
/*
* If we're further behind than half the buffer, there's a chance
* the writer will bite our tail and mess up the samples under us.
*
* If we somehow ended up ahead of the head, we got messed up.
*
* In either case, truncate and restart at head.
*/
diff = head - old;
if (diff < 0) {
fprintf(stderr, "WARNING: failed to keep up with mmap data\n");
/*
* head points to a known good entry, start there.
*/
old = head;
}
if (old != head)
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(buf, size);
}
buf = &data[old & md->mask];
size = head - old;
old += size;
write_output(buf, size);
md->prev = old;
perf_mmap__write_tail(md, old);
}
static volatile int done = 0;
static volatile int signr = -1;
static void sig_handler(int sig)
{
done = 1;
signr = sig;
}
static void sig_atexit(void)
{
if (child_pid >