#define SCE_RT_SIGRETURN 6
#define SCE_nr_stubs 7
+extern struct module *dtrace_kmod;
+
+extern void dtrace_os_init(void);
+extern void dtrace_os_exit(void);
+
extern void dtrace_enable(void);
extern void dtrace_disable(void);
extern void dtrace_stacktrace(stacktrace_state_t *);
+#define FBT_PUSHL_EBP 0x55
+#define FBT_RET 0xc3
+#define FBT_RET_IMM16 0xc2
+
+typedef void *(fbt_provide_fn)(struct module *, char *, uint8_t,
+ uint8_t *, void *);
+
+extern void dtrace_fbt_init(fbt_provide_fn);
+
#endif /* _DTRACE_OS_H_ */
#include <linux/errno.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/stddef.h>
#define KSYM_NAME_LEN 128
struct module;
#ifdef CONFIG_KALLSYMS
+/* To avoid using get_symbol_offset for every symbol, we carry prefix along. */
+struct kallsym_iter {
+ loff_t pos;
+ unsigned long value;
+ unsigned int nameoff; /* If iterating in core kernel symbols. */
+ char type;
+ char name[KSYM_NAME_LEN];
+ char module_name[MODULE_NAME_LEN];
+ int exported;
+};
+
/* Lookup the address for a symbol. Returns 0 if not found. */
unsigned long kallsyms_lookup_name(const char *name);
int lookup_symbol_name(unsigned long addr, char *symname);
int lookup_symbol_attrs(unsigned long addr, unsigned long *size, unsigned long *offset, char *modname, char *name);
+extern void kallsyms_iter_reset(struct kallsym_iter *, loff_t);
+extern int kallsyms_iter_update(struct kallsym_iter *, loff_t);
#else /* !CONFIG_KALLSYMS */
static inline unsigned long kallsyms_lookup_name(const char *name)
#include <linux/percpu.h>
#include <asm/module.h>
+#include <trace/events/module.h>
+#include <linux/sdt.h>
+
/* In stripped ARM and x86-64 modules, ~ is surprisingly rare. */
#define MODULE_SIG_STRING "~Module signature appended~\n"
#endif
#if defined(CONFIG_DTRACE) || defined(CONFIG_DTRACE_MODULE)
- struct sdt_probedesc *sdt_probes;
+ size_t fbt_nprobes;
+
+ sdt_probedesc_t *sdt_probes;
unsigned int num_dtrace_probes; /* from kernel build */
size_t sdt_nprobes; /* managed at probe load time */
int mod_nenabled; /* # of enabled dtrace probes in module */
void dtrace_register_builtins(void);
-#ifdef DEBUG
-#define DPRINTK(fmt, args...) printk(KERN_INFO "%s: " fmt, __func__, ## args)
-#else
-#define DPRINTK(fmt, args...)
-#endif
-
#endif /* __KERNEL__ */
#else /* DTRACE not enabled: */
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
+#include <linux/kallsyms.h>
#include <asm/insn.h>
#include <asm/stacktrace.h>
#include <asm/syscalls.h>
+/*
+ * Perform OS specific DTrace setup.
+ */
+struct module *dtrace_kmod = NULL;
+EXPORT_SYMBOL(dtrace_kmod);
+
+void dtrace_os_init(void)
+{
+ if (dtrace_kmod != NULL) {
+ pr_warning("%s: cannot be called twice\n", __func__);
+ return;
+ }
+
+ dtrace_kmod = kzalloc(sizeof(struct module), GFP_KERNEL);
+ if (dtrace_kmod == NULL) {
+ pr_warning("%s: cannot allocate kernel pseudo-module\n",
+ __func__);
+ return;
+ }
+
+ dtrace_kmod->state = MODULE_STATE_LIVE;
+ strlcpy(dtrace_kmod->name, "vmlinux", MODULE_NAME_LEN);
+}
+EXPORT_SYMBOL(dtrace_os_init);
+
+void dtrace_os_exit(void)
+{
+ if (dtrace_kmod == NULL) {
+ pr_warning("%s: kernel pseudo-module not allocated\n",
+ __func__);
+ return;
+ }
+
+ kfree(dtrace_kmod);
+ dtrace_kmod = NULL;
+}
+EXPORT_SYMBOL(dtrace_os_exit);
+
/*
* Return a high resolution timer value that is guaranteed to always increase.
*/
EXPORT_SYMBOL(dtrace_invop_remove);
/*---------------------------------------------------------------------------*\
-(* SYSTEM CALL PROBING SUPPORT *)
+(* SYSTEM CALL TRACING SUPPORT *)
\*---------------------------------------------------------------------------*/
void (*systrace_probe)(dtrace_id_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t,
uintptr_t, uintptr_t);
return rc;
}
+
+/*---------------------------------------------------------------------------*\
+(* FUNCTION BOUNDARY TRACING (FBT) SUPPORT *)
+\*---------------------------------------------------------------------------*/
+
+void dtrace_fbt_init(fbt_provide_fn *pfn)
+{
+ loff_t pos = 0;
+ struct kallsym_iter iter, sym;
+
+ kallsyms_iter_update(&iter, 0);
+ if (!kallsyms_iter_update(&iter, pos++))
+ return;
+
+ while (pos > 0) {
+ sym = iter;
+ if (!kallsyms_iter_update(&iter, pos++))
+ pos = 0;
+
+ if (sym.module_name[0] != '\0')
+ break;
+
+ if (sym.type == 'T' || sym.type == 't' || sym.type == 'W') {
+ uint8_t *addr, *end;
+
+ addr = (uint8_t *)sym.value;
+ end = (uint8_t *)iter.value;
+
+ if (*addr == FBT_PUSHL_EBP) {
+ struct insn insn;
+ void *pfbt = NULL;
+
+ (*pfn)(dtrace_kmod, sym.name, FBT_PUSHL_EBP,
+ addr, NULL);
+
+ while (addr < end) {
+ uint8_t opc;
+
+ insn_init(&insn, addr, 1);
+ insn_get_opcode(&insn);
+
+ opc = insn.opcode.bytes[0];
+
+ if (opc == FBT_RET ||
+ opc == FBT_RET_IMM16) {
+ pfbt = (*pfn)(dtrace_kmod,
+ sym.name, opc,
+ addr, pfbt);
+ }
+
+ insn_get_length(&insn);
+
+ addr += insn.length;
+ }
+ }
+ }
+ }
+}
+EXPORT_SYMBOL(dtrace_fbt_init);
/* register static dtrace probe points */
-#define DEBUG 1
-
#include <linux/kernel.h>
#include <linux/memory.h>
#include <linux/module.h>
+#include <linux/dtrace_os.h>
#include <linux/sdt.h>
#include <linux/slab.h>
#include <linux/string.h>
const char *sdt_prefix = "__dtrace_probe_";
-struct module *dtrace_kmod;
-EXPORT_SYMBOL(dtrace_kmod);
-
void sdt_probe_enable(sdt_instr_t *addr)
{
text_poke(addr, ((unsigned char []){SDT_TRAP_INSTR}), 1);
void *nextpi;
uint8_t nops[SDT_NOP_SIZE];
+ if (dtrace_kmod == NULL) {
+ pr_warning("%s: no kernel pseudo-module allocated\n",
+ __func__);
+ return;
+ }
+
+ if (dtrace_sdt_nprobes == 0)
+ return;
+
/*
* A little unusual, but potentially necessary. While we could use a
* single NOP sequence of length SDT_NOP_SIZE, we need to consider the
add_nops(nops, 1);
add_nops(nops + 1, SDT_NOP_SIZE - 1);
- dtrace_kmod = kzalloc(sizeof(struct module), GFP_KERNEL);
- if (!dtrace_kmod) {
- pr_warning("%s: cannot allocate kernel pseudo-module\n",
- __func__);
- return;
- }
- dtrace_kmod->state = MODULE_STATE_LIVE;
- strlcpy(dtrace_kmod->name, "vmlinux", MODULE_NAME_LEN);
-
- if (dtrace_sdt_nprobes == 0)
- return;
-
for (cnt = 0; cnt < dtrace_sdt_nprobes; cnt++) {
char *func = pi->name + pi->name_len + 1;
return !!module_address_lookup(addr, symbolsize, offset, NULL, namebuf);
}
+EXPORT_SYMBOL_GPL(kallsyms_lookup_size_offset);
/*
* Lookup an address
}
EXPORT_SYMBOL(__print_symbol);
-/* To avoid using get_symbol_offset for every symbol, we carry prefix along. */
-struct kallsym_iter {
- loff_t pos;
- unsigned long value;
- unsigned int nameoff; /* If iterating in core kernel symbols. */
- char type;
- char name[KSYM_NAME_LEN];
- char module_name[MODULE_NAME_LEN];
- int exported;
-};
-
static int get_ksymbol_mod(struct kallsym_iter *iter)
{
if (module_get_kallsym(iter->pos - kallsyms_num_syms, &iter->value,
return off - iter->nameoff;
}
-static void reset_iter(struct kallsym_iter *iter, loff_t new_pos)
+void kallsyms_iter_reset(struct kallsym_iter *iter, loff_t new_pos)
{
iter->name[0] = '\0';
iter->nameoff = get_symbol_offset(new_pos);
iter->pos = new_pos;
}
+EXPORT_SYMBOL_GPL(kallsyms_iter_reset);
/* Returns false if pos at or past end of file. */
-static int update_iter(struct kallsym_iter *iter, loff_t pos)
+int kallsyms_iter_update(struct kallsym_iter *iter, loff_t pos)
{
/* Module symbols can be accessed randomly. */
if (pos >= kallsyms_num_syms) {
/* If we're not on the desired position, reset to new position. */
if (pos != iter->pos)
- reset_iter(iter, pos);
+ kallsyms_iter_reset(iter, pos);
iter->nameoff += get_ksymbol_core(iter);
iter->pos++;
return 1;
}
+EXPORT_SYMBOL_GPL(kallsyms_iter_update);
static void *s_next(struct seq_file *m, void *p, loff_t *pos)
{
(*pos)++;
- if (!update_iter(m->private, *pos))
+ if (!kallsyms_iter_update(m->private, *pos))
return NULL;
return p;
}
static void *s_start(struct seq_file *m, loff_t *pos)
{
- if (!update_iter(m->private, *pos))
+ if (!kallsyms_iter_update(m->private, *pos))
return NULL;
return m->private;
}
iter = __seq_open_private(file, &kallsyms_op, sizeof(*iter));
if (!iter)
return -ENOMEM;
- reset_iter(iter, 0);
+ kallsyms_iter_reset(iter, 0);
return 0;
}
if (*pos == 0) {
memset(&kdb_walk_kallsyms_iter, 0,
sizeof(kdb_walk_kallsyms_iter));
- reset_iter(&kdb_walk_kallsyms_iter, 0);
+ kallsyms_iter_reset(&kdb_walk_kallsyms_iter, 0);
}
while (1) {
- if (!update_iter(&kdb_walk_kallsyms_iter, *pos))
+ if (!kallsyms_iter_update(&kdb_walk_kallsyms_iter, *pos))
return NULL;
++*pos;
/* Some debugging symbols have no name. Ignore them. */
#include <linux/sched/sysctl.h>
#include <linux/slab.h>
#include <linux/compat.h>
+#include <linux/sdt.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
{
struct task_struct *p = current;
+ DTRACE_SCHED1(tick, struct task_struct *, p);
+
/* Note: this timer irq context must be accounted for as well. */
account_process_tick(p, user_tick);
run_local_timers();