linux/tools/perf/builtin-probe.c
Ian Munsie c055564217 perf: Fix endianness argument compatibility with OPT_BOOLEAN() and introduce OPT_INCR()
Parsing an option from the command line with OPT_BOOLEAN on a
bool data type would not work on a big-endian machine due to the
manner in which the boolean was being cast into an int and
incremented. For example, running 'perf probe --list' on a
PowerPC machine would fail to properly set the list_events bool
and would therefore print out the usage information and
terminate.

This patch makes OPT_BOOLEAN work as expected with a bool
datatype. For cases where the original OPT_BOOLEAN was
intentionally being used to increment an int each time it was
passed in on the command line, this patch introduces OPT_INCR
with the old behaviour of OPT_BOOLEAN (the verbose variable is
currently the only such example of this).

I have reviewed every use of OPT_BOOLEAN to verify that a true
C99 bool was passed. Where integers were used, I verified that
they were only being used for boolean logic and changed them to
bools to ensure that they would not be mistakenly used as ints.
The major exception was the verbose variable which now uses
OPT_INCR instead of OPT_BOOLEAN.

Signed-off-by: Ian Munsie <imunsie@au.ibm.com>
Acked-by: David S. Miller <davem@davemloft.net>
Cc: <stable@kernel.org> # NOTE: wont apply to .3[34].x cleanly, please backport
Cc: Git development list <git@vger.kernel.org>
Cc: Ian Munsie <imunsie@au1.ibm.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Eric B Munson <ebmunson@us.ibm.com>
Cc: Valdis.Kletnieks@vt.edu
Cc: WANG Cong <amwang@redhat.com>
Cc: Thiago Farina <tfransosi@gmail.com>
Cc: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Xiao Guangrong <xiaoguangrong@cn.fujitsu.com>
Cc: Jaswinder Singh Rajput <jaswinderrajput@gmail.com>
Cc: Arjan van de Ven <arjan@linux.intel.com>
Cc: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Tom Zanussi <tzanussi@gmail.com>
Cc: Anton Blanchard <anton@samba.org>
Cc: John Kacur <jkacur@redhat.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
LKML-Reference: <1271147857-11604-1-git-send-email-imunsie@au.ibm.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2010-04-14 11:26:44 +02:00

236 lines
6.3 KiB
C

