perf/core improvements and fixes:

Developer stuff:
 
 o More prep work to support Intel PT: (Adrian Hunter)
   - Polishing 'script' BTS output
   - 'inject' can specify --kallsym
   - VDSO is per machine, not a global var
   - Expose data addr lookup functions previously private to 'script'
   - Large mmap fixes in events processing
 
 o Fix build on gcc 4.4.7 (Arnaldo Carvalho de Melo)
 
 o Event ordering fixes (Jiri Olsa)
 
 o Include standard stringify macros in power pc code (Sukadev Bhattiprolu)
 
 Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJT0nkWAAoJENZQFvNTUqpAEJcP/0yIjEORa1J2pKFueyLSaY3K
 JBeol2NJLfj5fxxNjmHdTl8uV/UGorOobxkQ+s3Liujg8hGQL97KonHrMXSw4/Oo
 Lj14qOlC3YiFzvbpU3U5NaK4t3OPCLB8paQWBw8/Wx0nDtqz70538hTq55mNd9JZ
 RfSBfcJ7gedvZNfZoU0+GjukT6c+waOj9ADKUeIoefXyrvspggkFyh4UeeP2Y4ri
 FlkzDPsKndD+eZhG4ZrN7VPuEUyn4OymYPcJ7eie7kAXlEydNQECoXsLEWo8gygR
 emC4Dk4qfDhmPESJm5EDeeB3OYca810rSmtMcmiBG2bfqrIQhDlo+DrKYddIUYQl
 HJp2NlK1gMd2I0cwW/80G4Fkz4y2mxsdbps9/76qFMnLSGLrNTbmh95Fgyw5kaP9
 UIU0hXALdNUlKTBpNHFuhjSufVbFE3qRSsH2wXWM3FOkJFMksw0hSqa8NgQ1Y2IZ
 Iy6EfOhkqr5PkrgokHgEuhqB7akaD/I5NW089gr5vmbORkxnamaNuSXHuV1RqftM
 uupYvFUN/WGDhFyUtDJ2sFOpRC8P6XHFVFDCT7wJ2JsdQP7KGbhgi5emSnTld8so
 4N5gJSYg7FGK16I0eF+Ro5t2TxdOk3LaXES98h8RFNvAqYZMT8iDirAz9ACAUnJ3
 wES53HBJn/O7eKD4LqGO
 =W4Hp
 -----END PGP SIGNATURE-----

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

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

Infrastructure changes:

 o More prep work to support Intel PT: (Adrian Hunter)
   - Polishing 'script' BTS output
   - 'inject' can specify --kallsym
   - VDSO is per machine, not a global var
   - Expose data addr lookup functions previously private to 'script'
   - Large mmap fixes in events processing

 o Fix build on gcc 4.4.7 (Arnaldo Carvalho de Melo)

 o Event ordering fixes (Jiri Olsa)

 o Include standard stringify macros in power pc code (Sukadev Bhattiprolu)

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Ingo Molnar 2014-07-28 10:09:03 +02:00
commit 068f1d3f45
29 changed files with 476 additions and 121 deletions

View file

@ -41,6 +41,9 @@ OPTIONS
tasks slept. sched_switch contains a callchain where a task slept and
sched_stat contains a timeslice how long a task slept.
--kallsyms=<file>::
kallsyms pathname
SEE ALSO
--------
linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-archive[1]

View file

@ -5,9 +5,7 @@
#include <string.h>
#include "../../util/header.h"
#define __stringify_1(x) #x
#define __stringify(x) __stringify_1(x)
#include "../../util/util.h"
#define mfspr(rn) ({unsigned long rval; \
asm volatile("mfspr %0," __stringify(rn) \

View file

@ -37,3 +37,12 @@ int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
return 0;
}
u64 rdtsc(void)
{
unsigned int low, high;
asm volatile("rdtsc" : "=a" (low), "=d" (high));
return low | ((u64)high) << 32;
}

View file

