From: Masami Hiramatsu <mhiramat@redhat.com>
To: Ingo Molnar <mingo@elte.hu>, Steven Rostedt <rostedt@goodmis.org>,
lkml <linux-kernel@vger.kernel.org>
Cc: "Ananth N Mavinakayanahalli" <ananth@in.ibm.com>,
"Avi Kivity" <avi@redhat.com>, "Andi Kleen" <ak@linux.intel.com>,
"Christoph Hellwig" <hch@infradead.org>,
"Frank Ch. Eigler" <fche@redhat.com>,
"Frederic Weisbecker" <fweisbec@gmail.com>,
"H. Peter Anvin" <hpa@zytor.com>,
"Jason Baron" <jbaron@redhat.com>,
"Jim Keniston" <jkenisto@us.ibm.com>,
"K.Prasad" <prasad@linux.vnet.ibm.com>,
"Lai Jiangshan" <laijs@cn.fujitsu.com>,
"Li Zefan" <lizf@cn.fujitsu.com>,
PrzemysławPawełczyk <przemyslaw@pawelczyk.it>,
"Roland McGrath" <roland@redhat.com>,
"Sam Ravnborg" <sam@ravnborg.org>,
"Srikar Dronamraju" <srikar@linux.vnet.ibm.com>,
"Tom Zanussi" <tzanussi@gmail.com>,
"Vegard Nossum" <vegard.nossum@gmail.com>,
systemtap <systemtap@sources.redhat.com>,
kvm <kvm@vger.kernel.org>,
DLE <dle-develop@lists.sourceforge.net>
Subject: [TOOL] c2kpe: C expression to kprobe event format converter
Date: Thu, 13 Aug 2009 16:59:19 -0400 [thread overview]
Message-ID: <4A847EA7.20307@redhat.com> (raw)
In-Reply-To: <20090813203403.31965.20973.stgit@localhost.localdomain>
[-- Attachment #1: Type: text/plain, Size: 2014 bytes --]
This program converts probe point in C expression to kprobe event
format for kprobe-based event tracer. This helps to define kprobes
events by C source line number or function name, and local variable
name. Currently, this supports only x86(32/64) kernels.
Compile
--------
Before compilation, please install libelf and libdwarf development
packages.
(e.g. elfutils-libelf-devel and libdwarf-devel on Fedora)
$ gcc -Wall -lelf -ldwarf c2kpe.c -o c2kpe
Synopsis
--------
$ c2kpe [options] FUNCTION[+OFFS][@SRC] [VAR [VAR ...]]
or
$ c2kpe [options] @SRC:LINE [VAR [VAR ...]]
FUNCTION: Probing function name.
OFFS: Offset in bytes.
SRC: Source file path.
LINE: Line number
VAR: Local variable name.
options:
-r KREL Kernel release version (e.g. 2.6.31-rc5)
-m DEBUGINFO Dwarf-format binary file (vmlinux or kmodule)
Example
-------
$ c2kpe sys_read fd buf count
sys_read+0 %di %si %dx
$ c2kpe @mm/filemap.c:339 inode pos
sync_page_range+125 -48(%bp) %r14
Example with kprobe-tracer
--------------------------
Since C expression may be converted multiple results, I recommend to use
readline.
$ c2kpe sys_read fd buf count | while read i; do \
echo "p $i" > $DEBUGFS/tracing/kprobe_events ;\
done
Note
----
- This requires a kernel compiled with CONFIG_DEBUG_INFO.
- Specifying @SRC speeds up c2kpe, because we can skip CUs which don't
include specified SRC file.
- c2kpe doesn't check whether the offset byte is correctly on the
instruction boundary. I recommend you to use @SRC:LINE expression for
tracing function body.
- This tool doesn't search kmodule file. You need to specify kmodule
file if you want to probe it.
TODO
----
- Fix bugs.
- Support multiple probepoints from stdin.
- Better kmodule support.
- Use elfutils-libdw?
- Merge into trace-cmd or perf-tools?
--
Masami Hiramatsu
Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division
e-mail: mhiramat@redhat.com
[-- Attachment #2: c2kpe.c --]
[-- Type: text/plain, Size: 21095 bytes --]
/*
* c2kpe : C expression to kprobe event converter
*
* 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.
*
*/
#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 <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <libdwarf/dwarf.h>
#include <libdwarf/libdwarf.h>
/* Default vmlinux search paths */
#define NR_SEARCH_PATH 2
const char *default_search_path[NR_SEARCH_PATH] = {
"/lib/modules/%s/build/vmlinux", /* Custom build kernel */
"/usr/lib/debug/lib/modules/%s/vmlinux", /* Red Hat debuginfo */
};
#define _stringify(n) #n
#define stringify(n) _stringify(n)
#ifdef DEBUG
#define debug(fmt ...) \
fprintf(stderr, "DBG(" __FILE__ ":" stringify(__LINE__) "): " fmt)
#else
#define debug(fmt ...) do {} while (0)
#endif
#define ERR_IF(cnd) \
do { if (cnd) { \
fprintf(stderr, "Error (" __FILE__ ":" stringify(__LINE__) \
"): " stringify(cnd) "\n"); \
exit(1); \
}} while (0)
#define MAX_PATH_LEN 256
/* Dwarf_Die Linkage to parent Die */
struct die_link {
struct die_link *parent; /* Parent die */
Dwarf_Die die; /* Current die */
};
#define X86_32_MAX_REGS 8
const char *x86_32_regs_table[X86_32_MAX_REGS] = {
"%ax",
"%cx",
"%dx",
"%bx",
"sa", /* Stack address */
"%bp",
"%si",
"%di",
};
#define X86_64_MAX_REGS 16
const char *x86_64_regs_table[X86_64_MAX_REGS] = {
"%ax",
"%dx",
"%cx",
"%bx",
"%si",
"%di",
"%bp",
"%sp",
"%r8",
"%r9",
"%r10",
"%r11",
"%r12",
"%r13",
"%r14",
"%r15",
};
/* TODO: switching by dwarf address size */
#ifdef __x86_64__
#define ARCH_MAX_REGS X86_64_MAX_REGS
#define arch_regs_table x86_64_regs_table
#else
#define ARCH_MAX_REGS X86_32_MAX_REGS
#define arch_regs_table x86_32_regs_table
#endif
/* Return architecture dependent register string */
static inline const char *get_arch_regstr(unsigned int n)
{
return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL;
}
struct probe_finder {
/* Inputs */
char *file; /* File name */
int line; /* Line number */
char *function; /* Function name */
int offset; /* Offset bytes */
Dwarf_Addr addr; /* Address */
int nr_args; /* Number of arguments */
char **args; /* Arguments */
/* Working area */
Dwarf_Addr cu_base; /* Current CU base address */
Dwarf_Locdesc fbloc; /* Location of Current Frame Base */
Dwarf_Unsigned fno; /* File number */
Dwarf_Off inl_offs; /* Inline offset */
const char *var; /* Current variable name */
/* Output */
int found; /* Number of found probe points */
};
/* Find a probe point */
static void find_probepoint(struct probe_finder *pf);
/* Session management structure */
static struct {
char *kver;
char *modpath;
int maxprobe;
struct probe_finder finder;
Dwarf_Debug dbg;
Dwarf_Error err;
int nfound;
} session;
static void usage(const char *msg)
{
if (msg)
printf("%s\n\n", msg);
printf("c2kpe: C expression to kprobe event converter\n");
printf("Usage: c2kpe [-r KREL] [-m mod|vmlinux] FUNC|SRC [ARG...]\n");
printf(" FUNC:\tFUNCNAME[+OFFS_BYTE][@SRCPATH]\n");
printf(" SRC:\t@SRCPATH:LINE\n");
printf(" ARG:\tLocal variable name\n");
/* TODO: @global, $vars, $params */
exit(0);
}
static void semantic_error(const char *msg)
{
fprintf(stderr, "Semantic error: %s\n", msg);
exit(1);
}
static void parse_probe_point(int argc, char *argv[], struct probe_finder *pf)
{
char *arg;
char *ptr;
arg = argv[0];
if (arg[0] == '@') {
/* Source Line */
arg++;
ptr = strchr(arg, ':');
if (!ptr || ptr[1] == '\0')
semantic_error("Line number is required.");
*ptr++ = '\0';
pf->file = arg;
if (strlen(arg) == 0)
semantic_error("No file name.");
pf->line = atoi(ptr);
debug("file:%s line:%d\n", pf->file, pf->line);
} else {
/* Function name */
pf->function = arg;
ptr = strchr(arg, '+');
if (ptr) {
if (ptr[1] == '\0' || ptr[1] == '@')
semantic_error("Offset is required.");
*ptr++ = '\0';
pf->offset = atoi(ptr);
arg = ptr;
}
ptr = strchr(arg, '@');
if (ptr) {
*ptr++ = '\0';
pf->file = ptr;
}
debug("fname:%s file:%s offset:%d\n",
pf->function, pf->file, pf->offset);
}
pf->nr_args = argc - 1;
if (pf->nr_args > 0)
pf->args = &argv[1];
debug("%d arguments\n", pf->nr_args);
}
static void parse_args(int argc, char *argv[])
{
int opt;
if (argc < 1)
usage("Need a probe point.");
while ((opt = getopt(argc, argv, "r:m:n:")) != -1) {
switch (opt) {
case 'r':
session.kver = optarg;
break;
case 'm':
session.modpath = optarg;
break;
case 'n':
session.maxprobe = atoi(optarg);
break;
default:
usage("Unexpected option found.");
}
}
if (optind >= argc)
usage("Need a probe point.");
parse_probe_point(argc - optind, &argv[optind], &session.finder);
}
static int open_default_vmlinux(const char *kver)
{
struct utsname uts;
char fname[MAX_PATH_LEN];
int fd, ret, i;
if (!kver) {
ret = uname(&uts);
if (ret) {
debug("uname() failed.\n");
return -errno;
}
kver = uts.release;
}
for (i = 0; i < NR_SEARCH_PATH; i++) {
ret = snprintf(fname, MAX_PATH_LEN,
default_search_path[i], kver);
if (ret >= MAX_PATH_LEN || ret < 0) {
debug("Filename(%d,%s) is too long.\n", i, uts.release);
errno = E2BIG;
return -E2BIG;
}
debug("try to open %s\n", fname);
fd = open(fname, O_RDONLY);
if (fd >= 0)
break;
}
return fd;
}
int main(int argc, char *argv[])
{
int fd, ret;
parse_args(argc, argv);
if (session.modpath)
fd = open(session.modpath, O_RDONLY);
else
fd = open_default_vmlinux(session.kver);
if (fd < 0) {
perror("file open");
exit(1);
}
/* TODO: handle errors */
ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &session.dbg, &session.err);
if (ret != DW_DLV_OK) {
fprintf(stderr, "Failed to call dwarf_init(). "
"Maybe, not a dwarf file?\n");
exit(1);
}
find_probepoint(&session.finder);
ret = dwarf_finish(session.dbg, &session.err);
ERR_IF(ret != DW_DLV_OK);
close(fd);
return 0;
}
/*---------------------
* Dwarf Analysys Part
*---------------------*/
/*
* Compare the tail of two strings.
* Return 0 if whole of either string is same as another's tail part.
*/
static int strtailcmp(const char *s1, const char *s2)
{
int i1 = strlen(s1);
int i2 = strlen(s2);
while (--i1 > 0 && --i2 > 0) {
if (s1[i1] != s2[i2])
return s1[i1] - s2[i2];
}
return 0;
}
/* Find the fileno of the target file. */
static Dwarf_Unsigned die_get_fileno(Dwarf_Die cu_die, const char *fname)
{
Dwarf_Signed cnt, i;
Dwarf_Unsigned found = 0;
char **srcs;
int ret;
if (!fname)
return 0;
ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &session.err);
if (ret == DW_DLV_OK) {
for (i = 0; i < cnt && !found; i++) {
if (strtailcmp(srcs[i], fname) == 0)
found = i + 1;
dwarf_dealloc(session.dbg, srcs[i], DW_DLA_STRING);
}
for (;i < cnt; i++)
dwarf_dealloc(session.dbg, srcs[i], DW_DLA_STRING);
dwarf_dealloc(session.dbg, srcs, DW_DLA_LIST);
}
if (found) debug("found fno: %d\n", (int)found);
return found;
}
/* Compare diename and tname */
static int die_compare_name(Dwarf_Die die, const char *tname)
{
char *name;
int ret;
ret = dwarf_diename(die, &name, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (ret == DW_DLV_OK) {
//debug("diename: %s\n", name);
ret = strcmp(tname, name);
dwarf_dealloc(session.dbg, name, DW_DLA_STRING);
} else
ret = -1;
return ret;
}
/* Check the address is in the subprogram(function). */
static int die_within_subprogram(Dwarf_Die sp_die, Dwarf_Addr addr,
Dwarf_Signed *offs)
{
Dwarf_Addr lopc, hipc;
int ret;
ret = dwarf_lowpc(sp_die, &lopc, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (ret == DW_DLV_NO_ENTRY)
return 0;
ret = dwarf_highpc(sp_die, &hipc, &session.err);
ERR_IF(ret != DW_DLV_OK);
if (lopc <= addr && addr < hipc) {
*offs = addr - lopc;
return 1;
} else
return 0;
}
/* Check the die is inlined function */
static Dwarf_Bool die_inlined_subprogram(Dwarf_Die die)
{
/* TODO: check strictly */
Dwarf_Bool inl;
int ret;
ret = dwarf_hasattr(die, DW_AT_inline, &inl, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
return inl;
}
/* Get the offset of abstruct_origin */
static Dwarf_Off die_get_abstract_origin(Dwarf_Die die)
{
Dwarf_Attribute attr;
Dwarf_Off cu_offs;
int ret;
ret = dwarf_attr(die, DW_AT_abstract_origin, &attr, &session.err);
ERR_IF(ret != DW_DLV_OK);
ret = dwarf_formref(attr, &cu_offs, &session.err);
ERR_IF(ret != DW_DLV_OK);
dwarf_dealloc(session.dbg, attr, DW_DLA_ATTR);
return cu_offs;
}
/* Get entry pc(or low pc, 1st entry of ranges) of the die */
static Dwarf_Addr die_get_entrypc(Dwarf_Die die)
{
Dwarf_Attribute attr;
Dwarf_Addr addr;
Dwarf_Off offs;
Dwarf_Ranges *ranges;
Dwarf_Signed cnt;
int ret;
/* Try to get entry pc */
ret = dwarf_attr(die, DW_AT_entry_pc, &attr, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (ret == DW_DLV_OK) {
ret = dwarf_formaddr(attr, &addr, &session.err);
ERR_IF(ret != DW_DLV_OK);
dwarf_dealloc(session.dbg, attr, DW_DLA_ATTR);
return addr;
}
/* Try to get low pc */
ret = dwarf_lowpc(die, &addr, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (ret == DW_DLV_OK)
return addr;
/* Try to get ranges */
ret = dwarf_attr(die, DW_AT_ranges, &attr, &session.err);
ERR_IF(ret != DW_DLV_OK);
ret = dwarf_formref(attr, &offs, &session.err);
ERR_IF(ret != DW_DLV_OK);
ret = dwarf_get_ranges(session.dbg, offs, &ranges, &cnt, NULL,
&session.err);
ERR_IF(ret != DW_DLV_OK);
addr = ranges[0].dwr_addr1;
dwarf_ranges_dealloc(session.dbg, ranges, cnt);
return addr;
}
/*
* Search a Die from Die tree.
* Note: cur_link->die should be deallocated in this function.
*/
static int __search_die_tree(struct die_link *cur_link,
int (*die_cb)(struct die_link *, void *),
void *data)
{
Dwarf_Die new_die;
struct die_link link;
int ret;
if (!die_cb)
return 0;
/* Check current die */
while (!(ret = die_cb(cur_link, data))) {
/* Check child die */
ret = dwarf_child(cur_link->die, &new_die, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (ret == DW_DLV_OK) {
link.parent = cur_link;
link.die = new_die;
ret = __search_die_tree(&link, die_cb, data);
if (ret)
break;
}
/* Move to next sibling */
ret = dwarf_siblingof(session.dbg, cur_link->die, &new_die,
&session.err);
ERR_IF(ret == DW_DLV_ERROR);
dwarf_dealloc(session.dbg, cur_link->die, DW_DLA_DIE);
cur_link->die = new_die;
if (ret == DW_DLV_NO_ENTRY)
return 0;
}
dwarf_dealloc(session.dbg, cur_link->die, DW_DLA_DIE);
return ret;
}
/* Search a die in its children's die tree */
static int search_die_from_children(Dwarf_Die parent_die,
int (*die_cb)(struct die_link *, void *),
void *data)
{
struct die_link link;
int ret;
link.parent = NULL;
ret = dwarf_child(parent_die, &link.die, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (ret == DW_DLV_OK)
return __search_die_tree(&link, die_cb, data);
else
return 0;
}
/* Find a locdesc corresponding to the address */
static int attr_get_locdesc(Dwarf_Attribute attr, Dwarf_Locdesc *desc,
Dwarf_Addr addr)
{
Dwarf_Signed lcnt;
Dwarf_Locdesc **llbuf;
int ret, i;
ret = dwarf_loclist_n(attr, &llbuf, &lcnt, &session.err);
ERR_IF(ret != DW_DLV_OK);
ret = DW_DLV_NO_ENTRY;
for (i = 0; i < lcnt; ++i) {
if (llbuf[i]->ld_lopc <= addr &&
llbuf[i]->ld_hipc > addr ) {
memcpy(desc, llbuf[i], sizeof(Dwarf_Locdesc));
desc->ld_s =
malloc(sizeof(Dwarf_Loc) * llbuf[i]->ld_cents);
ERR_IF(desc->ld_s == NULL);
memcpy(desc->ld_s, llbuf[i]->ld_s,
sizeof(Dwarf_Loc) * llbuf[i]->ld_cents);
ret = DW_DLV_OK;
break;
}
dwarf_dealloc(session.dbg, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK);
dwarf_dealloc(session.dbg, llbuf[i], DW_DLA_LOCDESC);
}
/* Releasing loop */
for (; i < lcnt; ++i) {
dwarf_dealloc(session.dbg, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK);
dwarf_dealloc(session.dbg, llbuf[i], DW_DLA_LOCDESC);
}
dwarf_dealloc(session.dbg, llbuf, DW_DLA_LIST);
return ret;
}
/*---------------------------------
* Probe finder related functions
*-------------------------------*/
/* Show a location */
static void show_location(Dwarf_Loc *loc, struct probe_finder *pf)
{
Dwarf_Small op;
Dwarf_Unsigned regn;
Dwarf_Signed offs;
int deref = 0;
const char *regs;
op = loc->lr_atom;
/* If this is based on frame buffer, set the offset */
if (op == DW_OP_fbreg) {
deref = 1;
offs = (Dwarf_Signed)loc->lr_number;
op = pf->fbloc.ld_s[0].lr_atom;
loc = &pf->fbloc.ld_s[0];
} else
offs = 0;
if (op >= DW_OP_breg0 && op <= DW_OP_breg31) {
regn = op - DW_OP_breg0;
offs += (Dwarf_Signed)loc->lr_number;
deref = 1;
} else if (op >= DW_OP_reg0 && op <= DW_OP_reg31) {
regn = op - DW_OP_reg0;
} else if (op == DW_OP_bregx) {
regn = loc->lr_number;
offs += (Dwarf_Signed)loc->lr_number2;
deref = 1;
} else if (op == DW_OP_regx) {
regn = loc->lr_number;
} else {
fprintf(stderr, "Error: Dwarf_OP %d is not supported.\n", op);
exit(1);
}
regs = get_arch_regstr(regn);
if (!regs) {
fprintf(stderr,
"Error: %lld exceeds max register number.\n", regn);
exit(1);
}
if (deref)
printf(" %+lld(%s)", offs, regs);
else
printf(" %s", regs);
}
/* Show a variables in kprobe event format */
static void show_variable(Dwarf_Die vr_die, struct probe_finder *pf)
{
Dwarf_Attribute attr;
Dwarf_Locdesc ld;
int ret;
ret = dwarf_attr(vr_die, DW_AT_location, &attr, &session.err);
ERR_IF(ret != DW_DLV_OK);
ret = attr_get_locdesc(attr, &ld, (pf->addr - pf->cu_base));
ERR_IF(ret != DW_DLV_OK);
/* TODO? */
if (ld.ld_cents != 1) {
fprintf(stderr, "This variable type is not supported.\n");
exit(1);
}
show_location(&ld.ld_s[0], pf);
free(ld.ld_s);
dwarf_dealloc(session.dbg, attr, DW_DLA_ATTR);
}
static int variable_callback(struct die_link *link, void *data)
{
struct probe_finder *pf = (struct probe_finder *)data;
Dwarf_Half tag;
int ret;
ret = dwarf_tag(link->die, &tag, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if ((tag == DW_TAG_formal_parameter ||
tag == DW_TAG_variable) &&
(die_compare_name(link->die, pf->var) == 0)) {
show_variable(link->die, pf);
return 1;
}
/* TODO: Support struct members */
return 0;
}
/* Find a variable in a subprogram die */
static void find_variable(const char *var, Dwarf_Die sp_die,
struct probe_finder *pf)
{
int ret;
debug("Searching %s variable in context.\n", var);
pf->var = var;
/* Search child die for local variables and parameters. */
ret = search_die_from_children(sp_die, variable_callback, pf);
if (!ret) {
fprintf(stderr, "\nFailed to find %s in this function.\n", var);
exit(1);
}
}
/* Get a frame base on the address */
static void get_current_frame_base(Dwarf_Die sp_die, struct probe_finder *pf)
{
Dwarf_Attribute attr;
int ret;
ret = dwarf_attr(sp_die, DW_AT_frame_base, &attr, &session.err);
ERR_IF(ret != DW_DLV_OK);
ret = attr_get_locdesc(attr, &pf->fbloc, (pf->addr - pf->cu_base));
ERR_IF(ret != DW_DLV_OK);
dwarf_dealloc(session.dbg, attr, DW_DLA_ATTR);
}
static void free_current_frame_base(struct probe_finder *pf)
{
free(pf->fbloc.ld_s);
memset(&pf->fbloc, 0, sizeof(Dwarf_Locdesc));
}
/* Show a probe point to stdout */
static void show_probepoint(Dwarf_Die sp_die, Dwarf_Signed offs,
struct probe_finder *pf)
{
char *name;
int ret, i;
ret = dwarf_diename(sp_die, &name, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (ret == DW_DLV_OK) {
printf("%s%+d", name, (int)offs);
dwarf_dealloc(session.dbg, name, DW_DLA_STRING);
} else {
/* This function has no name. */
printf("0x%llx", pf->addr);
}
get_current_frame_base(sp_die, pf);
for (i = 0; i < pf->nr_args; i++)
find_variable(pf->args[i], sp_die, pf);
free_current_frame_base(pf);
printf("\n");
pf->found++;
}
static int probeaddr_callback(struct die_link *link, void *data)
{
struct probe_finder *pf = (struct probe_finder *)data;
Dwarf_Half tag;
Dwarf_Signed offs;
int ret;
ret = dwarf_tag(link->die, &tag, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (tag == DW_TAG_subprogram &&
die_within_subprogram(link->die, pf->addr, &offs)) {
show_probepoint(link->die, offs, pf);
return 1;
}
return 0;
}
/* Find probe point from its line number */
static void find_by_line(Dwarf_Die cu_die, struct probe_finder *pf)
{
Dwarf_Signed cnt, i;
Dwarf_Line *lines;
Dwarf_Unsigned lineno = 0;
Dwarf_Addr addr;
Dwarf_Unsigned fno;
int ret;
ret = dwarf_srclines(cu_die, &lines, &cnt, &session.err);
ERR_IF(ret != DW_DLV_OK);
for (i = 0; i < cnt; i++) {
ret = dwarf_line_srcfileno(lines[i], &fno, &session.err);
ERR_IF(ret != DW_DLV_OK);
if (fno != pf->fno)
continue;
ret = dwarf_lineno(lines[i], &lineno, &session.err);
ERR_IF(ret != DW_DLV_OK);
if (lineno != pf->line)
continue;
ret = dwarf_lineaddr(lines[i], &addr, &session.err);
ERR_IF(ret != DW_DLV_OK);
debug("Probe point found: 0x%llx\n", addr);
pf->addr = addr;
/* Search a real subprogram including this line, */
ret = search_die_from_children(cu_die, probeaddr_callback, pf);
if (ret == 0) {
fprintf(stderr,
"Probe point is not found in subprograms.\n");
exit(1);
}
/* Continuing, because target line might be inlined. */
}
dwarf_srclines_dealloc(session.dbg, lines, cnt);
}
/* Search function from function name */
static int probefunc_callback(struct die_link *link, void *data)
{
struct probe_finder *pf = (struct probe_finder *)data;
struct die_link *lk;
Dwarf_Signed offs;
Dwarf_Half tag;
int ret;
ret = dwarf_tag(link->die, &tag, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (tag == DW_TAG_subprogram) {
if (die_compare_name(link->die, pf->function) == 0) {
if (die_inlined_subprogram(link->die)) {
/* Inlined function, save it. */
ret = dwarf_die_CU_offset(link->die,
&pf->inl_offs,
&session.err);
ERR_IF(ret != DW_DLV_OK);
debug("inline definition offset %lld\n",
pf->inl_offs);
return 0;
}
dwarf_lowpc(link->die, &pf->addr, &session.err);
pf->addr += pf->offset;
/* TODO: Check the address in this function */
show_probepoint(link->die, pf->offset, pf);
/* Continue to search */
}
} else if (tag == DW_TAG_inlined_subroutine && pf->inl_offs) {
if (die_get_abstract_origin(link->die) == pf->inl_offs) {
pf->addr = die_get_entrypc(link->die);
pf->addr += pf->offset;
debug("found inline addr: 0x%llx\n", pf->addr);
/* Inlined function. Get a real subprogram */
for (lk = link->parent; lk != NULL; lk = lk->parent) {
tag = 0;
dwarf_tag(lk->die, &tag, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (tag == DW_TAG_subprogram &&
!die_inlined_subprogram(lk->die))
goto found;
}
fprintf(stderr, "Failed to find real subprogram.\n");
exit(1);
found:
ret = die_within_subprogram(lk->die, pf->addr, &offs);
ERR_IF(!ret);
show_probepoint(lk->die, offs, pf);
/* Continue to search */
}
}
return 0;
}
static void find_by_func(Dwarf_Die cu_die, struct probe_finder *pf)
{
search_die_from_children(cu_die, probefunc_callback, pf);
}
static void find_probepoint(struct probe_finder *pf)
{
Dwarf_Unsigned cuh_len = 0;
Dwarf_Half vstamp = 0;
Dwarf_Unsigned abbrev = 0;
Dwarf_Half addr_size = 0;
Dwarf_Unsigned next_cuh = 0;
Dwarf_Die cu_die = 0;
int cu_number = 0, ret;
pf->found = 0;
while (++cu_number) {
/* Search CU (Compilation Unit) */
ret = dwarf_next_cu_header(session.dbg, &cuh_len, &vstamp,
&abbrev, &addr_size, &next_cuh, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (ret == DW_DLV_NO_ENTRY)
break;
/* Get the DIE(Debugging Information Entry) of this CU */
ret = dwarf_siblingof(session.dbg, 0, &cu_die, &session.err);
ERR_IF(ret != DW_DLV_OK);
/* Check if target file is included. */
if (pf->file)
pf->fno = die_get_fileno(cu_die, pf->file);
if (!pf->file || pf->fno) {
/* Save CU base address (for frame_base) */
ret = dwarf_lowpc(cu_die, &pf->cu_base, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (ret == DW_DLV_NO_ENTRY)
pf->cu_base = 0;
if (pf->line)
find_by_line(cu_die, pf);
if (pf->function)
find_by_func(cu_die, pf);
}
dwarf_dealloc(session.dbg, cu_die, DW_DLA_DIE);
}
if (pf->found == 0) {
fprintf(stderr, "Probe point is not found.\n");
exit(1);
}
}
WARNING: multiple messages have this Message-ID (diff)
From: Masami Hiramatsu <mhiramat@redhat.com>
To: Ingo Molnar <mingo@elte.hu>, Steven Rostedt <rostedt@goodmis.org>,
lkml <linux-kernel@vger.kernel.org>
Cc: "Ananth N Mavinakayanahalli" <ananth@in.ibm.com>,
"Avi Kivity" <avi@redhat.com>, "Andi Kleen" <ak@linux.intel.com>,
"Christoph Hellwig" <hch@infradead.org>,
"Frank Ch. Eigler" <fche@redhat.com>,
"Frederic Weisbecker" <fweisbec@gmail.com>,
"H. Peter Anvin" <hpa@zytor.com>,
"Jason Baron" <jbaron@redhat.com>,
"Jim Keniston" <jkenisto@us.ibm.com>,
"K.Prasad" <prasad@linux.vnet.ibm.com>,
"Lai Jiangshan" <laijs@cn.fujitsu.com>,
"Li Zefan" <lizf@cn.fujitsu.com>,
PrzemysławPawełczyk <przemyslaw@pawelczyk.it>,
"Roland McGrath" <roland@redhat.com>,
"Sam Ravnborg" <sam@ravnborg.org>,
"Srikar Dronamraju" <srikar@linux.vnet.ibm.com>,
"Tom Zanussi" <tzanussi@gmail.com>,
"Vegard Nossum" <vegard.nossum@gma>
Subject: [TOOL] c2kpe: C expression to kprobe event format converter
Date: Thu, 13 Aug 2009 16:59:19 -0400 [thread overview]
Message-ID: <4A847EA7.20307@redhat.com> (raw)
In-Reply-To: <20090813203403.31965.20973.stgit@localhost.localdomain>
[-- Attachment #1: Type: text/plain, Size: 2014 bytes --]
This program converts probe point in C expression to kprobe event
format for kprobe-based event tracer. This helps to define kprobes
events by C source line number or function name, and local variable
name. Currently, this supports only x86(32/64) kernels.
Compile
--------
Before compilation, please install libelf and libdwarf development
packages.
(e.g. elfutils-libelf-devel and libdwarf-devel on Fedora)
$ gcc -Wall -lelf -ldwarf c2kpe.c -o c2kpe
Synopsis
--------
$ c2kpe [options] FUNCTION[+OFFS][@SRC] [VAR [VAR ...]]
or
$ c2kpe [options] @SRC:LINE [VAR [VAR ...]]
FUNCTION: Probing function name.
OFFS: Offset in bytes.
SRC: Source file path.
LINE: Line number
VAR: Local variable name.
options:
-r KREL Kernel release version (e.g. 2.6.31-rc5)
-m DEBUGINFO Dwarf-format binary file (vmlinux or kmodule)
Example
-------
$ c2kpe sys_read fd buf count
sys_read+0 %di %si %dx
$ c2kpe @mm/filemap.c:339 inode pos
sync_page_range+125 -48(%bp) %r14
Example with kprobe-tracer
--------------------------
Since C expression may be converted multiple results, I recommend to use
readline.
$ c2kpe sys_read fd buf count | while read i; do \
echo "p $i" > $DEBUGFS/tracing/kprobe_events ;\
done
Note
----
- This requires a kernel compiled with CONFIG_DEBUG_INFO.
- Specifying @SRC speeds up c2kpe, because we can skip CUs which don't
include specified SRC file.
- c2kpe doesn't check whether the offset byte is correctly on the
instruction boundary. I recommend you to use @SRC:LINE expression for
tracing function body.
- This tool doesn't search kmodule file. You need to specify kmodule
file if you want to probe it.
TODO
----
- Fix bugs.
- Support multiple probepoints from stdin.
- Better kmodule support.
- Use elfutils-libdw?
- Merge into trace-cmd or perf-tools?
--
Masami Hiramatsu
Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division
e-mail: mhiramat@redhat.com
[-- Attachment #2: c2kpe.c --]
[-- Type: text/plain, Size: 21095 bytes --]
/*
* c2kpe : C expression to kprobe event converter
*
* 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.
*
*/
#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 <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <libdwarf/dwarf.h>
#include <libdwarf/libdwarf.h>
/* Default vmlinux search paths */
#define NR_SEARCH_PATH 2
const char *default_search_path[NR_SEARCH_PATH] = {
"/lib/modules/%s/build/vmlinux", /* Custom build kernel */
"/usr/lib/debug/lib/modules/%s/vmlinux", /* Red Hat debuginfo */
};
#define _stringify(n) #n
#define stringify(n) _stringify(n)
#ifdef DEBUG
#define debug(fmt ...) \
fprintf(stderr, "DBG(" __FILE__ ":" stringify(__LINE__) "): " fmt)
#else
#define debug(fmt ...) do {} while (0)
#endif
#define ERR_IF(cnd) \
do { if (cnd) { \
fprintf(stderr, "Error (" __FILE__ ":" stringify(__LINE__) \
"): " stringify(cnd) "\n"); \
exit(1); \
}} while (0)
#define MAX_PATH_LEN 256
/* Dwarf_Die Linkage to parent Die */
struct die_link {
struct die_link *parent; /* Parent die */
Dwarf_Die die; /* Current die */
};
#define X86_32_MAX_REGS 8
const char *x86_32_regs_table[X86_32_MAX_REGS] = {
"%ax",
"%cx",
"%dx",
"%bx",
"sa", /* Stack address */
"%bp",
"%si",
"%di",
};
#define X86_64_MAX_REGS 16
const char *x86_64_regs_table[X86_64_MAX_REGS] = {
"%ax",
"%dx",
"%cx",
"%bx",
"%si",
"%di",
"%bp",
"%sp",
"%r8",
"%r9",
"%r10",
"%r11",
"%r12",
"%r13",
"%r14",
"%r15",
};
/* TODO: switching by dwarf address size */
#ifdef __x86_64__
#define ARCH_MAX_REGS X86_64_MAX_REGS
#define arch_regs_table x86_64_regs_table
#else
#define ARCH_MAX_REGS X86_32_MAX_REGS
#define arch_regs_table x86_32_regs_table
#endif
/* Return architecture dependent register string */
static inline const char *get_arch_regstr(unsigned int n)
{
return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL;
}
struct probe_finder {
/* Inputs */
char *file; /* File name */
int line; /* Line number */
char *function; /* Function name */
int offset; /* Offset bytes */
Dwarf_Addr addr; /* Address */
int nr_args; /* Number of arguments */
char **args; /* Arguments */
/* Working area */
Dwarf_Addr cu_base; /* Current CU base address */
Dwarf_Locdesc fbloc; /* Location of Current Frame Base */
Dwarf_Unsigned fno; /* File number */
Dwarf_Off inl_offs; /* Inline offset */
const char *var; /* Current variable name */
/* Output */
int found; /* Number of found probe points */
};
/* Find a probe point */
static void find_probepoint(struct probe_finder *pf);
/* Session management structure */
static struct {
char *kver;
char *modpath;
int maxprobe;
struct probe_finder finder;
Dwarf_Debug dbg;
Dwarf_Error err;
int nfound;
} session;
static void usage(const char *msg)
{
if (msg)
printf("%s\n\n", msg);
printf("c2kpe: C expression to kprobe event converter\n");
printf("Usage: c2kpe [-r KREL] [-m mod|vmlinux] FUNC|SRC [ARG...]\n");
printf(" FUNC:\tFUNCNAME[+OFFS_BYTE][@SRCPATH]\n");
printf(" SRC:\t@SRCPATH:LINE\n");
printf(" ARG:\tLocal variable name\n");
/* TODO: @global, $vars, $params */
exit(0);
}
static void semantic_error(const char *msg)
{
fprintf(stderr, "Semantic error: %s\n", msg);
exit(1);
}
static void parse_probe_point(int argc, char *argv[], struct probe_finder *pf)
{
char *arg;
char *ptr;
arg = argv[0];
if (arg[0] == '@') {
/* Source Line */
arg++;
ptr = strchr(arg, ':');
if (!ptr || ptr[1] == '\0')
semantic_error("Line number is required.");
*ptr++ = '\0';
pf->file = arg;
if (strlen(arg) == 0)
semantic_error("No file name.");
pf->line = atoi(ptr);
debug("file:%s line:%d\n", pf->file, pf->line);
} else {
/* Function name */
pf->function = arg;
ptr = strchr(arg, '+');
if (ptr) {
if (ptr[1] == '\0' || ptr[1] == '@')
semantic_error("Offset is required.");
*ptr++ = '\0';
pf->offset = atoi(ptr);
arg = ptr;
}
ptr = strchr(arg, '@');
if (ptr) {
*ptr++ = '\0';
pf->file = ptr;
}
debug("fname:%s file:%s offset:%d\n",
pf->function, pf->file, pf->offset);
}
pf->nr_args = argc - 1;
if (pf->nr_args > 0)
pf->args = &argv[1];
debug("%d arguments\n", pf->nr_args);
}
static void parse_args(int argc, char *argv[])
{
int opt;
if (argc < 1)
usage("Need a probe point.");
while ((opt = getopt(argc, argv, "r:m:n:")) != -1) {
switch (opt) {
case 'r':
session.kver = optarg;
break;
case 'm':
session.modpath = optarg;
break;
case 'n':
session.maxprobe = atoi(optarg);
break;
default:
usage("Unexpected option found.");
}
}
if (optind >= argc)
usage("Need a probe point.");
parse_probe_point(argc - optind, &argv[optind], &session.finder);
}
static int open_default_vmlinux(const char *kver)
{
struct utsname uts;
char fname[MAX_PATH_LEN];
int fd, ret, i;
if (!kver) {
ret = uname(&uts);
if (ret) {
debug("uname() failed.\n");
return -errno;
}
kver = uts.release;
}
for (i = 0; i < NR_SEARCH_PATH; i++) {
ret = snprintf(fname, MAX_PATH_LEN,
default_search_path[i], kver);
if (ret >= MAX_PATH_LEN || ret < 0) {
debug("Filename(%d,%s) is too long.\n", i, uts.release);
errno = E2BIG;
return -E2BIG;
}
debug("try to open %s\n", fname);
fd = open(fname, O_RDONLY);
if (fd >= 0)
break;
}
return fd;
}
int main(int argc, char *argv[])
{
int fd, ret;
parse_args(argc, argv);
if (session.modpath)
fd = open(session.modpath, O_RDONLY);
else
fd = open_default_vmlinux(session.kver);
if (fd < 0) {
perror("file open");
exit(1);
}
/* TODO: handle errors */
ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &session.dbg, &session.err);
if (ret != DW_DLV_OK) {
fprintf(stderr, "Failed to call dwarf_init(). "
"Maybe, not a dwarf file?\n");
exit(1);
}
find_probepoint(&session.finder);
ret = dwarf_finish(session.dbg, &session.err);
ERR_IF(ret != DW_DLV_OK);
close(fd);
return 0;
}
/*---------------------
* Dwarf Analysys Part
*---------------------*/
/*
* Compare the tail of two strings.
* Return 0 if whole of either string is same as another's tail part.
*/
static int strtailcmp(const char *s1, const char *s2)
{
int i1 = strlen(s1);
int i2 = strlen(s2);
while (--i1 > 0 && --i2 > 0) {
if (s1[i1] != s2[i2])
return s1[i1] - s2[i2];
}
return 0;
}
/* Find the fileno of the target file. */
static Dwarf_Unsigned die_get_fileno(Dwarf_Die cu_die, const char *fname)
{
Dwarf_Signed cnt, i;
Dwarf_Unsigned found = 0;
char **srcs;
int ret;
if (!fname)
return 0;
ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &session.err);
if (ret == DW_DLV_OK) {
for (i = 0; i < cnt && !found; i++) {
if (strtailcmp(srcs[i], fname) == 0)
found = i + 1;
dwarf_dealloc(session.dbg, srcs[i], DW_DLA_STRING);
}
for (;i < cnt; i++)
dwarf_dealloc(session.dbg, srcs[i], DW_DLA_STRING);
dwarf_dealloc(session.dbg, srcs, DW_DLA_LIST);
}
if (found) debug("found fno: %d\n", (int)found);
return found;
}
/* Compare diename and tname */
static int die_compare_name(Dwarf_Die die, const char *tname)
{
char *name;
int ret;
ret = dwarf_diename(die, &name, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (ret == DW_DLV_OK) {
//debug("diename: %s\n", name);
ret = strcmp(tname, name);
dwarf_dealloc(session.dbg, name, DW_DLA_STRING);
} else
ret = -1;
return ret;
}
/* Check the address is in the subprogram(function). */
static int die_within_subprogram(Dwarf_Die sp_die, Dwarf_Addr addr,
Dwarf_Signed *offs)
{
Dwarf_Addr lopc, hipc;
int ret;
ret = dwarf_lowpc(sp_die, &lopc, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (ret == DW_DLV_NO_ENTRY)
return 0;
ret = dwarf_highpc(sp_die, &hipc, &session.err);
ERR_IF(ret != DW_DLV_OK);
if (lopc <= addr && addr < hipc) {
*offs = addr - lopc;
return 1;
} else
return 0;
}
/* Check the die is inlined function */
static Dwarf_Bool die_inlined_subprogram(Dwarf_Die die)
{
/* TODO: check strictly */
Dwarf_Bool inl;
int ret;
ret = dwarf_hasattr(die, DW_AT_inline, &inl, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
return inl;
}
/* Get the offset of abstruct_origin */
static Dwarf_Off die_get_abstract_origin(Dwarf_Die die)
{
Dwarf_Attribute attr;
Dwarf_Off cu_offs;
int ret;
ret = dwarf_attr(die, DW_AT_abstract_origin, &attr, &session.err);
ERR_IF(ret != DW_DLV_OK);
ret = dwarf_formref(attr, &cu_offs, &session.err);
ERR_IF(ret != DW_DLV_OK);
dwarf_dealloc(session.dbg, attr, DW_DLA_ATTR);
return cu_offs;
}
/* Get entry pc(or low pc, 1st entry of ranges) of the die */
static Dwarf_Addr die_get_entrypc(Dwarf_Die die)
{
Dwarf_Attribute attr;
Dwarf_Addr addr;
Dwarf_Off offs;
Dwarf_Ranges *ranges;
Dwarf_Signed cnt;
int ret;
/* Try to get entry pc */
ret = dwarf_attr(die, DW_AT_entry_pc, &attr, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (ret == DW_DLV_OK) {
ret = dwarf_formaddr(attr, &addr, &session.err);
ERR_IF(ret != DW_DLV_OK);
dwarf_dealloc(session.dbg, attr, DW_DLA_ATTR);
return addr;
}
/* Try to get low pc */
ret = dwarf_lowpc(die, &addr, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (ret == DW_DLV_OK)
return addr;
/* Try to get ranges */
ret = dwarf_attr(die, DW_AT_ranges, &attr, &session.err);
ERR_IF(ret != DW_DLV_OK);
ret = dwarf_formref(attr, &offs, &session.err);
ERR_IF(ret != DW_DLV_OK);
ret = dwarf_get_ranges(session.dbg, offs, &ranges, &cnt, NULL,
&session.err);
ERR_IF(ret != DW_DLV_OK);
addr = ranges[0].dwr_addr1;
dwarf_ranges_dealloc(session.dbg, ranges, cnt);
return addr;
}
/*
* Search a Die from Die tree.
* Note: cur_link->die should be deallocated in this function.
*/
static int __search_die_tree(struct die_link *cur_link,
int (*die_cb)(struct die_link *, void *),
void *data)
{
Dwarf_Die new_die;
struct die_link link;
int ret;
if (!die_cb)
return 0;
/* Check current die */
while (!(ret = die_cb(cur_link, data))) {
/* Check child die */
ret = dwarf_child(cur_link->die, &new_die, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (ret == DW_DLV_OK) {
link.parent = cur_link;
link.die = new_die;
ret = __search_die_tree(&link, die_cb, data);
if (ret)
break;
}
/* Move to next sibling */
ret = dwarf_siblingof(session.dbg, cur_link->die, &new_die,
&session.err);
ERR_IF(ret == DW_DLV_ERROR);
dwarf_dealloc(session.dbg, cur_link->die, DW_DLA_DIE);
cur_link->die = new_die;
if (ret == DW_DLV_NO_ENTRY)
return 0;
}
dwarf_dealloc(session.dbg, cur_link->die, DW_DLA_DIE);
return ret;
}
/* Search a die in its children's die tree */
static int search_die_from_children(Dwarf_Die parent_die,
int (*die_cb)(struct die_link *, void *),
void *data)
{
struct die_link link;
int ret;
link.parent = NULL;
ret = dwarf_child(parent_die, &link.die, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (ret == DW_DLV_OK)
return __search_die_tree(&link, die_cb, data);
else
return 0;
}
/* Find a locdesc corresponding to the address */
static int attr_get_locdesc(Dwarf_Attribute attr, Dwarf_Locdesc *desc,
Dwarf_Addr addr)
{
Dwarf_Signed lcnt;
Dwarf_Locdesc **llbuf;
int ret, i;
ret = dwarf_loclist_n(attr, &llbuf, &lcnt, &session.err);
ERR_IF(ret != DW_DLV_OK);
ret = DW_DLV_NO_ENTRY;
for (i = 0; i < lcnt; ++i) {
if (llbuf[i]->ld_lopc <= addr &&
llbuf[i]->ld_hipc > addr ) {
memcpy(desc, llbuf[i], sizeof(Dwarf_Locdesc));
desc->ld_s =
malloc(sizeof(Dwarf_Loc) * llbuf[i]->ld_cents);
ERR_IF(desc->ld_s == NULL);
memcpy(desc->ld_s, llbuf[i]->ld_s,
sizeof(Dwarf_Loc) * llbuf[i]->ld_cents);
ret = DW_DLV_OK;
break;
}
dwarf_dealloc(session.dbg, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK);
dwarf_dealloc(session.dbg, llbuf[i], DW_DLA_LOCDESC);
}
/* Releasing loop */
for (; i < lcnt; ++i) {
dwarf_dealloc(session.dbg, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK);
dwarf_dealloc(session.dbg, llbuf[i], DW_DLA_LOCDESC);
}
dwarf_dealloc(session.dbg, llbuf, DW_DLA_LIST);
return ret;
}
/*---------------------------------
* Probe finder related functions
*-------------------------------*/
/* Show a location */
static void show_location(Dwarf_Loc *loc, struct probe_finder *pf)
{
Dwarf_Small op;
Dwarf_Unsigned regn;
Dwarf_Signed offs;
int deref = 0;
const char *regs;
op = loc->lr_atom;
/* If this is based on frame buffer, set the offset */
if (op == DW_OP_fbreg) {
deref = 1;
offs = (Dwarf_Signed)loc->lr_number;
op = pf->fbloc.ld_s[0].lr_atom;
loc = &pf->fbloc.ld_s[0];
} else
offs = 0;
if (op >= DW_OP_breg0 && op <= DW_OP_breg31) {
regn = op - DW_OP_breg0;
offs += (Dwarf_Signed)loc->lr_number;
deref = 1;
} else if (op >= DW_OP_reg0 && op <= DW_OP_reg31) {
regn = op - DW_OP_reg0;
} else if (op == DW_OP_bregx) {
regn = loc->lr_number;
offs += (Dwarf_Signed)loc->lr_number2;
deref = 1;
} else if (op == DW_OP_regx) {
regn = loc->lr_number;
} else {
fprintf(stderr, "Error: Dwarf_OP %d is not supported.\n", op);
exit(1);
}
regs = get_arch_regstr(regn);
if (!regs) {
fprintf(stderr,
"Error: %lld exceeds max register number.\n", regn);
exit(1);
}
if (deref)
printf(" %+lld(%s)", offs, regs);
else
printf(" %s", regs);
}
/* Show a variables in kprobe event format */
static void show_variable(Dwarf_Die vr_die, struct probe_finder *pf)
{
Dwarf_Attribute attr;
Dwarf_Locdesc ld;
int ret;
ret = dwarf_attr(vr_die, DW_AT_location, &attr, &session.err);
ERR_IF(ret != DW_DLV_OK);
ret = attr_get_locdesc(attr, &ld, (pf->addr - pf->cu_base));
ERR_IF(ret != DW_DLV_OK);
/* TODO? */
if (ld.ld_cents != 1) {
fprintf(stderr, "This variable type is not supported.\n");
exit(1);
}
show_location(&ld.ld_s[0], pf);
free(ld.ld_s);
dwarf_dealloc(session.dbg, attr, DW_DLA_ATTR);
}
static int variable_callback(struct die_link *link, void *data)
{
struct probe_finder *pf = (struct probe_finder *)data;
Dwarf_Half tag;
int ret;
ret = dwarf_tag(link->die, &tag, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if ((tag == DW_TAG_formal_parameter ||
tag == DW_TAG_variable) &&
(die_compare_name(link->die, pf->var) == 0)) {
show_variable(link->die, pf);
return 1;
}
/* TODO: Support struct members */
return 0;
}
/* Find a variable in a subprogram die */
static void find_variable(const char *var, Dwarf_Die sp_die,
struct probe_finder *pf)
{
int ret;
debug("Searching %s variable in context.\n", var);
pf->var = var;
/* Search child die for local variables and parameters. */
ret = search_die_from_children(sp_die, variable_callback, pf);
if (!ret) {
fprintf(stderr, "\nFailed to find %s in this function.\n", var);
exit(1);
}
}
/* Get a frame base on the address */
static void get_current_frame_base(Dwarf_Die sp_die, struct probe_finder *pf)
{
Dwarf_Attribute attr;
int ret;
ret = dwarf_attr(sp_die, DW_AT_frame_base, &attr, &session.err);
ERR_IF(ret != DW_DLV_OK);
ret = attr_get_locdesc(attr, &pf->fbloc, (pf->addr - pf->cu_base));
ERR_IF(ret != DW_DLV_OK);
dwarf_dealloc(session.dbg, attr, DW_DLA_ATTR);
}
static void free_current_frame_base(struct probe_finder *pf)
{
free(pf->fbloc.ld_s);
memset(&pf->fbloc, 0, sizeof(Dwarf_Locdesc));
}
/* Show a probe point to stdout */
static void show_probepoint(Dwarf_Die sp_die, Dwarf_Signed offs,
struct probe_finder *pf)
{
char *name;
int ret, i;
ret = dwarf_diename(sp_die, &name, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (ret == DW_DLV_OK) {
printf("%s%+d", name, (int)offs);
dwarf_dealloc(session.dbg, name, DW_DLA_STRING);
} else {
/* This function has no name. */
printf("0x%llx", pf->addr);
}
get_current_frame_base(sp_die, pf);
for (i = 0; i < pf->nr_args; i++)
find_variable(pf->args[i], sp_die, pf);
free_current_frame_base(pf);
printf("\n");
pf->found++;
}
static int probeaddr_callback(struct die_link *link, void *data)
{
struct probe_finder *pf = (struct probe_finder *)data;
Dwarf_Half tag;
Dwarf_Signed offs;
int ret;
ret = dwarf_tag(link->die, &tag, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (tag == DW_TAG_subprogram &&
die_within_subprogram(link->die, pf->addr, &offs)) {
show_probepoint(link->die, offs, pf);
return 1;
}
return 0;
}
/* Find probe point from its line number */
static void find_by_line(Dwarf_Die cu_die, struct probe_finder *pf)
{
Dwarf_Signed cnt, i;
Dwarf_Line *lines;
Dwarf_Unsigned lineno = 0;
Dwarf_Addr addr;
Dwarf_Unsigned fno;
int ret;
ret = dwarf_srclines(cu_die, &lines, &cnt, &session.err);
ERR_IF(ret != DW_DLV_OK);
for (i = 0; i < cnt; i++) {
ret = dwarf_line_srcfileno(lines[i], &fno, &session.err);
ERR_IF(ret != DW_DLV_OK);
if (fno != pf->fno)
continue;
ret = dwarf_lineno(lines[i], &lineno, &session.err);
ERR_IF(ret != DW_DLV_OK);
if (lineno != pf->line)
continue;
ret = dwarf_lineaddr(lines[i], &addr, &session.err);
ERR_IF(ret != DW_DLV_OK);
debug("Probe point found: 0x%llx\n", addr);
pf->addr = addr;
/* Search a real subprogram including this line, */
ret = search_die_from_children(cu_die, probeaddr_callback, pf);
if (ret == 0) {
fprintf(stderr,
"Probe point is not found in subprograms.\n");
exit(1);
}
/* Continuing, because target line might be inlined. */
}
dwarf_srclines_dealloc(session.dbg, lines, cnt);
}
/* Search function from function name */
static int probefunc_callback(struct die_link *link, void *data)
{
struct probe_finder *pf = (struct probe_finder *)data;
struct die_link *lk;
Dwarf_Signed offs;
Dwarf_Half tag;
int ret;
ret = dwarf_tag(link->die, &tag, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (tag == DW_TAG_subprogram) {
if (die_compare_name(link->die, pf->function) == 0) {
if (die_inlined_subprogram(link->die)) {
/* Inlined function, save it. */
ret = dwarf_die_CU_offset(link->die,
&pf->inl_offs,
&session.err);
ERR_IF(ret != DW_DLV_OK);
debug("inline definition offset %lld\n",
pf->inl_offs);
return 0;
}
dwarf_lowpc(link->die, &pf->addr, &session.err);
pf->addr += pf->offset;
/* TODO: Check the address in this function */
show_probepoint(link->die, pf->offset, pf);
/* Continue to search */
}
} else if (tag == DW_TAG_inlined_subroutine && pf->inl_offs) {
if (die_get_abstract_origin(link->die) == pf->inl_offs) {
pf->addr = die_get_entrypc(link->die);
pf->addr += pf->offset;
debug("found inline addr: 0x%llx\n", pf->addr);
/* Inlined function. Get a real subprogram */
for (lk = link->parent; lk != NULL; lk = lk->parent) {
tag = 0;
dwarf_tag(lk->die, &tag, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (tag == DW_TAG_subprogram &&
!die_inlined_subprogram(lk->die))
goto found;
}
fprintf(stderr, "Failed to find real subprogram.\n");
exit(1);
found:
ret = die_within_subprogram(lk->die, pf->addr, &offs);
ERR_IF(!ret);
show_probepoint(lk->die, offs, pf);
/* Continue to search */
}
}
return 0;
}
static void find_by_func(Dwarf_Die cu_die, struct probe_finder *pf)
{
search_die_from_children(cu_die, probefunc_callback, pf);
}
static void find_probepoint(struct probe_finder *pf)
{
Dwarf_Unsigned cuh_len = 0;
Dwarf_Half vstamp = 0;
Dwarf_Unsigned abbrev = 0;
Dwarf_Half addr_size = 0;
Dwarf_Unsigned next_cuh = 0;
Dwarf_Die cu_die = 0;
int cu_number = 0, ret;
pf->found = 0;
while (++cu_number) {
/* Search CU (Compilation Unit) */
ret = dwarf_next_cu_header(session.dbg, &cuh_len, &vstamp,
&abbrev, &addr_size, &next_cuh, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (ret == DW_DLV_NO_ENTRY)
break;
/* Get the DIE(Debugging Information Entry) of this CU */
ret = dwarf_siblingof(session.dbg, 0, &cu_die, &session.err);
ERR_IF(ret != DW_DLV_OK);
/* Check if target file is included. */
if (pf->file)
pf->fno = die_get_fileno(cu_die, pf->file);
if (!pf->file || pf->fno) {
/* Save CU base address (for frame_base) */
ret = dwarf_lowpc(cu_die, &pf->cu_base, &session.err);
ERR_IF(ret == DW_DLV_ERROR);
if (ret == DW_DLV_NO_ENTRY)
pf->cu_base = 0;
if (pf->line)
find_by_line(cu_die, pf);
if (pf->function)
find_by_func(cu_die, pf);
}
dwarf_dealloc(session.dbg, cu_die, DW_DLA_DIE);
}
if (pf->found == 0) {
fprintf(stderr, "Probe point is not found.\n");
exit(1);
}
}
next prev parent reply other threads:[~2009-08-13 20:56 UTC|newest]
Thread overview: 79+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-08-13 20:34 [PATCH -tip v14 00/12] tracing: kprobe-based event tracer and x86 instruction decoder Masami Hiramatsu
2009-08-13 20:34 ` Masami Hiramatsu
2009-08-13 20:34 ` [PATCH -tip v14 01/12] x86: instruction decoder API Masami Hiramatsu
2009-08-13 20:34 ` Masami Hiramatsu
2009-08-19 23:42 ` Frederic Weisbecker
2009-08-20 0:21 ` Frederic Weisbecker
2009-08-20 15:03 ` Masami Hiramatsu
2009-08-20 15:03 ` Masami Hiramatsu
2009-08-20 15:25 ` Frederic Weisbecker
2009-08-20 16:16 ` Masami Hiramatsu
2009-08-20 16:16 ` Masami Hiramatsu
2009-08-20 18:07 ` Frederic Weisbecker
2009-08-20 19:01 ` Masami Hiramatsu
2009-08-20 19:01 ` Masami Hiramatsu
2009-08-20 20:14 ` Frederic Weisbecker
2009-08-20 14:42 ` Masami Hiramatsu
2009-08-20 14:42 ` Masami Hiramatsu
2009-08-20 14:46 ` Frederic Weisbecker
2009-08-13 20:34 ` [PATCH -tip v14 02/12] x86: x86 instruction decoder build-time selftest Masami Hiramatsu
2009-08-13 20:34 ` Masami Hiramatsu
2009-08-13 20:34 ` [PATCH -tip v14 03/12] kprobes: checks probe address is instruction boudary on x86 Masami Hiramatsu
2009-08-13 20:34 ` Masami Hiramatsu
2009-08-18 23:03 ` Frederic Weisbecker
2009-08-18 23:17 ` Masami Hiramatsu
2009-08-18 23:17 ` Masami Hiramatsu
2009-08-18 23:43 ` Frederic Weisbecker
2009-08-19 0:19 ` Masami Hiramatsu
2009-08-19 0:19 ` Masami Hiramatsu
2009-08-19 0:46 ` Frederic Weisbecker
2009-08-13 20:34 ` [PATCH -tip v14 04/12] kprobes: cleanup fix_riprel() using insn decoder " Masami Hiramatsu
2009-08-13 20:34 ` Masami Hiramatsu
2009-08-13 20:34 ` [PATCH -tip v14 05/12] x86: add pt_regs register and stack access APIs Masami Hiramatsu
2009-08-13 20:34 ` Masami Hiramatsu
2009-08-13 20:34 ` Masami Hiramatsu
2009-08-13 20:34 ` Masami Hiramatsu
2009-08-13 20:34 ` [PATCH -tip v14 06/12] tracing: ftrace dynamic ftrace_event_call support Masami Hiramatsu
2009-08-13 20:34 ` Masami Hiramatsu
2009-08-13 20:35 ` [PATCH -tip v14 07/12] tracing: Introduce TRACE_FIELD_ZERO() macro Masami Hiramatsu
2009-08-13 20:35 ` Masami Hiramatsu
2009-08-19 1:09 ` Frederic Weisbecker
2009-08-19 2:20 ` Masami Hiramatsu
2009-08-19 2:20 ` Masami Hiramatsu
2009-08-19 13:58 ` Frederic Weisbecker
2009-08-13 20:35 ` [PATCH -tip v14 08/12] tracing: add kprobe-based event tracer Masami Hiramatsu
2009-08-13 20:35 ` Masami Hiramatsu
2009-08-19 1:23 ` Frederic Weisbecker
2009-08-13 20:35 ` [PATCH -tip v14 09/12] tracing: Kprobe-tracer supports more than 6 arguments Masami Hiramatsu
2009-08-13 20:35 ` Masami Hiramatsu
2009-08-13 20:35 ` [PATCH -tip v14 10/12] tracing: Generate names for each kprobe event automatically Masami Hiramatsu
2009-08-13 20:35 ` Masami Hiramatsu
2009-08-13 20:35 ` [PATCH -tip v14 11/12] tracing: Kprobe tracer assigns new event ids for each event Masami Hiramatsu
2009-08-13 20:35 ` Masami Hiramatsu
2009-08-13 20:35 ` [PATCH -tip v14 12/12] tracing: Add kprobes event profiling interface Masami Hiramatsu
2009-08-13 20:35 ` Masami Hiramatsu
2009-08-13 20:57 ` [TOOL] kprobestest : Kprobe stress test tool Masami Hiramatsu
2009-08-13 20:57 ` Masami Hiramatsu
2009-08-20 18:43 ` Frederic Weisbecker
2009-08-20 19:45 ` Masami Hiramatsu
2009-08-20 19:45 ` Masami Hiramatsu
2009-08-21 0:01 ` Frederic Weisbecker
2009-08-21 1:00 ` Masami Hiramatsu
2009-08-21 1:00 ` Masami Hiramatsu
2009-08-21 19:43 ` [PATCH tracing/kprobes 1/4] x86: Fix x86 instruction decoder selftest to check only .text Masami Hiramatsu
2009-08-21 19:43 ` Masami Hiramatsu
2009-08-23 19:34 ` Frederic Weisbecker
2009-08-21 19:43 ` [PATCH tracing/kprobes 2/4] x86: Check awk features before generating inat-tables.c Masami Hiramatsu
2009-08-21 19:43 ` Masami Hiramatsu
2009-08-21 19:43 ` [PATCH tracing/kprobes 3/4] tracing/kprobes: Fix format typo in trace_kprobes Masami Hiramatsu
2009-08-21 19:43 ` Masami Hiramatsu
2009-08-21 19:43 ` [PATCH tracing/kprobes 4/4] tracing/kprobes: Change trace_arg to probe_arg Masami Hiramatsu
2009-08-21 19:43 ` Masami Hiramatsu
2009-08-13 20:59 ` Masami Hiramatsu [this message]
2009-08-13 20:59 ` [TOOL] c2kpe: C expression to kprobe event format converter Masami Hiramatsu
2009-08-13 21:05 ` Christoph Hellwig
2009-08-13 21:05 ` Christoph Hellwig
2009-08-30 19:50 ` Frederic Weisbecker
2009-08-31 4:14 ` Masami Hiramatsu
2009-08-31 4:14 ` Masami Hiramatsu
2009-08-31 22:14 ` Frederic Weisbecker
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4A847EA7.20307@redhat.com \
--to=mhiramat@redhat.com \
--cc=ak@linux.intel.com \
--cc=ananth@in.ibm.com \
--cc=avi@redhat.com \
--cc=dle-develop@lists.sourceforge.net \
--cc=fche@redhat.com \
--cc=fweisbec@gmail.com \
--cc=hch@infradead.org \
--cc=hpa@zytor.com \
--cc=jbaron@redhat.com \
--cc=jkenisto@us.ibm.com \
--cc=kvm@vger.kernel.org \
--cc=laijs@cn.fujitsu.com \
--cc=linux-kernel@vger.kernel.org \
--cc=lizf@cn.fujitsu.com \
--cc=mingo@elte.hu \
--cc=prasad@linux.vnet.ibm.com \
--cc=przemyslaw@pawelczyk.it \
--cc=roland@redhat.com \
--cc=rostedt@goodmis.org \
--cc=sam@ravnborg.org \
--cc=srikar@linux.vnet.ibm.com \
--cc=systemtap@sources.redhat.com \
--cc=tzanussi@gmail.com \
--cc=vegard.nossum@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.