/*
* builtin-probe.c
*
* Builtin probe command: Set up probe events by C expression
*
* Written by Masami Hiramatsu <mhiramat@redhat.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#define _GNU_SOURCE
#include <sys/utsname.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#undef _GNU_SOURCE
#include "perf.h"
#include "builtin.h"
#include "util/util.h"
#include "util/strlist.h"
#include "util/symbol.h"
#include "util/debug.h"
#include "util/debugfs.h"
#include "util/parse-options.h"
#include "util/parse-events.h" /* For debugfs_path */
#include "util/probe-finder.h"
#include "util/probe-event.h"
#define MAX_PATH_LEN 256
/* Session management structure */
static struct {
bool list_events;
bool force_add;
bool show_lines;
int nevents;
struct perf_probe_event events[MAX_PROBES];
struct strlist *dellist;
struct line_range line_range;
} params;
/* Parse an event definition. Note that any error must die. */
static void parse_probe_event(const char *str)
{
struct perf_probe_event *pev = &params.events[params.nevents];
pr_debug("probe-definition(%d): %s\n", params.nevents, str);
if (++params.nevents == MAX_PROBES)
die("Too many probes (> %d) are specified.", MAX_PROBES);
/* Parse a perf-probe command into event */
parse_perf_probe_command(str, pev);
pr_debug("%d arguments\n", pev->nargs);
}
static void parse_probe_event_argv(int argc, const char **argv)
{
int i, len;
char *buf;
/* Bind up rest arguments */
len = 0;
for (i = 0; i < argc; i++)
len += strlen(argv[i]) + 1;
buf = xzalloc(len + 1);
len = 0;
for (i = 0; i < argc; i++)
len += sprintf(&buf[len], "%s ", argv[i]);
parse_probe_event(buf);
free(buf);
}
static int opt_add_probe_event(const struct option *opt __used,
const char *str, int unset __used)
{
if (str)
parse_probe_event(str);
return 0;
}
static int opt_del_probe_event(const struct option *opt __used,
const char *str, int unset __used)
{
if (str) {
if (!params.dellist)
params.dellist = strlist__new(true, NULL);
strlist__add(params.dellist, str);
}
return 0;
}
#ifdef DWARF_SUPPORT
static int opt_show_lines(const struct option *opt __used,
const char *str, int unset __used)
{
if (str)
parse_line_range_desc(str, &params.line_range);
INIT_LIST_HEAD(&params.line_range.line_list);
params.show_lines = true;
return 0;
}
#endif
static const char * const probe_usage[] = {
"perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]",
"perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]",
"perf probe [<options>] --del '[GROUP:]EVENT' ...",
"perf probe --list",
#ifdef DWARF_SUPPORT
"perf probe --line 'LINEDESC'",
#endif
NULL
};
static const struct option options[] = {
OPT_INCR('v', "verbose", &verbose,
"be more verbose (show parsed arguments, etc)"),
OPT_BOOLEAN('l', "list", &params.list_events,
"list up current probe events"),
OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.",
opt_del_probe_event),
OPT_CALLBACK('a', "add", NULL,
#ifdef DWARF_SUPPORT
"[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT"
" [ARG ...]",
#else
"[EVENT=]FUNC[+OFF|%return] [ARG ...]",
#endif
"probe point definition, where\n"
"\t\tGROUP:\tGroup name (optional)\n"
"\t\tEVENT:\tEvent name\n"
"\t\tFUNC:\tFunction name\n"
"\t\tOFF:\tOffset from function entry (in byte)\n"
"\t\t%return:\tPut the probe at function return\n"
#ifdef DWARF_SUPPORT
"\t\tSRC:\tSource code path\n"
"\t\tRL:\tRelative line number from function entry.\n"
"\t\tAL:\tAbsolute line number in file.\n"
"\t\tPT:\tLazy expression of line code.\n"
"\t\tARG:\tProbe argument (local variable name or\n"
"\t\t\tkprobe-tracer argument format.)\n",
#else
"\t\tARG:\tProbe argument (kprobe-tracer argument format.)\n",
#endif
opt_add_probe_event),
OPT_BOOLEAN('f', "force", &params.force_add, "forcibly add events"
" with existing name"),
#ifdef DWARF_SUPPORT
OPT_CALLBACK('L', "line", NULL,
"FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]",
"Show source code lines.", opt_show_lines),
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"),
#endif
OPT__DRY_RUN(&probe_event_dry_run),
OPT_END()
};
int cmd_probe(int argc, const char **argv, const char *prefix __used)
{
argc = parse_options(argc, argv, options, probe_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (argc > 0) {
if (strcmp(argv[0], "-") == 0) {
pr_warning(" Error: '-' is not supported.\n");
usage_with_options(probe_usage, options);
}
parse_probe_event_argv(argc, argv);
}
if ((!params.nevents && !params.dellist && !params.list_events &&
!params.show_lines))
usage_with_options(probe_usage, options);
if (debugfs_valid_mountpoint(debugfs_path) < 0)
die("Failed to find debugfs path.");
if (params.list_events) {
if (params.nevents != 0 || params.dellist) {
pr_warning(" Error: Don't use --list with"
" --add/--del.\n");
usage_with_options(probe_usage, options);
}
if (params.show_lines) {
pr_warning(" Error: Don't use --list with --line.\n");
usage_with_options(probe_usage, options);
}
show_perf_probe_events();
return 0;
}
#ifdef DWARF_SUPPORT
if (params.show_lines) {
if (params.nevents != 0 || params.dellist) {
pr_warning(" Error: Don't use --line with"
" --add/--del.\n");
usage_with_options(probe_usage, options);
}
show_line_range(&params.line_range);
return 0;
}
#endif
if (params.dellist) {
del_perf_probe_events(params.dellist);
strlist__delete(params.dellist);
if (params.nevents == 0)
return 0;
}
add_perf_probe_events(params.events, params.nevents, params.force_add);
return 0;
}