runtime/linux/kprobes.c - systemtap
Global variables defined
Data types defined
Functions defined
Macros defined
Source code
#ifndef _KPROBES_C_
#define _KPROBES_C_
#if !defined(CONFIG_KPROBES)
#error "Need CONFIG_KPROBES!"
#endif
#include <linux/kprobes.h>
#ifdef DEBUG_KPROBES
#define dbug_stapkp(args...) do { \
_stp_dbug(__FUNCTION__, __LINE__, args); \
} while (0)
#define dbug_stapkp_cond(cond, args...) do { \
if (cond) \
dbug_stapkp(args); \
} while (0)
#else
#define dbug_stapkp(args...) ;
#define dbug_stapkp_cond(cond, args...) ;
#endif
#ifndef KRETACTIVE
#define KRETACTIVE (max(15, 6 * (int)num_possible_cpus()))
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) \
&& !defined(STP_ON_THE_FLY_TIMER_ENABLE)
#error "STP_ON_THE_FLY_TIMER_ENABLE undefined"
#endif
struct stap_dwarf_kprobe {
union { struct kprobe kp; struct kretprobe krp; } u;
#ifdef __ia64__
struct kprobe dummy;
#endif
};
struct stap_dwarf_probe {
const unsigned return_p:1;
const unsigned maxactive_p:1;
const unsigned optional_p:1;
unsigned registered_p:1;
const unsigned short maxactive_val;
const unsigned short saved_longs;
const unsigned short saved_strings;
STAP_DWARF_PROBE_STR_module;
STAP_DWARF_PROBE_STR_section;
const unsigned long address;
const struct stap_probe * const probe;
const struct stap_probe * const entry_probe;
struct stap_dwarf_kprobe * const kprobe;
};
static int
enter_kprobe_probe(struct kprobe *inst,
struct pt_regs *regs);
static int
enter_kretprobe_common(struct kretprobe_instance *inst,
struct pt_regs *regs, int entry);
static int
enter_kretprobe_probe(struct kretprobe_instance *inst,
struct pt_regs *regs)
{
return enter_kretprobe_common(inst, regs, 0);
}
static int
enter_kretprobe_entry_probe(struct kretprobe_instance *inst,
struct pt_regs *regs)
{
return enter_kretprobe_common(inst, regs, 1);
}
static unsigned long
stapkp_relocate_addr(struct stap_dwarf_probe *sdp)
{
return _stp_kmodule_relocate(sdp->module, sdp->section, sdp->address);
}
static int
stapkp_prepare_kprobe(struct stap_dwarf_probe *sdp)
{
struct kprobe *kp = &sdp->kprobe->u.kp;
unsigned long addr = stapkp_relocate_addr(sdp);
if (addr == 0)
return 1;
kp->addr = (void *) addr;
kp->pre_handler = &enter_kprobe_probe;
#ifdef __ia64__ sdp->kprobe->dummy.addr = kp->addr;
sdp->kprobe->dummy.pre_handler = NULL;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
if (!sdp->probe->cond_enabled) {
kp->flags |= KPROBE_FLAG_DISABLED;
dbug_otf("registering as disabled (kprobe) pidx %zu\n",
sdp->probe->index);
}
#endif
return 0;
}
static int
stapkp_arch_register_kprobe(struct stap_dwarf_probe *sdp)
{
int ret = 0;
struct kprobe *kp = &sdp->kprobe->u.kp;
#ifndef __ia64__
ret = register_kprobe(kp);
dbug_stapkp_cond(ret == 0, "+kprobe %p\n", kp->addr);
#else ret = register_kprobe(&sdp->kprobe->dummy);
if (ret == 0) {
ret = register_kprobe(kp);
if (ret != 0)
unregister_kprobe(&sdp->kprobe->dummy);
}
dbug_stapkp_cond(ret == 0, "+kprobe %p\n", sdp->kprobe->dummy.addr);
dbug_stapkp_cond(ret == 0, "+kprobe %p\n", kp->addr);
#endif
sdp->registered_p = (ret ? 0 : 1);
return ret;
}
static int
stapkp_register_kprobe(struct stap_dwarf_probe *sdp)
{
int ret = stapkp_prepare_kprobe(sdp);
if (ret == 0)
ret = stapkp_arch_register_kprobe(sdp);
return ret;
}
static int
stapkp_prepare_kretprobe(struct stap_dwarf_probe *sdp)
{
struct kretprobe *krp = &sdp->kprobe->u.krp;
unsigned long addr = stapkp_relocate_addr(sdp);
if (addr == 0)
return 1;
krp->kp.addr = (void *) addr;
if (sdp->maxactive_p)
krp->maxactive = sdp->maxactive_val;
else
krp->maxactive = KRETACTIVE;
krp->handler = &enter_kretprobe_probe;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)
if (sdp->entry_probe) {
krp->entry_handler = &enter_kretprobe_entry_probe;
krp->data_size = sdp->saved_longs * sizeof(int64_t) +
sdp->saved_strings * MAXSTRINGLEN;
}
#endif
#ifdef __ia64__ sdp->kprobe->dummy.addr = krp->kp.addr;
sdp->kprobe->dummy.pre_handler = NULL;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
if (!sdp->probe->cond_enabled) {
krp->kp.flags |= KPROBE_FLAG_DISABLED;
dbug_otf("registering as disabled (kretprobe) pidx %zu\n",
sdp->probe->index);
}
#endif
return 0;
}
static int
stapkp_arch_register_kretprobe(struct stap_dwarf_probe *sdp)
{
int ret = 0;
struct kretprobe *krp = &sdp->kprobe->u.krp;
#ifndef __ia64__
ret = register_kretprobe(krp);
dbug_stapkp_cond(ret == 0, "+kretprobe %p\n", krp->kp.addr);
#else ret = register_kprobe(&sdp->kprobe->dummy);
if (ret == 0) {
ret = register_kretprobe(krp);
if (ret != 0)
unregister_kprobe(&sdp->kprobe->dummy);
}
dbug_stapkp_cond(ret == 0, "+kprobe %p\n", sdp->kprobe->dummy.addr);
dbug_stapkp_cond(ret == 0, "+kretprobe %p\n", krp->kp.addr);
#endif
sdp->registered_p = (ret ? 0 : 1);
return ret;
}
static int
stapkp_register_kretprobe(struct stap_dwarf_probe *sdp)
{
int ret = stapkp_prepare_kretprobe(sdp);
if (ret == 0)
ret = stapkp_arch_register_kretprobe(sdp);
return ret;
}
static int
stapkp_register_probe(struct stap_dwarf_probe *sdp)
{
if (sdp->registered_p)
return 0;
return sdp->return_p ? stapkp_register_kretprobe(sdp)
: stapkp_register_kprobe(sdp);
}
static void
stapkp_add_missed(struct stap_dwarf_probe *sdp)
{
if (sdp->return_p) {
struct kretprobe *krp = &sdp->kprobe->u.krp;
atomic_add(krp->nmissed, skipped_count());
#ifdef STP_TIMING
if (krp->nmissed)
_stp_warn ("Skipped due to missed kretprobe/1 on '%s': %d\n",
sdp->probe->pp, krp->nmissed);
#endif
atomic_add(krp->kp.nmissed, skipped_count());
#ifdef STP_TIMING
if (krp->kp.nmissed)
_stp_warn ("Skipped due to missed kretprobe/2 on '%s': %lu\n",
sdp->probe->pp, krp->kp.nmissed);
#endif
} else {
struct kprobe *kp = &sdp->kprobe->u.kp;
atomic_add (kp->nmissed, skipped_count());
#ifdef STP_TIMING
if (kp->nmissed)
_stp_warn ("Skipped due to missed kprobe on '%s': %lu\n",
sdp->probe->pp, kp->nmissed);
#endif
}
}
static void
stapkp_unregister_probe(struct stap_dwarf_probe *sdp)
{
struct stap_dwarf_kprobe *sdk = sdp->kprobe;
if (!sdp->registered_p)
return;
if (sdp->return_p) {
unregister_kretprobe (&sdk->u.krp);
dbug_stapkp("-kretprobe %p\n", sdk->u.krp.kp.addr);
} else {
unregister_kprobe (&sdk->u.kp);
dbug_stapkp("-kprobe %p\n", sdk->u.kp.addr);
}
#if defined(__ia64__)
unregister_kprobe (&sdk->dummy);
dbug_stapkp("-kprobe %p\n", sdk->dummy.addr);
#endif
sdp->registered_p = 0;
stapkp_add_missed(sdp);
memset(sdk, 0, sizeof(struct stap_dwarf_kprobe));
}
#if defined(STAPCONF_UNREGISTER_KPROBES)
static void * stap_unreg_kprobes[];
enum collect_type {
COLLECT_KPROBES,
#if defined(__ia64__)
COLLECT_DUMMYS,
#endif
COLLECT_KRETPROBES
};
static size_t
stapkp_collect_registered_probes(struct stap_dwarf_probe *probes,
size_t nprobes, enum collect_type type)
{
size_t i, j;
j = 0;
for (i = 0; i < nprobes; i++) {
struct stap_dwarf_probe *sdp = &probes[i];
struct stap_dwarf_kprobe *sdk = sdp->kprobe;
if (!sdp->registered_p)
continue;
if (type == COLLECT_KPROBES && !sdp->return_p)
stap_unreg_kprobes[j++] = &sdk->u.kp;
else if (type == COLLECT_KRETPROBES && sdp->return_p)
stap_unreg_kprobes[j++] = &sdk->u.krp;
#if defined(__ia64__)
else if (type == COLLECT_DUMMYS)
stap_unreg_kprobes[j++] = &sdk->dummy;
#endif
}
return j;
}
static void
stapkp_batch_unregister_probes(struct stap_dwarf_probe *probes,
size_t nprobes)
{
size_t i, n;
n = stapkp_collect_registered_probes(probes,
nprobes, COLLECT_KPROBES);
unregister_kprobes((struct kprobe **)stap_unreg_kprobes, n);
dbug_stapkp_cond(n > 0, "-kprobe * %zd\n", n);
n = stapkp_collect_registered_probes(probes,
nprobes, COLLECT_KRETPROBES);
unregister_kretprobes((struct kretprobe **)stap_unreg_kprobes, n);
dbug_stapkp_cond(n > 0, "-kretprobe * %zd\n", n);
#ifdef __ia64__
n = stapkp_collect_registered_probes(probes,
nprobes, COLLECT_DUMMYS);
unregister_kprobes((struct kprobe **)stap_unreg_kprobes, n);
dbug_stapkp_cond(n > 0, "-kprobe * %zd\n", n);
#endif
for (i = 0; i < nprobes; i++) {
struct stap_dwarf_probe *sdp = &probes[i];
if (!sdp->registered_p)
continue;
sdp->registered_p = 0;
stapkp_add_missed(sdp);
memset(sdp->kprobe, 0, sizeof(struct stap_dwarf_kprobe));
}
}
#endif
static void
stapkp_unregister_probes(struct stap_dwarf_probe *probes,
size_t nprobes)
{
#if defined(STAPCONF_UNREGISTER_KPROBES)
stapkp_batch_unregister_probes(probes, nprobes);
#else
size_t i;
for (i = 0; i < nprobes; i++) {
struct stap_dwarf_probe *sdp = &probes[i];
if (!sdp->registered_p)
continue;
stapkp_unregister_probe(sdp);
}
#endif
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
static int
stapkp_enabled(struct stap_dwarf_probe *sdp)
{
if (!sdp->registered_p)
return 0;
return sdp->return_p ? !kprobe_disabled(&sdp->kprobe->u.krp.kp)
: !kprobe_disabled(&sdp->kprobe->u.kp);
}
static int
stapkp_should_enable_probe(struct stap_dwarf_probe *sdp)
{
return sdp->registered_p
&& !stapkp_enabled(sdp)
&& sdp->probe->cond_enabled;
}
static int
stapkp_enable_probe(struct stap_dwarf_probe *sdp)
{
int ret = 0;
dbug_otf("enabling (k%sprobe) pidx %zu\n",
sdp->return_p ? "ret" : "", sdp->probe->index);
ret = sdp->return_p ? enable_kretprobe(&sdp->kprobe->u.krp)
: enable_kprobe(&sdp->kprobe->u.kp);
if (ret != 0) {
stapkp_unregister_probe(sdp);
dbug_otf("failed to enable (k%sprobe) pidx %zu (rc %d)\n",
sdp->return_p ? "ret" : "", sdp->probe->index, ret);
}
return ret;
}
static int
stapkp_should_disable_probe(struct stap_dwarf_probe *sdp)
{
return sdp->registered_p
&& stapkp_enabled(sdp)
&& !sdp->probe->cond_enabled;
}
static int
stapkp_disable_probe(struct stap_dwarf_probe *sdp)
{
int ret = 0;
dbug_otf("disabling (k%sprobe) pidx %zu\n",
sdp->return_p ? "ret" : "", sdp->probe->index);
ret = sdp->return_p ? disable_kretprobe(&sdp->kprobe->u.krp)
: disable_kprobe(&sdp->kprobe->u.kp);
if (ret != 0) {
stapkp_unregister_probe(sdp);
dbug_otf("failed to disable (k%sprobe) pidx %zu (rc %d)\n",
sdp->return_p ? "ret" : "", sdp->probe->index, ret);
}
return ret;
}
static int
stapkp_refresh_probe(struct stap_dwarf_probe *sdp)
{
if (stapkp_should_enable_probe(sdp))
return stapkp_enable_probe(sdp);
if (stapkp_should_disable_probe(sdp))
return stapkp_disable_probe(sdp);
return 0;
}
#endif
static int
stapkp_init(struct stap_dwarf_probe *probes,
size_t nprobes)
{
size_t i;
for (i = 0; i < nprobes; i++) {
struct stap_dwarf_probe *sdp = &probes[i];
int rc = 0;
rc = stapkp_register_probe(sdp);
if (rc == 1) continue;
if (rc && !sdp->optional_p) {
_stp_warn("probe %s (address 0x%lx) registration error (rc %d)",
sdp->probe->pp, stapkp_relocate_addr(sdp), rc);
}
}
return 0;
}
static void
stapkp_refresh(const char *modname,
struct stap_dwarf_probe *probes,
size_t nprobes)
{
size_t i;
for (i = 0; i < nprobes; i++) {
struct stap_dwarf_probe *sdp = &probes[i];
if (modname && sdp->module
&& strcmp(modname, sdp->module) == 0) {
int rc;
unsigned long addr = stapkp_relocate_addr(sdp);
if (sdp->registered_p == 0 && addr != 0)
stapkp_register_probe(sdp);
else if (sdp->registered_p == 1 && addr == 0)
stapkp_unregister_probe(sdp);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
} else if (stapkp_should_enable_probe(sdp)
|| stapkp_should_disable_probe(sdp)) {
stapkp_refresh_probe(sdp);
#endif
}
}
}
static void
stapkp_exit(struct stap_dwarf_probe *probes,
size_t nprobes)
{
stapkp_unregister_probes(probes, nprobes);
}
#endif