|
@@ -71,19 +71,23 @@ static void advance_output(struct perf_record *rec, size_t size)
|
|
rec->bytes_written += size;
|
|
rec->bytes_written += size;
|
|
}
|
|
}
|
|
|
|
|
|
-static void write_output(struct perf_record *rec, void *buf, size_t size)
|
|
|
|
|
|
+static int write_output(struct perf_record *rec, void *buf, size_t size)
|
|
{
|
|
{
|
|
while (size) {
|
|
while (size) {
|
|
int ret = write(rec->output, buf, size);
|
|
int ret = write(rec->output, buf, size);
|
|
|
|
|
|
- if (ret < 0)
|
|
|
|
- die("failed to write");
|
|
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ pr_err("failed to write\n");
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
|
|
size -= ret;
|
|
size -= ret;
|
|
buf += ret;
|
|
buf += ret;
|
|
|
|
|
|
rec->bytes_written += ret;
|
|
rec->bytes_written += ret;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static int process_synthesized_event(struct perf_tool *tool,
|
|
static int process_synthesized_event(struct perf_tool *tool,
|
|
@@ -92,11 +96,13 @@ static int process_synthesized_event(struct perf_tool *tool,
|
|
struct machine *machine __used)
|
|
struct machine *machine __used)
|
|
{
|
|
{
|
|
struct perf_record *rec = container_of(tool, struct perf_record, tool);
|
|
struct perf_record *rec = container_of(tool, struct perf_record, tool);
|
|
- write_output(rec, event, event->header.size);
|
|
|
|
|
|
+ if (write_output(rec, event, event->header.size) < 0)
|
|
|
|
+ return -1;
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static void perf_record__mmap_read(struct perf_record *rec,
|
|
|
|
|
|
+static int perf_record__mmap_read(struct perf_record *rec,
|
|
struct perf_mmap *md)
|
|
struct perf_mmap *md)
|
|
{
|
|
{
|
|
unsigned int head = perf_mmap__read_head(md);
|
|
unsigned int head = perf_mmap__read_head(md);
|
|
@@ -104,9 +110,10 @@ static void perf_record__mmap_read(struct perf_record *rec,
|
|
unsigned char *data = md->base + rec->page_size;
|
|
unsigned char *data = md->base + rec->page_size;
|
|
unsigned long size;
|
|
unsigned long size;
|
|
void *buf;
|
|
void *buf;
|
|
|
|
+ int rc = 0;
|
|
|
|
|
|
if (old == head)
|
|
if (old == head)
|
|
- return;
|
|
|
|
|
|
+ return 0;
|
|
|
|
|
|
rec->samples++;
|
|
rec->samples++;
|
|
|
|
|
|
@@ -117,17 +124,26 @@ static void perf_record__mmap_read(struct perf_record *rec,
|
|
size = md->mask + 1 - (old & md->mask);
|
|
size = md->mask + 1 - (old & md->mask);
|
|
old += size;
|
|
old += size;
|
|
|
|
|
|
- write_output(rec, buf, size);
|
|
|
|
|
|
+ if (write_output(rec, buf, size) < 0) {
|
|
|
|
+ rc = -1;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
buf = &data[old & md->mask];
|
|
buf = &data[old & md->mask];
|
|
size = head - old;
|
|
size = head - old;
|
|
old += size;
|
|
old += size;
|
|
|
|
|
|
- write_output(rec, buf, size);
|
|
|
|
|
|
+ if (write_output(rec, buf, size) < 0) {
|
|
|
|
+ rc = -1;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
|
|
md->prev = old;
|
|
md->prev = old;
|
|
perf_mmap__write_tail(md, old);
|
|
perf_mmap__write_tail(md, old);
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ return rc;
|
|
}
|
|
}
|
|
|
|
|
|
static volatile int done = 0;
|
|
static volatile int done = 0;
|
|
@@ -183,12 +199,13 @@ static bool perf_evlist__equal(struct perf_evlist *evlist,
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
-static void perf_record__open(struct perf_record *rec)
|
|
|
|
|
|
+static int perf_record__open(struct perf_record *rec)
|
|
{
|
|
{
|
|
struct perf_evsel *pos;
|
|
struct perf_evsel *pos;
|
|
struct perf_evlist *evlist = rec->evlist;
|
|
struct perf_evlist *evlist = rec->evlist;
|
|
struct perf_session *session = rec->session;
|
|
struct perf_session *session = rec->session;
|
|
struct perf_record_opts *opts = &rec->opts;
|
|
struct perf_record_opts *opts = &rec->opts;
|
|
|
|
+ int rc = 0;
|
|
|
|
|
|
perf_evlist__config_attrs(evlist, opts);
|
|
perf_evlist__config_attrs(evlist, opts);
|
|
|
|
|
|
@@ -222,10 +239,13 @@ try_again:
|
|
|
|
|
|
if (err == EPERM || err == EACCES) {
|
|
if (err == EPERM || err == EACCES) {
|
|
ui__error_paranoid();
|
|
ui__error_paranoid();
|
|
- exit(EXIT_FAILURE);
|
|
|
|
|
|
+ rc = -err;
|
|
|
|
+ goto out;
|
|
} else if (err == ENODEV && opts->target.cpu_list) {
|
|
} else if (err == ENODEV && opts->target.cpu_list) {
|
|
- die("No such device - did you specify"
|
|
|
|
- " an out-of-range profile CPU?\n");
|
|
|
|
|
|
+ pr_err("No such device - did you specify"
|
|
|
|
+ " an out-of-range profile CPU?\n");
|
|
|
|
+ rc = -err;
|
|
|
|
+ goto out;
|
|
} else if (err == EINVAL) {
|
|
} else if (err == EINVAL) {
|
|
if (!opts->exclude_guest_missing &&
|
|
if (!opts->exclude_guest_missing &&
|
|
(attr->exclude_guest || attr->exclude_host)) {
|
|
(attr->exclude_guest || attr->exclude_host)) {
|
|
@@ -272,7 +292,8 @@ try_again:
|
|
if (err == ENOENT) {
|
|
if (err == ENOENT) {
|
|
ui__error("The %s event is not supported.\n",
|
|
ui__error("The %s event is not supported.\n",
|
|
perf_evsel__name(pos));
|
|
perf_evsel__name(pos));
|
|
- exit(EXIT_FAILURE);
|
|
|
|
|
|
+ rc = -err;
|
|
|
|
+ goto out;
|
|
}
|
|
}
|
|
|
|
|
|
printf("\n");
|
|
printf("\n");
|
|
@@ -280,34 +301,46 @@ try_again:
|
|
err, strerror(err));
|
|
err, strerror(err));
|
|
|
|
|
|
#if defined(__i386__) || defined(__x86_64__)
|
|
#if defined(__i386__) || defined(__x86_64__)
|
|
- if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP)
|
|
|
|
- die("No hardware sampling interrupt available."
|
|
|
|
- " No APIC? If so then you can boot the kernel"
|
|
|
|
- " with the \"lapic\" boot parameter to"
|
|
|
|
- " force-enable it.\n");
|
|
|
|
|
|
+ if (attr->type == PERF_TYPE_HARDWARE &&
|
|
|
|
+ err == EOPNOTSUPP) {
|
|
|
|
+ pr_err("No hardware sampling interrupt available."
|
|
|
|
+ " No APIC? If so then you can boot the kernel"
|
|
|
|
+ " with the \"lapic\" boot parameter to"
|
|
|
|
+ " force-enable it.\n");
|
|
|
|
+ rc = -err;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
#endif
|
|
#endif
|
|
|
|
|
|
- die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
|
|
|
|
|
|
+ pr_err("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
|
|
|
|
+ rc = -err;
|
|
|
|
+ goto out;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (perf_evlist__set_filters(evlist)) {
|
|
if (perf_evlist__set_filters(evlist)) {
|
|
error("failed to set filter with %d (%s)\n", errno,
|
|
error("failed to set filter with %d (%s)\n", errno,
|
|
strerror(errno));
|
|
strerror(errno));
|
|
- exit(-1);
|
|
|
|
|
|
+ rc = -1;
|
|
|
|
+ goto out;
|
|
}
|
|
}
|
|
|
|
|
|
if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0) {
|
|
if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0) {
|
|
- if (errno == EPERM)
|
|
|
|
- die("Permission error mapping pages.\n"
|
|
|
|
- "Consider increasing "
|
|
|
|
- "/proc/sys/kernel/perf_event_mlock_kb,\n"
|
|
|
|
- "or try again with a smaller value of -m/--mmap_pages.\n"
|
|
|
|
- "(current value: %d)\n", opts->mmap_pages);
|
|
|
|
- else if (!is_power_of_2(opts->mmap_pages))
|
|
|
|
- die("--mmap_pages/-m value must be a power of two.");
|
|
|
|
-
|
|
|
|
- die("failed to mmap with %d (%s)\n", errno, strerror(errno));
|
|
|
|
|
|
+ if (errno == EPERM) {
|
|
|
|
+ pr_err("Permission error mapping pages.\n"
|
|
|
|
+ "Consider increasing "
|
|
|
|
+ "/proc/sys/kernel/perf_event_mlock_kb,\n"
|
|
|
|
+ "or try again with a smaller value of -m/--mmap_pages.\n"
|
|
|
|
+ "(current value: %d)\n", opts->mmap_pages);
|
|
|
|
+ rc = -errno;
|
|
|
|
+ } else if (!is_power_of_2(opts->mmap_pages)) {
|
|
|
|
+ pr_err("--mmap_pages/-m value must be a power of two.");
|
|
|
|
+ rc = -EINVAL;
|
|
|
|
+ } else {
|
|
|
|
+ pr_err("failed to mmap with %d (%s)\n", errno, strerror(errno));
|
|
|
|
+ rc = -errno;
|
|
|
|
+ }
|
|
|
|
+ goto out;
|
|
}
|
|
}
|
|
|
|
|
|
if (rec->file_new)
|
|
if (rec->file_new)
|
|
@@ -315,11 +348,14 @@ try_again:
|
|
else {
|
|
else {
|
|
if (!perf_evlist__equal(session->evlist, evlist)) {
|
|
if (!perf_evlist__equal(session->evlist, evlist)) {
|
|
fprintf(stderr, "incompatible append\n");
|
|
fprintf(stderr, "incompatible append\n");
|
|
- exit(-1);
|
|
|
|
|
|
+ rc = -1;
|
|
|
|
+ goto out;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
perf_session__set_id_hdr_size(session);
|
|
perf_session__set_id_hdr_size(session);
|
|
|
|
+out:
|
|
|
|
+ return rc;
|
|
}
|
|
}
|
|
|
|
|
|
static int process_buildids(struct perf_record *rec)
|
|
static int process_buildids(struct perf_record *rec)
|
|
@@ -335,10 +371,13 @@ static int process_buildids(struct perf_record *rec)
|
|
size, &build_id__mark_dso_hit_ops);
|
|
size, &build_id__mark_dso_hit_ops);
|
|
}
|
|
}
|
|
|
|
|
|
-static void perf_record__exit(int status __used, void *arg)
|
|
|
|
|
|
+static void perf_record__exit(int status, void *arg)
|
|
{
|
|
{
|
|
struct perf_record *rec = arg;
|
|
struct perf_record *rec = arg;
|
|
|
|
|
|
|
|
+ if (status != 0)
|
|
|
|
+ return;
|
|
|
|
+
|
|
if (!rec->opts.pipe_output) {
|
|
if (!rec->opts.pipe_output) {
|
|
rec->session->header.data_size += rec->bytes_written;
|
|
rec->session->header.data_size += rec->bytes_written;
|
|
|
|
|
|
@@ -393,17 +432,26 @@ static struct perf_event_header finished_round_event = {
|
|
.type = PERF_RECORD_FINISHED_ROUND,
|
|
.type = PERF_RECORD_FINISHED_ROUND,
|
|
};
|
|
};
|
|
|
|
|
|
-static void perf_record__mmap_read_all(struct perf_record *rec)
|
|
|
|
|
|
+static int perf_record__mmap_read_all(struct perf_record *rec)
|
|
{
|
|
{
|
|
int i;
|
|
int i;
|
|
|
|
+ int rc = 0;
|
|
|
|
|
|
for (i = 0; i < rec->evlist->nr_mmaps; i++) {
|
|
for (i = 0; i < rec->evlist->nr_mmaps; i++) {
|
|
- if (rec->evlist->mmap[i].base)
|
|
|
|
- perf_record__mmap_read(rec, &rec->evlist->mmap[i]);
|
|
|
|
|
|
+ if (rec->evlist->mmap[i].base) {
|
|
|
|
+ if (perf_record__mmap_read(rec, &rec->evlist->mmap[i]) != 0) {
|
|
|
|
+ rc = -1;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
if (perf_header__has_feat(&rec->session->header, HEADER_TRACING_DATA))
|
|
if (perf_header__has_feat(&rec->session->header, HEADER_TRACING_DATA))
|
|
- write_output(rec, &finished_round_event, sizeof(finished_round_event));
|
|
|
|
|
|
+ rc = write_output(rec, &finished_round_event,
|
|
|
|
+ sizeof(finished_round_event));
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ return rc;
|
|
}
|
|
}
|
|
|
|
|
|
static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
|
|
static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
|
|
@@ -463,7 +511,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
|
|
output = open(output_name, flags, S_IRUSR | S_IWUSR);
|
|
output = open(output_name, flags, S_IRUSR | S_IWUSR);
|
|
if (output < 0) {
|
|
if (output < 0) {
|
|
perror("failed to create output file");
|
|
perror("failed to create output file");
|
|
- exit(-1);
|
|
|
|
|
|
+ return -1;
|
|
}
|
|
}
|
|
|
|
|
|
rec->output = output;
|
|
rec->output = output;
|
|
@@ -503,7 +551,10 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- perf_record__open(rec);
|
|
|
|
|
|
+ if (perf_record__open(rec) != 0) {
|
|
|
|
+ err = -1;
|
|
|
|
+ goto out_delete_session;
|
|
|
|
+ }
|
|
|
|
|
|
/*
|
|
/*
|
|
* perf_session__delete(session) will be called at perf_record__exit()
|
|
* perf_session__delete(session) will be called at perf_record__exit()
|
|
@@ -513,19 +564,20 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
|
|
if (opts->pipe_output) {
|
|
if (opts->pipe_output) {
|
|
err = perf_header__write_pipe(output);
|
|
err = perf_header__write_pipe(output);
|
|
if (err < 0)
|
|
if (err < 0)
|
|
- return err;
|
|
|
|
|
|
+ goto out_delete_session;
|
|
} else if (rec->file_new) {
|
|
} else if (rec->file_new) {
|
|
err = perf_session__write_header(session, evsel_list,
|
|
err = perf_session__write_header(session, evsel_list,
|
|
output, false);
|
|
output, false);
|
|
if (err < 0)
|
|
if (err < 0)
|
|
- return err;
|
|
|
|
|
|
+ goto out_delete_session;
|
|
}
|
|
}
|
|
|
|
|
|
if (!rec->no_buildid
|
|
if (!rec->no_buildid
|
|
&& !perf_header__has_feat(&session->header, HEADER_BUILD_ID)) {
|
|
&& !perf_header__has_feat(&session->header, HEADER_BUILD_ID)) {
|
|
pr_err("Couldn't generate buildids. "
|
|
pr_err("Couldn't generate buildids. "
|
|
"Use --no-buildid to profile anyway.\n");
|
|
"Use --no-buildid to profile anyway.\n");
|
|
- return -1;
|
|
|
|
|
|
+ err = -1;
|
|
|
|
+ goto out_delete_session;
|
|
}
|
|
}
|
|
|
|
|
|
rec->post_processing_offset = lseek(output, 0, SEEK_CUR);
|
|
rec->post_processing_offset = lseek(output, 0, SEEK_CUR);
|
|
@@ -533,7 +585,8 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
|
|
machine = perf_session__find_host_machine(session);
|
|
machine = perf_session__find_host_machine(session);
|
|
if (!machine) {
|
|
if (!machine) {
|
|
pr_err("Couldn't find native kernel information.\n");
|
|
pr_err("Couldn't find native kernel information.\n");
|
|
- return -1;
|
|
|
|
|
|
+ err = -1;
|
|
|
|
+ goto out_delete_session;
|
|
}
|
|
}
|
|
|
|
|
|
if (opts->pipe_output) {
|
|
if (opts->pipe_output) {
|
|
@@ -541,14 +594,14 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
|
|
process_synthesized_event);
|
|
process_synthesized_event);
|
|
if (err < 0) {
|
|
if (err < 0) {
|
|
pr_err("Couldn't synthesize attrs.\n");
|
|
pr_err("Couldn't synthesize attrs.\n");
|
|
- return err;
|
|
|
|
|
|
+ goto out_delete_session;
|
|
}
|
|
}
|
|
|
|
|
|
err = perf_event__synthesize_event_types(tool, process_synthesized_event,
|
|
err = perf_event__synthesize_event_types(tool, process_synthesized_event,
|
|
machine);
|
|
machine);
|
|
if (err < 0) {
|
|
if (err < 0) {
|
|
pr_err("Couldn't synthesize event_types.\n");
|
|
pr_err("Couldn't synthesize event_types.\n");
|
|
- return err;
|
|
|
|
|
|
+ goto out_delete_session;
|
|
}
|
|
}
|
|
|
|
|
|
if (have_tracepoints(&evsel_list->entries)) {
|
|
if (have_tracepoints(&evsel_list->entries)) {
|
|
@@ -564,7 +617,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
|
|
process_synthesized_event);
|
|
process_synthesized_event);
|
|
if (err <= 0) {
|
|
if (err <= 0) {
|
|
pr_err("Couldn't record tracing data.\n");
|
|
pr_err("Couldn't record tracing data.\n");
|
|
- return err;
|
|
|
|
|
|
+ goto out_delete_session;
|
|
}
|
|
}
|
|
advance_output(rec, err);
|
|
advance_output(rec, err);
|
|
}
|
|
}
|
|
@@ -592,20 +645,24 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
|
|
perf_event__synthesize_guest_os);
|
|
perf_event__synthesize_guest_os);
|
|
|
|
|
|
if (!opts->target.system_wide)
|
|
if (!opts->target.system_wide)
|
|
- perf_event__synthesize_thread_map(tool, evsel_list->threads,
|
|
|
|
|
|
+ err = perf_event__synthesize_thread_map(tool, evsel_list->threads,
|
|
process_synthesized_event,
|
|
process_synthesized_event,
|
|
machine);
|
|
machine);
|
|
else
|
|
else
|
|
- perf_event__synthesize_threads(tool, process_synthesized_event,
|
|
|
|
|
|
+ err = perf_event__synthesize_threads(tool, process_synthesized_event,
|
|
machine);
|
|
machine);
|
|
|
|
|
|
|
|
+ if (err != 0)
|
|
|
|
+ goto out_delete_session;
|
|
|
|
+
|
|
if (rec->realtime_prio) {
|
|
if (rec->realtime_prio) {
|
|
struct sched_param param;
|
|
struct sched_param param;
|
|
|
|
|
|
param.sched_priority = rec->realtime_prio;
|
|
param.sched_priority = rec->realtime_prio;
|
|
if (sched_setscheduler(0, SCHED_FIFO, ¶m)) {
|
|
if (sched_setscheduler(0, SCHED_FIFO, ¶m)) {
|
|
pr_err("Could not set realtime priority.\n");
|
|
pr_err("Could not set realtime priority.\n");
|
|
- exit(-1);
|
|
|
|
|
|
+ err = -1;
|
|
|
|
+ goto out_delete_session;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -620,7 +677,10 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
|
|
for (;;) {
|
|
for (;;) {
|
|
int hits = rec->samples;
|
|
int hits = rec->samples;
|
|
|
|
|
|
- perf_record__mmap_read_all(rec);
|
|
|
|
|
|
+ if (perf_record__mmap_read_all(rec) < 0) {
|
|
|
|
+ err = -1;
|
|
|
|
+ goto out_delete_session;
|
|
|
|
+ }
|
|
|
|
|
|
if (hits == rec->samples) {
|
|
if (hits == rec->samples) {
|
|
if (done)
|
|
if (done)
|