@ -439,6 +439,8 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
"where and how long tasks slept"),
OPT_INCR('v', "verbose", &verbose,
"be more verbose (show build ids, etc)"),
OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, "file",
"kallsyms pathname"),
OPT_END()
};
const char * const inject_usage[] = {

View file

@ -238,6 +238,7 @@ static struct perf_event_header finished_round_event = {
static int record__mmap_read_all(struct record *rec)
{
u64 bytes_written = rec->bytes_written;
int i;
int rc = 0;
@ -250,7 +251,11 @@ static int record__mmap_read_all(struct record *rec)
}
}
if (perf_header__has_feat(&rec->session->header, HEADER_TRACING_DATA))
/*
* Mark the round finished in case we wrote
* at least one event.
*/
if (bytes_written != rec->bytes_written)
rc = record__write(rec, &finished_round_event, sizeof(finished_round_event));
out:

View file

@ -358,27 +358,6 @@ static void print_sample_start(struct perf_sample *sample,
}
}
static bool is_bts_event(struct perf_event_attr *attr)
{
return ((attr->type == PERF_TYPE_HARDWARE) &&
(attr->config & PERF_COUNT_HW_BRANCH_INSTRUCTIONS) &&
(attr->sample_period == 1));
}
static bool sample_addr_correlates_sym(struct perf_event_attr *attr)
{
if ((attr->type == PERF_TYPE_SOFTWARE) &&
((attr->config == PERF_COUNT_SW_PAGE_FAULTS) ||
(attr->config == PERF_COUNT_SW_PAGE_FAULTS_MIN) ||
(attr->config == PERF_COUNT_SW_PAGE_FAULTS_MAJ)))
return true;
if (is_bts_event(attr))
return true;
return false;
}
static void print_sample_addr(union perf_event *event,
struct perf_sample *sample,
struct machine *machine,
@ -386,24 +365,13 @@ static void print_sample_addr(union perf_event *event,
struct perf_event_attr *attr)
{
struct addr_location al;
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
printf("%16" PRIx64, sample->addr);
if (!sample_addr_correlates_sym(attr))
return;
thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
sample->addr, &al);
if (!al.map)
thread__find_addr_map(thread, machine, cpumode, MAP__VARIABLE,
sample->addr, &al);
al.cpu = sample->cpu;
al.sym = NULL;
if (al.map)
al.sym = map__find_symbol(al.map, al.addr, NULL);
perf_event__preprocess_sample_addr(event, sample, machine, thread, &al);
if (PRINT_FIELD(SYM)) {
printf(" ");
@ -427,25 +395,35 @@ static void print_sample_bts(union perf_event *event,
struct addr_location *al)
{
struct perf_event_attr *attr = &evsel->attr;
bool print_srcline_last = false;
/* print branch_from information */
if (PRINT_FIELD(IP)) {
if (!symbol_conf.use_callchain)
printf(" ");
else
unsigned int print_opts = output[attr->type].print_ip_opts;
if (symbol_conf.use_callchain && sample->callchain) {
printf("\n");
perf_evsel__print_ip(evsel, sample, al,
output[attr->type].print_ip_opts,
} else {
printf(" ");
if (print_opts & PRINT_IP_OPT_SRCLINE) {
print_srcline_last = true;
print_opts &= ~PRINT_IP_OPT_SRCLINE;
}
}
perf_evsel__print_ip(evsel, sample, al, print_opts,
PERF_MAX_STACK_DEPTH);
}
printf(" => ");
/* print branch_to information */
if (PRINT_FIELD(ADDR) ||
((evsel->attr.sample_type & PERF_SAMPLE_ADDR) &&
!output[attr->type].user_set))
!output[attr->type].user_set)) {
printf(" => ");
print_sample_addr(event, sample, al->machine, thread, attr);
}
if (print_srcline_last)
map__fprintf_srcline(al->map, al->addr, "\n ", stdout);
printf("\n");
}

View file

@ -1994,10 +1994,10 @@ static int perf_evlist__add_pgfault(struct perf_evlist *evlist,
struct perf_event_attr attr = {
.type = PERF_TYPE_SOFTWARE,
.mmap_data = 1,
.sample_period = 1,
};
attr.config = config;
attr.sample_period = 1;
event_attr_init(&attr);

View file

@ -25,15 +25,6 @@
} \
}
static u64 rdtsc(void)
{
unsigned int low, high;
asm volatile("rdtsc" : "=a" (low), "=d" (high));
return low | ((u64)high) << 32;
}
/**
* test__perf_time_to_tsc - test converting perf time to TSC.
*

View file

@ -1,3 +1,4 @@
#include <sched.h>
#include "util.h"
#include "../perf.h"
#include "cloexec.h"
@ -14,9 +15,13 @@ static int perf_flag_probe(void)
};
int fd;
int err;
int cpu = sched_getcpu();
if (cpu < 0)
cpu = 0;
/* check cloexec flag */
fd = sys_perf_event_open(&attr, 0, -1, -1,
fd = sys_perf_event_open(&attr, -1, cpu, -1,
PERF_FLAG_FD_CLOEXEC);
err = errno;
@ -30,7 +35,7 @@ static int perf_flag_probe(void)
err, strerror(err));
/* not supported, confirm error related to PERF_FLAG_FD_CLOEXEC */
fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
fd = sys_perf_event_open(&attr, -1, cpu, -1, 0);
err = errno;
if (WARN_ONCE(fd < 0,

View file

@ -216,7 +216,7 @@ static int open_dso(struct dso *dso, struct machine *machine)
{
int fd = __open_dso(dso, machine);
if (fd > 0) {
if (fd >= 0) {
dso__list_add(dso);
/*
* Check if we crossed the allowed number
@ -331,26 +331,44 @@ int dso__data_fd(struct dso *dso, struct machine *machine)
};
int i = 0;
if (dso->data.status == DSO_DATA_STATUS_ERROR)
return -1;
if (dso->data.fd >= 0)
return dso->data.fd;
goto out;
if (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND) {
dso->data.fd = open_dso(dso, machine);
return dso->data.fd;
goto out;
}
do {
int fd;
dso->binary_type = binary_type_data[i++];
fd = open_dso(dso, machine);
if (fd >= 0)
return dso->data.fd = fd;
dso->data.fd = open_dso(dso, machine);
if (dso->data.fd >= 0)
goto out;
} while (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND);
out:
if (dso->data.fd >= 0)
dso->data.status = DSO_DATA_STATUS_OK;
else
dso->data.status = DSO_DATA_STATUS_ERROR;
return -EINVAL;
return dso->data.fd;
}
bool dso__data_status_seen(struct dso *dso, enum dso_data_status_seen by)
{
u32 flag = 1 << by;
if (dso->data.status_seen & flag)
return true;
dso->data.status_seen |= flag;
return false;
}
static void
@ -526,6 +544,28 @@ static int data_file_size(struct dso *dso)
return 0;
}
/**
* dso__data_size - Return dso data size
* @dso: dso object
* @machine: machine object
*
* Return: dso data size
*/
off_t dso__data_size(struct dso *dso, struct machine *machine)
{
int fd;
fd = dso__data_fd(dso, machine);
if (fd < 0)
return fd;
if (data_file_size(dso))
return -1;
/* For now just estimate dso data size is close to file size */
return dso->data.file_size;
}
static ssize_t data_read_offset(struct dso *dso, u64 offset,
u8 *data, ssize_t size)
{
@ -701,6 +741,7 @@ struct dso *dso__new(const char *name)
dso->symbols[i] = dso->symbol_names[i] = RB_ROOT;
dso->data.cache = RB_ROOT;
dso->data.fd = -1;
dso->data.status = DSO_DATA_STATUS_UNKNOWN;
dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND;
dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND;
dso->is_64_bit = (sizeof(void *) == 8);
@ -899,3 +940,14 @@ size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp)
return ret;
}
enum dso_type dso__type(struct dso *dso, struct machine *machine)
{
int fd;
fd = dso__data_fd(dso, machine);
if (fd < 0)
return DSO__TYPE_UNKNOWN;
return dso__type_fd(fd);
}

View file

@ -5,6 +5,7 @@
#include <linux/rbtree.h>
#include <stdbool.h>
#include <linux/types.h>
#include <linux/bitops.h>
#include "map.h"
#include "build-id.h"
@ -40,6 +41,23 @@ enum dso_swap_type {
DSO_SWAP__YES,
};
enum dso_data_status {
DSO_DATA_STATUS_ERROR = -1,
DSO_DATA_STATUS_UNKNOWN = 0,
DSO_DATA_STATUS_OK = 1,
};
enum dso_data_status_seen {
DSO_DATA_STATUS_SEEN_ITRACE,
};
enum dso_type {
DSO__TYPE_UNKNOWN,
DSO__TYPE_64BIT,
DSO__TYPE_32BIT,
DSO__TYPE_X32BIT,
};
#define DSO__SWAP(dso, type, val) \
({ \
type ____r = val; \
@ -104,6 +122,8 @@ struct dso {
struct {
struct rb_root cache;
int fd;
int status;
u32 status_seen;
size_t file_size;
struct list_head open_entry;
} data;
@ -154,6 +174,7 @@ int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type t
* The dso__data_* external interface provides following functions:
* dso__data_fd
* dso__data_close
* dso__data_size
* dso__data_read_offset
* dso__data_read_addr
*
@ -191,11 +212,13 @@ int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type t
int dso__data_fd(struct dso *dso, struct machine *machine);
void dso__data_close(struct dso *dso);
off_t dso__data_size(struct dso *dso, struct machine *machine);
ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
u64 offset, u8 *data, ssize_t size);
ssize_t dso__data_read_addr(struct dso *dso, struct map *map,
struct machine *machine, u64 addr,
u8 *data, ssize_t size);
bool dso__data_status_seen(struct dso *dso, enum dso_data_status_seen by);
struct map *dso__new_map(const char *name);
struct dso *dso__kernel_findnew(struct machine *machine, const char *name,
@ -230,4 +253,6 @@ static inline bool dso__is_kcore(struct dso *dso)
void dso__free_a2l(struct dso *dso);
enum dso_type dso__type(struct dso *dso, struct machine *machine);
#endif /* __PERF_DSO */

View file

@ -874,3 +874,45 @@ int perf_event__preprocess_sample(const union perf_event *event,
return 0;
}
bool is_bts_event(struct perf_event_attr *attr)
{
return attr->type == PERF_TYPE_HARDWARE &&
(attr->config & PERF_COUNT_HW_BRANCH_INSTRUCTIONS) &&
attr->sample_period == 1;
}
bool sample_addr_correlates_sym(struct perf_event_attr *attr)
{
if (attr->type == PERF_TYPE_SOFTWARE &&
(attr->config == PERF_COUNT_SW_PAGE_FAULTS ||
attr->config == PERF_COUNT_SW_PAGE_FAULTS_MIN ||
attr->config == PERF_COUNT_SW_PAGE_FAULTS_MAJ))
return true;
if (is_bts_event(attr))
return true;
return false;
}
void perf_event__preprocess_sample_addr(union perf_event *event,
struct perf_sample *sample,
struct machine *machine,
struct thread *thread,
struct addr_location *al)
{
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
sample->addr, al);
if (!al->map)
thread__find_addr_map(thread, machine, cpumode, MAP__VARIABLE,
sample->addr, al);
al->cpu = sample->cpu;
al->sym = NULL;
if (al->map)
al->sym = map__find_symbol(al->map, al->addr, NULL);
}

View file

@ -288,6 +288,16 @@ int perf_event__preprocess_sample(const union perf_event *event,
struct addr_location *al,
struct perf_sample *sample);
struct thread;
bool is_bts_event(struct perf_event_attr *attr);
bool sample_addr_correlates_sym(struct perf_event_attr *attr);
void perf_event__preprocess_sample_addr(union perf_event *event,
struct perf_sample *sample,
struct machine *machine,
struct thread *thread,
struct addr_location *al);
const char *perf_event__name(unsigned int id);
size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,

View file

@ -200,6 +200,47 @@ static int write_buildid(const char *name, size_t name_len, u8 *build_id,
return write_padded(fd, name, name_len + 1, len);
}
static int __dsos__hit_all(struct list_head *head)
{
struct dso *pos;
list_for_each_entry(pos, head, node)
pos->hit = true;
return 0;
}
static int machine__hit_all_dsos(struct machine *machine)
{
int err;
err = __dsos__hit_all(&machine->kernel_dsos);
if (err)
return err;
return __dsos__hit_all(&machine->user_dsos);
}
int dsos__hit_all(struct perf_session *session)
{
struct rb_node *nd;
int err;
err = machine__hit_all_dsos(&session->machines.host);
if (err)
return err;
for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
struct machine *pos = rb_entry(nd, struct machine, rb_node);
err = machine__hit_all_dsos(pos);
if (err)
return err;
}
return 0;
}
static int __dsos__write_buildid_table(struct list_head *head,
struct machine *machine,
pid_t pid, u16 misc, int fd)
@ -215,9 +256,9 @@ static int __dsos__write_buildid_table(struct list_head *head,
if (!pos->hit)
continue;
if (is_vdso_map(pos->short_name)) {
name = (char *) VDSO__MAP_NAME;
name_len = sizeof(VDSO__MAP_NAME) + 1;
if (dso__is_vdso(pos)) {
name = pos->short_name;
name_len = pos->short_name_len + 1;
} else if (dso__is_kcore(pos)) {
machine__mmap_name(machine, nm, sizeof(nm));
name = nm;
@ -298,7 +339,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
len = scnprintf(filename, size, "%s%s%s",
debugdir, slash ? "/" : "",
is_vdso ? VDSO__MAP_NAME : realname);
is_vdso ? DSO__NAME_VDSO : realname);
if (mkdir_p(filename, 0755))
goto out_free;
@ -386,7 +427,7 @@ static int dso__cache_build_id(struct dso *dso, struct machine *machine,
const char *debugdir)
{
bool is_kallsyms = dso->kernel && dso->long_name[0] != '/';
bool is_vdso = is_vdso_map(dso->short_name);
bool is_vdso = dso__is_vdso(dso);
const char *name = dso->long_name;
char nm[PATH_MAX];

View file

@ -151,6 +151,8 @@ int perf_event__process_build_id(struct perf_tool *tool,
struct perf_session *session);
bool is_perf_magic(u64 magic);
int dsos__hit_all(struct perf_session *session);
/*
* arch specific callback
*/

View file

@ -8,6 +8,7 @@
#include "sort.h"
#include "strlist.h"
#include "thread.h"
#include "vdso.h"
#include <stdbool.h>
#include <symbol/kallsyms.h>
#include "unwind.h"
@ -23,6 +24,8 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
INIT_LIST_HEAD(&machine->dead_threads);
machine->last_match = NULL;
machine->vdso_info = NULL;
machine->kmaps.machine = machine;
machine->pid = pid;
@ -45,6 +48,8 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
thread__set_comm(thread, comm, 0);
}
machine->current_tid = NULL;
return 0;
}
@ -103,7 +108,9 @@ void machine__exit(struct machine *machine)
map_groups__exit(&machine->kmaps);
dsos__delete(&machine->user_dsos);
dsos__delete(&machine->kernel_dsos);
vdso__exit(machine);
zfree(&machine->root_dir);
zfree(&machine->current_tid);
}
void machine__delete(struct machine *machine)
@ -1092,14 +1099,14 @@ int machine__process_mmap2_event(struct machine *machine,
else
type = MAP__FUNCTION;
map = map__new(&machine->user_dsos, event->mmap2.start,
map = map__new(machine, event->mmap2.start,
event->mmap2.len, event->mmap2.pgoff,
event->mmap2.pid, event->mmap2.maj,
event->mmap2.min, event->mmap2.ino,
event->mmap2.ino_generation,
event->mmap2.prot,
event->mmap2.flags,
event->mmap2.filename, type);
event->mmap2.filename, type, thread);
if (map == NULL)
goto out_problem;
@ -1142,11 +1149,11 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event
else
type = MAP__FUNCTION;
map = map__new(&machine->user_dsos, event->mmap.start,
map = map__new(machine, event->mmap.start,
event->mmap.len, event->mmap.pgoff,
event->mmap.pid, 0, 0, 0, 0, 0, 0,
event->mmap.filename,
type);
type, thread);
if (map == NULL)
goto out_problem;
@ -1481,3 +1488,46 @@ int __machine__synthesize_threads(struct machine *machine, struct perf_tool *too
/* command specified */
return 0;
}
pid_t machine__get_current_tid(struct machine *machine, int cpu)
{
if (cpu < 0 || cpu >= MAX_NR_CPUS || !machine->current_tid)
return -1;
return machine->current_tid[cpu];
}
int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid,
pid_t tid)
{
struct thread *thread;
if (cpu < 0)
return -EINVAL;
if (!machine->current_tid) {
int i;
machine->current_tid = calloc(MAX_NR_CPUS, sizeof(pid_t));
if (!machine->current_tid)
return -ENOMEM;
for (i = 0; i < MAX_NR_CPUS; i++)
machine->current_tid[i] = -1;
}
if (cpu >= MAX_NR_CPUS) {
pr_err("Requested CPU %d too large. ", cpu);
pr_err("Consider raising MAX_NR_CPUS\n");
return -EINVAL;
}
machine->current_tid[cpu] = tid;
thread = machine__findnew_thread(machine, pid, tid);
if (!thread)
return -ENOMEM;
thread->cpu = cpu;
return 0;
}

View file

@ -20,6 +20,8 @@ union perf_event;
extern const char *ref_reloc_sym_names[];
struct vdso_info;
struct machine {
struct rb_node rb_node;
pid_t pid;
@ -28,11 +30,13 @@ struct machine {
struct rb_root threads;
struct list_head dead_threads;
struct thread *last_match;
struct vdso_info *vdso_info;
struct list_head user_dsos;
struct list_head kernel_dsos;
struct map_groups kmaps;
struct map *vmlinux_maps[MAP__NR_TYPES];
symbol_filter_t symbol_filter;
pid_t *current_tid;
};
static inline
@ -191,4 +195,8 @@ int machine__synthesize_threads(struct machine *machine, struct target *target,
perf_event__process, data_mmap);
}
pid_t machine__get_current_tid(struct machine *machine, int cpu);
int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid,
pid_t tid);
#endif /* __PERF_MACHINE_H */

View file

@ -13,6 +13,7 @@
#include "build-id.h"
#include "util.h"
#include "debug.h"
#include "machine.h"
#include <linux/string.h>
const char *map_type__name[MAP__NR_TYPES] = {
@ -137,10 +138,10 @@ void map__init(struct map *map, enum map_type type,
map->erange_warned = false;
}
struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
struct map *map__new(struct machine *machine, u64 start, u64 len,
u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino,
u64 ino_gen, u32 prot, u32 flags, char *filename,
enum map_type type)
enum map_type type, struct thread *thread)
{
struct map *map = malloc(sizeof(*map));
@ -173,9 +174,9 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
if (vdso) {
pgoff = 0;
dso = vdso__dso_findnew(dsos__list);
dso = vdso__dso_findnew(machine, thread);
} else
dso = __dsos__findnew(dsos__list, filename);
dso = __dsos__findnew(&machine->user_dsos, filename);
if (dso == NULL)
goto out_delete;

View file

@ -104,6 +104,7 @@ u64 map__rip_2objdump(struct map *map, u64 rip);
u64 map__objdump_2mem(struct map *map, u64 ip);
struct symbol;
struct thread;
/* map__for_each_symbol - iterate over the symbols in the given map
*
@ -119,10 +120,10 @@ typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym);
void map__init(struct map *map, enum map_type type,
u64 start, u64 end, u64 pgoff, struct dso *dso);
struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
struct map *map__new(struct machine *machine, u64 start, u64 len,
u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino,
u64 ino_gen, u32 prot, u32 flags,
char *filename, enum map_type type);
char *filename, enum map_type type, struct thread *thread);
struct map *map__new2(u64 start, struct dso *dso, enum map_type type);
void map__delete(struct map *map);
struct map *map__clone(struct map *map);

View file

@ -14,7 +14,6 @@
#include "util.h"
#include "cpumap.h"
#include "perf_regs.h"
#include "vdso.h"
static int perf_session__open(struct perf_session *session)
{
@ -156,7 +155,6 @@ void perf_session__delete(struct perf_session *session)
if (session->file)
perf_data_file__close(session->file);
free(session);
vdso__exit();
}
static int process_event_synth_tracing_data_stub(struct perf_tool *tool
@ -511,6 +509,7 @@ static int flush_sample_queue(struct perf_session *s,
os->last_flush = iter->timestamp;
list_del(&iter->list);
list_add(&iter->list, &os->sample_cache);
os->nr_samples--;
if (show_progress)
ui_progress__update(&prog, 1);
@ -523,8 +522,6 @@ static int flush_sample_queue(struct perf_session *s,
list_entry(head->prev, struct sample_queue, list);
}
os->nr_samples = 0;
return 0;
}
@ -994,8 +991,10 @@ static int perf_session_deliver_event(struct perf_session *session,
}
}
static int perf_session__process_user_event(struct perf_session *session, union perf_event *event,
struct perf_tool *tool, u64 file_offset)
static s64 perf_session__process_user_event(struct perf_session *session,
union perf_event *event,
struct perf_tool *tool,
u64 file_offset)
{
int fd = perf_data_file__fd(session->file);
int err;
@ -1037,7 +1036,7 @@ static void event_swap(union perf_event *event, bool sample_id_all)
swap(event, sample_id_all);
}
static int perf_session__process_event(struct perf_session *session,
static s64 perf_session__process_event(struct perf_session *session,
union perf_event *event,
struct perf_tool *tool,
u64 file_offset)
@ -1148,7 +1147,7 @@ static int __perf_session__process_pipe_events(struct perf_session *session,
union perf_event *event;
uint32_t size, cur_size = 0;
void *buf = NULL;
int skip = 0;
s64 skip = 0;
u64 head;
ssize_t err;
void *p;
@ -1277,13 +1276,13 @@ int __perf_session__process_events(struct perf_session *session,
u64 file_size, struct perf_tool *tool)
{
int fd = perf_data_file__fd(session->file);
u64 head, page_offset, file_offset, file_pos;
u64 head, page_offset, file_offset, file_pos, size;
int err, mmap_prot, mmap_flags, map_idx = 0;
size_t mmap_size;
char *buf, *mmaps[NUM_MMAPS];
union perf_event *event;
uint32_t size;
struct ui_progress prog;
s64 skip;
perf_tool__fill_defaults(tool);
@ -1344,7 +1343,8 @@ more:
size = event->header.size;
if (size < sizeof(struct perf_event_header) ||
perf_session__process_event(session, event, tool, file_pos) < 0) {
(skip = perf_session__process_event(session, event, tool, file_pos))
< 0) {
pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n",
file_offset + head, event->header.size,
event->header.type);
@ -1352,6 +1352,9 @@ more:
goto out_err;
}
if (skip)
size += skip;
head += size;
file_pos += size;

View file

@ -622,7 +622,7 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
GElf_Shdr shdr;
ss->adjust_symbols = (ehdr.e_type == ET_EXEC ||
ehdr.e_type == ET_REL ||
is_vdso_map(dso->short_name) ||
dso__is_vdso(dso) ||
elf_section_by_name(elf, &ehdr, &shdr,
".gnu.prelink_undo",
NULL) != NULL);
@ -1028,6 +1028,39 @@ int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data,
return err;
}
enum dso_type dso__type_fd(int fd)
{
enum dso_type dso_type = DSO__TYPE_UNKNOWN;
GElf_Ehdr ehdr;
Elf_Kind ek;
Elf *elf;
elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
if (elf == NULL)
goto out;
ek = elf_kind(elf);
if (ek != ELF_K_ELF)
goto out_end;
if (gelf_getclass(elf) == ELFCLASS64) {
dso_type = DSO__TYPE_64BIT;
goto out_end;
}
if (gelf_getehdr(elf, &ehdr) == NULL)
goto out_end;
if (ehdr.e_machine == EM_X86_64)
dso_type = DSO__TYPE_X32BIT;
else
dso_type = DSO__TYPE_32BIT;
out_end:
elf_end(elf);
out:
return dso_type;
}
static int copy_bytes(int from, off_t from_offs, int to, off_t to_offs, u64 len)
{
ssize_t r;

View file

@ -305,6 +305,27 @@ static int fd__is_64_bit(int fd)
return e_ident[EI_CLASS] == ELFCLASS64;
}
enum dso_type dso__type_fd(int fd)
{
Elf64_Ehdr ehdr;
int ret;
ret = fd__is_64_bit(fd);
if (ret < 0)
return DSO__TYPE_UNKNOWN;
if (ret)
return DSO__TYPE_64BIT;
if (readn(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr))
return DSO__TYPE_UNKNOWN;
if (ehdr.e_machine == EM_X86_64)
return DSO__TYPE_X32BIT;
return DSO__TYPE_32BIT;
}
int dso__load_sym(struct dso *dso, struct map *map __maybe_unused,
struct symsrc *ss,
struct symsrc *runtime_ss __maybe_unused,

View file

@ -243,6 +243,8 @@ struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type,
struct symbol *dso__first_symbol(struct dso *dso, enum map_type type);
struct symbol *dso__next_symbol(struct symbol *sym);
enum dso_type dso__type_fd(int fd);
int filename__read_build_id(const char *filename, void *bf, size_t size);
int sysfs__read_build_id(const char *filename, void *bf, size_t size);
int modules__parse(const char *filename, void *arg,

View file

@ -34,6 +34,7 @@ struct thread *thread__new(pid_t pid, pid_t tid)
thread->pid_ = pid;
thread->tid = tid;
thread->ppid = -1;
thread->cpu = -1;
INIT_LIST_HEAD(&thread->comm_list);
comm_str = malloc(32);

View file

@ -17,6 +17,7 @@ struct thread {
pid_t pid_; /* Not all tools update this */
pid_t tid;
pid_t ppid;
int cpu;
char shortname[3];
bool comm_set;
bool dead; /* if set thread has exited */

View file

@ -23,3 +23,8 @@ u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc)
return tc->time_zero + quot * tc->time_mult +
((rem * tc->time_mult) >> tc->time_shift);
}
u64 __weak rdtsc(void)
{
return 0;
}

View file

@ -7,5 +7,6 @@
u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc);
u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc);
u64 rdtsc(void);
#endif

View file

@ -11,11 +11,34 @@
#include "vdso.h"
#include "util.h"
#include "symbol.h"
#include "machine.h"
#include "linux/string.h"
#include "debug.h"
static bool vdso_found;
static char vdso_file[] = "/tmp/perf-vdso.so-XXXXXX";
#define VDSO__TEMP_FILE_NAME "/tmp/perf-vdso.so-XXXXXX"
struct vdso_file {
bool found;
bool error;
char temp_file_name[sizeof(VDSO__TEMP_FILE_NAME)];
const char *dso_name;
};
struct vdso_info {
struct vdso_file vdso;
};
static struct vdso_info *vdso_info__new(void)
{
static const struct vdso_info vdso_info_init = {
.vdso = {
.temp_file_name = VDSO__TEMP_FILE_NAME,
.dso_name = DSO__NAME_VDSO,
},
};
return memdup(&vdso_info_init, sizeof(vdso_info_init));
}
static int find_vdso_map(void **start, void **end)
{
@ -48,7 +71,7 @@ static int find_vdso_map(void **start, void **end)
return !found;
}
static char *get_file(void)
static char *get_file(struct vdso_file *vdso_file)
{
char *vdso = NULL;
char *buf = NULL;
@ -56,10 +79,10 @@ static char *get_file(void)
size_t size;
int fd;
if (vdso_found)
return vdso_file;
if (vdso_file->found)
return vdso_file->temp_file_name;
if (find_vdso_map(&start, &end))
if (vdso_file->error || find_vdso_map(&start, &end))
return NULL;
size = end - start;
@ -68,45 +91,78 @@ static char *get_file(void)
if (!buf)
return NULL;
fd = mkstemp(vdso_file);
fd = mkstemp(vdso_file->temp_file_name);
if (fd < 0)
goto out;
if (size == (size_t) write(fd, buf, size))
vdso = vdso_file;
vdso = vdso_file->temp_file_name;
close(fd);
out:
free(buf);
vdso_found = (vdso != NULL);
vdso_file->found = (vdso != NULL);
vdso_file->error = !vdso_file->found;
return vdso;
}
void vdso__exit(void)
void vdso__exit(struct machine *machine)
{
if (vdso_found)
unlink(vdso_file);
struct vdso_info *vdso_info = machine->vdso_info;
if (!vdso_info)
return;
if (vdso_info->vdso.found)
unlink(vdso_info->vdso.temp_file_name);
zfree(&machine->vdso_info);
}
struct dso *vdso__dso_findnew(struct list_head *head)
static struct dso *vdso__new(struct machine *machine, const char *short_name,
const char *long_name)
{
struct dso *dso = dsos__find(head, VDSO__MAP_NAME, true);
struct dso *dso;
if (!dso) {
char *file;
file = get_file();
if (!file)
return NULL;
dso = dso__new(VDSO__MAP_NAME);
if (dso != NULL) {
dsos__add(head, dso);
dso__set_long_name(dso, file, false);
}
dso = dso__new(short_name);
if (dso != NULL) {
dsos__add(&machine->user_dsos, dso);
dso__set_long_name(dso, long_name, false);
}
return dso;
}
struct dso *vdso__dso_findnew(struct machine *machine,
struct thread *thread __maybe_unused)
{
struct vdso_info *vdso_info;
struct dso *dso;
if (!machine->vdso_info)
machine->vdso_info = vdso_info__new();
vdso_info = machine->vdso_info;
if (!vdso_info)
return NULL;
dso = dsos__find(&machine->user_dsos, DSO__NAME_VDSO, true);
if (!dso) {
char *file;
file = get_file(&vdso_info->vdso);
if (!file)
return NULL;
dso = vdso__new(machine, DSO__NAME_VDSO, file);
}
return dso;
}
bool dso__is_vdso(struct dso *dso)
{
return !strcmp(dso->short_name, DSO__NAME_VDSO);
}

View file

@ -7,12 +7,21 @@
#define VDSO__MAP_NAME "[vdso]"
#define DSO__NAME_VDSO "[vdso]"
static inline bool is_vdso_map(const char *filename)
{
return !strcmp(filename, VDSO__MAP_NAME);
}
struct dso *vdso__dso_findnew(struct list_head *head);
void vdso__exit(void);
struct dso;
bool dso__is_vdso(struct dso *dso);
struct machine;
struct thread;
struct dso *vdso__dso_findnew(struct machine *machine, struct thread *thread);
void vdso__exit(struct machine *machine);
#endif /* __PERF_VDSO__ */