Commit 8c1df400 authored by Ingo Molnar's avatar Ingo Molnar
Browse files

Merge branch 'perf/core' of...

Merge branch 'perf/core' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux-2.6 into perf/core
parents 6c529a26 21dd9ae5
......@@ -66,6 +66,8 @@ OPTIONS
--force::
Don't complain, do it.
--symfs=<directory>::
Look for files with symbols relative to this directory.
SEE ALSO
--------
......
......@@ -117,7 +117,7 @@ LINE SYNTAX
-----------
Line range is described by following syntax.
"FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]"
"FUNC[:RLN[+NUM|-RLN2]]|SRC[:ALN[+NUM|-ALN2]]"
FUNC specifies the function name of showing lines. 'RLN' is the start line
number from function entry line, and 'RLN2' is the end line number. As same as
......
......@@ -116,6 +116,9 @@ OPTIONS
--force::
Don't complain, do it.
--symfs=<directory>::
Look for files with symbols relative to this directory.
SEE ALSO
--------
linkperf:perf-stat[1]
......@@ -38,6 +38,8 @@ OPTIONS
--process::
Select the processes to display, by name or PID
--symfs=<directory>::
Look for files with symbols relative to this directory.
SEE ALSO
--------
......
......@@ -375,6 +375,8 @@ static struct perf_event_ops event_ops = {
.mmap = event__process_mmap,
.comm = event__process_comm,
.fork = event__process_task,
.ordered_samples = true,
.ordering_requires_timestamps = true,
};
static int __cmd_annotate(void)
......@@ -382,7 +384,7 @@ static int __cmd_annotate(void)
int ret;
struct perf_session *session;
session = perf_session__new(input_name, O_RDONLY, force, false);
session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops);
if (session == NULL)
return -ENOMEM;
......
......@@ -39,7 +39,8 @@ static int __cmd_buildid_list(void)
int err = -1;
struct perf_session *session;
session = perf_session__new(input_name, O_RDONLY, force, false);
session = perf_session__new(input_name, O_RDONLY, force, false,
&build_id__mark_dso_hit_ops);
if (session == NULL)
return -1;
......
......@@ -61,6 +61,8 @@ static struct perf_event_ops event_ops = {
.exit = event__process_task,
.fork = event__process_task,
.lost = event__process_lost,
.ordered_samples = true,
.ordering_requires_timestamps = true,
};
static void perf_session__insert_hist_entry_by_name(struct rb_root *root,
......@@ -142,8 +144,8 @@ static int __cmd_diff(void)
int ret, i;
struct perf_session *session[2];
session[0] = perf_session__new(input_old, O_RDONLY, force, false);
session[1] = perf_session__new(input_new, O_RDONLY, force, false);
session[0] = perf_session__new(input_old, O_RDONLY, force, false, &event_ops);
session[1] = perf_session__new(input_new, O_RDONLY, force, false, &event_ops);
if (session[0] == NULL || session[1] == NULL)
return -ENOMEM;
......@@ -192,6 +194,8 @@ static const struct option options[] = {
OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",
"separator for columns, no spaces will be added between "
"columns '.' is reserved."),
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
"Look for files with symbols relative to this directory"),
OPT_END()
};
......
......@@ -196,7 +196,7 @@ static int __cmd_inject(void)
inject_ops.tracing_data = event__repipe_tracing_data;
}
session = perf_session__new(input_name, O_RDONLY, false, true);
session = perf_session__new(input_name, O_RDONLY, false, true, &inject_ops);
if (session == NULL)
return -ENOMEM;
......
......@@ -481,7 +481,8 @@ static void sort_result(void)
static int __cmd_kmem(void)
{
int err = -EINVAL;
struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0, false);
struct perf_session *session = perf_session__new(input_name, O_RDONLY,
0, false, &event_ops);
if (session == NULL)
return -ENOMEM;
......
......@@ -858,7 +858,7 @@ static struct perf_event_ops eops = {
static int read_events(void)
{
session = perf_session__new(input_name, O_RDONLY, 0, false);
session = perf_session__new(input_name, O_RDONLY, 0, false, &eops);
if (!session)
die("Initializing perf session failed\n");
......
......@@ -285,7 +285,7 @@ static void create_counter(int counter, int cpu)
if (system_wide)
attr->sample_type |= PERF_SAMPLE_CPU;
if (sample_time)
if (sample_time || system_wide || !no_inherit || cpu_list)
attr->sample_type |= PERF_SAMPLE_TIME;
if (raw_samples) {
......@@ -327,6 +327,9 @@ try_again:
* Old kernel, no attr->sample_id_type_all field
*/
sample_id_all_avail = false;
if (!sample_time && !raw_samples)
attr->sample_type &= ~PERF_SAMPLE_TIME;
goto retry_sample_id;
}
......@@ -572,7 +575,7 @@ static int __cmd_record(int argc, const char **argv)
}
session = perf_session__new(output_name, O_WRONLY,
write_mode == WRITE_FORCE, false);
write_mode == WRITE_FORCE, false, NULL);
if (session == NULL) {
pr_err("Not enough memory for reading perf file header\n");
return -1;
......
......@@ -244,6 +244,8 @@ static struct perf_event_ops event_ops = {
.event_type = event__process_event_type,
.tracing_data = event__process_tracing_data,
.build_id = event__process_build_id,
.ordered_samples = true,
.ordering_requires_timestamps = true,
};
extern volatile int session_done;
......@@ -308,7 +310,7 @@ static int __cmd_report(void)
signal(SIGINT, sig_handler);
session = perf_session__new(input_name, O_RDONLY, force, false);
session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops);
if (session == NULL)
return -ENOMEM;
......@@ -481,6 +483,8 @@ static const struct option options[] = {
"columns '.' is reserved."),
OPT_BOOLEAN('U', "hide-unresolved", &hide_unresolved,
"Only display entries resolved to a symbol"),
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
"Look for files with symbols relative to this directory"),
OPT_END()
};
......
......@@ -1643,7 +1643,8 @@ static struct perf_event_ops event_ops = {
static int read_events(void)
{
int err = -EINVAL;
struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0, false);
struct perf_session *session = perf_session__new(input_name, O_RDONLY,
0, false, &event_ops);
if (session == NULL)
return -ENOMEM;
......
......@@ -779,7 +779,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)
if (!script_name)
setup_pager();
session = perf_session__new(input_name, O_RDONLY, 0, false);
session = perf_session__new(input_name, O_RDONLY, 0, false, &event_ops);
if (session == NULL)
return -ENOMEM;
......
......@@ -937,7 +937,8 @@ static struct perf_event_ops event_ops = {
static int __cmd_timechart(void)
{
struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0, false);
struct perf_session *session = perf_session__new(input_name, O_RDONLY,
0, false, &event_ops);
int ret = -EINVAL;
if (session == NULL)
......@@ -1021,6 +1022,8 @@ static const struct option options[] = {
OPT_CALLBACK('p', "process", NULL, "process",
"process selector. Pass a pid or process name.",
parse_process),
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
"Look for files with symbols relative to this directory"),
OPT_END()
};
......
......@@ -1272,7 +1272,7 @@ static int __cmd_top(void)
* FIXME: perf_session__new should allow passing a O_MMAP, so that all this
* mmap reading, etc is encapsulated in it. Use O_WRONLY for now.
*/
struct perf_session *session = perf_session__new(NULL, O_WRONLY, false, false);
struct perf_session *session = perf_session__new(NULL, O_WRONLY, false, false, NULL);
if (session == NULL)
return -ENOMEM;
......
......@@ -1092,6 +1092,12 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
FILE *file;
int err = 0;
u64 len;
char symfs_filename[PATH_MAX];
if (filename) {
snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
symbol_conf.symfs, filename);
}
if (filename == NULL) {
if (dso->has_build_id) {
......@@ -1100,9 +1106,9 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
return -ENOMEM;
}
goto fallback;
} else if (readlink(filename, command, sizeof(command)) < 0 ||
} else if (readlink(symfs_filename, command, sizeof(command)) < 0 ||
strstr(command, "[kernel.kallsyms]") ||
access(filename, R_OK)) {
access(symfs_filename, R_OK)) {
free(filename);
fallback:
/*
......@@ -1111,6 +1117,8 @@ fallback:
* DSO is the same as when 'perf record' ran.
*/
filename = dso->long_name;
snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
symbol_conf.symfs, filename);
free_filename = false;
}
......@@ -1137,7 +1145,7 @@ fallback:
"objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand",
map__rip_2objdump(map, sym->start),
map__rip_2objdump(map, sym->end),
filename, filename);
symfs_filename, filename);
pr_debug("Executing: %s\n", command);
......
......@@ -95,7 +95,7 @@ static int init_vmlinux(void)
goto out;
if (machine__create_kernel_maps(&machine) < 0) {
pr_debug("machine__create_kernel_maps ");
pr_debug("machine__create_kernel_maps() failed.\n");
goto out;
}
out:
......@@ -140,7 +140,8 @@ static int open_vmlinux(const char *module)
{
const char *path = kernel_get_module_path(module);
if (!path) {
pr_err("Failed to find path of %s module", module ?: "kernel");
pr_err("Failed to find path of %s module.\n",
module ?: "kernel");
return -ENOENT;
}
pr_debug("Try to open %s\n", path);
......@@ -217,7 +218,7 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
pr_warning("Warning: No dwarf info found in the vmlinux - "
"please rebuild kernel with CONFIG_DEBUG_INFO=y.\n");
if (!need_dwarf) {
pr_debug("Trying to use symbols.\nn");
pr_debug("Trying to use symbols.\n");
return 0;
}
}
......@@ -286,42 +287,49 @@ static int get_real_path(const char *raw_path, const char *comp_dir,
#define LINEBUF_SIZE 256
#define NR_ADDITIONAL_LINES 2
static int show_one_line(FILE *fp, int l, bool skip, bool show_num)
static int __show_one_line(FILE *fp, int l, bool skip, bool show_num)
{
char buf[LINEBUF_SIZE];
const char *color = PERF_COLOR_BLUE;
const char *color = show_num ? "" : PERF_COLOR_BLUE;
const char *prefix = NULL;
if (fgets(buf, LINEBUF_SIZE, fp) == NULL)
goto error;
if (!skip) {
if (show_num)
fprintf(stdout, "%7d %s", l, buf);
else
color_fprintf(stdout, color, " %s", buf);
}
while (strlen(buf) == LINEBUF_SIZE - 1 &&
buf[LINEBUF_SIZE - 2] != '\n') {
do {
if (fgets(buf, LINEBUF_SIZE, fp) == NULL)
goto error;
if (!skip) {
if (show_num)
fprintf(stdout, "%s", buf);
else
color_fprintf(stdout, color, "%s", buf);
if (skip)
continue;
if (!prefix) {
prefix = show_num ? "%7d " : " ";
color_fprintf(stdout, color, prefix, l);
}
}
color_fprintf(stdout, color, "%s", buf);
return 0;
} while (strchr(buf, '\n') == NULL);
return 1;
error:
if (feof(fp))
if (ferror(fp)) {
pr_warning("Source file is shorter than expected.\n");
else
pr_warning("File read error: %s\n", strerror(errno));
return -1;
}
return 0;
}
return -1;
static int _show_one_line(FILE *fp, int l, bool skip, bool show_num)
{
int rv = __show_one_line(fp, l, skip, show_num);
if (rv == 0) {
pr_warning("Source file is shorter than expected.\n");
rv = -1;
}
return rv;
}
#define show_one_line_with_num(f,l) _show_one_line(f,l,false,true)
#define show_one_line(f,l) _show_one_line(f,l,false,false)
#define skip_one_line(f,l) _show_one_line(f,l,true,false)
#define show_one_line_or_eof(f,l) __show_one_line(f,l,false,false)
/*
* Show line-range always requires debuginfo to find source file and
* line number.
......@@ -370,7 +378,7 @@ int show_line_range(struct line_range *lr, const char *module)
fprintf(stdout, "<%s:%d>\n", lr->function,
lr->start - lr->offset);
else
fprintf(stdout, "<%s:%d>\n", lr->file, lr->start);
fprintf(stdout, "<%s:%d>\n", lr->path, lr->start);
fp = fopen(lr->path, "r");
if (fp == NULL) {
......@@ -379,26 +387,30 @@ int show_line_range(struct line_range *lr, const char *module)
return -errno;
}
/* Skip to starting line number */
while (l < lr->start && ret >= 0)
ret = show_one_line(fp, l++, true, false);
if (ret < 0)
goto end;
while (l < lr->start) {
ret = skip_one_line(fp, l++);
if (ret < 0)
goto end;
}
list_for_each_entry(ln, &lr->line_list, list) {
while (ln->line > l && ret >= 0)
ret = show_one_line(fp, (l++) - lr->offset,
false, false);
if (ret >= 0)
ret = show_one_line(fp, (l++) - lr->offset,
false, true);
for (; ln->line > l; l++) {
ret = show_one_line(fp, l - lr->offset);
if (ret < 0)
goto end;
}
ret = show_one_line_with_num(fp, l++ - lr->offset);
if (ret < 0)
goto end;
}
if (lr->end == INT_MAX)
lr->end = l + NR_ADDITIONAL_LINES;
while (l <= lr->end && !feof(fp) && ret >= 0)
ret = show_one_line(fp, (l++) - lr->offset, false, false);
while (l <= lr->end) {
ret = show_one_line_or_eof(fp, l++ - lr->offset);
if (ret <= 0)
break;
}
end:
fclose(fp);
return ret;
......@@ -457,7 +469,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
fd = open_vmlinux(module);
if (fd < 0) {
pr_warning("Failed to open debuginfo file.\n");
pr_warning("Failed to open debug information file.\n");
return fd;
}
......@@ -517,56 +529,87 @@ int show_available_vars(struct perf_probe_event *pevs __unused,
}
#endif
static int parse_line_num(char **ptr, int *val, const char *what)
{
const char *start = *ptr;
errno = 0;
*val = strtol(*ptr, ptr, 0);
if (errno || *ptr == start) {
semantic_error("'%s' is not a valid number.\n", what);
return -EINVAL;
}
return 0;
}
/*
* Stuff 'lr' according to the line range described by 'arg'.
* The line range syntax is described by:
*
* SRC[:SLN[+NUM|-ELN]]
* FNC[:SLN[+NUM|-ELN]]
*/
int parse_line_range_desc(const char *arg, struct line_range *lr)
{
const char *ptr;
char *tmp;
/*
* <Syntax>
* SRC:SLN[+NUM|-ELN]
* FUNC[:SLN[+NUM|-ELN]]
*/
ptr = strchr(arg, ':');
if (ptr) {
lr->start = (int)strtoul(ptr + 1, &tmp, 0);
if (*tmp == '+') {
lr->end = lr->start + (int)strtoul(tmp + 1, &tmp, 0);
lr->end--; /*
* Adjust the number of lines here.
* If the number of lines == 1, the
* the end of line should be equal to
* the start of line.
*/
} else if (*tmp == '-')
lr->end = (int)strtoul(tmp + 1, &tmp, 0);
else
lr->end = INT_MAX;
char *range, *name = strdup(arg);
int err;
if (!name)
return -ENOMEM;
lr->start = 0;
lr->end = INT_MAX;
range = strchr(name, ':');
if (range) {
*range++ = '\0';
err = parse_line_num(&range, &lr->start, "start line");
if (err)
goto err;
if (*range == '+' || *range == '-') {
const char c = *range++;
err = parse_line_num(&range, &lr->end, "end line");
if (err)
goto err;
if (c == '+') {
lr->end += lr->start;
/*
* Adjust the number of lines here.
* If the number of lines == 1, the
* the end of line should be equal to
* the start of line.
*/
lr->end--;
}
}
pr_debug("Line range is %d to %d\n", lr->start, lr->end);
err = -EINVAL;
if (lr->start > lr->end) {
semantic_error("Start line must be smaller"
" than end line.\n");
return -EINVAL;
goto err;
}
if (*tmp != '\0') {
semantic_error("Tailing with invalid character '%d'.\n",
*tmp);
return -EINVAL;
if (*range != '\0') {
semantic_error("Tailing with invalid str '%s'.\n", range);
goto err;
}
tmp = strndup(arg, (ptr - arg));
} else {
tmp = strdup(arg);
lr->end = INT_MAX;
}
if (tmp == NULL)
return -ENOMEM;
if (strchr(tmp, '.'))
lr->file = tmp;
if (strchr(name, '.'))
lr->file = name;
else
lr->function = tmp;
lr->function = name;
return 0;
err:
free(name);
return err;
}
/* Check the name is good for event/group */
......@@ -690,39 +733,40 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
/* Exclusion check */
if (pp->lazy_line && pp->line) {
semantic_error("Lazy pattern can't be used with line number.");
semantic_error("Lazy pattern can't be used with"
" line number.\n");
return -EINVAL;
}
if (pp->lazy_line && pp->offset) {
semantic_error("Lazy pattern can't be used with offset.");
semantic_error("Lazy pattern can't be used with offset.\n");
return -EINVAL;
}
if (pp->line && pp->offset) {
semantic_error("Offset can't be used with line number.");
semantic_error("Offset can't be used with line number.\n");
return -EINVAL;
}
if (!pp->line && !pp->lazy_line && pp->file && !pp->function) {
semantic_error("File always requires line number or "
"lazy pattern.");
"lazy pattern.\n");
return -EINVAL;
}
if (pp->offset && !pp->function) {
semantic_error("Offset requires an entry function.");
semantic_error("Offset requires an entry function.\n");
return -EINVAL;
}
if (pp->retprobe && !pp->function) {
semantic_error("Return probe requires an entry function.");
semantic_error("Return probe requires an entry function.\n");
return -EINVAL;
}
if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) {
semantic_error("Offset/Line/Lazy pattern can't be used with "
"return probe.");
"return probe.\n");