tracing: kprobe-based event tracer and x86 instruction decoder
Subsystems (5)
v6 cover tracing: kprobe-based event tracer and x86 instruction decoder
Hi,
Here are the patches of kprobe-based event tracer for x86, version 6, which allows you to probe various kernel events through ftrace interface. I merged tracer patches into the last patch. Now the tracer supports selftest and basic filter function.
This version supports only x86(-32/-64) (but porting it on other arch just needs kprobes/kretprobes and register and stack access APIs).
This patchset also includes x86(-64) instruction decoder which supports non-SSE/FP opcodes and includes x86 opcode map. I think it will be possible to share this opcode map with KVM's decoder.
This series can be applied on the latest linux-2.6-tip tree.
This patchset includes following changes:
- Add x86 instruction decoder [1/5]
- Check insertion point safety in kprobe [2/5]
- Cleanup fix_riprel() with insn decoder [3/5]
- Add arch-dep register and stack fetching functions [4/5]
- Add kprobe-based event tracer [5/5]
Future items:
- Support per-probe event-filtering interface.
- .init function tracing support.
- Support primitive types(long, ulong, int, uint, etc) for args.
Kprobe-based Event Tracer
=========================
Overview
--------
This tracer is similar to the events tracer which is based on Tracepoint infrastructure. Instead of Tracepoint, this tracer is based on kprobes(kprobe and kretprobe). It probes anywhere where kprobes can probe(this means, all functions body except for __kprobes functions).
Unlike the function tracer, this tracer can probe instructions inside of kernel functions. It allows you to check which instruction has been executed.
Unlike the Tracepoint based events tracer, this tracer can add new probe points on the fly.
Similar to the events tracer, this tracer doesn't need to be activated via current_tracer, instead of that, just set probe points via /debug/tracing/kprobe_events.
Synopsis of kprobe_events
-------------------------
p SYMBOL[+offs|-offs]|MEMADDR [FETCHARGS] : set a probe
r SYMBOL[+0] [FETCHARGS] : set a return probe
FETCHARGS:
%REG : Fetch register REG
sN : Fetch Nth entry of stack (N >= 0)
@ADDR : Fetch memory at ADDR (ADDR should be in kernel)
@SYM[+|-offs] : Fetch memory at SYM +|- offs (SYM should be a data symbol)
aN : Fetch function argument. (N >= 0)(*)
rv : Fetch return value.(**)
ra : Fetch return address.(**)
+|-offs(FETCHARG) : fetch memory at FETCHARG +|- offs address.(***)
(*) aN may not correct on asmlinkaged functions and at the middle of
function body.
(**) only for return probe.
(***) this is useful for fetching a field of data structures.
Usage examples
--------------
echo p do_sys_open a0 a1 a2 a3 > /debug/tracing/kprobe_events
This sets a kprobe on the top of do_sys_open() function with recording
1st to 4th arguments.
echo r do_sys_open rv ra >> /debug/tracing/kprobe_events
This sets a kretprobe on the return point of do_sys_open() function with
recording return value and return address.
echo > /debug/tracing/kprobe_events
This clears all probe points. and you can see the traced information via
/debug/tracing/trace.
cat /debug/tracing/trace
# tracer: nop
#
# TASK-PID CPU# TIMESTAMP FUNCTION
# | | | | |
<...>-1447 [001] 1038282.286875: do_sys_open+0x0/0xd6: 0x3 0x7fffd1ec4440 0x8000 0x0
<...>-1447 [001] 1038282.286878: sys_openat+0xc/0xe <-do_sys_open: 0xfffffffffffffffe 0xffffffff81367a3a
<...>-1447 [001] 1038282.286885: do_sys_open+0x0/0xd6: 0xffffff9c 0x40413c 0x8000 0x1b6
<...>-1447 [001] 1038282.286915: sys_open+0x1b/0x1d <-do_sys_open: 0x3 0xffffffff81367a3a
<...>-1447 [001] 1038282.286969: do_sys_open+0x0/0xd6: 0xffffff9c 0x4041c6 0x98800 0x10
<...>-1447 [001] 1038282.286976: sys_open+0x1b/0x1d <-do_sys_open: 0x3 0xffffffff81367a3a Each line shows when the kernel hits a probe, and <- SYMBOL means kernel
returns from SYMBOL(e.g. "sys_open+0x1b/0x1d <- do_sys_open" means kernel returns from do_sys_open to sys_open+0x1b).
Thank you,
---
Masami Hiramatsu (5):
tracing: add kprobe-based event tracer
x86: add pt_regs register and stack access APIs
kprobes: cleanup fix_riprel() using insn decoder on x86
kprobes: checks probe address is instruction boudary on x86
x86: instruction decorder API
Documentation/trace/kprobes.txt | 81 +++ arch/x86/include/asm/inat.h | 125 ++++ arch/x86/include/asm/insn.h | 134 +++++ arch/x86/include/asm/ptrace.h | 67 ++ arch/x86/kernel/kprobes.c | 197 +++---- arch/x86/kernel/ptrace.c | 60 ++ arch/x86/lib/Makefile | 13 arch/x86/lib/inat.c | 80 +++ arch/x86/lib/insn.c | 471 +++++++++++++++++ arch/x86/lib/x86-opcode-map.txt | 711 +++++++++++++++++++++++++ arch/x86/scripts/gen-insn-attr-x86.awk | 314 +++++++++++ kernel/trace/Kconfig | 9 kernel/trace/Makefile | 1 kernel/trace/trace.h | 22 + kernel/trace/trace_event_types.h | 20 + kernel/trace/trace_kprobe.c | 903 ++++++++++++++++++++++++++++++++ 16 files changed, 3103 insertions(+), 105 deletions(-) create mode 100644 Documentation/trace/kprobes.txt create mode 100644 arch/x86/include/asm/inat.h create mode 100644 arch/x86/include/asm/insn.h create mode 100644 arch/x86/lib/inat.c create mode 100644 arch/x86/lib/insn.c create mode 100644 arch/x86/lib/x86-opcode-map.txt create mode 100644 arch/x86/scripts/gen-insn-attr-x86.awk create mode 100644 kernel/trace/trace_kprobe.c
Signature · 7 lines
Masami Hiramatsu Software Engineer Hitachi Computer Products (America) Inc. Software Solutions Division e-mail: [email protected]
PATCH v6 1/5 x86: instruction decorder API
Add x86 instruction decoder to arch-specific libraries. This decoder can decode x86 instructions used in kernel into prefix, opcode, modrm, sib, displacement and immediates. This can also show the length of instructions.
This version introduces instruction attributes for decoding instructions. The instruction attribute tables are generated from the opcode map file (x86-opcode-map.txt) by the generator script(gen-insn-attr-x86.awk).
Currently, the opcode maps are based on opcode maps in Intel(R) 64 and IA-32 Architectures Software Developers Manual Vol.2: Appendix.A, and consist of below two types of opcode tables.
1-byte/2-bytes/3-bytes opcodes, which has 256 elements, are written as below;
Table: table-name
Referrer: escaped-name
opcode: mnemonic|GrpXXX [operand1[,operand2...]] [(extra1)[,(extra2)...] [| 2nd-mnemonic ...]
(or)
opcode: escape # escaped-name
EndTable
Group opcodes, which has 8 elements, are written as below;
GrpTable: GrpXXX
reg: mnemonic [operand1[,operand2...]] [(extra1)[,(extra2)...] [| 2nd-mnemonic ...] EndTable
These opcode maps do NOT include most of SSE and FP opcodes, because those opcodes are not used in the kernel.
Changes from v5:
- Rename insn_init_kernel() to kernel_insn_init().
- Use $(AWK) instead of awk in Makefile
- Use /usr/bin/env to find awk
- Use charactor classes instead of [a-z] etc.
- Remove inat-tables.c when running make clean
TODO:
- Integrate user-space test harness as a build-time test.
---
arch/x86/include/asm/inat.h | 125 ++++++ arch/x86/include/asm/insn.h | 134 ++++++ arch/x86/lib/Makefile | 13 + arch/x86/lib/inat.c | 80 ++++ arch/x86/lib/insn.c | 471 +++++++++++++++++++++ arch/x86/lib/x86-opcode-map.txt | 711 ++++++++++++++++++++++++++++++++ arch/x86/scripts/gen-insn-attr-x86.awk | 314 ++++++++++++++ 7 files changed, 1848 insertions(+), 0 deletions(-) create mode 100644 arch/x86/include/asm/inat.h create mode 100644 arch/x86/include/asm/insn.h create mode 100644 arch/x86/lib/inat.c create mode 100644 arch/x86/lib/insn.c create mode 100644 arch/x86/lib/x86-opcode-map.txt create mode 100644 arch/x86/scripts/gen-insn-attr-x86.awk
arch/x86/include/asm/inat.h new file +125 -0
@@ -0,0 +1,125 @@ +#ifndef _ASM_INAT_INAT_H +#define _ASM_INAT_INAT_H +/* +* x86 instruction attributes +* +* Written by Masami Hiramatsu <[email protected]> +* +* 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 <linux/types.h> + +/* Instruction attributes */ +typedef u32 insn_attr_t ; + +/* +* Internal bits. Don't use bitmasks directly, because these bits are +* unstable. You should add checking macros and use that macro in +* your code. +*/ + +#define INAT_OPCODE_TABLE_SIZE 256 +#define INAT_GROUP_TABLE_SIZE 8 + +/* Legacy instruction prefixes */ +#define INAT_PFX_OPNDSZ 1/* 0x66 */ /* LPFX1 */ +#define INAT_PFX_REPNE 2/* 0xF2 */ /* LPFX2 */ +#define INAT_PFX_REPE 3/* 0xF3 */ /* LPFX3 */ +#define INAT_PFX_LOCK 4/* 0xF0 */ +#define INAT_PFX_CS 5/* 0x2E */ +#define INAT_PFX_DS 6/* 0x3E */ +#define INAT_PFX_ES 7/* 0x26 */ +#define INAT_PFX_FS 8/* 0x64 */ +#define INAT_PFX_GS 9/* 0x65 */ +#define INAT_PFX_SS 10/* 0x36 */ +#define INAT_PFX_ADDRSZ 11/* 0x67 */ + +#define INAT_LPREFIX_MAX 3 + +/* Immediate size */ +#define INAT_IMM_BYTE 1 +#define INAT_IMM_WORD 2 +#define INAT_IMM_DWORD 3 +#define INAT_IMM_QWORD 4 +#define INAT_IMM_PTR 5 +#define INAT_IMM_VWORD32 6 +#define INAT_IMM_VWORD 7 + +/* Legacy prefix */ +#define INAT_PFX_OFFS 0 +#define INAT_PFX_BITS 4 +#define INAT_PFX_MAX ((1 << INAT_PFX_BITS) - 1) +#define INAT_PFX_MASK (INAT_PFX_MAX << INAT_PFX_OFFS) +/* Escape opcodes */ +#define INAT_ESC_OFFS (INAT_PFX_OFFS + INAT_PFX_BITS) +#define INAT_ESC_BITS 2 +#define INAT_ESC_MAX ((1 << INAT_ESC_BITS) - 1) +#define INAT_ESC_MASK (INAT_ESC_MAX << INAT_ESC_OFFS) +/* Group opcodes (1-16) */ +#define INAT_GRP_OFFS (INAT_ESC_OFFS + INAT_ESC_BITS) +#define INAT_GRP_BITS 5 +#define INAT_GRP_MAX ((1 << INAT_GRP_BITS) - 1) +#define INAT_GRP_MASK (INAT_GRP_MAX << INAT_GRP_OFFS) +/* Immediates */ +#define INAT_IMM_OFFS (INAT_GRP_OFFS + INAT_GRP_BITS) +#define INAT_IMM_BITS 3 +#define INAT_IMM_MASK (((1 << INAT_IMM_BITS) - 1) << INAT_IMM_OFFS) +/* Flags */ +#define INAT_FLAG_OFFS (INAT_IMM_OFFS + INAT_IMM_BITS) +#define INAT_REXPFX (1 << INAT_FLAG_OFFS) +#define INAT_MODRM (1 << (INAT_FLAG_OFFS + 1)) +#define INAT_FORCE64 (1 << (INAT_FLAG_OFFS + 2)) +#define INAT_ADDIMM (1 << (INAT_FLAG_OFFS + 3)) +#define INAT_MOFFSET (1 << (INAT_FLAG_OFFS + 4)) +#define INAT_VARIANT (1 << (INAT_FLAG_OFFS + 5)) + +/* Attribute search APIs */ +extern insn_attr_t inat_get_opcode_attribute (u8 opcode); +extern insn_attr_t inat_get_escape_attribute (u8 opcode,u8 last_pfx, +insn_attr_t esc_attr); +extern insn_attr_t inat_get_group_attribute (u8 modrm,u8 last_pfx, +insn_attr_t esc_attr); + +/* Attribute checking macros. Use these macros in your code */ +#define INAT_IS_PREFIX (attr) (attr & INAT_PFX_MASK) +#define INAT_IS_ADDRSZ (attr) ((attr & INAT_PFX_MASK) == INAT_PFX_ADDRSZ) +#define INAT_IS_OPNDSZ (attr) ((attr & INAT_PFX_MASK) == INAT_PFX_OPNDSZ) +#define INAT_LPREFIX_NUM (attr) \ + (((attr & INAT_PFX_MASK) > INAT_LPREFIX_MAX) ? 0 :\ + (attr & INAT_PFX_MASK)) +#define INAT_MAKE_PREFIX (pfx) (pfx << INAT_PFX_OFFS) + +#define INAT_IS_ESCAPE (attr) (attr & INAT_ESC_MASK) +#define INAT_ESCAPE_NUM (attr) ((attr & INAT_ESC_MASK) >> INAT_ESC_OFFS) +#define INAT_MAKE_ESCAPE (esc) (esc << INAT_ESC_OFFS) + +#define INAT_IS_GROUP (attr) (attr & INAT_GRP_MASK) +#define INAT_GROUP_NUM (attr) ((attr & INAT_GRP_MASK) >> INAT_GRP_OFFS) +#define INAT_GROUP_COMMON (attr) (attr & ~INAT_GRP_MASK) +#define INAT_MAKE_GROUP (grp) ((grp << INAT_GRP_OFFS) | INAT_MODRM) + +#define INAT_HAS_IMM (attr) (attr & INAT_IMM_MASK) +#define INAT_IMM_SIZE (attr) ((attr & INAT_IMM_MASK) >> INAT_IMM_OFFS) +#define INAT_MAKE_IMM (imm) (imm << INAT_IMM_OFFS) + +#define INAT_IS_REX_PREFIX (attr) (attr & INAT_REXPFX) +#define INAT_HAS_MODRM (attr) (attr & INAT_MODRM) +#define INAT_IS_FORCE64 (attr) (attr & INAT_FORCE64) +#define INAT_HAS_ADDIMM (attr) (attr & INAT_ADDIMM) +#define INAT_HAS_MOFFSET (attr) (attr & INAT_MOFFSET) +#define INAT_HAS_VARIANT (attr) (attr & INAT_VARIANT) + +#endif
arch/x86/include/asm/insn.h new file +134 -0
134 lines not on this page · raw message
arch/x86/lib/Makefile +13 -0
@@ -2,12 +2,25 @@ # Makefile for x86 specific library files. # +inat_tables_script = $(srctree)/arch/x86/scripts/gen-insn-attr-x86.awk +inat_tables_maps = $(srctree)/arch/x86/lib/x86-opcode-map.txt +quiet_cmd_inat_tables = GEN $@ + cmd_inat_tables = $(AWK) -f $(inat_tables_script) $(inat_tables_maps) > $@ + +$(obj)/inat-tables.c: $(inat_tables_script) $(inat_tables_maps) + $(call cmd,inat_tables) + +$(obj)/inat.o: $(obj)/inat-tables.c + +clean-files := inat-tables.c + obj-$(CONFIG_SMP) := msr-on-cpu.o lib-y := delay.o lib-y += thunk_$(BITS).o lib-y += usercopy_$(BITS).o getuser.o putuser.o lib-y += memcpy_$(BITS).o +lib-y += insn.o inat.o ifeq ($(CONFIG_X86_32),y) lib-y += checksum_32.o
arch/x86/lib/inat.c new file +80 -0
80 lines not on this page · raw message
arch/x86/lib/insn.c new file +471 -0
471 lines not on this page · raw message
arch/x86/lib/x86-opcode-map.txt new file +711 -0
711 lines not on this page · raw message
arch/x86/scripts/gen-insn-attr-x86.awk new file +314 -0
314 lines not on this page · raw message
Signature · 7 lines
Masami Hiramatsu Software Engineer Hitachi Computer Products (America) Inc. Software Solutions Division e-mail: [email protected]
Re: [PATCH -tip v6 1/5] x86: instruction decorder API
Quoted 3 lines · Masami Hiramatsu
Changes from v5:
- Use
/usr/bin/envto find awk
This is fine, but not in the below case.
arch/x86/scripts/gen-insn-attr-x86.awk line 1
Quoted 4 lines
arch/x86/scripts/gen-insn-attr-x86.awk new file +1
-0@@ -0,0 +1,314 @@ +#!/usr/bin/env awk -f
Depending on multi-argument shebang is IMO bad. In shebang generally (but not in all systems) everything after interpreter part is treated as the first argument. Therefore executing your gen-insn-attr-x86.awk is equivalent to:
/usr/bin/env 'awk -f' gen-insn-attr-x86.awk
which is obviously wrong.
Signature · 1 line
Przemysław Pawełczyk
Re: [PATCH -tip v6 1/5] x86: instruction decorder API
Quoted 18 lines · Przemysław Pawełczyk
Quoted 3 lines · Masami Hiramatsu
Changes from v5:
- Use
/usr/bin/envto find awkThis is fine, but not in the below case.
arch/x86/scripts/gen-insn-attr-x86.awk line 1
Quoted 4 lines
arch/x86/scripts/gen-insn-attr-x86.awk new file +1
-0@@ -0,0 +1,314 @@ +#!/usr/bin/env awk -fDepending on multi-argument shebang is IMO bad. In shebang generally (but not in all systems) everything after interpreter part is treated as the first argument. Therefore executing your gen-insn-attr-x86.awk is equivalent to:
Quoted 3 lines
/usr/bin/env'awk -f' gen-insn-attr-x86.awkwhich is obviously wrong.
Oops, right. Anyway, there is no reason that we make it executable because it's always called from Makefile. So, I think just removing that line is better way.
Thank you,
Signature · 7 lines
Masami Hiramatsu Software Engineer Hitachi Computer Products (America) Inc. Software Solutions Division e-mail: [email protected]
H. Peter Anvin
reviewerRe: [PATCH -tip v6 1/5] x86: instruction decorder API
Quoted 9 lines · Masami Hiramatsu
Quoted 4 lines
/usr/bin/env'awk -f' gen-insn-attr-x86.awkwhich is obviously wrong.
Oops, right. Anyway, there is no reason that we make it executable because it's always called from
Makefile. So, I think just removing that line is better way.
I suggest leaving in a #!/bin/awk line just for documentation's sake, though.
-hpa
Signature · 2 lines
H. Peter Anvin, Intel Open Source Technology Center I work for Intel. I don't speak on their behalf.
Re: [PATCH -tip v6 1/5] x86: instruction decorder API
Quoted 11 lines · H. Peter Anvin
Quoted 7 lines · Masami Hiramatsu
Quoted 3 lines
/usr/bin/env'awk -f' gen-insn-attr-x86.awkwhich is obviously wrong.
Oops, right. Anyway, there is no reason that we make it executable because it's always called from
Makefile. So, I think just removing that line is better way.I suggest leaving in a #!/bin/awk line just for documentation's sake, though.
OK, I'd leave that, and change usage line to use awk command instead of run the script directly.
Thank you!
Quoted 3 lines
-hpa
Signature · 7 lines
Masami Hiramatsu Software Engineer Hitachi Computer Products (America) Inc. Software Solutions Division e-mail: [email protected]
PATCH v6 1/5 x86: instruction decorder API
Add x86 instruction decoder to arch-specific libraries. This decoder can decode x86 instructions used in kernel into prefix, opcode, modrm, sib, displacement and immediates. This can also show the length of instructions.
This version introduces instruction attributes for decoding instructions. The instruction attribute tables are generated from the opcode map file (x86-opcode-map.txt) by the generator script(gen-insn-attr-x86.awk).
Currently, the opcode maps are based on opcode maps in Intel(R) 64 and IA-32 Architectures Software Developers Manual Vol.2: Appendix.A, and consist of below two types of opcode tables.
1-byte/2-bytes/3-bytes opcodes, which has 256 elements, are written as below;
Table: table-name
Referrer: escaped-name
opcode: mnemonic|GrpXXX [operand1[,operand2...]] [(extra1)[,(extra2)...] [| 2nd-mnemonic ...]
(or)
opcode: escape # escaped-name
EndTable
Group opcodes, which has 8 elements, are written as below;
GrpTable: GrpXXX
reg: mnemonic [operand1[,operand2...]] [(extra1)[,(extra2)...] [| 2nd-mnemonic ...] EndTable
These opcode maps do NOT include most of SSE and FP opcodes, because those opcodes are not used in the kernel.
Changes from v6:
- Use /bin/awk -f for shebang line of awk script(just for documantation's sake)
- Set rex_prefix.got true after decoding prefixes.
TODO:
- Integrate user-space test harness as a build-time test.
---
arch/x86/include/asm/inat.h | 125 ++++++ arch/x86/include/asm/insn.h | 134 ++++++ arch/x86/lib/Makefile | 13 + arch/x86/lib/inat.c | 80 ++++ arch/x86/lib/insn.c | 471 +++++++++++++++++++++ arch/x86/lib/x86-opcode-map.txt | 711 ++++++++++++++++++++++++++++++++ arch/x86/scripts/gen-insn-attr-x86.awk | 314 ++++++++++++++ 7 files changed, 1848 insertions(+), 0 deletions(-) create mode 100644 arch/x86/include/asm/inat.h create mode 100644 arch/x86/include/asm/insn.h create mode 100644 arch/x86/lib/inat.c create mode 100644 arch/x86/lib/insn.c create mode 100644 arch/x86/lib/x86-opcode-map.txt create mode 100644 arch/x86/scripts/gen-insn-attr-x86.awk
arch/x86/include/asm/inat.h new file +125 -0
@@ -0,0 +1,125 @@ +#ifndef _ASM_INAT_INAT_H +#define _ASM_INAT_INAT_H +/* +* x86 instruction attributes +* +* Written by Masami Hiramatsu <[email protected]> +* +* 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 <linux/types.h> + +/* Instruction attributes */ +typedef u32 insn_attr_t ; + +/* +* Internal bits. Don't use bitmasks directly, because these bits are +* unstable. You should add checking macros and use that macro in +* your code. +*/ + +#define INAT_OPCODE_TABLE_SIZE 256 +#define INAT_GROUP_TABLE_SIZE 8 + +/* Legacy instruction prefixes */ +#define INAT_PFX_OPNDSZ 1/* 0x66 */ /* LPFX1 */ +#define INAT_PFX_REPNE 2/* 0xF2 */ /* LPFX2 */ +#define INAT_PFX_REPE 3/* 0xF3 */ /* LPFX3 */ +#define INAT_PFX_LOCK 4/* 0xF0 */ +#define INAT_PFX_CS 5/* 0x2E */ +#define INAT_PFX_DS 6/* 0x3E */ +#define INAT_PFX_ES 7/* 0x26 */ +#define INAT_PFX_FS 8/* 0x64 */ +#define INAT_PFX_GS 9/* 0x65 */ +#define INAT_PFX_SS 10/* 0x36 */ +#define INAT_PFX_ADDRSZ 11/* 0x67 */ + +#define INAT_LPREFIX_MAX 3 + +/* Immediate size */ +#define INAT_IMM_BYTE 1 +#define INAT_IMM_WORD 2 +#define INAT_IMM_DWORD 3 +#define INAT_IMM_QWORD 4 +#define INAT_IMM_PTR 5 +#define INAT_IMM_VWORD32 6 +#define INAT_IMM_VWORD 7 + +/* Legacy prefix */ +#define INAT_PFX_OFFS 0 +#define INAT_PFX_BITS 4 +#define INAT_PFX_MAX ((1 << INAT_PFX_BITS) - 1) +#define INAT_PFX_MASK (INAT_PFX_MAX << INAT_PFX_OFFS) +/* Escape opcodes */ +#define INAT_ESC_OFFS (INAT_PFX_OFFS + INAT_PFX_BITS) +#define INAT_ESC_BITS 2 +#define INAT_ESC_MAX ((1 << INAT_ESC_BITS) - 1) +#define INAT_ESC_MASK (INAT_ESC_MAX << INAT_ESC_OFFS) +/* Group opcodes (1-16) */ +#define INAT_GRP_OFFS (INAT_ESC_OFFS + INAT_ESC_BITS) +#define INAT_GRP_BITS 5 +#define INAT_GRP_MAX ((1 << INAT_GRP_BITS) - 1) +#define INAT_GRP_MASK (INAT_GRP_MAX << INAT_GRP_OFFS) +/* Immediates */ +#define INAT_IMM_OFFS (INAT_GRP_OFFS + INAT_GRP_BITS) +#define INAT_IMM_BITS 3 +#define INAT_IMM_MASK (((1 << INAT_IMM_BITS) - 1) << INAT_IMM_OFFS) +/* Flags */ +#define INAT_FLAG_OFFS (INAT_IMM_OFFS + INAT_IMM_BITS) +#define INAT_REXPFX (1 << INAT_FLAG_OFFS) +#define INAT_MODRM (1 << (INAT_FLAG_OFFS + 1)) +#define INAT_FORCE64 (1 << (INAT_FLAG_OFFS + 2)) +#define INAT_ADDIMM (1 << (INAT_FLAG_OFFS + 3)) +#define INAT_MOFFSET (1 << (INAT_FLAG_OFFS + 4)) +#define INAT_VARIANT (1 << (INAT_FLAG_OFFS + 5)) + +/* Attribute search APIs */ +extern insn_attr_t inat_get_opcode_attribute (u8 opcode); +extern insn_attr_t inat_get_escape_attribute (u8 opcode,u8 last_pfx, +insn_attr_t esc_attr); +extern insn_attr_t inat_get_group_attribute (u8 modrm,u8 last_pfx, +insn_attr_t esc_attr); + +/* Attribute checking macros. Use these macros in your code */ +#define INAT_IS_PREFIX (attr) (attr & INAT_PFX_MASK) +#define INAT_IS_ADDRSZ (attr) ((attr & INAT_PFX_MASK) == INAT_PFX_ADDRSZ) +#define INAT_IS_OPNDSZ (attr) ((attr & INAT_PFX_MASK) == INAT_PFX_OPNDSZ) +#define INAT_LPREFIX_NUM (attr) \ + (((attr & INAT_PFX_MASK) > INAT_LPREFIX_MAX) ? 0 :\ + (attr & INAT_PFX_MASK)) +#define INAT_MAKE_PREFIX (pfx) (pfx << INAT_PFX_OFFS) + +#define INAT_IS_ESCAPE (attr) (attr & INAT_ESC_MASK) +#define INAT_ESCAPE_NUM (attr) ((attr & INAT_ESC_MASK) >> INAT_ESC_OFFS) +#define INAT_MAKE_ESCAPE (esc) (esc << INAT_ESC_OFFS) + +#define INAT_IS_GROUP (attr) (attr & INAT_GRP_MASK) +#define INAT_GROUP_NUM (attr) ((attr & INAT_GRP_MASK) >> INAT_GRP_OFFS) +#define INAT_GROUP_COMMON (attr) (attr & ~INAT_GRP_MASK) +#define INAT_MAKE_GROUP (grp) ((grp << INAT_GRP_OFFS) | INAT_MODRM) + +#define INAT_HAS_IMM (attr) (attr & INAT_IMM_MASK) +#define INAT_IMM_SIZE (attr) ((attr & INAT_IMM_MASK) >> INAT_IMM_OFFS) +#define INAT_MAKE_IMM (imm) (imm << INAT_IMM_OFFS) + +#define INAT_IS_REX_PREFIX (attr) (attr & INAT_REXPFX) +#define INAT_HAS_MODRM (attr) (attr & INAT_MODRM) +#define INAT_IS_FORCE64 (attr) (attr & INAT_FORCE64) +#define INAT_HAS_ADDIMM (attr) (attr & INAT_ADDIMM) +#define INAT_HAS_MOFFSET (attr) (attr & INAT_MOFFSET) +#define INAT_HAS_VARIANT (attr) (attr & INAT_VARIANT) + +#endif
arch/x86/include/asm/insn.h new file +134 -0
134 lines not on this page · raw message
arch/x86/lib/Makefile +13 -0
@@ -2,12 +2,25 @@ # Makefile for x86 specific library files. # +inat_tables_script = $(srctree)/arch/x86/scripts/gen-insn-attr-x86.awk +inat_tables_maps = $(srctree)/arch/x86/lib/x86-opcode-map.txt +quiet_cmd_inat_tables = GEN $@ + cmd_inat_tables = $(AWK) -f $(inat_tables_script) $(inat_tables_maps) > $@ + +$(obj)/inat-tables.c: $(inat_tables_script) $(inat_tables_maps) + $(call cmd,inat_tables) + +$(obj)/inat.o: $(obj)/inat-tables.c + +clean-files := inat-tables.c + obj-$(CONFIG_SMP) := msr-on-cpu.o lib-y := delay.o lib-y += thunk_$(BITS).o lib-y += usercopy_$(BITS).o getuser.o putuser.o lib-y += memcpy_$(BITS).o +lib-y += insn.o inat.o ifeq ($(CONFIG_X86_32),y) lib-y += checksum_32.o
arch/x86/lib/inat.c new file +80 -0
80 lines not on this page · raw message
arch/x86/lib/insn.c new file +471 -0
471 lines not on this page · raw message
arch/x86/lib/x86-opcode-map.txt new file +711 -0
711 lines not on this page · raw message
arch/x86/scripts/gen-insn-attr-x86.awk new file +314 -0
314 lines not on this page · raw message
Signature · 7 lines
Masami Hiramatsu Software Engineer Hitachi Computer Products (America) Inc. Software Solutions Division e-mail: [email protected]
Re: [PATCH -tip v6.1 1/5] x86: instruction decorder API
This might be an unimportant detail, but I would suggest to fix the "decorder" typo in the Subject before the next revision. It seems to have persisted over 6 revisions now, but it still irritatess me @)
-Andi
Re: [PATCH -tip v6.1 1/5] x86: instruction decoder API
Quoted 3 lines · Andi Kleen
This might be an unimportant detail, but I would suggest to fix the "decorder" typo in the Subject before the next revision. It seems to have persisted over 6 revisions now, but it still irritatess me @)
Oops, sorry...
Signature · 7 lines
Masami Hiramatsu Software Engineer Hitachi Computer Products (America) Inc. Software Solutions Division e-mail: [email protected]
PATCH v6 2/5 kprobes: checks probe address is instruction boudary on x86
In reply to Masami HiramatsuEnsure safeness of inserting kprobes by checking whether the specified address is at the first byte of a instruction on x86.
This is done by decoding probed function from its head to the probe point.
Changes from v5:
- Add comments to explain recover_probed_function() and decording logic.
---
arch/x86/kernel/kprobes.c | 69 +++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 69 insertions(+), 0 deletions(-)
arch/x86/kernel/kprobes.c +69 -0
@@ -48,12 +48,14 @@#include <linux/preempt.h> #include <linux/module.h> #include <linux/kdebug.h> +#include <linux/kallsyms.h> #include <asm/cacheflush.h> #include <asm/desc.h> #include <asm/pgtable.h> #include <asm/uaccess.h> #include <asm/alternative.h> +#include <asm/insn.h> void jprobe_return_end (void );
@@ -244,6 +246,71 @@ retry: } } +/* Recover the probed instruction at addr for further analysis. */ +static int recover_probed_instruction (kprobe_opcode_t *buf,unsigned long addr) +{ +struct kprobe *kp; + kp =get_kprobe ((void *)addr); +if (!kp) +return -EINVAL ; + +/* +* Basically, kp->ainsn.insn has an original instruction. +* However, RIP-relative instruction can not do single-stepping +* at different place, fix_riprel() tweaks the displacement of +* that instruction. In that case, we can't recover the instruction +* from the kp->ainsn.insn. +* +* On the other hand, kp->opcode has a copy of the first byte of +* the probed instruction, which is overwritten by int3. And +* the instruction at kp->addr is not modified by kprobes except +* for the first byte, we can recover the original instruction +* from it and kp->opcode. +*/ +memcpy (buf, kp->addr,MAX_INSN_SIZE *sizeof (kprobe_opcode_t)); + buf[0 ] = kp->opcode; +return 0 ; +} + +/* Dummy buffers for kallsyms_lookup */ +static char __dummy_buf[KSYM_NAME_LEN ]; + +/* Check if paddr is at an instruction boundary */ +static int __kprobes can_probe (unsigned long paddr) +{ +int ret; +unsigned long addr, offset =0 ; +struct insn insn; +kprobe_opcode_t buf[MAX_INSN_SIZE ]; + +if (!kallsyms_lookup (paddr,NULL , &offset,NULL , __dummy_buf)) +return 0 ; + +/* Decode instructions */ + addr = paddr - offset; +while (addr < paddr) { +kernel_insn_init (&insn, (void *)addr); +insn_get_opcode (&insn); + +/* Check if the instruction has been modified. */ +if (OPCODE1 (&insn) ==BREAKPOINT_INSTRUCTION ) { + ret =recover_probed_instruction (buf, addr); +if (ret) +/* +* Another debugging subsystem might insert +* this breakpoint. In that case, we can't +* recover it. +*/ +return 0 ; +kernel_insn_init (&insn, buf); + } +insn_get_length (&insn); + addr += insn.length; + } + +return (addr == paddr); +} +/* * Returns non-zero if opcode modifies the interrupt flag. */
@@ -359,6 +426,8 @@ static void __kprobes arch_copy_kprobe(struct kprobe *p)int __kprobes arch_prepare_kprobe (struct kprobe *p) { +if (!can_probe ((unsigned long )p->addr)) +return -EILSEQ ;/* insn: must be on special executable page on x86. */ p->ainsn.insn =get_insn_slot ();if (!p->ainsn.insn)
Signature · 7 lines
Masami Hiramatsu Software Engineer Hitachi Computer Products (America) Inc. Software Solutions Division e-mail: [email protected]
PATCH v6 3/5 kprobes: cleanup fix_riprel() using insn decoder on x86
In reply to Masami HiramatsuCleanup fix_riprel() in arch/x86/kernel/kprobes.c by using x86 instruction decoder.
---
arch/x86/kernel/kprobes.c | 128 ++++++++------------------------------------- 1 files changed, 23 insertions(+), 105 deletions(-)
arch/x86/kernel/kprobes.c +23 -105
@@ -108,50 +108,6 @@ static const u32 twobyte_is_boostable[256 / 32] = {/* ----------------------------------------------- */ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ }; -static const u32 onebyte_has_modrm[256 /32 ] = { -/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ -/* ----------------------------------------------- */ -W (0x00 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ) |/* 00 */ -W (0x10 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ) ,/* 10 */ -W (0x20 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ) |/* 20 */ -W (0x30 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ) ,/* 30 */ -W (0x40 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ) |/* 40 */ -W (0x50 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ) ,/* 50 */ -W (0x60 ,0 ,0 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,1 ,0 ,1 ,0 ,0 ,0 ,0 ) |/* 60 */ -W (0x70 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ) ,/* 70 */ -W (0x80 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ) |/* 80 */ -W (0x90 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ) ,/* 90 */ -W (0xa0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ) |/* a0 */ -W (0xb0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ) ,/* b0 */ -W (0xc0 ,1 ,1 ,0 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ) |/* c0 */ -W (0xd0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ) ,/* d0 */ -W (0xe0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ) |/* e0 */ -W (0xf0 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 )/* f0 */ -/* ----------------------------------------------- */ -/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ -}; -static const u32 twobyte_has_modrm[256 /32 ] = { -/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ -/* ----------------------------------------------- */ -W (0x00 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,0 ,1 ) |/* 0f */ -W (0x10 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ) ,/* 1f */ -W (0x20 ,1 ,1 ,1 ,1 ,1 ,0 ,1 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ) |/* 2f */ -W (0x30 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ) ,/* 3f */ -W (0x40 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ) |/* 4f */ -W (0x50 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ) ,/* 5f */ -W (0x60 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ) |/* 6f */ -W (0x70 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ) ,/* 7f */ -W (0x80 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ) |/* 8f */ -W (0x90 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ) ,/* 9f */ -W (0xa0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ) |/* af */ -W (0xb0 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ) ,/* bf */ -W (0xc0 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ) |/* cf */ -W (0xd0 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ) ,/* df */ -W (0xe0 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ) |/* ef */ -W (0xf0 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,0 )/* ff */ -/* ----------------------------------------------- */ -/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ -};#undef Wstruct kretprobe_blackpoint kretprobe_blacklist[] = {
@@ -344,68 +300,30 @@ static int __kprobes is_IF_modifier(kprobe_opcode_t *insn)static void __kprobes fix_riprel (struct kprobe *p) {#ifdef CONFIG_X86_64 -u8 *insn = p->ainsn.insn; -s64 disp; -int need_modrm; - -/* Skip legacy instruction prefixes. */ -while (1 ) { -switch (*insn) { -case 0x66 : -case 0x67 : -case 0x2e : -case 0x3e : -case 0x26 : -case 0x64 : -case 0x65 : -case 0x36 : -case 0xf0 : -case 0xf3 : -case 0xf2 : - ++insn; -continue ; - } -break ; - } +struct insn insn; +kernel_insn_init (&insn, p->ainsn.insn); -/* Skip REX instruction prefix. */ -if (is_REX_prefix (insn)) - ++insn; - -if (*insn ==0x0f ) { -/* Two-byte opcode. */ - ++insn; - need_modrm =test_bit (*insn, - (unsigned long *)twobyte_has_modrm); - }else -/* One-byte opcode. */ - need_modrm =test_bit (*insn, - (unsigned long *)onebyte_has_modrm); - -if (need_modrm) { -u8 modrm = *++insn; -if ((modrm &0xc7 ) ==0x05 ) { -/* %rip+disp32 addressing mode */ -/* Displacement follows ModRM byte. */ - ++insn; -/* -* The copied instruction uses the %rip-relative -* addressing mode. Adjust the displacement for the -* difference between the original location of this -* instruction and the location of the copy that will -* actually be run. The tricky bit here is making sure -* that the sign extension happens correctly in this -* calculation, since we need a signed 32-bit result to -* be sign-extended to 64 bits when it's added to the -* %rip value and yield the same 64-bit result that the -* sign-extension of the original signed 32-bit -* displacement would have given. -*/ - disp = (u8 *) p->addr + *((s32 *) insn) - - (u8 *) p->ainsn.insn; -BUG_ON ((s64 ) (s32 ) disp != disp);/* Sanity check. */ - *(s32 *)insn = (s32 ) disp; - } +if (insn_rip_relative (&insn)) { +s64 newdisp; +u8 *disp; +insn_get_displacement (&insn); +/* +* The copied instruction uses the %rip-relative addressing +* mode. Adjust the displacement for the difference between +* the original location of this instruction and the location +* of the copy that will actually be run. The tricky bit here +* is making sure that the sign extension happens correctly in +* this calculation, since we need a signed 32-bit result to +* be sign-extended to 64 bits when it's added to the %rip +* value and yield the same 64-bit result that the sign- +* extension of the original signed 32-bit displacement would +* have given. +*/ + newdisp = (u8 *) p->addr + (s64 ) insn.displacement.value - + (u8 *) p->ainsn.insn; +BUG_ON ((s64 ) (s32 ) newdisp != newdisp);/* Sanity check. */ + disp = (u8 *) p->ainsn.insn +INSN_DISPLACEMENT_OFFS (&insn); + *(s32 *) disp = (s32 ) newdisp; }#endif }
Signature · 7 lines
Masami Hiramatsu Software Engineer Hitachi Computer Products (America) Inc. Software Solutions Division e-mail: [email protected]
PATCH v6 4/5 x86: add pt_regs register and stack access APIs
In reply to Masami HiramatsuAdd following APIs for accessing registers and stack entries from pt_regs.
- query_register_offset(const char *name) Query the offset of "name" register.
- query_register_name(unsigned offset)
Query the name of register by its offset.
- get_register(struct pt_regs *regs, unsigned offset) Get the value of a register by its offset.
- within_kernel_stack(struct pt_regs *regs, unsigned long addr) Check the address is in the kernel stack.
- get_kernel_stack_nth(struct pt_regs *reg, unsigned nth) Get Nth entry of the kernel stack. (N >= 0)
- get_argument_nth(struct pt_regs *reg, unsigned nth) Get Nth argument at function call. (N >= 0)
Changes from v5:
- Rename valid_stack_address to within_kernel_stack.
- Rename get_stack_nth to get_kernel_stack_nth.
---
arch/x86/include/asm/ptrace.h | 67 +++++++++++++++++++++++++++++++++++++++++ arch/x86/kernel/ptrace.c | 60 +++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 0 deletions(-)
arch/x86/include/asm/ptrace.h +67 -0
@@ -7,6 +7,7 @@#ifdef __KERNEL__#include <asm/segment.h> +#include <asm/page_types.h> #endif #ifndef __ASSEMBLY__
@@ -216,6 +217,72 @@ static inline unsigned long user_stack_pointer(struct pt_regs *regs)return regs->sp; } +/* Query offset/name of register from its name/offset */ +extern int query_register_offset (const char *name); +extern const char *query_register_name (unsigned offset); +#define MAX_REG_OFFSET (offsetof(struct pt_regs, ss)) + +/* Get register value from its offset */ +static inline unsigned long get_register (struct pt_regs *regs,unsigned offset) +{ +if (unlikely (offset >MAX_REG_OFFSET )) +return 0 ; +return *(unsigned long *)((unsigned long )regs + offset); +} + +/* Check the address in the stack */ +static inline int within_kernel_stack (struct pt_regs *regs,unsigned long addr) +{ +return ((addr & ~(THREAD_SIZE -1 )) == + (kernel_stack_pointer (regs) & ~(THREAD_SIZE -1 ))); +} + +/* Get Nth entry of the stack */ +static inline unsigned long get_kernel_stack_nth (struct pt_regs *regs, +unsigned n) +{ +unsigned long *addr = (unsigned long *)kernel_stack_pointer (regs); + addr += n; +if (within_kernel_stack (regs, (unsigned long )addr)) +return *addr; +else +return 0 ; +} + +/* Get Nth argument at function call */ +static inline unsigned long get_argument_nth (struct pt_regs *regs,unsigned n) +{ +#ifdef CONFIG_X86_32 +#define NR_REGPARMS 3 +if (n <NR_REGPARMS ) { +switch (n) { +case 0 :return regs->ax; +case 1 :return regs->dx; +case 2 :return regs->cx; + } +return 0 ; +#else /* CONFIG_X86_64 */ +#define NR_REGPARMS 6 +if (n <NR_REGPARMS ) { +switch (n) { +case 0 :return regs->di; +case 1 :return regs->si; +case 2 :return regs->dx; +case 3 :return regs->cx; +case 4 :return regs->r8; +case 5 :return regs->r9; + } +return 0 ; +#endif + }else { +/* +* The typical case: arg n is on the stack. +* (Note: stack[0] = return address, so skip it) +*/ +return get_kernel_stack_nth (regs,1 + n -NR_REGPARMS ); + } +} +/* * These are defined as per linux/ptrace.h, which see. */
arch/x86/kernel/ptrace.c +60 -0
66 lines not on this page · raw message
Signature · 7 lines
Masami Hiramatsu Software Engineer Hitachi Computer Products (America) Inc. Software Solutions Division e-mail: [email protected]
PATCH v6 5/5 tracing: add kprobe-based event tracer
In reply to Masami HiramatsuAdd kprobes based event tracer on ftrace.
This tracer is similar to the events tracer which is based on Tracepoint infrastructure. Instead of Tracepoint, this tracer is based on kprobes(kprobe and kretprobe). It probes anywhere where kprobes can probe(this means, all functions body except for __kprobes functions).
This tracer supports following probe arguments for each probe.
%REG : Fetch register REG
sN : Fetch Nth entry of stack (N >= 0)
@ADDR : Fetch memory at ADDR (ADDR should be in kernel)
@SYM[+|-offs] : Fetch memory at SYM +|- offs (SYM should be a data symbol) aN : Fetch function argument. (N >= 0) rv : Fetch return value. ra : Fetch return address.
+|-offs(FETCHARG) : fetch memory at FETCHARG +|- offs address.
Changes from v5:
- Add __kprobes to functions which will be invoked by kprobes.
- Merge tracer-core patch and trace-argument support patch.
- Add kprobe_trace_entry and kretprobe_trace_entry for recording kprobe events.
- Support common arguments filtering via tracing/events/ftrace/kprobe(or kretprobe)/filter.
- Add a selftest.
---
Documentation/trace/kprobes.txt | 81 +++ kernel/trace/Kconfig | 9 kernel/trace/Makefile | 1 kernel/trace/trace.h | 22 + kernel/trace/trace_event_types.h | 20 + kernel/trace/trace_kprobe.c | 903 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 1036 insertions(+), 0 deletions(-) create mode 100644 Documentation/trace/kprobes.txt create mode 100644 kernel/trace/trace_kprobe.c
Documentation/trace/kprobes.txt new file +81 -0
@@ -0,0 +1,81 @@ + Kprobe-based Event Tracer + ========================= + + Documentation is written by Masami Hiramatsu + + +Overview +-------- +This tracer is similar to the events tracer which is based on Tracepoint +infrastructure. Instead of Tracepoint, this tracer is based on kprobes(kprobe +and kretprobe). It probes anywhere where kprobes can probe(this means, all +functions body except for __kprobes functions). + +Unlike the function tracer, this tracer can probe instructions inside of +kernel functions. It allows you to check which instruction has been executed. + +Unlike the Tracepoint based events tracer, this tracer can add new probe points +on the fly. + +Similar to the events tracer, this tracer doesn't need to be activated via +current_tracer, instead of that, just set probe points via +/debug/tracing/kprobe_events. + + +Synopsis of kprobe_events +------------------------- + p SYMBOL[+offs|-offs]|MEMADDR [FETCHARGS] : set a probe + r SYMBOL[+0] [FETCHARGS] : set a return probe + + FETCHARGS: + %REG : Fetch register REG + sN : Fetch Nth entry of stack (N >= 0) + @ADDR : Fetch memory at ADDR (ADDR should be in kernel) + @SYM[+|-offs] : Fetch memory at SYM +|- offs (SYM should be a data symbol) + aN : Fetch function argument. (N >= 0)(*) + rv : Fetch return value.(**) + ra : Fetch return address.(**) + +|-offs(FETCHARG) : fetch memory at FETCHARG +|- offs address.(***) + + (*) aN may not correct on asmlinkaged functions and at the middle of + function body. + (**) only for return probe. + (***) this is useful for fetching a field of data structures. + + +Usage examples +-------------- + + echo p do_sys_open a0 a1 a2 a3 > /debug/tracing/kprobe_events + + This sets a kprobe on the top of do_sys_open() function with recording +1st to 4th arguments. + + echo r do_sys_open rv ra >> /debug/tracing/kprobe_events + + This sets a kretprobe on the return point of do_sys_open() function with +recording return value and return address. + + echo > /debug/tracing/kprobe_events + + This clears all probe points. and you can see the traced information via +/debug/tracing/trace. + + cat /debug/tracing/trace +# tracer: nop +# +# TASK-PID CPU# TIMESTAMP FUNCTION +# | | | | | + <...>-1447 [001] 1038282.286875: do_sys_open+0x0/0xd6: 0x3 0x7fffd1ec4440 0x8000 0x0 + <...>-1447 [001] 1038282.286878: sys_openat+0xc/0xe <-do_sys_open: 0xfffffffffffffffe 0xffffffff81367a3a + <...>-1447 [001] 1038282.286885: do_sys_open+0x0/0xd6: 0xffffff9c 0x40413c 0x8000 0x1b6 + <...>-1447 [001] 1038282.286915: sys_open+0x1b/0x1d <-do_sys_open: 0x3 0xffffffff81367a3a + <...>-1447 [001] 1038282.286969: do_sys_open+0x0/0xd6: 0xffffff9c 0x4041c6 0x98800 0x10 + <...>-1447 [001] 1038282.286976: sys_open+0x1b/0x1d <-do_sys_open: 0x3 0xffffffff81367a3a + + + Each line shows when the kernel hits a probe, and <- SYMBOL means kernel +returns from SYMBOL(e.g. "sys_open+0x1b/0x1d <- do_sys_open" means kernel +returns from do_sys_open to sys_open+0x1b). + +
kernel/trace/Kconfig +9 -0
@@ -398,6 +398,15 @@ config BLK_DEV_IO_TRACE If unsure, say N. +config KPROBE_TRACER + depends on KPROBES + depends on X86 + bool "Trace kprobes" + select TRACING + help + This tracer probes everywhere where kprobes can probe it, and + records various registers and memories specified by user. + config DYNAMIC_FTRACE bool "enable/disable ftrace tracepoints dynamically" depends on FUNCTION_TRACER
kernel/trace/Makefile +1 -0
@@ -52,5 +52,6 @@ obj-$(CONFIG_FTRACE_SYSCALLS) += trace_syscalls.o obj-$(CONFIG_EVENT_PROFILE) += trace_event_profile.o obj-$(CONFIG_EVENT_TRACING) += trace_events_filter.o obj-$(CONFIG_EVENT_TRACING) += trace_mm.o +obj-$(CONFIG_KPROBE_TRACER) += trace_kprobe.o libftrace-y := ftrace.o
kernel/trace/trace.h +22 -0
@@ -40,6 +40,8 @@ enum trace_type {TRACE_KMEM_FREE ,TRACE_POWER ,TRACE_BLK , +TRACE_KPROBE , +TRACE_KRETPROBE , __TRACE_LAST_TYPE, };
@@ -207,6 +209,22 @@ struct syscall_trace_exit {unsigned long ret; }; +#define TRACE_KPROBE_ARGS 6 + +struct kprobe_trace_entry { +struct trace_entry ent; +unsigned long ip; +int nargs; +unsigned long args[TRACE_KPROBE_ARGS ]; +}; + +struct kretprobe_trace_entry { +struct trace_entry ent; +unsigned long func; +unsigned long ret_ip; +int nargs; +unsigned long args[TRACE_KPROBE_ARGS ]; +};/* * trace_flag_type is an enumeration that holds different
@@ -323,6 +341,10 @@ extern void __ftrace_bad_type(void); TRACE_SYSCALL_ENTER); \ IF_ASSIGN(var, ent, struct syscall_trace_exit, \TRACE_SYSCALL_EXIT ); \ + IF_ASSIGN(var, ent, struct kprobe_trace_entry, \ +TRACE_KPROBE ); \ + IF_ASSIGN(var, ent, struct kretprobe_trace_entry, \ +TRACE_KRETPROBE ); \__ftrace_bad_type (); \ }while (0 )
kernel/trace/trace_event_types.h +20 -0
@@ -172,4 +172,24 @@ TRACE_EVENT_FORMAT(kmem_free, TRACE_KMEM_FREE, kmemtrace_free_entry, ignore,TP_RAW_FMT ("type:%u call_site:%lx ptr:%p" ) ); +TRACE_EVENT_FORMAT (kprobe,TRACE_KPROBE , kprobe_trace_entry, ignore, +TRACE_STRUCT ( + TRACE_FIELD(unsigned long, ip, ip) + TRACE_FIELD(int, nargs, nargs) + TRACE_FIELD_SPECIAL(unsigned long args[TRACE_KPROBE_ARGS], + args,TRACE_KPROBE_ARGS , args) + ), +TP_RAW_FMT ("%08lx: args:0x%lx ..." ) +); + +TRACE_EVENT_FORMAT (kretprobe,TRACE_KRETPROBE , kretprobe_trace_entry, ignore, +TRACE_STRUCT ( + TRACE_FIELD(unsigned long, func, func) + TRACE_FIELD(unsigned long, ret_ip, ret_ip) + TRACE_FIELD(int, nargs, nargs) + TRACE_FIELD_SPECIAL(unsigned long args[TRACE_KPROBE_ARGS], + args,TRACE_KPROBE_ARGS , args) + ), +TP_RAW_FMT ("%08lx <- %08lx: args:0x%lx ..." ) +);#undef TRACE_SYSTEM
kernel/trace/trace_kprobe.c new file +903 -0
903 lines not on this page · raw message
Signature · 7 lines
Masami Hiramatsu Software Engineer Hitachi Computer Products (America) Inc. Software Solutions Division e-mail: [email protected]