Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 182355
b: refs/heads/master
c: 631c9de
h: refs/heads/master
i:
  182353: 04115d5
  182351: 41703c8
v: v3
  • Loading branch information
Masami Hiramatsu authored and Ingo Molnar committed Jan 13, 2010
1 parent 6e23c78 commit e2fe7d9
Show file tree
Hide file tree
Showing 7 changed files with 403 additions and 19 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 6964cd2c8efe6e048401f1fe3952a06c563c34c1
refs/heads/master: 631c9def804b2c92b5cca04fb9ff7b5df9e35094
20 changes: 20 additions & 0 deletions trunk/tools/perf/Documentation/perf-probe.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ or
'perf probe' [options] --del='[GROUP:]EVENT' [...]
or
'perf probe' --list
or
'perf probe' --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'

DESCRIPTION
-----------
Expand Down Expand Up @@ -45,6 +47,11 @@ OPTIONS
--list::
List up current probe events.

-L::
--line=::
Show source code lines which can be probed. This needs an argument
which specifies a range of the source code.

PROBE SYNTAX
------------
Probe points are defined by following syntax.
Expand All @@ -56,6 +63,19 @@ Probe points are defined by following syntax.
It is also possible to specify a probe point by the source line number by using 'SRC:ALN' syntax, where 'SRC' is the source file path and 'ALN' is the line number.
'ARG' specifies the arguments of this probe point. You can use the name of local variable, or kprobe-tracer argument format (e.g. $retval, %ax, etc).

LINE SYNTAX
-----------
Line range is descripted by following syntax.

"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
probe syntax, 'SRC' means the source file path, 'ALN' is start line number,
and 'ALN2' is end line number in the file. It is also possible to specify how
many lines to show by using 'NUM'.
So, "source.c:100-120" shows lines between 100th to l20th in source.c file. And "func:10+20" shows 20 lines from 10th line of func function.

SEE ALSO
--------
linkperf:perf-trace[1], linkperf:perf-record[1]
76 changes: 63 additions & 13 deletions trunk/tools/perf/builtin-probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,13 @@ static struct {
bool need_dwarf;
bool list_events;
bool force_add;
bool show_lines;
int nr_probe;
struct probe_point probes[MAX_PROBES];
struct strlist *dellist;
struct perf_session *psession;
struct map *kmap;
struct line_range line_range;
} session;


Expand Down Expand Up @@ -116,6 +118,15 @@ static int opt_del_probe_event(const struct option *opt __used,
return 0;
}

static int opt_show_lines(const struct option *opt __used,
const char *str, int unset __used)
{
if (str)
parse_line_range_desc(str, &session.line_range);
INIT_LIST_HEAD(&session.line_range.line_list);
session.show_lines = true;
return 0;
}
/* Currently just checking function name from symbol map */
static void evaluate_probe_point(struct probe_point *pp)
{
Expand Down Expand Up @@ -144,6 +155,7 @@ static const char * const probe_usage[] = {
"perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]",
"perf probe [<options>] --del '[GROUP:]EVENT' ...",
"perf probe --list",
"perf probe --line 'LINEDESC'",
NULL
};

Expand Down Expand Up @@ -182,9 +194,32 @@ static const struct option options[] = {
opt_add_probe_event),
OPT_BOOLEAN('f', "force", &session.force_add, "forcibly add events"
" with existing name"),
#ifndef NO_LIBDWARF
OPT_CALLBACK('L', "line", NULL,
"FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]",
"Show source code lines.", opt_show_lines),
#endif
OPT_END()
};

/* Initialize symbol maps for vmlinux */
static void init_vmlinux(void)
{
symbol_conf.sort_by_name = true;
if (symbol_conf.vmlinux_name == NULL)
symbol_conf.try_vmlinux_path = true;
else
pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name);
if (symbol__init() < 0)
die("Failed to init symbol map.");
session.psession = perf_session__new(NULL, O_WRONLY, false);
if (session.psession == NULL)
die("Failed to init perf_session.");
session.kmap = session.psession->vmlinux_maps[MAP__FUNCTION];
if (!session.kmap)
die("Could not find kernel map.\n");
}

int cmd_probe(int argc, const char **argv, const char *prefix __used)
{
int i, ret;
Expand All @@ -203,7 +238,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
parse_probe_event_argv(argc, argv);
}

if ((!session.nr_probe && !session.dellist && !session.list_events))
if ((!session.nr_probe && !session.dellist && !session.list_events &&
!session.show_lines))
usage_with_options(probe_usage, options);

if (debugfs_valid_mountpoint(debugfs_path) < 0)
Expand All @@ -215,29 +251,43 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
" --add/--del.\n");
usage_with_options(probe_usage, options);
}
if (session.show_lines) {
pr_warning(" Error: Don't use --list with --line.\n");
usage_with_options(probe_usage, options);
}
show_perf_probe_events();
return 0;
}

