From: Nick Alcock Date: Thu, 12 May 2011 22:46:51 +0000 (-0700) Subject: dtrace: Initial import of kernelspace code. X-Git-Tag: v4.1.12-92~313^2~194 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=1c9ecb4e39a1b901f4d79c393c04bd957b60f729;p=users%2Fjedix%2Flinux-maple.git dtrace: Initial import of kernelspace code. Signed-off-by: Nick Alcock --- diff --git a/arch/x86/include/asm/stacktrace.h b/arch/x86/include/asm/stacktrace.h index 70bbe39043a9..c57a075b9ef1 100644 --- a/arch/x86/include/asm/stacktrace.h +++ b/arch/x86/include/asm/stacktrace.h @@ -43,6 +43,13 @@ struct stacktrace_ops { walk_stack_t walk_stack; }; +extern unsigned long print_context_stack(struct thread_info *tinfo, + unsigned long *stack, + unsigned long bp, + const struct stacktrace_ops *ops, + void *data, unsigned long *end, + int *graph); + void dump_trace(struct task_struct *tsk, struct pt_regs *regs, unsigned long *stack, unsigned long bp, const struct stacktrace_ops *ops, void *data); diff --git a/include/linux/module.h b/include/linux/module.h index c883b86ea964..b3389fc9a5a4 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -350,6 +350,11 @@ struct module { bool klp_alive; #endif +#if defined(CONFIG_DTRACE) || defined(CONFIG_DTRACE_MODULE) + struct sdt_probedesc *sdt_probes; + unsigned int num_dtrace_probes; +#endif + #ifdef CONFIG_MODULE_UNLOAD /* What modules depend on me? */ struct list_head source_list; diff --git a/include/linux/sched.h b/include/linux/sched.h index 26a2e6122734..548a3b6c0037 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1713,6 +1713,14 @@ struct task_struct { int order; unsigned int may_oom:1; } memcg_oom; +#ifdef CONFIG_DTRACE + uint32_t predcache; + ktime_t dtrace_vtime; + ktime_t dtrace_start; + uint8_t dtrace_stop; + uint8_t dtrace_sig; + + void *dtrace_helpers; #endif #ifdef CONFIG_UPROBES struct uprobe_task *utask; diff --git a/include/linux/sdt.h b/include/linux/sdt.h new file mode 100644 index 000000000000..c339478653ce --- /dev/null +++ b/include/linux/sdt.h @@ -0,0 +1,442 @@ +#ifndef _SDT_H_ +#define _SDT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(CONFIG_DTRACE) || defined(CONFIG_DTRACE_MODULE) + +#ifndef __KERNEL__ + +#define DTRACE_PROBE(provider, name) { \ + extern void __dtrace_##provider##___##name(void); \ + __dtrace_##provider##___##name(); \ +} + +#define DTRACE_PROBE1(provider, name, arg1) { \ + extern void __dtrace_##provider##___##name(unsigned long); \ + __dtrace_##provider##___##name((unsigned long)arg1); \ +} + +#define DTRACE_PROBE2(provider, name, arg1, arg2) { \ + extern void __dtrace_##provider##___##name(unsigned long, \ + unsigned long); \ + __dtrace_##provider##___##name((unsigned long)arg1, \ + (unsigned long)arg2); \ +} + +#define DTRACE_PROBE3(provider, name, arg1, arg2, arg3) { \ + extern void __dtrace_##provider##___##name(unsigned long, \ + unsigned long, unsigned long); \ + __dtrace_##provider##___##name((unsigned long)arg1, \ + (unsigned long)arg2, (unsigned long)arg3); \ +} + +#define DTRACE_PROBE4(provider, name, arg1, arg2, arg3, arg4) { \ + extern void __dtrace_##provider##___##name(unsigned long, \ + unsigned long, unsigned long, unsigned long); \ + __dtrace_##provider##___##name((unsigned long)arg1, \ + (unsigned long)arg2, (unsigned long)arg3, \ + (unsigned long)arg4); \ +} + +#define DTRACE_PROBE5(provider, name, arg1, arg2, arg3, arg4, arg5) { \ + extern void __dtrace_##provider##___##name(unsigned long, \ + unsigned long, unsigned long, unsigned long, unsigned long);\ + __dtrace_##provider##___##name((unsigned long)arg1, \ + (unsigned long)arg2, (unsigned long)arg3, \ + (unsigned long)arg4, (unsigned long)arg5); \ +} + +#else /* __KERNEL__ */ + +#define DTRACE_PROBE(name) { \ + extern void __dtrace_probe_##name(void); \ + __dtrace_probe_##name(); \ +} + +#define DTRACE_PROBE1(name, type1, arg1) { \ + extern void __dtrace_probe_##name(uintptr_t); \ + __dtrace_probe_##name((uintptr_t)(arg1)); \ +} + +#define DTRACE_PROBE2(name, type1, arg1, type2, arg2) { \ + extern void __dtrace_probe_##name(uintptr_t, uintptr_t); \ + __dtrace_probe_##name((uintptr_t)(arg1), (uintptr_t)(arg2)); \ +} + +#define DTRACE_PROBE3(name, type1, arg1, type2, arg2, type3, arg3) { \ + extern void __dtrace_probe_##name(uintptr_t, uintptr_t, uintptr_t); \ + __dtrace_probe_##name((uintptr_t)(arg1), (uintptr_t)(arg2), \ + (uintptr_t)(arg3)); \ +} + +#define DTRACE_PROBE4(name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4) { \ + extern void __dtrace_probe_##name(uintptr_t, uintptr_t, \ + uintptr_t, uintptr_t); \ + __dtrace_probe_##name((uintptr_t)(arg1), (uintptr_t)(arg2), \ + (uintptr_t)(arg3), (uintptr_t)(arg4)); \ +} + +#define DTRACE_PROBE5(name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5) { \ + extern void __dtrace_probe_##name(uintptr_t, uintptr_t, \ + uintptr_t, uintptr_t, uintptr_t); \ + __dtrace_probe_##name((uintptr_t)(arg1), (uintptr_t)(arg2), \ + (uintptr_t)(arg3), (uintptr_t)(arg4), (uintptr_t)(arg5)); \ +} + +#define DTRACE_PROBE6(name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5, type6, arg6) { \ + extern void __dtrace_probe_##name(uintptr_t, uintptr_t, \ + uintptr_t, uintptr_t, uintptr_t, uintptr_t); \ + __dtrace_probe_##name((uintptr_t)(arg1), (uintptr_t)(arg2), \ + (uintptr_t)(arg3), (uintptr_t)(arg4), (uintptr_t)(arg5), \ + (uintptr_t)(arg6)); \ +} + +#define DTRACE_PROBE7(name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6, type7, arg7) { \ + extern void __dtrace_probe_##name(uintptr_t, uintptr_t, \ + uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); \ + __dtrace_probe_##name((uintptr_t)(arg1), (uintptr_t)(arg2), \ + (uintptr_t)(arg3), (uintptr_t)(arg4), (uintptr_t)(arg5), \ + (uintptr_t)(arg6), (uintptr_t)(arg7)); \ +} + +#define DTRACE_PROBE8(name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6, type7, arg7, type8, arg8) { \ + extern void __dtrace_probe_##name(uintptr_t, uintptr_t, \ + uintptr_t, uintptr_t, uintptr_t, uintptr_t, \ + uintptr_t, uintptr_t); \ + __dtrace_probe_##name((uintptr_t)(arg1), (uintptr_t)(arg2), \ + (uintptr_t)(arg3), (uintptr_t)(arg4), (uintptr_t)(arg5), \ + (uintptr_t)(arg6), (uintptr_t)(arg7), (uintptr_t)(arg8)); \ +} + +/* + * vmlinux dtrace_probe__ caller reloc info; + * comes from vmlinux_info.S + */ +extern unsigned long dtrace_relocs_count __attribute__((weak)); +extern void *dtrace_relocs __attribute__((weak)); + +struct reloc_info { + unsigned long probe_offset; + unsigned long section_base; + unsigned long probe_name_len; + char probe_name[0]; +} __aligned(sizeof(unsigned long)); + +void dtrace_register_builtins(void); + +#ifdef DEBUG +#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__, ## args) +#else +#define DPRINTK(fmt, args...) +#endif + +#endif /* __KERNEL__ */ + +#else /* DTRACE not enabled: */ + +#define DTRACE_PROBE(name) do { } while (0) +#define DTRACE_PROBE1(name, type1, arg1) DTRACE_PROBE(name) +#define DTRACE_PROBE2(name, type1, arg1, type2, arg2) DTRACE_PROBE(name) +#define DTRACE_PROBE3(name, type1, arg1, type2, arg2, type3, arg3) \ + DTRACE_PROBE(name) +#define DTRACE_PROBE4(name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4) DTRACE_PROBE(name) +#define DTRACE_PROBE5(name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5) DTRACE_PROBE(name) +#define DTRACE_PROBE6(name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6) DTRACE_PROBE(name) +#define DTRACE_PROBE7(name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6, type7, arg7) DTRACE_PROBE(name) +#define DTRACE_PROBE8(name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6, type7, arg7, type8, arg8) \ + DTRACE_PROBE(name) + +#endif /* CONFIG_DTRACE */ + + +#define DTRACE_SCHED(name) \ + DTRACE_PROBE(__sched_##name); + +#define DTRACE_SCHED1(name, type1, arg1) \ + DTRACE_PROBE1(__sched_##name, type1, arg1); + +#define DTRACE_SCHED2(name, type1, arg1, type2, arg2) \ + DTRACE_PROBE2(__sched_##name, type1, arg1, type2, arg2); + +#define DTRACE_SCHED3(name, type1, arg1, type2, arg2, type3, arg3) \ + DTRACE_PROBE3(__sched_##name, type1, arg1, type2, arg2, type3, arg3); + +#define DTRACE_SCHED4(name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4) \ + DTRACE_PROBE4(__sched_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4); + +#define DTRACE_PROC(name) \ + DTRACE_PROBE(__proc_##name); + +#define DTRACE_PROC1(name, type1, arg1) \ + DTRACE_PROBE1(__proc_##name, type1, arg1); + +#define DTRACE_PROC2(name, type1, arg1, type2, arg2) \ + DTRACE_PROBE2(__proc_##name, type1, arg1, type2, arg2); + +#define DTRACE_PROC3(name, type1, arg1, type2, arg2, type3, arg3) \ + DTRACE_PROBE3(__proc_##name, type1, arg1, type2, arg2, type3, arg3); + +#define DTRACE_PROC4(name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4) \ + DTRACE_PROBE4(__proc_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4); + +#define DTRACE_IO(name) \ + DTRACE_PROBE(__io_##name); + +#define DTRACE_IO1(name, type1, arg1) \ + DTRACE_PROBE1(__io_##name, type1, arg1); + +#define DTRACE_IO2(name, type1, arg1, type2, arg2) \ + DTRACE_PROBE2(__io_##name, type1, arg1, type2, arg2); + +#define DTRACE_IO3(name, type1, arg1, type2, arg2, type3, arg3) \ + DTRACE_PROBE3(__io_##name, type1, arg1, type2, arg2, type3, arg3); + +#define DTRACE_IO4(name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4) \ + DTRACE_PROBE4(__io_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4); + +#define DTRACE_ISCSI_2(name, type1, arg1, type2, arg2) \ + DTRACE_PROBE2(__iscsi_##name, type1, arg1, type2, arg2); + +#define DTRACE_ISCSI_3(name, type1, arg1, type2, arg2, type3, arg3) \ + DTRACE_PROBE3(__iscsi_##name, type1, arg1, type2, arg2, type3, arg3); + +#define DTRACE_ISCSI_4(name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4) \ + DTRACE_PROBE4(__iscsi_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4); + +#define DTRACE_ISCSI_5(name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5) \ + DTRACE_PROBE5(__iscsi_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5); + +#define DTRACE_ISCSI_6(name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5, type6, arg6) \ + DTRACE_PROBE6(__iscsi_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5, type6, arg6); + +#define DTRACE_ISCSI_7(name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5, type6, arg6, type7, arg7) \ + DTRACE_PROBE7(__iscsi_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5, type6, arg6, \ + type7, arg7); + +#define DTRACE_ISCSI_8(name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5, type6, arg6, \ + type7, arg7, type8, arg8) \ + DTRACE_PROBE8(__iscsi_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5, type6, arg6, \ + type7, arg7, type8, arg8); + +#define DTRACE_NFSV3_3(name, type1, arg1, type2, arg2, \ + type3, arg3) \ + DTRACE_PROBE3(__nfsv3_##name, type1, arg1, type2, arg2, \ + type3, arg3); +#define DTRACE_NFSV3_4(name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4) \ + DTRACE_PROBE4(__nfsv3_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4); + +#define DTRACE_NFSV4_1(name, type1, arg1) \ + DTRACE_PROBE1(__nfsv4_##name, type1, arg1); + +#define DTRACE_NFSV4_2(name, type1, arg1, type2, arg2) \ + DTRACE_PROBE2(__nfsv4_##name, type1, arg1, type2, arg2); + +#define DTRACE_NFSV4_3(name, type1, arg1, type2, arg2, type3, arg3) \ + DTRACE_PROBE3(__nfsv4_##name, type1, arg1, type2, arg2, type3, arg3); + +#define DTRACE_SMB_1(name, type1, arg1) \ + DTRACE_PROBE1(__smb_##name, type1, arg1); + +#define DTRACE_SMB_2(name, type1, arg1, type2, arg2) \ + DTRACE_PROBE2(__smb_##name, type1, arg1, type2, arg2); + +#define DTRACE_IP(name) \ + DTRACE_PROBE(__ip_##name); + +#define DTRACE_IP1(name, type1, arg1) \ + DTRACE_PROBE1(__ip_##name, type1, arg1); + +#define DTRACE_IP2(name, type1, arg1, type2, arg2) \ + DTRACE_PROBE2(__ip_##name, type1, arg1, type2, arg2); + +#define DTRACE_IP3(name, type1, arg1, type2, arg2, type3, arg3) \ + DTRACE_PROBE3(__ip_##name, type1, arg1, type2, arg2, type3, arg3); + +#define DTRACE_IP4(name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4) \ + DTRACE_PROBE4(__ip_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4); + +#define DTRACE_IP5(name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5) \ + DTRACE_PROBE5(__ip_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5); + +#define DTRACE_IP6(name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5, type6, arg6) \ + DTRACE_PROBE6(__ip_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5, type6, arg6); + +#define DTRACE_IP7(name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6, type7, arg7) \ + DTRACE_PROBE7(__ip_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5, type6, arg6, \ + type7, arg7); + +#define DTRACE_TCP(name) \ + DTRACE_PROBE(__tcp_##name); + +#define DTRACE_TCP1(name, type1, arg1) \ + DTRACE_PROBE1(__tcp_##name, type1, arg1); + +#define DTRACE_TCP2(name, type1, arg1, type2, arg2) \ + DTRACE_PROBE2(__tcp_##name, type1, arg1, type2, arg2); + +#define DTRACE_TCP3(name, type1, arg1, type2, arg2, type3, arg3) \ + DTRACE_PROBE3(__tcp_##name, type1, arg1, type2, arg2, type3, arg3); + +#define DTRACE_TCP4(name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4) \ + DTRACE_PROBE4(__tcp_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4); + +#define DTRACE_TCP5(name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5) \ + DTRACE_PROBE5(__tcp_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5); + +#define DTRACE_TCP6(name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5, type6, arg6) \ + DTRACE_PROBE6(__tcp_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5, type6, arg6); + +#define DTRACE_UDP(name) \ + DTRACE_PROBE(__udp_##name); + +#define DTRACE_UDP1(name, type1, arg1) \ + DTRACE_PROBE1(__udp_##name, type1, arg1); + +#define DTRACE_UDP2(name, type1, arg1, type2, arg2) \ + DTRACE_PROBE2(__udp_##name, type1, arg1, type2, arg2); + +#define DTRACE_UDP3(name, type1, arg1, type2, arg2, type3, arg3) \ + DTRACE_PROBE3(__udp_##name, type1, arg1, type2, arg2, type3, arg3); + +#define DTRACE_UDP4(name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4) \ + DTRACE_PROBE4(__udp_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4); + +#define DTRACE_UDP5(name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5) \ + DTRACE_PROBE5(__udp_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5); + + +#define DTRACE_SYSEVENT2(name, type1, arg1, type2, arg2) \ + DTRACE_PROBE2(__sysevent_##name, type1, arg1, type2, arg2); + +#define DTRACE_XPV(name) \ + DTRACE_PROBE(__xpv_##name); + +#define DTRACE_XPV1(name, type1, arg1) \ + DTRACE_PROBE1(__xpv_##name, type1, arg1); + +#define DTRACE_XPV2(name, type1, arg1, type2, arg2) \ + DTRACE_PROBE2(__xpv_##name, type1, arg1, type2, arg2); + +#define DTRACE_XPV3(name, type1, arg1, type2, arg2, type3, arg3) \ + DTRACE_PROBE3(__xpv_##name, type1, arg1, type2, arg2, type3, arg3); + +#define DTRACE_XPV4(name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4) \ + DTRACE_PROBE4(__xpv_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4); + +#define DTRACE_FC_1(name, type1, arg1) \ + DTRACE_PROBE1(__fc_##name, type1, arg1); + +#define DTRACE_FC_2(name, type1, arg1, type2, arg2) \ + DTRACE_PROBE2(__fc_##name, type1, arg1, type2, arg2); + +#define DTRACE_FC_3(name, type1, arg1, type2, arg2, type3, arg3) \ + DTRACE_PROBE3(__fc_##name, type1, arg1, type2, arg2, type3, arg3); + +#define DTRACE_FC_4(name, type1, arg1, type2, arg2, type3, arg3, type4, arg4) \ + DTRACE_PROBE4(__fc_##name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4); + +#define DTRACE_FC_5(name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5) \ + DTRACE_PROBE5(__fc_##name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5); + +#define DTRACE_SRP_1(name, type1, arg1) \ + DTRACE_PROBE1(__srp_##name, type1, arg1); + +#define DTRACE_SRP_2(name, type1, arg1, type2, arg2) \ + DTRACE_PROBE2(__srp_##name, type1, arg1, type2, arg2); + +#define DTRACE_SRP_3(name, type1, arg1, type2, arg2, type3, arg3) \ + DTRACE_PROBE3(__srp_##name, type1, arg1, type2, arg2, type3, arg3); + +#define DTRACE_SRP_4(name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4) \ + DTRACE_PROBE4(__srp_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4); + +#define DTRACE_SRP_5(name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5) \ + DTRACE_PROBE5(__srp_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5); + +#define DTRACE_SRP_6(name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6) \ + DTRACE_PROBE6(__srp_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5, type6, arg6); + +#define DTRACE_SRP_7(name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6, type7, arg7) \ + DTRACE_PROBE7(__srp_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5, type6, arg6, type7, arg7); + +#define DTRACE_SRP_8(name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6, type7, arg7, type8, arg8) \ + DTRACE_PROBE8(__srp_##name, type1, arg1, type2, arg2, \ + type3, arg3, type4, arg4, type5, arg5, type6, arg6, \ + type7, arg7, type8, arg8); + +extern const char *sdt_prefix; + +typedef struct sdt_probedesc { + char *sdpd_name; /* name of this probe */ + unsigned long sdpd_offset; /* offset of call in text */ + struct sdt_probedesc *sdpd_next; /* next static probe */ +} sdt_probedesc_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _SDT_H_ */ diff --git a/init/Kconfig b/init/Kconfig index dc24dec60232..3fcaedfb970d 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1764,6 +1764,8 @@ config PROFILING Say Y here to enable the extended profiling support mechanisms used by profilers such as OProfile. +source "kernel/dtrace/Kconfig" + # # Place an empty function call at each tracepoint site. Can be # dynamically changed for a probe function. diff --git a/init/main.c b/init/main.c index 2115055faeac..da860d2f58c8 100644 --- a/init/main.c +++ b/init/main.c @@ -81,6 +81,7 @@ #include #include #include +#include #include #include @@ -673,6 +674,10 @@ asmlinkage __visible void __init start_kernel(void) ftrace_init(); +#if defined(CONFIG_DT_SDT) || defined(CONFIG_DT_SDT_MODULE) + dtrace_register_builtins(); +#endif + /* Do the rest non-__init'ed, we're now alive */ rest_init(); } diff --git a/kernel/Makefile b/kernel/Makefile index 60c302cfb4d3..6e6e12873c43 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -98,6 +98,7 @@ obj-$(CONFIG_CRASH_DUMP) += crash_dump.o obj-$(CONFIG_JUMP_LABEL) += jump_label.o obj-$(CONFIG_CONTEXT_TRACKING) += context_tracking.o obj-$(CONFIG_TORTURE_TEST) += torture.o +obj-$(CONFIG_DTRACE) += dtrace/ $(obj)/configs.o: $(obj)/config_data.h diff --git a/kernel/dtrace/Kconfig b/kernel/dtrace/Kconfig new file mode 100644 index 000000000000..72a1f2aca856 --- /dev/null +++ b/kernel/dtrace/Kconfig @@ -0,0 +1,70 @@ +# +# DTrace Configuration +# + +menuconfig DTRACE + bool "DTrace (Dynamic Tracing) Support" + default y + depends on X86_64 + select KALLSYMS + help + To be written. + +if DTRACE + +config DT_CORE + tristate "DTrace core" + default m + help + To be written. + +if DT_CORE + +config DT_FASTTRAP + tristate "Fasttrap Tracing" + default m + help + To be written. + +config DT_FBT + tristate "Function Boundary Tracing" + default m + help + To be written. + +config DT_LOCKSTAT + tristate "Lock Statistics" + default m + help + To be written. + +config DT_PROFILE + tristate "Profile Interrupt Tracing" + default m + help + To be written. + +config DT_SDT + tristate "Statically Defined Tracing" + default m + select KALLSYMS + help + To be written. + +config DT_SYSTRACE + tristate "System Call Tracing" + default m + help + To be written. + +config DT_DEBUG + bool "DTrace debugging" + default m + help + This controls the inclusion of various piece of code that perform + internal checks within the DTrace core. It also enables all the + assertions within the DTrace code. + +endif # DT_CORE + +endif #DTRACE diff --git a/kernel/dtrace/Makefile b/kernel/dtrace/Makefile new file mode 100644 index 000000000000..b200a7fb7a66 --- /dev/null +++ b/kernel/dtrace/Makefile @@ -0,0 +1,29 @@ +# +# Makefile for DTrace +# + +obj-$(CONFIG_DT_CORE) += dtrace.o +obj-$(CONFIG_DT_FASTTRAP) += fasttrap.o +obj-$(CONFIG_DT_FBT) += fbt.o +obj-$(CONFIG_DT_LOCKSTAT) += lockstat.o +obj-$(CONFIG_DT_PROFILE) += profile.o +obj-$(CONFIG_DT_SDT) += sdt.o sdt_register.o +obj-$(CONFIG_DT_SYSTRACE) += systrace.o + +dtrace-y := dtrace_mod.o dtrace_dev.o \ + dtrace_asm.o dtrace_isa.o \ + dtrace_actdesc.o dtrace_anon.o \ + dtrace_buffer.o dtrace_dif.o dtrace_dof.o \ + dtrace_ecb.o dtrace_enable.o \ + dtrace_fmt.o dtrace_hash.o dtrace_helper.o \ + dtrace_match.o dtrace_priv.o \ + dtrace_probe.o dtrace_probe_ctx.o \ + dtrace_ptofapi.o dtrace_predicate.o \ + dtrace_spec.o dtrace_state.o dtrace_util.o \ + cyclic.o +fasttrap-y := fasttrap_mod.o fasttrap_dev.o +fbt-y := fbt_mod.o fbt_dev.o +lockstat-y := lockstat_mod.o lockstat_dev.o +profile-y := profile_mod.o profile_dev.o +sdt-y := sdt_mod.o sdt_dev.o +systrace-y := systrace_mod.o systrace_dev.o diff --git a/kernel/dtrace/cyclic.c b/kernel/dtrace/cyclic.c new file mode 100644 index 000000000000..881b30a4e6a3 --- /dev/null +++ b/kernel/dtrace/cyclic.c @@ -0,0 +1,23 @@ +/* + * FILE: cyclic.c + * DESCRIPTION: Cyclic implementation + * + * Copyright (C) 2010 Oracle Corporation + */ + +#include "cyclic.h" + +/* + * Add a new cyclic to the system. + */ +cyclic_id_t cyclic_add(cyc_handler_t *hdlr, cyc_time_t *when) +{ + return 0; +} + +/* + * Remove the specific cyclic from the system. + */ +void cyclic_remove(cyclic_id_t id) +{ +} diff --git a/kernel/dtrace/cyclic.h b/kernel/dtrace/cyclic.h new file mode 100644 index 000000000000..c4b5057a4ca1 --- /dev/null +++ b/kernel/dtrace/cyclic.h @@ -0,0 +1,33 @@ +#ifndef _CYCLIC_H_ +#define _CYCLIC_H_ + +#include +#include + +#define CY_LOW_LEVEL 0 +#define CY_LOCK_LEVEL 1 +#define CY_HIGH_LEVEL 2 +#define CY_SOFT_LEVELS 2 +#define CY_LEVELS 3 + +typedef uintptr_t cyclic_id_t; +typedef uint16_t cyc_level_t; +typedef void (*cyc_func_t)(void *); + +#define CYCLIC_NONE ((cyclic_id_t)0) + +typedef struct cyc_handler { + cyc_func_t cyh_func; + void *cyh_arg; + cyc_level_t cyh_level; +} cyc_handler_t; + +typedef struct cyc_time { + ktime_t cyt_when; + ktime_t cyt_interval; +} cyc_time_t; + +extern cyclic_id_t cyclic_add(cyc_handler_t *, cyc_time_t *); +extern void cyclic_remove(cyclic_id_t); + +#endif /* _CYCLIC_H_ */ diff --git a/kernel/dtrace/dtrace_ioctl.h b/kernel/dtrace/dtrace_ioctl.h new file mode 100644 index 000000000000..dc219378cd59 --- /dev/null +++ b/kernel/dtrace/dtrace_ioctl.h @@ -0,0 +1,30 @@ +#ifndef _DTRACE_IOCTL_H_ +#define _DTRACE_IOCTL_H_ + +#include +#include + +#define DTRACEIOC 0xd4 +#define DTRACEIOC_PROVIDER _IOR(DTRACEIOC, 1, dtrace_providerdesc_t) +#define DTRACEIOC_PROBES _IOR(DTRACEIOC, 2, dtrace_probedesc_t) +#define DTRACEIOC_BUFSNAP _IOR(DTRACEIOC, 4, dtrace_bufdesc_t) +#define DTRACEIOC_PROBEMATCH _IOR(DTRACEIOC, 5, dtrace_probedesc_t) +#define DTRACEIOC_ENABLE _IOW(DTRACEIOC, 6, void *) +#define DTRACEIOC_AGGSNAP _IOR(DTRACEIOC, 7, dtrace_bufdesc_t) +#define DTRACEIOC_EPROBE _IOW(DTRACEIOC, 8, dtrace_eprobedesc_t) +#define DTRACEIOC_PROBEARG _IOR(DTRACEIOC, 9, dtrace_argdesc_t) +#define DTRACEIOC_CONF _IOR(DTRACEIOC, 10, dtrace_conf_t) +#define DTRACEIOC_STATUS _IOR(DTRACEIOC, 11, dtrace_status_t) +#define DTRACEIOC_GO _IOW(DTRACEIOC, 12, processorid_t) +#define DTRACEIOC_STOP _IOW(DTRACEIOC, 13, processorid_t) +#define DTRACEIOC_AGGDESC _IOR(DTRACEIOC, 15, dtrace_aggdesc_t) +#define DTRACEIOC_FORMAT _IOR(DTRACEIOC, 16, dtrace_fmtdesc_t) +#define DTRACEIOC_DOFGET _IOR(DTRACEIOC, 17, dof_hdr_t) +#define DTRACEIOC_REPLICATE _IOR(DTRACEIOC, 18, void *) + +#define DTRACEHIOC 0xd8 +#define DTRACEHIOC_ADD _IOW(DTRACEHIOC, 1, int) +#define DTRACEHIOC_REMOVE _IOW(DTRACEHIOC, 2, int) +#define DTRACEHIOC_ADDDOF _IOW(DTRACEHIOC, 3, dof_helper_t) + +#endif diff --git a/kernel/dtrace/sdt_register.c b/kernel/dtrace/sdt_register.c new file mode 100644 index 000000000000..2a00c982491a --- /dev/null +++ b/kernel/dtrace/sdt_register.c @@ -0,0 +1,118 @@ +/* register static dtrace probe points */ + +#define DEBUG 1 + +#include +#include +#include +#include +#include + +///#include +///#include +///#include +///#include +///#include +///#include +///#include +///#include +///#include +///#include +///#include "reloc.h" + +#define LOAD_ADDR 0xffffffff00000000ULL /* temporary */ + +#define SDT_NOP 0x90 +#define SDT_NOPS 5 + +const char *sdt_prefix = "__dtrace_probe_"; + +static struct module *kernmod; /* for kernel builtins; TBD: temporary ??? */ + +static int sdt_reloc_resolve(struct module *mp, char *symname, uint8_t *instr) +{ + sdt_probedesc_t *sdp; + int i; + + /* + * The "statically defined tracing" (SDT) provider for DTrace uses + * a mechanism similar to TNF, but somewhat simpler. (Surprise, + * surprise.) The SDT mechanism works by replacing calls to the + * undefined routine __dtrace_probe_[name] with nop instructions. + * The relocations are logged, and SDT itself will later patch the + * running binary appropriately. + */ + if (strncmp(symname, sdt_prefix, strlen(sdt_prefix)) != 0) + return 1; + + symname += strlen(sdt_prefix); + + sdp = kmalloc(sizeof(sdt_probedesc_t), GFP_KERNEL); + if (!sdp) + return 1; + sdp->sdpd_name = kmalloc(strlen(symname) + 1, GFP_KERNEL); + if (!sdp->sdpd_name) { + kfree(sdp); + return 1; + } + memcpy(sdp->sdpd_name, symname, strlen(symname) + 1); + + /* TBD: use a kernel list? */ + sdp->sdpd_offset = (uintptr_t)instr; + sdp->sdpd_next = mp->sdt_probes; + mp->sdt_probes = sdp; + + DPRINTK("sdt_probes -> 0x%p\n", mp->sdt_probes); + DPRINTK("this probe: instr offset=0x%lx, next ptr=0x%p, probe_name=%s\n", + sdp->sdpd_offset, sdp->sdpd_next, sdp->sdpd_name); +#if 0 + // NOT YET SAFE, since instr is relative, not absolute // + /* TBD: need a safer write-to-exec-memory ? */ + for (i = 0; i < SDT_NOPS; i++) + instr[i - 1] = SDT_NOP; +#endif + + return 0; +} + +void dtrace_register_builtins(void) +{ + unsigned long cnt; + struct reloc_info *ri = (struct reloc_info *)&dtrace_relocs; + void *nextri; + + kernmod = kzalloc(sizeof(struct module), GFP_KERNEL); + if (!kernmod) { + printk(KERN_WARNING + "%s: cannot allocate kernel builtin module memory\n", + __func__); + return; + } + + DPRINTK("%lu SDT relocation entries beg. @0x%p\n", + dtrace_relocs_count, &dtrace_relocs); + + if (dtrace_relocs_count == 0) + return; + + for (cnt = 0; cnt < dtrace_relocs_count; cnt++) { + DPRINTK("SDT relocs [%lu]: " + "probe_offset=0x%lx, section_base=0x%lx, " + "name_len=0x%lx, probe_name=%s\n", + cnt, ri->probe_offset, ri->section_base, + ri->probe_name_len, ri->probe_name); + if (sdt_reloc_resolve(kernmod, ri->probe_name, + (uint8_t *)ri->probe_offset)) + printk(KERN_WARNING "%s: cannot resolve %s\n", + __func__, ri->probe_name); + + nextri = (void *)ri + sizeof(struct reloc_info) + + roundup(ri->probe_name_len, BITS_PER_LONG / 8); + ri = nextri; + DPRINTK("SDT relocs: next entry at 0x%p\n", ri); + } + +#if 0 + dtrace_module_loaded(kernmod); +#endif +} diff --git a/scripts/Makefile b/scripts/Makefile index 2016a64497ab..7fe1c538a257 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -3,6 +3,7 @@ # the kernel for the build process. # --------------------------------------------------------------------------- # kallsyms: Find all symbols in vmlinux +# dtrace_relocs: find and save all __dtrace_probe_ calling probepoints # pnmttologo: Convert pnm files to logo files # conmakehash: Create chartable # conmakehash: Create arrays for initializing the kernel console tables @@ -11,6 +12,7 @@ HOST_EXTRACFLAGS += -I$(srctree)/tools/include hostprogs-$(CONFIG_KALLSYMS) += kallsyms +hostprogs-$(CONFIG_DTRACE) += dtrace_relocs hostprogs-$(CONFIG_LOGO) += pnmtologo hostprogs-$(CONFIG_VT) += conmakehash hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount diff --git a/scripts/dtrace_relocs.c b/scripts/dtrace_relocs.c new file mode 100644 index 000000000000..a220d7b27070 --- /dev/null +++ b/scripts/dtrace_relocs.c @@ -0,0 +1,398 @@ +/* Generate assembler source containing __dtrace_probe_* calls (reloc info) + * + * Based on scripts/kallsyms.c + * Copyright 2002 by Kai Germaschewski + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + * Usage: dtrace_relocs input_file_text output_file_elf + */ + +#include +#include +#include +#include +#include +#include + +struct sym_entry { + unsigned long long addr; + unsigned char is_section; + unsigned char section_used; + int section_index; + unsigned long long section_base; + unsigned int len; + char *sym; +}; + +struct text_range { + const char *stext, *etext; + unsigned long long start, end; +}; + +static unsigned long long _text; // TBD: need this from System.map + +static struct sym_entry *table; +static unsigned int table_size, table_cnt; +static int this_section_index; +static unsigned long long this_section_addr; +static int relocs_count; + +static void usage(void) +{ + fprintf(stderr, "Usage: dtrace_relocs input_file_text output_file_elf\n"); + exit(1); +} + +// skip over whitespace (spaces or tabs) +static char *deblank(char *str) +{ + while (*str == ' ' || *str == '\t') + str++; + return str; +} + +static int find_section(char *sect, int sectlen) +{ + int ix; + struct sym_entry *sym = table; + +#if 0 + fprintf(stderr, "%s: search for sect=<%s>, sectlen=%d:\n", + __func__, sect, sectlen); +#endif + + for (ix = 0; ix < table_cnt; ix++, sym++) { +#if 0 + if (sym->is_section && strlen(sym->sym) == sectlen) + fprintf(stderr, "%s: ix=%d, symlen=%d, symname=<%s>\n", + __func__, ix, strlen(sym->sym), sym->sym); +#endif + if (sym->is_section && strlen(sym->sym) == sectlen && + strncmp(sym->sym, sect, sectlen) == 0) { + sym->section_used = true; + this_section_addr = sym->addr; + return sym->section_index; + } + } + + return -1; +} + +static int get_this_section(char buf[500]) +{ + char *sect; + int sectlen; + int sect_index; + + if (strncmp(buf, "RELOCATION RECORDS FOR [", 24) != 0) { + fprintf(stderr, "Bad relocation header: %s\n", buf); + exit(2); + } + + sect = buf + 24; + sectlen = strlen(sect); // includes a trailing newline +#if 0 + fprintf(stderr, "%s: sect=<%s>, sectlen=%d\n", + __func__, sect, sectlen); +#endif + if (*(sect + sectlen - 3) != ']' || *(sect + sectlen - 2) != ':') { + fprintf(stderr, "Bad relocation header: %s\n", buf); + exit(2); + } + *(sect + sectlen - 3) = '\0'; + sectlen -= 3; // drop the "]:\n" +#if 0 + fprintf(stderr, "%s: isolated section name=<%s>\n", __func__, sect); +#endif + sect_index = find_section(sect, sectlen); + if (sect_index < 0) { + fprintf(stderr, "Bad section name in relocation header: %s\n", + sect); + exit(2); + } + + return sect_index; +} + +/* + * scans 2 lines of section info; + * first line is already in buf; + * second line is noise for now; + */ +static int get_section_info(FILE *fin, char buf[500], struct sym_entry *sect) +{ + int rc; + int sect_index; + char sect_name[200], sect_align[100]; + unsigned long sect_size, file_offset; + unsigned long long vma, lma; + char sect_flags[500]; + char *flags; + + rc = sscanf(buf, " %d %s %lx %llx %llx %lx %s \n", + §_index, (char *)§_name, §_size, &vma, &lma, + &file_offset, (char *)§_align); +#ifdef SCANF + fprintf(stderr, "%s: sscanf.1 rc= %d\n", __func__, rc); +#endif + if (rc != 7) + return -1; + + if (!fgets(sect_flags, sizeof(sect_flags), fin)) + return -1; + +#ifdef SCANF + fprintf(stderr, "%s: fgets.2 read=<%s>", __func__, sect_flags); +#endif + flags = deblank(sect_flags); + + sect->addr = file_offset; + sect->is_section = true; + sect->section_used = false; + sect->section_index = sect_index; + sect->len = sect_size; + sect->sym = malloc(strlen(sect_name)); + if (!sect->sym) { + fprintf(stderr, "relocs failure: " + "unable to allocate required amount of memory\n"); + exit(1); + } + strcpy((char *)sect->sym, sect_name); + +#ifdef INFO + fprintf(stderr, "sect: index=%d, name=%s, addr/offset=0x%llx, sect_size=0x%x, align=%s, vma=0x%llx, lma=0x%llx, flags=%s\n", + sect_index, sect->sym, sect->addr, sect->len, sect_align, + vma, lma, flags); +#endif + + return 0; +} + +static int get_symbol_info(char buf[500], struct sym_entry *s) +{ + int rc; + unsigned long long relo_offset, pp_offset; + char relo_type[200]; + char probepoint[200]; + + //rc = sscanf(buf, " %llx %s %200s-%llx \n", + rc = sscanf(buf, " %llx %s %[^ -]-%llx \n", + &relo_offset, (char *)&relo_type, + (char *)&probepoint, &pp_offset); +#ifdef SCANF + fprintf(stderr, "%s: sscanf.1 rc= %d\n", __func__, rc); +#endif + if (rc != 4) + return -1; + + s->addr = relo_offset; + s->len = strlen(probepoint); + s->is_section = false; + s->section_used = false; + s->section_index = -1; + s->section_base = this_section_addr; + s->sym = malloc(s->len + 1); + if (!s->sym) { + fprintf(stderr, "relocs failure: " + "unable to allocate required amount of memory\n"); + exit(1); + } + strcpy((char *)s->sym, probepoint); + +#ifdef INFO + fprintf(stderr, "sym: addr/offset=0x%llx, strlen=%d, type=%s, name=%s\n", + s->addr, s->len, relo_type, s->sym); +#endif + + relocs_count++; + return 0; +} + +static void read_info(FILE *fin) +{ + char buf[500]; + bool in_sections = false, in_symbols = false; + + while (!feof(fin)) { + if (table_cnt >= table_size) { + table_size += 10000; + table = realloc(table, sizeof(*table) * table_size); + if (!table) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + } + + if (!fgets(buf, sizeof(buf), fin)) + break; +#ifdef SCANF + fprintf(stderr, "dtr: buf=<%s>\n", buf); +#endif + + if (strncmp(buf, "Sections:", 9) == 0) { + in_sections = true; + continue; + } + if (strncmp(buf, "RELOCATION RECORDS", 11) == 0) { + in_sections = false; + in_symbols = true; + // isolate & look up section name, get its index + // this call also sets 'this_section_addr' + this_section_index = get_this_section(buf); + continue; + } + + if (in_sections) { + if (strncmp(buf, "Idx ", 4) != 0) + if (get_section_info(fin, buf, &table[table_cnt]) == 0) + table_cnt++; + continue; + } + + if (in_symbols) + if (get_symbol_info(buf, &table[table_cnt]) == 0) + table_cnt++; + } +} + +static void output_label(FILE *fout, char *label) +{ + fprintf(fout, ".globl %s\n", label); + fprintf(fout, "\tALGN\n"); + fprintf(fout, "%s:\n", label); +} + +static void write_relocs(FILE *fout) +{ + unsigned int i; + int reloc_count = 0; + + fprintf(fout, "#include \n"); + fprintf(fout, "#if BITS_PER_LONG == 64\n"); + fprintf(fout, "#define PTR .quad\n"); + fprintf(fout, "#define ALGN .align 8\n"); + fprintf(fout, "#else\n"); + fprintf(fout, "#define PTR .long\n"); + fprintf(fout, "#define ALGN .align 4\n"); + fprintf(fout, "#endif\n"); + + fprintf(fout, "\t.section .rodata, \"a\"\n"); + fprintf(fout, "\n"); + + output_label(fout, "dtrace_relocs_count"); + fprintf(fout, "\tPTR\t%d\n", relocs_count); + fprintf(fout, "\n"); + + /* + * Provide proper symbols relocatability by their '_text' + * relativeness. The symbol names cannot be used to construct + * normal symbol references as the list of symbols contains + * symbols that are declared static and are private to their + * .o files. This prevents .tmp_kallsyms.o or any other + * object from referencing them. + */ + output_label(fout, "dtrace_relocs"); + for (i = 0; i < table_cnt; i++) { +#if 0 + if (_text <= table[i].addr) + fprintf(fout, "\tPTR\t_text + %#llx\n", + table[i].addr - _text); + else + fprintf(fout, "\tPTR\t_text - %#llx\n", + _text - table[i].addr); +#endif + // for reloc symbols (not sections): + // print symbol relative address, section base address, + // call target string length, call target string/name; + // TBD: figure out how to find the next 4-tuple; + if (!table[i].is_section) { + fprintf(fout, "\tPTR\t%#llx\n", table[i].addr); + fprintf(fout, "\tPTR\t%#llx\n", table[i].section_base); + fprintf(fout, "\tPTR\t%d\n", table[i].len); + fprintf(fout, "\t.asciz\t\"%s\"\n", table[i].sym); + fprintf(fout, "\tALGN\n"); + reloc_count++; + } + } + + fprintf(fout, "\n"); + + if (reloc_count != relocs_count) { + fprintf(fout, "relocs error: reloc counters do not agree (%d vs. %d\n)", + relocs_count, reloc_count); + exit(3); + } +} + +#if 0 +/* guess for "linker script provide" symbol */ +static int may_be_linker_script_provide_symbol(const struct sym_entry *se) +{ + const char *symbol = (char *)se->sym + 1; + int len = se->len - 1; + + if (len < 8) + return 0; + + if (symbol[0] != '_' || symbol[1] != '_') + return 0; + + /* __start_XXXXX */ + if (!memcmp(symbol + 2, "start_", 6)) + return 1; + + /* __stop_XXXXX */ + if (!memcmp(symbol + 2, "stop_", 5)) + return 1; + + /* __end_XXXXX */ + if (!memcmp(symbol + 2, "end_", 4)) + return 1; + + /* __XXXXX_start */ + if (!memcmp(symbol + len - 6, "_start", 6)) + return 1; + + /* __XXXXX_end */ + if (!memcmp(symbol + len - 4, "_end", 4)) + return 1; + + return 0; +} +#endif + +int main(int argc, char *argv[]) +{ + char *infile, *outfile; + FILE *fin, *fout; + + + if (argc != 3) + usage(); + + infile = argv[1]; + outfile = argv[2]; + + fin = fopen(infile, "r"); + if (!fin) { + fprintf(stderr, "relocs: cannot open input file '%s'\n", + infile); + exit(2); + } + fout = fopen(outfile, "w"); + if (!fout) { + fprintf(stderr, "relocs: cannot create output file '%s'\n", + outfile); + exit(2); + } + + read_info(fin); + fclose(fin); + + write_relocs(fout); + fclose(fout); + + return 0; +}