/*
* probe-finder.c : 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 <stdarg.h>
#include <ctype.h>
#include <dwarf-regs.h>
#include <linux/bitops.h>
#include "event.h"
#include "debug.h"
#include "util.h"
#include "symbol.h"
#include "probe-finder.h"
/* Kprobe tracer basic type is up to u64 */
#define MAX_BASIC_TYPE_BITS 64
/* Line number list operations */
/* Add a line to line number list */
static int line_list__add_line(struct list_head *head, int line)
{
struct line_node *ln;
struct list_head *p;
/* Reverse search, because new line will be the last one */
list_for_each_entry_reverse(ln, head, list) {
if (ln->line < line) {
p = &ln->list;
goto found;
} else if (ln->line == line) /* Already exist */
return 1;
}
/* List is empty, or the smallest entry */
p = head;
found:
pr_debug("line list: add a line %u\n", line);
ln = zalloc(sizeof(struct line_node));
if (ln == NULL)
return -ENOMEM;
ln->line = line;
INIT_LIST_HEAD(&ln->list);
list_add(&ln->list, p);
return 0;
}
/* Check if the line in line number list */
static int line_list__has_line(struct list_head *head, int line)
{
struct line_node *ln;
/* Reverse search, because new line will be the last one */
list_for_each_entry(ln, head, list)
if (ln->line == line)
return 1;
return 0;
}
/* Init line number list */
static void line_list__init(struct list_head *head)
{
INIT_LIST_HEAD(head);
}
/* Free line number list */
static void line_list__free(struct list_head *head)
{
struct line_node *ln;
while (!list_empty(head)) {
ln = list_first_entry(head, struct line_node, list);
list_del(&ln->list);
free(ln);
}
}
/* Dwarf FL wrappers */
static char *debuginfo_path; /* Currently dummy */
static const Dwfl_Callbacks offline_callbacks = {
.find_debuginfo = dwfl_standard_find_debuginfo,
.debuginfo_path = &debuginfo_path,
.section_address = dwfl_offline_section_address,
/* We use this table for core files too. */
.find_elf = dwfl_build_id_find_elf,
};
/* Get a Dwarf from offline image */
static int debuginfo__init_offline_dwarf(struct debuginfo *self,
const char *path)
{
Dwfl_Module *mod;
int fd;
fd = open(path, O_RDONLY);
if (fd < 0)
return fd;
self->dwfl = dwfl_begin(&offline_callbacks);
if (!self->dwfl)
goto error;
mod = dwfl_report_offline(self->dwfl, "", "", fd);
if (!mod)
goto error;
self->dbg = dwfl_module_getdwarf(mod, &self->bias);
if (!self->dbg)
goto error;
return 0;
error:
if (self->dwfl)
dwfl_end(self->dwfl);
else
close(fd);
memset(self, 0, sizeof(*self));
return -ENOENT;
}
#if _ELFUTILS_PREREQ(0, 148)
/* This method is buggy if elfutils is older than 0.148 */
static int __linux_kernel_find_elf(Dwfl_Module *mod,
void **userdata,
const char *module_name,
Dwarf_Addr base,
char **file_name, Elf **elfp)
{
int fd;
const char *path = kernel_get_module_path(module_name);
pr_debug2("Use file %s for %s\n", path, module_name);
if (path) {
fd = open(path, O_RDONLY);
if (fd >= 0) {
*file_name = strdup(path);
return fd;
}
}
/* If failed, try to call standard method */
return dwfl_linux_kernel_find_elf(mod, userdata, module_name, base,
file_name, elfp);
}
static const Dwfl_Callbacks kernel_callbacks = {
.find_debuginfo = dwfl_standard_find_debuginfo,
.debuginfo_path = &debuginfo_path,
.find_elf = __linux_kernel_find_elf,
.section_address = dwfl_linux_kernel_module_section_address,
};
/* Get a Dwarf from live kernel image */
static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self,
Dwarf_Addr addr)
{
self->dwfl = dwfl_begin(&kernel_callbacks);
if (!self->dwfl)
return -EINVAL;
/* Load the kernel dwarves: Don't care the result here */
dwfl_linux_kernel_report_kernel(self->dwfl);
dwfl_linux_kernel_report_modules(self->dwfl);
self->dbg = dwfl_addrdwarf(self->dwfl, addr, &self->bias);
/* Here, check whether we could get a real dwarf */
if (!self->dbg) {
pr_debug("Failed to find kernel dwarf at %lx\n",
(unsigned long)addr);
dwfl_end(self->dwfl);
memset(self, 0, sizeof(*self));
return -ENOENT;
}
return 0;
}
#else
/* With older elfutils, this just support kernel module... */
static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self,
Dwarf_Addr addr __used)
{
const char *path = kernel_get_module_path("kernel");
if (!path) {
pr_err("Failed to find vmlinux path\n");
return -ENOENT;
}
pr_debug2("Use file %s for debuginfo\n", path);
return debuginfo__init_offline_dwarf(self, path);
}
#endif
struct debuginfo *debuginfo__new(const char *path)
{
struct debuginfo *self = zalloc(sizeof(struct debuginfo));
if (!self)
return NULL;
if (debuginfo__init_offline_dwarf(self, path) < 0) {
free(self);
self = NULL;
}
return self;
}
struct debuginfo
|