#ifndef NO_LIBDWARF
if (session.show_lines) {
if (session.nr_probe != 0 || session.dellist) {
pr_warning(" Error: Don't use --line with"
" --add/--del.\n");
usage_with_options(probe_usage, options);
}
init_vmlinux();
fd = open_vmlinux();
if (fd < 0)
die("Could not open debuginfo file.");
ret = find_line_range(fd, &session.line_range);
if (ret <= 0)
die("Source line is not found.\n");
close(fd);
show_line_range(&session.line_range);
return 0;
}
#endif

if (session.dellist) {
del_trace_kprobe_events(session.dellist);
strlist__delete(session.dellist);
if (session.nr_probe == 0)
return 0;
}

/* Initialize symbol maps for vmlinux */
symbol_conf.sort_by_name = true;
if (symbol_conf.vmlinux_name == NULL)
symbol_conf.try_vmlinux_path = true;
if (symbol__init() < 0)
die("Failed to init symbol map.");
session.psession = perf_session__new(NULL, O_WRONLY, false);
if (session.psession == NULL)
die("Failed to init perf_session.");
session.kmap = session.psession->vmlinux_maps[MAP__FUNCTION];
if (!session.kmap)
die("Could not find kernel map.\n");
/* Add probes */
init_vmlinux();

if (session.need_dwarf)
#ifdef NO_LIBDWARF
Expand Down
100 changes: 100 additions & 0 deletions trunk/tools/perf/util/probe-event.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "strlist.h"
#include "debug.h"
#include "cache.h"
#include "color.h"
#include "parse-events.h" /* For debugfs_path */
#include "probe-event.h"

Expand All @@ -63,6 +64,42 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
return ret;
}

void 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 = (unsigned int)strtoul(ptr + 1, &tmp, 0);
if (*tmp == '+')
lr->end = lr->start + (unsigned int)strtoul(tmp + 1,
&tmp, 0);
else if (*tmp == '-')
lr->end = (unsigned int)strtoul(tmp + 1, &tmp, 0);
else
lr->end = 0;
pr_debug("Line range is %u to %u\n", lr->start, lr->end);
if (lr->end && lr->start > lr->end)
semantic_error("Start line must be smaller"
" than end line.");
if (*tmp != '\0')
semantic_error("Tailing with invalid character '%d'.",
*tmp);
tmp = strndup(arg, (ptr - arg));
} else
tmp = strdup(arg);

if (strchr(tmp, '.'))
lr->file = tmp;
else
lr->function = tmp;
}

/* Check the name is good for event/group */
static bool check_event_name(const char *name)
{
Expand Down Expand Up @@ -678,3 +715,66 @@ void del_trace_kprobe_events(struct strlist *dellist)
close(fd);
}

#define LINEBUF_SIZE 256

static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num)
{
char buf[LINEBUF_SIZE];
const char *color = PERF_COLOR_BLUE;

if (fgets(buf, LINEBUF_SIZE, fp) == NULL)
goto error;
if (!skip) {
if (show_num)
fprintf(stdout, "%7u %s", l, buf);
else
color_fprintf(stdout, color, " %s", buf);
}

while (strlen(buf) == LINEBUF_SIZE - 1 &&
buf[LINEBUF_SIZE - 2] != '\n') {
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);
}
}
return;
error:
if (feof(fp))
die("Source file is shorter than expected.");
else
die("File read error: %s", strerror(errno));
}

void show_line_range(struct line_range *lr)
{
unsigned int l = 1;
struct line_node *ln;
FILE *fp;

setup_pager();

if (lr->function)
fprintf(stdout, "<%s:%d>\n", lr->function,
lr->start - lr->offset);
else
fprintf(stdout, "<%s:%d>\n", lr->file, lr->start);

fp = fopen(lr->path, "r");
if (fp == NULL)
die("Failed to open %s: %s", lr->path, strerror(errno));
/* Skip to starting line number */
while (l < lr->start)
show_one_line(fp, l++, true, false);

list_for_each_entry(ln, &lr->line_list, list) {
while (ln->line > l)
show_one_line(fp, (l++) - lr->offset, false, false);
show_one_line(fp, (l++) - lr->offset, false, true);
}
fclose(fp);
}
2 changes: 2 additions & 0 deletions trunk/tools/perf/util/probe-event.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "probe-finder.h"
#include "strlist.h"

extern void parse_line_range_desc(const char *arg, struct line_range *lr);
extern void parse_perf_probe_event(const char *str, struct probe_point *pp,
bool *need_dwarf);
extern int synthesize_perf_probe_point(struct probe_point *pp);
Expand All @@ -15,6 +16,7 @@ extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes,
bool force_add);
extern void del_trace_kprobe_events(struct strlist *dellist);
extern void show_perf_probe_events(void);
extern void show_line_range(struct line_range *lr);

/* Maximum index number of event-name postfix */
#define MAX_EVENT_INDEX 1024
Expand Down
Loading

0 comments on commit e2fe7d9

Please sign in to comment.