Re: KGTP (Linux Kernel debugger and tracer) 20110424 release

From: Wanlong Gao
Date: Sun Apr 24 2011 - 10:48:16 EST


On 4/24/11, Hui Zhu <teawater@xxxxxxxxx> wrote:
> KGTP is a realtime and lightweight Linux Kernel GDB debugger and
> tracer that use Kprobe.
>
> It make Linux Kernel supply a GDB remote debug interface. Then GDB in
> current machine or remote machine(see "Make GDB connect to gtp") can
> debug Linux through GDB tracepoint without stop the Linux Kernel. And
> even if the board doesn't have GDB on it and doesn't have
> interface for remote debug. It can debug the Linux Kernel use offline
> debug (See "Offline debug").
>
> It support X86-32, X86-64, MIPS and ARM.
> http://www.tudou.com/programs/view/_p6VTgxmCRA/ This is a video(in
> Chinese) to show how to use it.
>
> Now, KGTP 20110424 release.
> You can get the package for it from
> http://kgtp.googlecode.com/files/kgtp_20110424.tar.bz2
> or
> svn co https://kgtp.googlecode.com/svn/tags/20110424
>
> The main change of this verion is add a special trace state variables
> $dump_stack, "collect" it will let Linux Kernel output stack dump
> directly.
> Following example let Linux Kernel show the stack dump of vfs_readdir:
> trace vfs_readdir
> commands
> collect $dump_stack
> end
> Then your kernel will printk like:
>
> [22779.208064] gtp 1:Pid: 441, comm: python Not tainted 2.6.39-rc3+ #46
> [22779.208068] Call Trace:
> [22779.208072] [<fe653cca>] gtp_get_var+0x4a/0xa0 [gtp]
> [22779.208076] [<fe653d79>] gtp_collect_var+0x59/0xa0 [gtp]
> [22779.208080] [<fe655974>] gtp_action_x+0x1bb4/0x1dc0 [gtp]
> [22779.208084] [<c05b6408>] ? _raw_spin_unlock+0x18/0x40
> [22779.208088] [<c023f152>] ? __find_get_block_slow+0xd2/0x160
> [22779.208091] [<c01a8c56>] ? delayacct_end+0x96/0xb0
> [22779.208100] [<c023f404>] ? __find_get_block+0x84/0x1d0
> [22779.208103] [<c05b6408>] ? _raw_spin_unlock+0x18/0x40
> [22779.208106] [<c02e0838>] ? find_revoke_record+0xa8/0xc0
> [22779.208109] [<c02e0c45>] ? jbd2_journal_cancel_revoke+0xd5/0xe0
> [22779.208112] [<c02db51f>] ? __jbd2_journal_temp_unlink_buffer+0x2f/0x110
> [22779.208115] [<fe655c4c>] gtp_kp_pre_handler+0xcc/0x1c0 [gtp]
> [22779.208118] [<c05b8a88>] kprobe_exceptions_notify+0x3d8/0x440
> [22779.208121] [<c05b7d54>] ? hw_breakpoint_exceptions_notify+0x14/0x180
> [22779.208124] [<c05b95eb>] ? sub_preempt_count+0x7b/0xb0
> [22779.208126] [<c0227ac5>] ? vfs_readdir+0x15/0xb0
> [22779.208128] [<c0227ac4>] ? vfs_readdir+0x14/0xb0
> [22779.208131] [<c05b9743>] notifier_call_chain+0x43/0x60
> [22779.208134] [<c05b9798>] __atomic_notifier_call_chain+0x38/0x50
> [22779.208137] [<c05b97cf>] atomic_notifier_call_chain+0x1f/0x30
> [22779.208140] [<c05b980d>] notify_die+0x2d/0x30
> [22779.208142] [<c05b71c5>] do_int3+0x35/0xa0
>
> Add a special trace state variables $clock. Access it in tracepoint
> condition and action will get the return of local_clock() that return
> the timestamp in nanoseconds.
>
> And according to Steven Rostedt's mail, move the interface from proc
> to debug fs. So, access kgtp need change to /sys/kernel/debug/gtp.
>
> The other change is:
> Make frame align better.
> Change sp_checked to type 0xff, Change stack of x from local to static.
> Fix the bug of $printk_tmp.
> Make getgtprsp.pl support special trace state variables.
>
> You can read the doc gtp.txt to get how to use kgtp.
>
> And according to the comments of Christoph. I make a patch for Linux
> Kernel and make it looks OK with checkpatch.pl.
>
> Signed-off-by: Hui Zhu <teawater@xxxxxxxxx>
>
> ---
> Documentation/trace/gtp.txt | 873 +++++++
> arch/arm/include/asm/gtp.h | 13
> arch/mips/include/asm/gtp.h | 18
> arch/x86/include/asm/gtp.h | 18
> lib/Kconfig.debug | 9
> lib/Makefile | 2
> lib/gtp.c | 5104
> ++++++++++++++++++++++++++++++++++++++++++++
> scripts/getgtprsp.pl | 102
> 8 files changed, 6139 insertions(+)
>
> --- /dev/null
> +++ b/Documentation/trace/gtp.txt
> @@ -0,0 +1,873 @@
> + Linux Kernel GDB tracepoint module (KGTP)
> + =========================================
> + By Hui Zhu <teawater@xxxxxxxxx>
> + https://code.google.com/p/kgtp/wiki/HOWTO
> + 2011-04-24
> +
> +Table of contents
> +-----------------
> +
> +What is KGTP
> +Report issue about KGTP
> +Get info about GDB tracepoint
> +Get KGTP through http
> +Get KGTP through svn
> +Config KGTP
> +Compile KGTP
> +Install KGTP
> +Uninstall KGTP
> +Howto use
> + Exec it
> + Make GDB connect to gtp
> + Add module symbols to GDB
> + Get GDB tracepoint commands
> + Get registers info from Kernel
> + Get the value of variable from Kernel
> + How to use use tracepoint condition
> + How to use trace state variables
> + Simple trace state variables
> + Special trace state variables $current_task,
> + $current_thread_info, $cpu_id, $dump_stack, $printk_level,
> + $printk_format, $printk_tmp and $clock
> + Show all the traced data of current frame
> + Get backtrace info from Kernel
> + Howto let tracepoint output value directly
> + Output stack dump directly
> + Switch collect to output the value directly
> + Use printf command in actions
> + Get status of KGTP from Kernel
> + Set the trace buffer into a circular buffer
> + Do not stop tracepoint when the GDB disconnect
> + Howto show the variable that value of it is optimized
> + Linux kernel "Compile with almost no optimization" patch
> + Update your GCC
> + Offline debug
> +
> +
> +
> +
> +What is KGTP
> +------------
> +
> +KGTP is a realtime and lightweight Linux Kernel GDB debugger and tracer
> that
> +use Kprobe.
> +
> +It make Linux Kernel supply a GDB remote debug interface. Then GDB in
> current
> +machine or remote machine(see "Make GDB connect to gtp") can debug Linux
> +through GDB tracepoint without stop the Linux Kernel.
> +And even if the board doesn't have GDB on it and doesn't have interface for
> +remote debug. It can debug the Linux Kernel use offline debug (See "Offline
> +debug").
> +It support X86-32, X86-64, MIPS and ARM.
> +http://www.tudou.com/programs/view/_p6VTgxmCRA/ This is a video(in Chinese)
> +to show how to use it.
> +
> +
> +
> +
> +Report issue about KGTP
> +-----------------------
> +You can post it in https://code.google.com/p/kgtp/issues/list or write
> Email
> +to teawater@xxxxxxxxxx
> +
> +
> +
> +
> +Get info about GDB tracepoint
> +-----------------------------
> +Please goto
> http://sourceware.org/gdb/current/onlinedocs/gdb/Tracepoints.html
> +
> +
> +
> +
> +Get KGTP through http
> +---------------------
> +Please goto http://code.google.com/p/kgtp/downloads/list OR UPDATE to down
> +the package.
> +
> +
> +
> +
> +Get KGTP through svn
> +--------------------
> +Some people have trouble with access to KGTP website. You can access kgtp
> +through svn:
> +
> +------------------------------------------------------------
> +svn checkout http://kgtp.googlecode.com/svn/ kgtp-read-only
> +------------------------------------------------------------
> +
> +kgtp-read-only/tags/ There is the each release of KGTP.
> +kgtp-read-only/trunk/ There is the main trunk of KGTP.
> +
> +
> +
> +
> +Config KGTP
> +-----------
> +
> +Before compile KGTP, you can choice which Kernel you want build with and
> +which compiler you want through change the Makefile. For example:
> +
> +-------------------------------------------
> +KERNELDIR := /lib/modules/`uname -r`/build
> +CROSS_COMPILE :=
> +-------------------------------------------
> +
> +KERNELDIR is set the directory which Kernel you want build for. Now it set
> to
> +the current kernel that you use.
> +CROSS_COMPILE is set the compiler that you want to build the KGTP. Empty
> mean
> +use current compiler.
> +ARCH is the architecture.
> +
> +------------------------------------------
> +KERNELDIR := /home/teawater/kernel/bamd64
> +CROSS_COMPILE :=x86_64-glibc_std-
> +ARCH := x86_64
> +------------------------------------------
> +
> +KERNELDIR is set to /home/teawater/kernel/bamd64. Compiler will
> +use x86_64-glibc_std-gcc.
> +
> +
> +
> +
> +Compile KGTP
> +------------
> +
> +For normal use:
> +
> +---------
> +cd kgtp/
> +make
> +---------
> +
> +If you want KGTP out the debug message,please use following (It is
> available
> +after 20100825):
> +
> +---------
> +cd kgtp/
> +make D=1
> +---------
> +
> +
> +
> +
> +Install KGTP
> +------------
> +
> +------------------
> +cd kgtp/
> +sudo make install
> +------------------
> +
> +
> +
> +
> +Uninstall KGTP
> +--------------
> +
> +--------------------
> +cd kgtp/
> +sudo make uninstall
> +--------------------
> +
> +
> +
> +
> +Howto use
> +---------
> +
> +Exec it
> +-------
> +
> +If you had installed the KGTP in your system, you can:
> +
> +------------------
> +sudo modprobe gtp
> +------------------
> +
> +Or you can use the kgtp module in the directory.
> +
> +-------------------
> +cd kgtp/
> +sudo insmod gtp.ko
> +-------------------
> +
> +
> +
> +Make GDB connect to gtp
> +-----------------------
> +
> +In current machine:
> +
> +---------------------------------
> +sudo gdb ./vmlinux
> +(gdb) target remote /sys/kernel/debug/gtp
> +Remote debugging using /sys/kernel/debug/gtp
> +0x0000000000000000 in ?? ()
> +---------------------------------
> +
> +In remote machine:
> +
> +---------------------------------------------
> +#Open the KGTP interface in current machine.
> +sudo su
> +nc -l 1234 </sys/kernel/debug/gtp >/sys/kernel/debug/gtp
> +(nc -l -p 1234 </sys/kernel/debug/gtp >/sys/kernel/debug/gtp for old
> version
> +netcat.)
> +#Let gdb connect to the port 1234
> +gdb ./vmlinux
> +(gdb) target remote xxx.xxx.xxx.xxx:1234
> +---------------------------------------------
> +
> +
> +
> +Add module symbols to GDB
> +-------------------------
> +
> +Sometime, you need add the Linux Kernel module(lkm) symbols to GDB to debug
> it.
> +Following a example howto add ext3.ko's symbols to GDB:
> +
> +--------------------------------------------------------------------------------
> +cat /proc/modules | grep ext
> +ext3 116512 3 - Live 0xf9083000
> +#Get the address 0xf9083000.
> +modinfo ext3
> +filename: /lib/modules/2.6.36-rc2+/kernel/fs/ext3/ext3.ko
> +#Get the directory name of ext3.
> +(gdb) add-symbol-file /lib/modules/2.6.36-rc2+/kernel/fs/ext3/ext3.ko
> 0xf9083000
> +#Then, GDB get the symbols of ext3.
> +--------------------------------------------------------------------------------
> +
> +You will see that it just add the text section info to GDB, sometime you
> need
> +add another section info of other section, for example:
> +
> +--------------------------------------------------------------------------------
> +cat /sys/module/ext3/sections/.bss
> +0xf908170c
> +#Get the bss address 0xf908170c.
> +(gdb) add-symbol-file /lib/modules/2.6.36-rc2+/kernel/fs/ext3/ext3.ko
> 0xf9083000 -s .bss 0xf908170c
> +#Then, GDB get the symbols of ext3.
> +--------------------------------------------------------------------------------
> +
> +
> +
> +Get GDB tracepoint commands
> +---------------------------
> +
> +Please goto
> http://sourceware.org/gdb/current/onlinedocs/gdb/Tracepoints.html
> +
> +
> +Get registers info from Kernel
> +------------------------------
> +
> +Following part is an example that record the value of all registers when
> Linux
> +Kernel call function "vfs_readdir".
> +
> +--------------------------------------------------------------------------------
> +(gdb) trace vfs_readdir
> +Tracepoint 1 at 0xc01a1ac0: file
> +/home/teawater/kernel/linux-2.6/fs/readdir.c, line 23.
> +(gdb) actions
> +Enter actions for tracepoint 1, one per line.
> +End with a line saying just "end".
> +>collect $reg
> +>end
> +(gdb) tstart
> +(gdb) shell ls
> +(gdb) tstop
> +(gdb) tfind
> +Found trace frame 0, tracepoint 1
> +#0 0xc01a1ac1 in vfs_readdir (file=0xc5528d00, filler=0xc01a1900
> <filldir64>,
> + buf=0xc0d09f90) at /home/teawater/kernel/linux-2.6/fs/readdir.c:23
> +23 /home/teawater/kernel/linux-2.6/fs/readdir.c: No such file or
> directory.
> + in /home/teawater/kernel/linux-2.6/fs/readdir.c
> +(gdb) info reg
> +eax 0xc5528d00 -984445696
> +ecx 0xc0d09f90 -1060069488
> +edx 0xc01a1900 -1072031488
> +ebx 0xfffffff7 -9
> +esp 0xc0d09f8c 0xc0d09f8c
> +ebp 0x0 0x0
> +esi 0x8061480 134616192
> +edi 0xc5528d00 -984445696
> +eip 0xc01a1ac1 0xc01a1ac1 <vfs_readdir+1>
> +eflags 0x286 [ PF SF IF ]
> +cs 0x60 96
> +ss 0x8061480 134616192
> +ds 0x7b 123
> +es 0x7b 123
> +fs 0x0 0
> +gs 0x0 0
> +(gdb) tfind
> +Found trace frame 1, tracepoint 1
> +0xc01a1ac1 23 in /home/teawater/kernel/linux-2.6/fs/readdir.c
> +(gdb) info reg
> +eax 0xc5528d00 -984445696
> +ecx 0xc0d09f90 -1060069488
> +edx 0xc01a1900 -1072031488
> +ebx 0xfffffff7 -9
> +esp 0xc0d09f8c 0xc0d09f8c
> +ebp 0x0 0x0
> +esi 0x8061480 134616192
> +edi 0xc5528d00 -984445696
> +eip 0xc01a1ac1 0xc01a1ac1 <vfs_readdir+1>
> +eflags 0x286 [ PF SF IF ]
> +cs 0x60 96
> +ss 0x8061480 134616192
> +ds 0x7b 123
> +es 0x7b 123
> +fs 0x0 0
> +gs 0x0 0
> +--------------------------------------------------------------------------------
> +
> +
> +
> +Get the value of variable from Kernel
> +-------------------------------------
> +
> +Following part is an example that record the value of "jiffies_64" when
> Linux
> +Kernel call function "vfs_readdir".
> +
> +--------------------------------------------------------------------------------
> +(gdb) trace vfs_readdir
> +Tracepoint 1 at 0xc01ed740: file
> /home/teawater/kernel/linux-2.6/fs/readdir.c, line 24.
> +(gdb) actions
> +Enter actions for tracepoint 1, one per line.
> +End with a line saying just "end".
> +>collect jiffies_64
> +>collect file->f_path.dentry->d_iname
> +>end
> +(gdb) tstart
> +(gdb) shell ls
> +arch drivers include kernel mm Module.symvers
> security System.map virt
> +block firmware init lib modules.builtin net
> sound t vmlinux
> +crypto fs ipc Makefile modules.order scripts
> source usr vmlinux.o
> +(gdb) tstop
> +(gdb) tfind
> +Found trace frame 0, tracepoint 1
> +#0 0xc01ed741 in vfs_readdir (file=0xf4063000, filler=0xc01ed580
> <filldir64>, buf=0xd6dfdf90)
> + at /home/teawater/kernel/linux-2.6/fs/readdir.c:24
> +24 {
> +(gdb) p jiffies_64
> +$1 = 4297248706
> +(gdb) p file->f_path.dentry->d_iname
> +$1 = "b26", '\000' <repeats 28 times>
> +--------------------------------------------------------------------------------
> +
> +
> +
> +How to use use tracepoint condition
> +-----------------------------------
> +
> +http://sourceware.org/gdb/current/onlinedocs/gdb/Tracepoint-Conditions.html
> +Like the breakpoint, we can set condition to tracepoint. But it speed is
> more
> +fast than breakpoint's condition because KGTP do all the condition check.
> +For example:
> +
> +------------------------------
> +(gdb) trace handle_irq
> +(gdb) condition 1 (irq == 47)
> +------------------------------
> +
> +This action of tracepoint 1 will work only when irq number is 47.
> +
> +
> +
> +How to use trace state variables
> +--------------------------------
> +
> +http://sourceware.org/gdb/current/onlinedocs/gdb/Trace-State-Variables.html
> +Tracepoint have special variables. You can trace it or use it in tracepoint
> +condition.
> +Note that only GDB 7.2.1 and later version support use trace state
> variables
> +directly, the old version GDB just can show the value of trace state
> variables
> +through command "info tvariables".
> +
> +
> +Simple trace state variables
> +----------------------------
> +
> +Define a trace state variable $c.
> +
> +-------------------
> +(gdb) tvariable $c
> +-------------------
> +
> +Trace state variable $c created, with initial value 0.
> +Use it in the action. Follow action will use $c to count how much irq
> happen
> +in Kernel.
> +
> +-----------------------------------------------------------------------
> +(gdb) trace handle_irq
> +(gdb) actions
> +Enter actions for tracepoint 3, one per line.
> +End with a line saying just "end".
> +>collect $c #Save current value of $c to the trace frame buffer.
> +>teval $c=$c+1 #Increase the $c.
> +>end
> +-----------------------------------------------------------------------
> +
> +You can get the current value of $c when the trace is running or stop.
> +
> +----------------------------------
> +(gdb) tstart
> +(gdb) info tvariables
> +$c 0 31554
> +(gdb) p $c
> +$5 = 33652
> +(gdb) tstop
> +(gdb) p $c
> +$9 = 105559
> +----------------------------------
> +
> +When use the tfind parse the trace frame buffer, if the value of trace
> state
> +variable is collect. You can use it.
> +
> +------------------------------
> +(gdb) tstop
> +(gdb) tfind
> +(gdb) info tvariables
> +$c 0 0
> +(gdb) p $c
> +$6 = 0
> +(gdb) tfind 100
> +(gdb) p $c
> +$7 = 100
> +------------------------------
> +
> +
> +Special trace state variables $current_task, $current_thread_info, $cpu_id,
> +$dump_stack, $printk_level, $printk_format, $printk_tmp and $clock
> +--------------------------------------------------------------------------
> +
> +KGTP have special trace state variables $current_task,
> $current_thread_info,
> +$cpu_id and $clock can very easy to access to some special value. You can
> see
> +them when GDB connect to the KGTP. You can use them in tracepoint condition
> +or actions.
> +Access $current_task in tracepoint condition and action will get the return
> +of get_current().
> +Access $current_thread_info in tracepoint condition and action will get the
> +return of current_thread_info().
> +Access $cpu_id in tracepoint condition and action will get the return of
> +smp_processor_id().
> +Access $clock in tracepoint condition and action will get the return of
> +local_clock() that return the timestamp in nanoseconds.
> +
> +And KGTP have other special trace state variables $dump_stack,
> $printk_level,
> +$printk_format and $printk_tmp, all of them are for output value directly
> that
> +will introduce in "Howto let tracepoint output value directly".
> +
> +Following example will count process 16663 call how many sys_read in $c
> +and collect the struct thread_info of current task:
> +
> +--------------------------------------------------------------------------------
> +(gdb) trace vfs_read if ((*(struct task_struct *)$current_task)->pid ==
> 16663)
> +(gdb) tvariable $c
> +(gdb) actions
> +Enter actions for tracepoint 4, one per line.
> +End with a line saying just "end".
> +>teval $c=$c+1
> +>collect (*(struct thread_info *)$current_thread_info)
> +>end
> +(gdb) tstart
> +(gdb) info tvariables
> +Name Initial Current
> +$c 0 184
> +$current_task 0 <unknown>
> +$current_thread_info 0 <unknown>
> +$cpu_id 0 <unknown>
> +(gdb) tstop
> +(gdb) tfind
> +(gdb) p *(struct thread_info *)$current_thread_info
> +$10 = {task = 0xf0ac6580, exec_domain = 0xc07b1400, flags = 0, status
> = 0, cpu = 1, preempt_count = 2, addr_limit = {
> + seg = 4294967295}, restart_block = {fn = 0xc0159fb0
> <do_no_restart_syscall>, {{arg0 = 138300720, arg1 = 11,
> + arg2 = 1, arg3 = 78}, futex = {uaddr = 0x83e4d30, val = 11,
> flags = 1, bitset = 78, time = 977063750,
> + uaddr2 = 0x0}, nanosleep = {index = 138300720, rmtp = 0xb,
> expires = 335007449089}, poll = {
> + ufds = 0x83e4d30, nfds = 11, has_timeout = 1, tv_sec = 78,
> tv_nsec = 977063750}}},
> + sysenter_return = 0xb77ce424, previous_esp = 0, supervisor_stack =
> 0xef340044 "", uaccess_err = 0}
> +--------------------------------------------------------------------------------
> +
> +nother example can show how much sys_read() execute in each cpu.
> +
> +--------------------------------------
> +tvariable $c0
> +tvariable $c1
> +trace sys_read
> + condition $bpnum ($cpu_id == 0)
> + commands
> + teval $c0=$c0+1
> + end
> +trace sys_read
> + condition $bpnum ($cpu_id == 1)
> + commands
> + teval $c1=$c1+1
> + end
> +info tvariables
> +Name Initial Current
> +$current_task 0 <unknown>
> +$cpu_id 0 <unknown>
> +$c0 0 3255
> +$c1 0 1904
> +--------------------------------------
> +
> +sys_read() execute 3255 times in cpu0 and 1904 times in cpu1.
> +
> +
> +
> +Show all the traced data of current frame
> +-----------------------------------------
> +
> +--------------------------------------------------------------------------------
> +(gdb) tdump
> +Data collected at tracepoint 1, trace frame 0:
> +$cr = void
> +file->f_path.dentry->d_iname =
> "gtp\000.google.chrome.g05ZYO\000\235", <incomplete sequence \364>
> +jiffies_64 = 4319751455
> +--------------------------------------------------------------------------------
> +
> +
> +
> +Get backtrace info from Kernel
> +------------------------------
> +
> +We can get the backtrace through collect the stack.
> +In x86_32, following action command will collect 512 bytes of stack.
> +
> +-----------------------------------
> +collect *(unsigned char *)$esp@512
> +-----------------------------------
> +
> +In x86_64, following command will collect 512 bytes of stack.
> +
> +-----------------------------------
> +collect *(unsigned char *)$rsp@512
> +-----------------------------------
> +
> +In MIPS or ARM, following command will collect 512 bytes of stack.
> +----------------------------------
> +collect *(unsigned char *)$sp@512
> +-----------------------------------
> +
> +Following part is an example about howto backtrace in x86_64:
> +
> +--------------------------------------------------------------------------------
> +(gdb) trace vfs_readdir
> +Tracepoint 1 at 0xffffffff8113f7fc: file
> /home/teawater/kernel/linux-2.6/fs/readdir.c, line 24.
> +(gdb) actions
> +Enter actions for tracepoint 1, one per line.
> +End with a line saying just "end".
> +>collect *(unsigned char *)$rsp@512
> +>end
> +(gdb) tstart
> +(gdb) shell ls
> +2 block firmware i ipc Makefile
> modules.order scripts source t~ vmlinux
> +a.out crypto fs include kernel mm
> Module.symvers security System.map usr vmlinux.o
> +arch drivers gdb.txt init lib modules.builtin net
> sound t virt
> +(gdb) tstop
> +(gdb) tfind
> +Found trace frame 0, tracepoint 1
> +#0 0xffffffff8113f7fd in vfs_readdir (file=0xffff880075f00780,
> filler=0xffffffff8113f630 <filldir>, buf=0xffff880005785f38)
> + at ./linux-2.6/fs/readdir.c:24
> +24 {
> +(gdb) bt
> +#0 0xffffffff8113f7fd in vfs_readdir (file=0xffff880075f00780,
> filler=0xffffffff8113f630 <filldir>, buf=0xffff880005785f38)
> + at ./linux-2.6/fs/readdir.c:24
> +#1 0xffffffff8113fa14 in sys_getdents (fd=<value optimized out>,
> dirent=0x801108, count=32768)
> + at ./linux-2.6/fs/readdir.c:214
> +#2 0xffffffff8100af42 in ?? () at
> ./linux-2.6/arch/x86/kernel/entry_64.S:487
> +--------------------------------------------------------------------------------
> +
> +
> +
> +Howto let tracepoint output value directly
> +------------------------------------------
> +
> +In the previous parts, you can get that to get the value from Linux Kernel
> you
> +need use tracepoint action "collect" save value to the tracepoint frame and
> +use GDB command "tfind" parse the the value out from the frame.
> +But we want get the value directly sometime, so KGTP support two ways to
> output
> +value directly.
> +
> +
> +Output stack dump directly
> +--------------------------
> +KGTP have special trace state variables $dump_stack, "collect" it will let
> +Linux Kernel output stack dump directly.
> +
> +Following example let Linux Kernel show the stack dump of vfs_readdir:
> +
> +--------------------------------------------------------------------------------
> +trace vfs_readdir
> + commands
> + collect $dump_stack
> + end
> +--------------------------------------------------------------------------------
> +
> +Then your kernel will printk like:
> +
> +--------------------------------------------------------------------------------
> +[22779.208064] gtp 1:Pid: 441, comm: python Not tainted 2.6.39-rc3+ #46
> +[22779.208068] Call Trace:
> +[22779.208072] [<fe653cca>] gtp_get_var+0x4a/0xa0 [gtp]
> +[22779.208076] [<fe653d79>] gtp_collect_var+0x59/0xa0 [gtp]
> +[22779.208080] [<fe655974>] gtp_action_x+0x1bb4/0x1dc0 [gtp]
> +[22779.208084] [<c05b6408>] ? _raw_spin_unlock+0x18/0x40
> +[22779.208088] [<c023f152>] ? __find_get_block_slow+0xd2/0x160
> +[22779.208091] [<c01a8c56>] ? delayacct_end+0x96/0xb0
> +[22779.208100] [<c023f404>] ? __find_get_block+0x84/0x1d0
> +[22779.208103] [<c05b6408>] ? _raw_spin_unlock+0x18/0x40
> +[22779.208106] [<c02e0838>] ? find_revoke_record+0xa8/0xc0
> +[22779.208109] [<c02e0c45>] ? jbd2_journal_cancel_revoke+0xd5/0xe0
> +[22779.208112] [<c02db51f>] ? __jbd2_journal_temp_unlink_buffer+0x2f/0x110
> +[22779.208115] [<fe655c4c>] gtp_kp_pre_handler+0xcc/0x1c0 [gtp]
> +[22779.208118] [<c05b8a88>] kprobe_exceptions_notify+0x3d8/0x440
> +[22779.208121] [<c05b7d54>] ? hw_breakpoint_exceptions_notify+0x14/0x180
> +[22779.208124] [<c05b95eb>] ? sub_preempt_count+0x7b/0xb0
> +[22779.208126] [<c0227ac5>] ? vfs_readdir+0x15/0xb0
> +[22779.208128] [<c0227ac4>] ? vfs_readdir+0x14/0xb0
> +[22779.208131] [<c05b9743>] notifier_call_chain+0x43/0x60
> +[22779.208134] [<c05b9798>] __atomic_notifier_call_chain+0x38/0x50
> +[22779.208137] [<c05b97cf>] atomic_notifier_call_chain+0x1f/0x30
> +[22779.208140] [<c05b980d>] notify_die+0x2d/0x30
> +[22779.208142] [<c05b71c5>] do_int3+0x35/0xa0
> +--------------------------------------------------------------------------------
> +
> +
> +Switch collect to output the value directly
> +-------------------------------------------
> +
> +KGTP have special trace state variables $printk_level, $printk_format
> +and $printk_tmp to support this function.
> +
> +$printk_level, if its value is 8 (this is the default value), "collect"
> will
> +save value to the tracepoint frame.
> +If its value is 0-7, "collect" will output the value through "printk"
> directly,
> +and value will be the level of printk. The level is:
> +0 KERN_EMERG system is unusable
> +1 KERN_ALERT action must be taken immediately
> +2 KERN_CRIT critical conditions
> +3 KERN_ERR error conditions
> +4 KERN_WARNING warning conditions
> +5 KERN_NOTICE normal but significant condition
> +6 KERN_INFO informational
> +7 KERN_DEBUG debug-level messages
> +
> +$printk_format, collect printk will output value in the format that set by
> it.
> +The format is:
> +0 This is the default value.
> + If the size of collect value is 1, 2, 4 or 8, it will be outputted as
> + a unsigned decimal.
> + If not, it will be outputted as a hexadecimal string.
> +1 Output value in signed decimal.
> +2 Output value in unsigned decimal.
> +3 Output value in unsigned hexadecimal.
> +4 Output value as a string.
> +5 Output value as a hexadecimal string.
> +
> +$printk_tmp, to output the value of global variable need set to it first.
> +
> +Following example show the a count number, pid, jiffies_64 and the file
> name
> +that call vfs_readdir:
> +
> +--------------------------------------------------------------------------------
> +tvariable $c
> +trace vfs_readdir
> + commands
> + teval $printk_level=0
> + collect $c=$c+1
> + collect (*(struct task_struct *)$current_task)->pid
> + collect $printk_tmp=jiffies_64
> + teval $printk_format=4
> + collect file->f_path.dentry->d_iname
> + end
> +--------------------------------------------------------------------------------
> +
> +Then your kernel will printk like:
> +
> +--------------------------------------------------------------------------------
> +gtp 1:$c=$c+1=41
> +gtp 1:(*(struct task_struct *)$current_task)->pid=12085
> +gtp 1:$printk_tmp=jiffies_64=4322021438
> +gtp 1:file->f_path.dentry->d_iname=b26
> +gtp 1:$c=$c+1=42
> +gtp 1:(*(struct task_struct *)$current_task)->pid=12085
> +gtp 1:$printk_tmp=jiffies_64=4322021438
> +gtp 1:file->f_path.dentry->d_iname=b26
> +--------------------------------------------------------------------------------
> +
> +"gtp 1" mean that it output by tracepoint 1.
> +
> +
> +Use printf command in actions
> +-----------------------------
> +
> +This way have a trouble is GDB is still not accept the patch that make
> +tracepoint support printf, So if you want use it, you need patch the patch
> in
> +http://sourceware.org/ml/gdb-patches/2011-03/msg00022.html and build your
> +GDB with yourself.
> +
> +Following example show the a count number, pid and the file name that call
> +vfs_readdir:
> +
> +--------------------------------------------------------------------------------
> +tvariable $c
> +trace vfs_readdir
> + commands
> + printf "<0>%d pid=%d name:%s\n", $c=$c+1, (*(struct task_struct
> *)$current_task)->pid, file->f_path.dentry->d_iname
> + end
> +--------------------------------------------------------------------------------
> +
> +Then your kernel will printk like:
> +
> +--------------------------------------------------------------------------------
> +1 pid=10888 name:bin
> +2 pid=10888 name:bin
> +3 pid=10893 name:teawater
> +4 pid=10893 name:teawater
> +--------------------------------------------------------------------------------
> +
> +Like what we use printk in Linux Kernel, please add kernel loglevel
> in the begin
> +and add "\n" in the end.
> +The kernel loglevel is:
> +KERN_EMERG "<0>" system is unusable
> +KERN_ALERT "<1>" action must be taken immediately
> +KERN_CRIT "<2>" critical conditions
> +KERN_ERR "<3>" error conditions
> +KERN_WARNING "<4>" warning conditions
> +KERN_NOTICE "<5>" normal but significant condition
> +KERN_INFO "<6>" informational
> +KERN_DEBUG "<7>" debug-level messages
> +
> +
> +
> +Get status of KGTP from Kernel
> +------------------------------
> +Please use GDB command "tstatus"
> +
> +
> +
> +Set the trace buffer into a circular buffer
> +-------------------------------------------
> +http://sourceware.org/gdb/current/onlinedocs/gdb/Starting-and-Stopping-Trace-Experiments.html
> +The frame buffer is not a circular buffer by default. When the buffer is
> full,
> +the tracepoint will stop.
> +
> +-----------------------------
> +set circular-trace-buffer on
> +-----------------------------
> +
> +Set frame buffer to a circular buffer. When the buffer is full, it will
> auto
> +discard traceframes (oldest first) and keep trace.
> +
> +
> +
> +Do not stop tracepoint when the GDB disconnect
> +----------------------------------------------
> +
> +http://sourceware.org/gdb/current/onlinedocs/gdb/Starting-and-Stopping-Trace-Experiments.html
> +The KGTP will stop and delete the trace frame when GDB disconnect with it
> by
> +default.
> +
> +----------------------------
> +set disconnected-tracing on
> +----------------------------
> +will open the KGTP disconnect-trace. After that, when GDB disconnect with
> KGTP,
> +KGTP will not stop trace. And after GDB reconnect to KGTP, it can keep
> control
> +the KGTP like nothing happen.
> +
> +
> +
> +Howto show the variable that value of it is optimized
> +-----------------------------------------------------
> +
> +Sometimes, GDB will output some value like:
> +
> +-------------------------------------------
> +inode has been optimized out of existence.
> +res has been optimized out of existence.
> +-------------------------------------------
> +
> +That is because value of inode and res is optimized. Linux Kernel is built
> +with -O2 so you will get this trouble sometimes.
> +There are 2 ways to handle it:
> +
> +Linux kernel "Compile with almost no optimization" patch
> +--------------------------------------------------------
> +If you do not care about the speed when you debug the Kernel, you can use
> the
> +patch for Linux Kernel in
> +http://code.google.com/p/kgtp/downloads/detail?name=co.patch It add a
> option
> +in "Kernel hacking" called "Compile with almost no optimization". It will
> make
> +kernel be built without -O2. It support x86_32, x86_64 and arm.
> +
> +Update your GCC
> +---------------
> +The VTA branch(http://gcc.gnu.org/wiki/Var_Tracking_Assignments) was merged
> +for GCC 4.5 Which helps a lot with generating dwarf for previously
> +"optimized out" values.
> +
> +
> +
> +Offline debug
> +-------------
> +
> +In the PC that can run the GDB:
> +Change the "target remote XXXX" to
> +
> +------------------------------------------
> +(gdb) target remote | perl ./getgtprsp.pl
> +------------------------------------------.
> +
> +After that, set tracepoint and start it as usual:
> +
> +--------------------------------------------------------------------------------
> +(gdb) trace vfs_readdir
> +Tracepoint 1 at 0xffffffff8114f3c0: file
> /home/teawater/kernel/linux-2.6/fs/readdir.c, line 24.
> +(gdb) actions
> +Enter actions for tracepoint 1, one per line.
> +End with a line saying just "end".
> +#If your GDB support tracepoint "printf" (see "Howto use tracepoint
> printf"), use it to show the value directly is better.
> +>collect $reg
> +>end
> +(gdb) tstart
> +(gdb) stop
> +(gdb) quit
> +--------------------------------------------------------------------------------
> +
> +Then you can find files gtpstart and gtpstop. Copy it to the machine that
> you
> +want debug.
> +
> +
> +In the debuged machine after insmod the gtp.ko:
> +Start the tracepoint:
> +
> +------------------------------------
> +cat gtpstart > /sys/kernel/debug/gtp
> +------------------------------------
> +
> +Stop the tracepoint:
> +
> +-----------------------------------
> +cat gtpstop > /sys/kernel/debug/gtp
> +-----------------------------------
> +
> +You can let Linux Kernel show the value directly, please see "Howto let
> +tracepoint output value directly".
> +
> +If you want save the value to the trace frame and parse later, you can use
> file
> +"/sys/kernel/debug/gtpframe" that have the trace frame. Copy it to the PC
> that
> +has GDB.
> +
> +In the PC that can run the GDB:
> +
> +--------------------------------------------------------------------------------
> +(gdb) target tfile ./gtpframe
> +Tracepoint 1 at 0xffffffff8114f3dc: file
> /home/teawater/kernel/linux-2.6/fs/readdir.c, line 24.
> +Created tracepoint 1 for target's tracepoint 1 at 0xffffffff8114f3c0.
> +(gdb) tfind
> +Found trace frame 0, tracepoint 1
> +#0 vfs_readdir (file=0xffff880036e8f300, filler=0xffffffff8114f240
> <filldir>, buf=0xffff880001e5bf38)
> + at /home/teawater/kernel/linux-2.6/fs/readdir.c:24
> +24 {
> +--------------------------------------------------------------------------------
> --- /dev/null
> +++ b/arch/arm/include/asm/gtp.h
> @@ -0,0 +1,13 @@
> +#ifndef _ASM_ARM_GTP_H_
> +#define _ASM_ARM_GTP_H_
> +
> +#define ULONGEST uint64_t
> +#define LONGEST int64_t
> +#define CORE_ADDR unsigned long
> +
> +#define GTP_REGS_PC(regs) ((regs)->uregs[15])
> +
> +#define GTP_REG_ASCII_SIZE 336
> +#define GTP_REG_BIN_SIZE 168
> +
> +#endif
> --- /dev/null
> +++ b/arch/mips/include/asm/gtp.h
> @@ -0,0 +1,18 @@
> +#ifndef _ASM_MIPS_GTP_H_
> +#define _ASM_MIPS_GTP_H_
> +
> +#define ULONGEST uint64_t
> +#define LONGEST int64_t
> +#define CORE_ADDR unsigned long
> +
> +#define GTP_REGS_PC(regs) ((regs)->cp0_epc)
> +
> +#ifdef CONFIG_32BIT
> +#define GTP_REG_ASCII_SIZE 304
> +#define GTP_REG_BIN_SIZE 152
> +#else
> +#define GTP_REG_ASCII_SIZE 608
> +#define GTP_REG_BIN_SIZE 304
> +#endif
> +
> +#endif
> --- /dev/null
> +++ b/arch/x86/include/asm/gtp.h
> @@ -0,0 +1,18 @@
> +#ifndef _ASM_X86_GTP_H_
> +#define _ASM_X86_GTP_H_
> +
> +#define ULONGEST uint64_t
> +#define LONGEST int64_t
> +#define CORE_ADDR unsigned long
> +
> +#define GTP_REGS_PC(regs) ((regs)->ip)
> +
> +#ifdef CONFIG_X86_32
> +#define GTP_REG_ASCII_SIZE 128
> +#define GTP_REG_BIN_SIZE 64
> +#else
> +#define GTP_REG_ASCII_SIZE 296
> +#define GTP_REG_BIN_SIZE 148
> +#endif
> +
> +#endif
> --- a/lib/Kconfig.debug
> +++ b/lib/Kconfig.debug
> @@ -1253,6 +1253,15 @@ config ASYNC_RAID6_TEST
>
> If unsure, say N.
>
> +config GTP
> + tristate "GDB tracepoint support"
> + depends on X86 || ARM || MIPS
> + depends on KPROBES
> + depends on DEBUG_FS
> + ---help---
> + Supply GDB tracepoint interface in /sys/kernel/debug/gtp.
> + See https://code.google.com/p/kgtp/wiki/HOWTO
> +
> source "samples/Kconfig"
>
> source "lib/Kconfig.kgdb"
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -115,6 +115,8 @@ obj-$(CONFIG_AVERAGE) += average.o
>
> obj-$(CONFIG_CPU_RMAP) += cpu_rmap.o
>
> +obj-$(CONFIG_GTP) += gtp.o
> +
> hostprogs-y := gen_crc32table
> clean-files := crc32table.h
>
> --- /dev/null
> +++ b/lib/gtp.c
> @@ -0,0 +1,5104 @@
> +/*
> + * Kernel GDB tracepoint module.
> + *
> + * 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.
> + *
> + * Copyright(C) Hui Zhu (teawater@xxxxxxxxx), 2010, 2011
> + */
> +
> +#include <linux/version.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/uaccess.h>
> +#include <linux/vmalloc.h>
> +#include <linux/poll.h>
> +#include <linux/kprobes.h>
> +#include <linux/debugfs.h>
> +#include <asm/gtp.h>
> +
> +#define KERN_NULL
> +
> +#ifdef GTPDEBUG
> +#define GTP_DEBUG KERN_WARNING
> +#endif
> +
> +#define GTP_RW_MAX 16384
> +
> +#define FRAME_ALIGN_SIZE sizeof(unsigned int)
> +#define FRAME_ALIGN(x) ((x + FRAME_ALIGN_SIZE - 1) \
> + & (~(FRAME_ALIGN_SIZE - 1)))
> +
> +#define FID_TYPE unsigned int
> +#define FID_SIZE sizeof(FID_TYPE)
> +#define FID(x) (*((FID_TYPE *)x))
> +#define FID_HEAD 0
> +#define FID_REG 1
> +#define FID_MEM 2
> +#define FID_VAR 3
> +#define FID_END 4
> +
> +/* GTP_FRAME_SIZE must align with FRAME_ALIGN_SIZE. */
> +#define GTP_FRAME_SIZE 5242880
> +#define GTP_FRAME_HEAD_SIZE (FID_SIZE + sizeof(char *) + sizeof(ULONGEST))
> +#define GTP_FRAME_REG_SIZE (FID_SIZE + sizeof(char *) \
> + + sizeof(struct pt_regs))
> +#define GTP_FRAME_MEM_SIZE (FID_SIZE + sizeof(char *) \
> + + sizeof(struct gtp_frame_mem))
> +#define GTP_FRAME_VAR_SIZE (FID_SIZE + sizeof(char *) \
> + + sizeof(struct gtp_frame_var))
> +
> +#define TOHEX(h) ((h) > 9 ? (h) + 'a' - 10 : (h) + '0')
> +
> +struct action_agent_exp {
> + unsigned int size;
> + uint8_t *buf;
> + int need_var_lock;
> +};
> +
> +struct action_m {
> + int regnum;
> + CORE_ADDR offset;
> + size_t size;
> +};
> +
> +struct action {
> + struct action *next;
> + unsigned char type;
> + char *src;
> + union {
> + ULONGEST reg_mask;
> + struct action_agent_exp exp;
> + struct action_m m;
> + } u;
> +};
> +
> +struct gtpsrc {
> + struct gtpsrc *next;
> + char *src;
> +};
> +
> +enum gtp_stop_type {
> + gtp_stop_normal = 0,
> + gtp_stop_frame_full,
> + gtp_stop_efault,
> + gtp_stop_access_wrong_reg,
> + gtp_stop_agent_expr_code_error,
> + gtp_stop_agent_expr_stack_overflow,
> +};
> +
> +struct gtp_entry {
> + struct gtp_entry *next;
> + int disable;
> + ULONGEST num;
> + ULONGEST addr;
> + ULONGEST step;
> + ULONGEST pass;
> + int nopass;
> + atomic_t current_pass;
> + int kpreg;
> + struct kprobe kp;
> + struct work_struct work;
> + enum gtp_stop_type reason;
> + struct action *cond;
> + struct action *action_list;
> + struct gtpsrc *src;
> + int have_printk;
> + struct gtpsrc *printk_str;
> +};
> +
> +struct gtp_var {
> + struct gtp_var *next;
> + unsigned int num;
> + uint64_t val;
> + char *src;
> +};
> +
> +struct gtp_frame_mem {
> + CORE_ADDR addr;
> + size_t size;
> +};
> +
> +struct gtp_frame_var {
> + unsigned int num;
> + uint64_t val;
> +};
> +
> +struct gtpro_entry {
> + struct gtpro_entry *next;
> + CORE_ADDR start;
> + CORE_ADDR end;
> +};
> +
> +static struct gtp_entry *gtp_list;
> +static struct gtp_entry *current_gtp;
> +static struct action *current_gtp_action;
> +static struct gtpsrc *current_gtp_src;
> +
> +static struct workqueue_struct *gtp_wq;
> +
> +static char gtp_read_ack;
> +static char *gtp_rw_buf;
> +static char *gtp_rw_bufp;
> +static size_t gtp_rw_size;
> +
> +static int gtp_start;
> +
> +static int gtp_disconnected_tracing;
> +static int gtp_circular;
> +
> +static DEFINE_SPINLOCK(gtp_var_lock);
> +static struct gtp_var *gtp_var_list;
> +static unsigned int gtp_var_head;
> +static unsigned int gtp_var_tail;
> +static struct gtp_var **gtp_var_array;
> +static struct gtp_var *current_gtp_var;
> +#define GTP_VAR_CURRENT_TASK_ID 1
> +static struct gtp_var gtp_var_current_task = {
> + .next = NULL,
> + .num = GTP_VAR_CURRENT_TASK_ID,
> + .src = "0:1:63757272656e745f7461736b",
> +};
> +#define GTP_VAR_CURRENT_THREAD_INFO_ID 2
> +static struct gtp_var gtp_var_current_thread_info = {
> + .next = &gtp_var_current_task,
> + .num = GTP_VAR_CURRENT_THREAD_INFO_ID,
> + .src = "0:1:63757272656e745f7468726561645f696e666f",
> +};
> +#define GTP_VAR_CLOCK_ID 3
> +static struct gtp_var gtp_var_clock = {
> + .next = &gtp_var_current_thread_info,
> + .num = GTP_VAR_CLOCK_ID,
> + .src = "0:1:636c6f636b",
> +};
> +#define GTP_VAR_CPU_ID 4
> +static struct gtp_var gtp_var_cpu_id = {
> + .next = &gtp_var_clock,
> + .num = GTP_VAR_CPU_ID,
> + .src = "0:1:6370755f6964",
> +};
> +#define GTP_VAR_PRINTK_TMP_ID 5
> +static struct gtp_var gtp_var_printk_tmp = {
> + .next = &gtp_var_cpu_id,
> + .num = GTP_VAR_PRINTK_TMP_ID,
> + .src = "0:1:7072696e746b5f746d70",
> +};
> +#define GTP_VAR_PRINTK_LEVEL_ID 6
> +static struct gtp_var gtp_var_printk_level = {
> + .next = &gtp_var_printk_tmp,
> + .num = GTP_VAR_PRINTK_LEVEL_ID,
> + .src = "8:1:7072696e746b5f6c6576656c",
> +};
> +#define GTP_VAR_PRINTK_FORMAT_ID 7
> +static struct gtp_var gtp_var_printk_format = {
> + .next = &gtp_var_printk_level,
> + .num = GTP_VAR_PRINTK_FORMAT_ID,
> + .src = "0:1:7072696e746b5f666f726d6174",
> +};
> +#define GTP_VAR_DUMP_STACK_ID 8
> +static struct gtp_var gtp_var_dump_stack = {
> + .next = &gtp_var_printk_format,
> + .num = GTP_VAR_DUMP_STACK_ID,
> + .src = "0:1:64756d705f737461636b",
> +};
> +#define GTP_VAR_LIST_FIRST (&gtp_var_dump_stack)
> +#define GTP_VAR_SPECIAL_MIN GTP_VAR_CURRENT_TASK_ID
> +#define GTP_VAR_SPECIAL_MAX GTP_VAR_DUMP_STACK_ID
> +#define GTP_VAR_IS_SPECIAL(x) ((x) >= GTP_VAR_SPECIAL_MIN \
> + && (x) <= GTP_VAR_SPECIAL_MAX)
> +#define GTP_VAR_NOT_GETV(x) ((x) == GTP_VAR_PRINTK_LEVEL_ID \
> + || (x) == GTP_VAR_PRINTK_FORMAT_ID)
> +#define GTP_VAR_NOT_SETV(x) (((x) >= GTP_VAR_CURRENT_TASK_ID \
> + && (x) <= GTP_VAR_CPU_ID) \
> + || (x) == GTP_VAR_DUMP_STACK_ID)
> +#define GTP_VAR_NOT_TRACEV(x) ((x) >= GTP_VAR_PRINTK_LEVEL_ID \
> + && (x) <= GTP_VAR_PRINTK_FORMAT_ID)
> +
> +static DEFINE_SPINLOCK(gtp_frame_lock);
> +static char *gtp_frame;
> +static char *gtp_frame_r_start;
> +static char *gtp_frame_w_start;
> +static char *gtp_frame_end;
> +static int gtp_frame_is_circular;
> +static char *gtp_frame_current;
> +static int gtp_frame_current_num;
> +static atomic_t gtp_frame_create;
> +static char *gtp_frame_file;
> +static size_t gtp_frame_file_size;
> +
> +static struct gtpro_entry *gtpro_list;
> +
> +#define GTP_PRINTF_MAX 256
> +static DEFINE_PER_CPU(char[GTP_PRINTF_MAX], gtp_printf);
> +
> +#ifdef CONFIG_X86
> +static ULONGEST
> +gtp_action_reg_read(struct pt_regs *regs, struct gtp_entry *tpe, int num)
> +{
> + ULONGEST ret;
> +
> + switch (num) {
> +#ifdef CONFIG_X86_32
> + case 0:
> + ret = regs->ax;
> + break;
> + case 1:
> + ret = regs->cx;
> + break;
> + case 2:
> + ret = regs->dx;
> + break;
> + case 3:
> + ret = regs->bx;
> + break;
> + case 4:
> + ret = (ULONGEST)(CORE_ADDR)&regs->sp;
> + break;
> + case 5:
> + ret = regs->bp;
> + break;
> + case 6:
> + ret = regs->si;
> + break;
> + case 7:
> + ret = regs->di;
> + break;
> + case 8:
> + ret = regs->ip - 1;
> + break;
> + case 9:
> + ret = regs->flags;
> + break;
> + case 10:
> + ret = regs->cs;
> + break;
> + case 11:
> + ret = regs->ss;
> + break;
> + case 12:
> + ret = regs->ds;
> + break;
> + case 13:
> + ret = regs->es;
> + break;
> + case 14:
> + ret = regs->fs;
> + break;
> + case 15:
> + ret = regs->gs;
> + break;
> +#else
> + case 0:
> + ret = regs->ax;
> + break;
> + case 1:
> + ret = regs->bx;
> + break;
> + case 2:
> + ret = regs->cx;
> + break;
> + case 3:
> + ret = regs->dx;
> + break;
> + case 4:
> + ret = regs->si;
> + break;
> + case 5:
> + ret = regs->di;
> + break;
> + case 6:
> + ret = regs->bp;
> + break;
> + case 7:
> + ret = regs->sp;
> + break;
> + case 8:
> + ret = regs->r8;
> + break;
> + case 9:
> + ret = regs->r9;
> + break;
> + case 10:
> + ret = regs->r10;
> + break;
> + case 11:
> + ret = regs->r11;
> + break;
> + case 12:
> + ret = regs->r12;
> + break;
> + case 13:
> + ret = regs->r13;
> + break;
> + case 14:
> + ret = regs->r14;
> + break;
> + case 15:
> + ret = regs->r15;
> + break;
> + case 16:
> + ret = regs->ip - 1;
> + break;
> + case 17:
> + ret = regs->flags;
> + break;
> + case 18:
> + ret = regs->cs;
> + break;
> + case 19:
> + ret = regs->ss;
> + break;
> +#endif
> + default:
> + ret = 0;
> + tpe->reason = gtp_stop_access_wrong_reg;
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static void
> +gtp_regs2ascii(struct pt_regs *regs, char *buf)
> +{
> +#ifdef CONFIG_X86_32
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_regs2ascii: ax = 0x%x\n",
> + (unsigned int) regs->ax);
> + printk(GTP_DEBUG "gtp_regs2ascii: cx = 0x%x\n",
> + (unsigned int) regs->cx);
> + printk(GTP_DEBUG "gtp_regs2ascii: dx = 0x%x\n",
> + (unsigned int) regs->dx);
> + printk(GTP_DEBUG "gtp_regs2ascii: bx = 0x%x\n",
> + (unsigned int) regs->bx);
> + printk(GTP_DEBUG "gtp_regs2ascii: sp = 0x%x\n",
> + (unsigned int) regs->sp);
> + printk(GTP_DEBUG "gtp_regs2ascii: bp = 0x%x\n",
> + (unsigned int) regs->bp);
> + printk(GTP_DEBUG "gtp_regs2ascii: si = 0x%x\n",
> + (unsigned int) regs->si);
> + printk(GTP_DEBUG "gtp_regs2ascii: di = 0x%x\n",
> + (unsigned int) regs->di);
> + printk(GTP_DEBUG "gtp_regs2ascii: ip = 0x%x\n",
> + (unsigned int) regs->ip);
> + printk(GTP_DEBUG "gtp_regs2ascii: flags = 0x%x\n",
> + (unsigned int) regs->flags);
> + printk(GTP_DEBUG "gtp_regs2ascii: cs = 0x%x\n",
> + (unsigned int) regs->cs);
> + printk(GTP_DEBUG "gtp_regs2ascii: ss = 0x%x\n",
> + (unsigned int) regs->ss);
> + printk(GTP_DEBUG "gtp_regs2ascii: ds = 0x%x\n",
> + (unsigned int) regs->ds);
> + printk(GTP_DEBUG "gtp_regs2ascii: es = 0x%x\n",
> + (unsigned int) regs->es);
> + printk(GTP_DEBUG "gtp_regs2ascii: fs = 0x%x\n",
> + (unsigned int) regs->fs);
> + printk(GTP_DEBUG "gtp_regs2ascii: gs = 0x%x\n",
> + (unsigned int) regs->gs);
> +#endif
> + sprintf(buf, "%08x", (unsigned int) swab32(regs->ax));
> + buf += 8;
> + sprintf(buf, "%08x", (unsigned int) swab32(regs->cx));
> + buf += 8;
> + sprintf(buf, "%08x", (unsigned int) swab32(regs->dx));
> + buf += 8;
> + sprintf(buf, "%08x", (unsigned int) swab32(regs->bx));
> + buf += 8;
> + sprintf(buf, "%08x", (unsigned int) swab32(regs->sp));
> + buf += 8;
> + sprintf(buf, "%08x", (unsigned int) swab32(regs->bp));
> + buf += 8;
> + sprintf(buf, "%08x", (unsigned int) swab32(regs->si));
> + buf += 8;
> + sprintf(buf, "%08x", (unsigned int) swab32(regs->di));
> + buf += 8;
> + sprintf(buf, "%08x", (unsigned int) swab32(regs->ip));
> + buf += 8;
> + sprintf(buf, "%08x", (unsigned int) swab32(regs->flags));
> + buf += 8;
> + sprintf(buf, "%08x", (unsigned int) swab32(regs->cs));
> + buf += 8;
> + sprintf(buf, "%08x", (unsigned int) swab32(regs->ss));
> + buf += 8;
> + sprintf(buf, "%08x", (unsigned int) swab32(regs->ds));
> + buf += 8;
> + sprintf(buf, "%08x", (unsigned int) swab32(regs->es));
> + buf += 8;
> + sprintf(buf, "%08x", (unsigned int) swab32(regs->fs));
> + buf += 8;
> + sprintf(buf, "%08x", (unsigned int) swab32(regs->gs));
> + buf += 8;
> +#else
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_regs2ascii: ax = 0x%lx\n", regs->ax);
> + printk(GTP_DEBUG "gtp_regs2ascii: bx = 0x%lx\n", regs->bx);
> + printk(GTP_DEBUG "gtp_regs2ascii: cx = 0x%lx\n", regs->cx);
> + printk(GTP_DEBUG "gtp_regs2ascii: dx = 0x%lx\n", regs->dx);
> + printk(GTP_DEBUG "gtp_regs2ascii: si = 0x%lx\n", regs->si);
> + printk(GTP_DEBUG "gtp_regs2ascii: di = 0x%lx\n", regs->di);
> + printk(GTP_DEBUG "gtp_regs2ascii: bp = 0x%lx\n", regs->bp);
> + printk(GTP_DEBUG "gtp_regs2ascii: sp = 0x%lx\n", regs->sp);
> + printk(GTP_DEBUG "gtp_regs2ascii: r8 = 0x%lx\n", regs->r8);
> + printk(GTP_DEBUG "gtp_regs2ascii: r9 = 0x%lx\n", regs->r9);
> + printk(GTP_DEBUG "gtp_regs2ascii: r10 = 0x%lx\n", regs->r10);
> + printk(GTP_DEBUG "gtp_regs2ascii: r11 = 0x%lx\n", regs->r11);
> + printk(GTP_DEBUG "gtp_regs2ascii: r12 = 0x%lx\n", regs->r12);
> + printk(GTP_DEBUG "gtp_regs2ascii: r13 = 0x%lx\n", regs->r13);
> + printk(GTP_DEBUG "gtp_regs2ascii: r14 = 0x%lx\n", regs->r14);
> + printk(GTP_DEBUG "gtp_regs2ascii: r15 = 0x%lx\n", regs->r15);
> + printk(GTP_DEBUG "gtp_regs2ascii: ip = 0x%lx\n", regs->ip);
> + printk(GTP_DEBUG "gtp_regs2ascii: flags = 0x%lx\n", regs->flags);
> + printk(GTP_DEBUG "gtp_regs2ascii: cs = 0x%lx\n", regs->cs);
> + printk(GTP_DEBUG "gtp_regs2ascii: ss = 0x%lx\n", regs->ss);
> +#endif
> + sprintf(buf, "%016lx", (unsigned long) swab64(regs->ax));
> + buf += 16;
> + sprintf(buf, "%016lx", (unsigned long) swab64(regs->bx));
> + buf += 16;
> + sprintf(buf, "%016lx", (unsigned long) swab64(regs->cx));
> + buf += 16;
> + sprintf(buf, "%016lx", (unsigned long) swab64(regs->dx));
> + buf += 16;
> + sprintf(buf, "%016lx", (unsigned long) swab64(regs->si));
> + buf += 16;
> + sprintf(buf, "%016lx", (unsigned long) swab64(regs->di));
> + buf += 16;
> + sprintf(buf, "%016lx", (unsigned long) swab64(regs->bp));
> + buf += 16;
> + sprintf(buf, "%016lx", (unsigned long) swab64(regs->sp));
> + buf += 16;
> + sprintf(buf, "%016lx", (unsigned long) swab64(regs->r8));
> + buf += 16;
> + sprintf(buf, "%016lx", (unsigned long) swab64(regs->r9));
> + buf += 16;
> + sprintf(buf, "%016lx", (unsigned long) swab64(regs->r10));
> + buf += 16;
> + sprintf(buf, "%016lx", (unsigned long) swab64(regs->r11));
> + buf += 16;
> + sprintf(buf, "%016lx", (unsigned long) swab64(regs->r12));
> + buf += 16;
> + sprintf(buf, "%016lx", (unsigned long) swab64(regs->r13));
> + buf += 16;
> + sprintf(buf, "%016lx", (unsigned long) swab64(regs->r14));
> + buf += 16;
> + sprintf(buf, "%016lx", (unsigned long) swab64(regs->r15));
> + buf += 16;
> + sprintf(buf, "%016lx", (unsigned long) swab64(regs->ip));
> + buf += 16;
> + sprintf(buf, "%08x",
> + (unsigned int) swab32((unsigned int)regs->flags));
> + buf += 8;
> + sprintf(buf, "%08x",
> + (unsigned int) swab32((unsigned int)regs->cs));
> + buf += 8;
> + sprintf(buf, "%08x",
> + (unsigned int) swab32((unsigned int)regs->ss));
> + buf += 8;
> +#endif
> +}
> +
> +static void
> +gtp_regs2bin(struct pt_regs *regs, char *buf)
> +{
> +#ifdef CONFIG_X86_32
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_regs2ascii: ax = 0x%x\n",
> + (unsigned int) regs->ax);
> + printk(GTP_DEBUG "gtp_regs2ascii: cx = 0x%x\n",
> + (unsigned int) regs->cx);
> + printk(GTP_DEBUG "gtp_regs2ascii: dx = 0x%x\n",
> + (unsigned int) regs->dx);
> + printk(GTP_DEBUG "gtp_regs2ascii: bx = 0x%x\n",
> + (unsigned int) regs->bx);
> + printk(GTP_DEBUG "gtp_regs2ascii: sp = 0x%x\n",
> + (unsigned int) regs->sp);
> + printk(GTP_DEBUG "gtp_regs2ascii: bp = 0x%x\n",
> + (unsigned int) regs->bp);
> + printk(GTP_DEBUG "gtp_regs2ascii: si = 0x%x\n",
> + (unsigned int) regs->si);
> + printk(GTP_DEBUG "gtp_regs2ascii: di = 0x%x\n",
> + (unsigned int) regs->di);
> + printk(GTP_DEBUG "gtp_regs2ascii: ip = 0x%x\n",
> + (unsigned int) regs->ip);
> + printk(GTP_DEBUG "gtp_regs2ascii: flags = 0x%x\n",
> + (unsigned int) regs->flags);
> + printk(GTP_DEBUG "gtp_regs2ascii: cs = 0x%x\n",
> + (unsigned int) regs->cs);
> + printk(GTP_DEBUG "gtp_regs2ascii: ss = 0x%x\n",
> + (unsigned int) regs->ss);
> + printk(GTP_DEBUG "gtp_regs2ascii: ds = 0x%x\n",
> + (unsigned int) regs->ds);
> + printk(GTP_DEBUG "gtp_regs2ascii: es = 0x%x\n",
> + (unsigned int) regs->es);
> + printk(GTP_DEBUG "gtp_regs2ascii: fs = 0x%x\n",
> + (unsigned int) regs->fs);
> + printk(GTP_DEBUG "gtp_regs2ascii: gs = 0x%x\n",
> + (unsigned int) regs->gs);
> +#endif
> + memcpy(buf, &regs->ax, 4);
> + buf += 4;
> + memcpy(buf, &regs->cx, 4);
> + buf += 4;
> + memcpy(buf, &regs->dx, 4);
> + buf += 4;
> + memcpy(buf, &regs->bx, 4);
> + buf += 4;
> + memcpy(buf, &regs->sp, 4);
> + buf += 4;
> + memcpy(buf, &regs->bp, 4);
> + buf += 4;
> + memcpy(buf, &regs->si, 4);
> + buf += 4;
> + memcpy(buf, &regs->di, 4);
> + buf += 4;
> + memcpy(buf, &regs->ip, 4);
> + buf += 4;
> + memcpy(buf, &regs->flags, 4);
> + buf += 4;
> + memcpy(buf, &regs->cs, 4);
> + buf += 4;
> + memcpy(buf, &regs->ss, 4);
> + buf += 4;
> + memcpy(buf, &regs->ds, 4);
> + buf += 4;
> + memcpy(buf, &regs->es, 4);
> + buf += 4;
> + memcpy(buf, &regs->fs, 4);
> + buf += 4;
> + memcpy(buf, &regs->gs, 4);
> + buf += 4;
> +#else
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_regs2ascii: ax = 0x%lx\n", regs->ax);
> + printk(GTP_DEBUG "gtp_regs2ascii: bx = 0x%lx\n", regs->bx);
> + printk(GTP_DEBUG "gtp_regs2ascii: cx = 0x%lx\n", regs->cx);
> + printk(GTP_DEBUG "gtp_regs2ascii: dx = 0x%lx\n", regs->dx);
> + printk(GTP_DEBUG "gtp_regs2ascii: si = 0x%lx\n", regs->si);
> + printk(GTP_DEBUG "gtp_regs2ascii: di = 0x%lx\n", regs->di);
> + printk(GTP_DEBUG "gtp_regs2ascii: bp = 0x%lx\n", regs->bp);
> + printk(GTP_DEBUG "gtp_regs2ascii: sp = 0x%lx\n", regs->sp);
> + printk(GTP_DEBUG "gtp_regs2ascii: r8 = 0x%lx\n", regs->r8);
> + printk(GTP_DEBUG "gtp_regs2ascii: r9 = 0x%lx\n", regs->r9);
> + printk(GTP_DEBUG "gtp_regs2ascii: r10 = 0x%lx\n", regs->r10);
> + printk(GTP_DEBUG "gtp_regs2ascii: r11 = 0x%lx\n", regs->r11);
> + printk(GTP_DEBUG "gtp_regs2ascii: r12 = 0x%lx\n", regs->r12);
> + printk(GTP_DEBUG "gtp_regs2ascii: r13 = 0x%lx\n", regs->r13);
> + printk(GTP_DEBUG "gtp_regs2ascii: r14 = 0x%lx\n", regs->r14);
> + printk(GTP_DEBUG "gtp_regs2ascii: r15 = 0x%lx\n", regs->r15);
> + printk(GTP_DEBUG "gtp_regs2ascii: ip = 0x%lx\n", regs->ip);
> + printk(GTP_DEBUG "gtp_regs2ascii: flags = 0x%lx\n", regs->flags);
> + printk(GTP_DEBUG "gtp_regs2ascii: cs = 0x%lx\n", regs->cs);
> + printk(GTP_DEBUG "gtp_regs2ascii: ss = 0x%lx\n", regs->ss);
> +#endif
> + memcpy(buf, &regs->ax, 8);
> + buf += 8;
> + memcpy(buf, &regs->bx, 8);
> + buf += 8;
> + memcpy(buf, &regs->cx, 8);
> + buf += 8;
> + memcpy(buf, &regs->dx, 8);
> + buf += 8;
> + memcpy(buf, &regs->si, 8);
> + buf += 8;
> + memcpy(buf, &regs->di, 8);
> + buf += 8;
> + memcpy(buf, &regs->bp, 8);
> + buf += 8;
> + memcpy(buf, &regs->sp, 8);
> + buf += 8;
> + memcpy(buf, &regs->r8, 8);
> + buf += 8;
> + memcpy(buf, &regs->r9, 8);
> + buf += 8;
> + memcpy(buf, &regs->r10, 8);
> + buf += 8;
> + memcpy(buf, &regs->r11, 8);
> + buf += 8;
> + memcpy(buf, &regs->r12, 8);
> + buf += 8;
> + memcpy(buf, &regs->r13, 8);
> + buf += 8;
> + memcpy(buf, &regs->r14, 8);
> + buf += 8;
> + memcpy(buf, &regs->r15, 8);
> + buf += 8;
> + memcpy(buf, &regs->ip, 8);
> + buf += 8;
> + memcpy(buf, &regs->flags, 4);
> + buf += 4;
> + memcpy(buf, &regs->cs, 4);
> + buf += 4;
> + memcpy(buf, &regs->ss, 4);
> + buf += 4;
> +#endif
> +}
> +#endif
> +
> +#ifdef CONFIG_MIPS
> +static ULONGEST
> +gtp_action_reg_read(struct pt_regs *regs, struct gtp_entry *tpe, int num)
> +{
> + ULONGEST ret;
> +
> + if (num > 90) {
> + /* GDB convert the reg number to a GDB
> + [1 * gdbarch_num_regs .. 2 * gdbarch_num_regs) REGNUM
> + in function mips_dwarf_dwarf2_ecoff_reg_to_regnum. */
> + num -= 90;
> + }
> +
> + if (num >= 0 && num <= 31) {
> + ret = regs->regs[num];
> + } else {
> + switch (num) {
> + case 32:
> + ret = regs->cp0_status;
> + break;
> + case 33:
> + ret = regs->lo;
> + break;
> + case 34:
> + ret = regs->hi;
> + break;
> + case 35:
> + ret = regs->cp0_badvaddr;
> + break;
> + case 36:
> + ret = regs->cp0_cause;
> + break;
> + case 37:
> + ret = regs->cp0_epc;
> + break;
> + default:
> + ret = 0;
> + tpe->reason = gtp_stop_access_wrong_reg;
> + break;
> + }
> + }
> +
> + return ret;
> +};
> +
> +static void
> +gtp_regs2ascii(struct pt_regs *regs, char *buf)
> +{
> +#ifdef GTP_DEBUG
> + {
> + int i;
> +
> + for (i = 0; i < 32; i++)
> + printk(GTP_DEBUG "gtp_gdbrsp_g: r%d = 0x%lx\n", i,
> + regs->regs[i]);
> + }
> + printk(GTP_DEBUG "gtp_gdbrsp_g: status = 0x%lx\n",
> + regs->cp0_status);
> + printk(GTP_DEBUG "gtp_gdbrsp_g: lo = 0x%lx\n", regs->lo);
> + printk(GTP_DEBUG "gtp_gdbrsp_g: hi = 0x%lx\n", regs->hi);
> + printk(GTP_DEBUG "gtp_gdbrsp_g: badvaddr = 0x%lx\n",
> + regs->cp0_badvaddr);
> + printk(GTP_DEBUG "gtp_gdbrsp_g: cause = 0x%lx\n", regs->cp0_cause);
> + printk(GTP_DEBUG "gtp_gdbrsp_g: pc = 0x%lx\n", regs->cp0_epc);
> +#endif
> +
> +#ifdef CONFIG_32BIT
> +#define OUTFORMAT "%08lx"
> +#define REGSIZE 8
> +#ifdef __LITTLE_ENDIAN
> +#define SWAB(a) swab32(a)
> +#else
> +#define SWAB(a) (a)
> +#endif
> +#else
> +#define OUTFORMAT "%016lx"
> +#define REGSIZE 16
> +#ifdef __LITTLE_ENDIAN
> +#define SWAB(a) swab64(a)
> +#else
> +#define SWAB(a) (a)
> +#endif
> +#endif
> + {
> + int i;
> +
> + for (i = 0; i < 32; i++) {
> + sprintf(buf, OUTFORMAT,
> + (unsigned long) SWAB(regs->regs[i]));
> + buf += REGSIZE;
> + }
> + }
> +
> + sprintf(buf, OUTFORMAT,
> + (unsigned long) SWAB(regs->cp0_status));
> + buf += REGSIZE;
> + sprintf(buf, OUTFORMAT,
> + (unsigned long) SWAB(regs->lo));
> + buf += REGSIZE;
> + sprintf(buf, OUTFORMAT,
> + (unsigned long) SWAB(regs->hi));
> + buf += REGSIZE;
> + sprintf(buf, OUTFORMAT,
> + (unsigned long) SWAB(regs->cp0_badvaddr));
> + buf += REGSIZE;
> + sprintf(buf, OUTFORMAT,
> + (unsigned long) SWAB(regs->cp0_cause));
> + buf += REGSIZE;
> + sprintf(buf, OUTFORMAT,
> + (unsigned long) SWAB(regs->cp0_epc));
> + buf += REGSIZE;
> +#undef OUTFORMAT
> +#undef REGSIZE
> +#undef SWAB
> +}
> +
> +static void
> +gtp_regs2bin(struct pt_regs *regs, char *buf)
> +{
> +#ifdef GTP_DEBUG
> + {
> + int i;
> +
> + for (i = 0; i < 32; i++)
> + printk(GTP_DEBUG "gtp_gdbrsp_g: r%d = 0x%lx\n", i,
> + regs->regs[i]);
> + }
> + printk(GTP_DEBUG "gtp_gdbrsp_g: status = 0x%lx\n",
> + regs->cp0_status);
> + printk(GTP_DEBUG "gtp_gdbrsp_g: lo = 0x%lx\n", regs->lo);
> + printk(GTP_DEBUG "gtp_gdbrsp_g: hi = 0x%lx\n", regs->hi);
> + printk(GTP_DEBUG "gtp_gdbrsp_g: badvaddr = 0x%lx\n",
> + regs->cp0_badvaddr);
> + printk(GTP_DEBUG "gtp_gdbrsp_g: cause = 0x%lx\n", regs->cp0_cause);
> + printk(GTP_DEBUG "gtp_gdbrsp_g: pc = 0x%lx\n", regs->cp0_epc);
> +#endif
> +
> +#ifdef CONFIG_32BIT
> +#define REGSIZE 4
> +#else
> +#define REGSIZE 8
> +#endif
> + {
> + int i;
> +
> + for (i = 0; i < 32; i++) {
> + memcpy(buf, &regs->regs[i], REGSIZE);
> + buf += REGSIZE;
> + }
> + }
> + memcpy(buf, &regs->cp0_status, REGSIZE);
> + buf += REGSIZE;
> + memcpy(buf, &regs->lo, REGSIZE);
> + buf += REGSIZE;
> + memcpy(buf, &regs->hi, REGSIZE);
> + buf += REGSIZE;
> + memcpy(buf, &regs->cp0_badvaddr, REGSIZE);
> + buf += REGSIZE;
> + memcpy(buf, &regs->cp0_cause, REGSIZE);
> + buf += REGSIZE;
> + memcpy(buf, &regs->cp0_epc, REGSIZE);
> + buf += REGSIZE;
> +#undef REGSIZE
> +}
> +#endif
> +
> +#ifdef CONFIG_ARM
> +static ULONGEST
> +gtp_action_reg_read(struct pt_regs *regs, struct gtp_entry *tpe, int num)
> +{
> + if (num >= 0 && num < 16)
> + return regs->uregs[num];
> + else if (num == 25)
> + return regs->uregs[16];
> +
> + tpe->reason = gtp_stop_access_wrong_reg;
> + return 0;
> +}
> +
> +static void
> +gtp_regs2ascii(struct pt_regs *regs, char *buf)
> +{
> +#ifdef __LITTLE_ENDIAN
> +#define SWAB(a) swab32(a)
> +#else
> +#define SWAB(a) (a)
> +#endif
> + int i;
> +
> + for (i = 0; i < 16; i++) {
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gdbrsp_g: r%d = 0x%lx\n",
> + i, regs->uregs[i]);
> +#endif
> + sprintf(buf, "%08lx", (unsigned long) SWAB(regs->uregs[i]));
> + buf += 8;
> + }
> +
> + /* f0-f7 fps */
> + memset(buf, '0', 200);
> + buf += 200;
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gdbrsp_g: cpsr = 0x%lx\n", regs->uregs[16]);
> +#endif
> + sprintf(buf, "%08lx",
> + (unsigned long) SWAB(regs->uregs[16]));
> + buf += 8;
> +#undef SWAB
> +}
> +
> +static void
> +gtp_regs2bin(struct pt_regs *regs, char *buf)
> +{
> + int i;
> +
> + for (i = 0; i < 16; i++) {
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gdbrsp_g: r%d = 0x%lx\n",
> + i, regs->uregs[i]);
> +#endif
> + memcpy(buf, &regs->uregs[i], 4);
> + buf += 4;
> + }
> +
> + /* f0-f7 fps */
> + memset(buf, '\0', 100);
> + buf += 100;
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gdbrsp_g: cpsr = 0x%lx\n", regs->uregs[16]);
> +#endif
> + memcpy(buf, &regs->uregs[16], 4);
> + buf += 4;
> +}
> +#endif
> +
> +static char *
> +gtp_frame_next(char *frame)
> +{
> + switch (FID(frame)) {
> + case FID_HEAD:
> + frame += FRAME_ALIGN(GTP_FRAME_HEAD_SIZE);
> + break;
> + case FID_REG:
> + frame += FRAME_ALIGN(GTP_FRAME_REG_SIZE);
> + break;
> + case FID_MEM: {
> + struct gtp_frame_mem *gfm;
> +
> + gfm = (struct gtp_frame_mem *) (frame + FID_SIZE
> + + sizeof(char *));
> + frame += FRAME_ALIGN(GTP_FRAME_MEM_SIZE + gfm->size);
> + }
> + break;
> + case FID_VAR:
> + frame += FRAME_ALIGN(GTP_FRAME_VAR_SIZE);
> + break;
> + case FID_END:
> + frame = gtp_frame;
> + break;
> + default:
> + return NULL;
> + break;
> + }
> +
> + return frame;
> +}
> +
> +#ifdef FRAME_ALLOC_RECORD
> +ULONGEST frame_alloc_size;
> +ULONGEST frame_alloc_size_hole;
> +#endif
> +
> +static char *
> +gtp_frame_alloc(size_t size)
> +{
> + char *ret = NULL;
> +
> +#ifdef FRAME_ALLOC_RECORD
> + frame_alloc_size += size;
> + frame_alloc_size_hole += (FRAME_ALIGN(size) - size);
> +#endif
> +
> + size = FRAME_ALIGN(size);
> +
> + if (size > GTP_FRAME_SIZE)
> + return NULL;
> +
> + spin_lock(&gtp_frame_lock);
> +
> + if (gtp_frame_w_start + size > gtp_frame_end) {
> + if (gtp_circular) {
> + gtp_frame_is_circular = 1;
> +#ifdef FRAME_ALLOC_RECORD
> + if (gtp_frame_w_start != gtp_frame_end
> + && gtp_frame_end - gtp_frame_w_start < FID_SIZE) {
> + printk(KERN_WARNING "Frame align wrong."
> + "start = %p end = %p\n",
> + gtp_frame_w_start, gtp_frame_end);
> + goto out;
> + }
> +#endif
> + if (gtp_frame_w_start != gtp_frame_end)
> + FID(gtp_frame_w_start) = FID_END;
> + gtp_frame_w_start = gtp_frame;
> + gtp_frame_r_start = gtp_frame;
> + } else
> + goto out;
> + }
> +
> + if (gtp_frame_is_circular) {
> + while (gtp_frame_w_start <= gtp_frame_r_start
> + && gtp_frame_w_start + size > gtp_frame_r_start) {
> + char *tmp = gtp_frame_next(gtp_frame_r_start);
> + if (tmp == NULL)
> + goto out;
> + gtp_frame_r_start = tmp;
> + }
> + }
> +
> + ret = gtp_frame_w_start;
> + gtp_frame_w_start += size;
> +
> +out:
> + spin_unlock(&gtp_frame_lock);
> + return ret;
> +}
> +
> +static char * *
> +gtp_action_head(struct gtp_entry *tpe)
> +{
> + char *tmp;
> + char **nextp;
> + ULONGEST *trace_nump;
> +
> + /* Get the head. */
> + tmp = gtp_frame_alloc(GTP_FRAME_HEAD_SIZE);
> + if (!tmp) {
> + tpe->reason = gtp_stop_frame_full;
> + return NULL;
> + }
> +
> + FID(tmp) = FID_HEAD;
> + tmp += FID_SIZE;
> +
> + nextp = (char **)tmp;
> + *nextp = NULL;
> + tmp += sizeof(char *);
> +
> + trace_nump = (ULONGEST *)tmp;
> + *trace_nump = tpe->num;
> +
> + return nextp;
> +}
> +
> +struct gtp_trace_s {
> + struct gtp_entry *tpe;
> + struct pt_regs *regs;
> + char **next;
> + int *run;
> + ULONGEST printk_tmp;
> + unsigned int printk_level;
> + unsigned int printk_format;
> + struct gtpsrc *printk_str;
> +};
> +
> +#define GTP_PRINTK_FORMAT_A 0
> +#define GTP_PRINTK_FORMAT_D 1
> +#define GTP_PRINTK_FORMAT_U 2
> +#define GTP_PRINTK_FORMAT_X 3
> +#define GTP_PRINTK_FORMAT_S 4
> +#define GTP_PRINTK_FORMAT_B 5
> +
> +static int
> +gtp_action_printk(struct gtp_trace_s *gts, ULONGEST addr, size_t size)
> +{
> + unsigned int printk_format = gts->printk_format;
> + char *pbuf = per_cpu(gtp_printf, smp_processor_id());
> +
> + if (gts->printk_str == NULL) {
> + gts->tpe->reason = gtp_stop_agent_expr_code_error;
> + printk(KERN_WARNING "gtp_action_printk: id:%d "
> + "printk doesn't have var name. Please "
> + "check actions of it.\n",
> + (int)gts->tpe->num);
> + return -1;
> + }
> +
> + if (size) {
> + if (size > GTP_PRINTF_MAX - 1)
> + size = GTP_PRINTF_MAX - 1;
> + if (gts->printk_format != GTP_PRINTK_FORMAT_S
> + && gts->printk_format != GTP_PRINTK_FORMAT_B
> + && size > 8)
> + size = 8;
> + if (probe_kernel_read(pbuf, (void *)(CORE_ADDR)addr, size)) {
> + gts->tpe->reason = gtp_stop_efault;
> + printk(KERN_WARNING "gtp_action_printk: id:%d "
> + "read %p %u get error.\n",
> + (int)gts->tpe->num, (void *)(CORE_ADDR)addr,
> + (unsigned int)size);
> + return -1;
> + }
> + } else {
> + size = sizeof(ULONGEST);
> + memcpy(pbuf, &addr, sizeof(ULONGEST));
> + }
> +
> + if (printk_format == GTP_PRINTK_FORMAT_A) {
> + if (size == 1 || size == 2 || size == 4 || size == 8)
> + printk_format = GTP_PRINTK_FORMAT_U;
> + else
> + printk_format = GTP_PRINTK_FORMAT_B;
> + }
> +
> + switch (printk_format) {
> + case GTP_PRINTK_FORMAT_D:
> + switch (size) {
> + case 1:
> + printk(KERN_NULL "<%d>%s%d\n", gts->printk_level,
> + gts->printk_str->src, pbuf[0]);
> + break;
> + case 2:
> + printk(KERN_NULL "<%d>%s%d\n", gts->printk_level,
> + gts->printk_str->src, (int)(*(short *)pbuf));
> + break;
> + case 4:
> + printk(KERN_NULL "<%d>%s%d\n", gts->printk_level,
> + gts->printk_str->src, *(int *)pbuf);
> + break;
> + case 8:
> + printk(KERN_NULL "<%d>%s%lld\n", gts->printk_level,
> + gts->printk_str->src, *(long long *)pbuf);
> + break;
> + default:
> + printk(KERN_WARNING "gtp_action_printk: id:%d "
> + "size %d cannot printk.\n",
> + (int)gts->tpe->num, (unsigned int)size);
> + gts->tpe->reason = gtp_stop_agent_expr_code_error;
> + return -1;
> + break;
> + }
> + break;
> + case GTP_PRINTK_FORMAT_U:
> + switch (size) {
> + case 1:
> + printk(KERN_NULL "<%d>%s%u\n", gts->printk_level,
> + gts->printk_str->src, pbuf[0]);
> + break;
> + case 2:
> + printk(KERN_NULL "<%d>%s%u\n", gts->printk_level,
> + gts->printk_str->src, (int)(*(short *)pbuf));
> + break;
> + case 4:
> + printk(KERN_NULL "<%d>%s%u\n", gts->printk_level,
> + gts->printk_str->src, *(int *)pbuf);
> + break;
> + case 8:
> + printk(KERN_NULL "<%d>%s%llu\n", gts->printk_level,
> + gts->printk_str->src, *(long long *)pbuf);
> + break;
> + default:
> + printk(KERN_WARNING "gtp_action_printk: id:%d "
> + "size %d cannot printk.\n",
> + (int)gts->tpe->num, (unsigned int)size);
> + gts->tpe->reason = gtp_stop_agent_expr_code_error;
> + return -1;
> + break;
> + }
> + break;
> + case GTP_PRINTK_FORMAT_X:
> + switch (size) {
> + case 1:
> + printk(KERN_NULL "<%d>%s0x%x\n", gts->printk_level,
> + gts->printk_str->src, pbuf[0]);
> + break;
> + case 2:
> + printk(KERN_NULL "<%d>%s0x%x\n", gts->printk_level,
> + gts->printk_str->src, (int)(*(short *)pbuf));
> + break;
> + case 4:
> + printk(KERN_NULL "<%d>%s0x%x\n", gts->printk_level,
> + gts->printk_str->src, *(int *)pbuf);
> + break;
> + case 8:
> + printk(KERN_NULL "<%d>%s0x%llx\n", gts->printk_level,
> + gts->printk_str->src, *(long long *)pbuf);
> + break;
> + default:
> + printk(KERN_WARNING "gtp_action_printk: id:%d "
> + "size %d cannot printk.\n",
> + (int)gts->tpe->num, (unsigned int)size);
> + gts->tpe->reason = gtp_stop_agent_expr_code_error;
> + return -1;
> + break;
> + }
> + break;
> + case GTP_PRINTK_FORMAT_S:
> + pbuf[GTP_PRINTF_MAX - 1] = '\0';
> + printk("<%d>%s%s\n", gts->printk_level, gts->printk_str->src,
> + pbuf);
> + break;
> + case GTP_PRINTK_FORMAT_B: {
> + size_t i;
> +
> + printk(KERN_NULL "<%d>%s", gts->printk_level,
> + gts->printk_str->src);
> + for (i = 0; i < size; i++)
> + printk("%02x", (unsigned int)pbuf[i]);
> + printk("\n");
> + }
> + break;
> + default:
> + printk(KERN_WARNING "gtp_action_printk: id:%d "
> + "printk format %u is not support.\n",
> + (int)gts->tpe->num, gts->printk_format);
> + gts->tpe->reason = gtp_stop_agent_expr_code_error;
> + return -1;
> + break;
> + }
> +
> + gts->printk_str = gts->printk_str->next;
> +
> + return 0;
> +}
> +
> +static int
> +gtp_action_memory_read(struct gtp_trace_s *gts, int reg, CORE_ADDR addr,
> + size_t size)
> +{
> + char *tmp;
> + struct gtp_frame_mem *fm;
> +
> + if (reg >= 0)
> + addr += (CORE_ADDR) gtp_action_reg_read(gts->regs,
> + gts->tpe, reg);
> + if (gts->tpe->reason != gtp_stop_normal)
> + return -1;
> +
> + if (gts->next == NULL) {
> + gts->next = gtp_action_head(gts->tpe);
> + if (!gts->next)
> + return -1;
> + }
> +
> + tmp = gtp_frame_alloc(GTP_FRAME_MEM_SIZE + size);
> + if (!tmp) {
> + gts->tpe->reason = gtp_stop_frame_full;
> + return -1;
> + }
> + *gts->next = tmp;
> +
> + FID(tmp) = FID_MEM;
> + tmp += FID_SIZE;
> +
> + gts->next = (char **)tmp;
> + *gts->next = NULL;
> + tmp += sizeof(char *);
> +
> + fm = (struct gtp_frame_mem *)tmp;
> + fm->addr = addr;
> + fm->size = size;
> + tmp += sizeof(struct gtp_frame_mem);
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_action_memory_read: id:%d %p %u\n",
> + (int)gts->tpe->num, (void *)addr, (unsigned int)size);
> +#endif
> +
> + if (probe_kernel_read(tmp, (void *)addr, size)) {
> + gts->tpe->reason = gtp_stop_efault;
> + memset(tmp, 0, size);
> + printk(KERN_WARNING "gtp_action_memory_read: id:%d read %p %u "
> + "get error.\n", (int)gts->tpe->num,
> + (void *)addr, (unsigned int)size);
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +static int
> +gtp_action_r(struct gtp_trace_s *gts, struct action *ae)
> +{
> + struct pt_regs *regs;
> + char *tmp;
> +
> + if (gts->next == NULL) {
> + gts->next = gtp_action_head(gts->tpe);
> + if (!gts->next)
> + return -1;
> + }
> +
> + tmp = gtp_frame_alloc(GTP_FRAME_REG_SIZE);
> + if (!tmp) {
> + gts->tpe->reason = gtp_stop_frame_full;
> + return -1;
> + }
> + *gts->next = tmp;
> +
> + FID(tmp) = FID_REG;
> + tmp += FID_SIZE;
> +
> + gts->next = (char **)tmp;
> + *gts->next = NULL;
> + tmp += sizeof(char *);
> +
> + regs = (struct pt_regs *)tmp;
> +
> + memcpy(regs, gts->regs, sizeof(struct pt_regs));
> +#ifdef CONFIG_X86_32
> + regs->sp = (unsigned long)&regs->sp;
> +#endif /* CONFIG_X86_32 */
> +#ifdef CONFIG_X86
> + regs->ip -= 1;
> +#endif /* CONFIG_X86 */
> +
> + return 0;
> +}
> +
> +static struct gtp_var *
> +gtp_gtp_var_array_find(unsigned int num)
> +{
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gtp_var_array_find: num:%u %u %u\n",
> + gtp_var_head, gtp_var_tail, num);
> +#endif
> +
> + if (num < gtp_var_head || num > gtp_var_tail)
> + return NULL;
> +
> + return gtp_var_array[num - gtp_var_head];
> +}
> +
> +uint64_t
> +gtp_get_var(struct gtp_trace_s *gts, struct gtp_var *tve)
> +{
> + switch (tve->num) {
> + case GTP_VAR_CURRENT_TASK_ID:
> + return (uint64_t)(CORE_ADDR)get_current();
> + break;
> + case GTP_VAR_CURRENT_THREAD_INFO_ID:
> + return (uint64_t)(CORE_ADDR)current_thread_info();
> + break;
> + case GTP_VAR_CLOCK_ID:
> + return (uint64_t)(CORE_ADDR)local_clock();
> + break;
> + case GTP_VAR_CPU_ID:
> + return (uint64_t)(CORE_ADDR)smp_processor_id();
> + break;
> + case GTP_VAR_PRINTK_TMP_ID:
> + return gts->printk_tmp;
> + break;
> + case GTP_VAR_DUMP_STACK_ID:
> + printk(KERN_NULL "gtp %d:", (int)gts->tpe->num);
> + dump_stack();
> + return 0;
> + break;
> + }
> +
> + return tve->val;
> +}
> +
> +static int
> +gtp_collect_var(struct gtp_trace_s *gts, struct gtp_var *tve)
> +{
> + struct gtp_frame_var *fvar;
> + char *tmp;
> +
> + if (gts->next == NULL) {
> + gts->next = gtp_action_head(gts->tpe);
> + if (!gts->next)
> + return -1;
> + }
> +
> + tmp = gtp_frame_alloc(GTP_FRAME_VAR_SIZE);
> + if (!tmp) {
> + gts->tpe->reason = gtp_stop_frame_full;
> + return -1;
> + }
> + *gts->next = tmp;
> +
> + FID(tmp) = FID_VAR;
> + tmp += FID_SIZE;
> +
> + gts->next = (char **)tmp;
> + *gts->next = NULL;
> + tmp += sizeof(char *);
> +
> + fvar = (struct gtp_frame_var *) tmp;
> + fvar->num = tve->num;
> + fvar->val = gtp_get_var(gts, tve);
> +
> + return 0;
> +}
> +
> +#define gtp_action_x_getv \
> + do { \
> + struct gtp_var *tve; \
> + \
> + tve = gtp_gtp_var_array_find(arg); \
> + if (!tve) \
> + goto code_error_out; \
> + \
> + stack[sp++] = top; \
> + \
> + top = gtp_get_var(gts, tve); \
> + } while (0)
> +
> +#define gtp_action_x_setv \
> + do { \
> + switch (arg) { \
> + case GTP_VAR_PRINTK_TMP_ID: \
> + gts->printk_tmp = top; \
> + break; \
> + case GTP_VAR_PRINTK_LEVEL_ID: \
> + gts->printk_level = (unsigned int)top; \
> + break; \
> + case GTP_VAR_PRINTK_FORMAT_ID: \
> + gts->printk_format = (unsigned int)top; \
> + break; \
> + default: { \
> + struct gtp_var *tve; \
> + \
> + tve = gtp_gtp_var_array_find(arg); \
> + if (!tve) \
> + goto code_error_out; \
> + /* Not check the other special \
> + trace state variables. \
> + Checked in gtp_check_x. */ \
> + tve->val = (uint64_t)top; \
> + } \
> + break; \
> + } \
> + } while (0)
> +
> +#define gtp_action_x_tracev \
> + do { \
> + if (gts->tpe->have_printk) \
> + pc += 2; \
> + else { \
> + struct gtp_var *tve; \
> + \
> + tve = gtp_gtp_var_array_find(arg); \
> + if (!tve) \
> + goto code_error_out; \
> + \
> + if (gtp_collect_var(gts, tve)) { \
> + /* gtp_collect_var will set error \
> + status with itself if it got \
> + error. */ \
> + goto out; \
> + } \
> + } \
> + } while (0)
> +
> +#define gtp_action_x_tracev_printk \
> + do { \
> + struct gtp_var *tve; \
> + \
> + tve = gtp_gtp_var_array_find(arg); \
> + if (!tve) \
> + goto code_error_out; \
> + \
> + if (gtp_action_printk(gts, gtp_get_var(gts, tve), 0)) { \
> + /* gtp_collect_var will set error status with \
> + itself if it got error. */ \
> + goto out; \
> + } \
> + } while (0)
> +
> +#define gtp_action_x_printf \
> + do { \
> + if (strstr((char *)(ebuf + pc), "%s")) { \
> + int i; \
> + char buf[50]; \
> + \
> + for (i = 0; i < 50; i++) { \
> + if (probe_kernel_read(buf + i, \
> + argv + i, 1)) \
> + goto code_error_out; \
> + if (!buf[i]) \
> + break; \
> + } \
> + snprintf(pbuf, psize, (char *)(ebuf + pc), \
> + buf); \
> + } else { \
> + snprintf(pbuf, psize, (char *)(ebuf + pc), \
> + argv); \
> + } \
> + } while (0)
> +
> +#define STACK_MAX 32
> +static DEFINE_PER_CPU(ULONGEST[STACK_MAX], action_x_stack);
> +
> +static int
> +gtp_action_x(struct gtp_trace_s *gts, struct action *ae)
> +{
> + int ret = 0;
> + unsigned int pc = 0, sp = 0;
> + ULONGEST top = 0;
> + int arg;
> + union {
> + union {
> + uint8_t bytes[1];
> + uint8_t val;
> + } u8;
> + union {
> + uint8_t bytes[2];
> + uint16_t val;
> + } u16;
> + union {
> + uint8_t bytes[4];
> + uint32_t val;
> + } u32;
> + union {
> + uint8_t bytes[8];
> + ULONGEST val;
> + } u64;
> + } cnv;
> + uint8_t *ebuf = ae->u.exp.buf;
> + int psize = GTP_PRINTF_MAX;
> + char *pbuf = per_cpu(gtp_printf, smp_processor_id());
> + ULONGEST *stack = per_cpu(action_x_stack, smp_processor_id());
> +
> + if (ae->u.exp.need_var_lock)
> + spin_lock(&gtp_var_lock);
> +
> + if (ae->type == 'X') {
> + while (pc < ae->u.exp.size) {
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_parse_x: cmd %x\n", ebuf[pc]);
> +#endif
> +
> + switch (ebuf[pc++]) {
> + /* add */
> + case 0x02:
> + top += stack[--sp];
> + break;
> + /* sub */
> + case 0x03:
> + top = stack[--sp] - top;
> + break;
> + /* mul */
> + case 0x04:
> + top *= stack[--sp];
> + break;
> +#ifndef CONFIG_MIPS
> + /* div_signed */
> + case 0x05:
> + if (top) {
> + LONGEST l = (LONGEST) stack[--sp];
> + do_div(l, (LONGEST) top);
> + top = l;
> + } else
> + goto code_error_out;
> + break;
> + /* div_unsigned */
> + case 0x06:
> + if (top) {
> + ULONGEST ul = stack[--sp];
> + do_div(ul, top);
> + top = ul;
> + } else
> + goto code_error_out;
> + break;
> + /* rem_signed */
> + case 0x07:
> + if (top) {
> + LONGEST l1 = (LONGEST) stack[--sp];
> + LONGEST l2 = (LONGEST) top;
> + top = do_div(l1, l2);
> + } else
> + goto code_error_out;
> + break;
> + /* rem_unsigned */
> + case 0x08:
> + if (top) {
> + ULONGEST ul1 = stack[--sp];
> + ULONGEST ul2 = top;
> + top = do_div(ul1, ul2);
> + } else
> + goto code_error_out;
> + break;
> +#endif
> + /* lsh */
> + case 0x09:
> + top = stack[--sp] << top;
> + break;
> + /* rsh_signed */
> + case 0x0a:
> + top = ((LONGEST) stack[--sp]) >> top;
> + break;
> + /* rsh_unsigned */
> + case 0x0b:
> + top = stack[--sp] >> top;
> + break;
> + /* trace */
> + case 0x0c:
> + --sp;
> + if (!gts->tpe->have_printk) {
> + if (gtp_action_memory_read
> + (gts, -1,
> + (CORE_ADDR) stack[sp],
> + (size_t) top))
> + goto out;
> + }
> + top = stack[--sp];
> + break;
> + /* trace_printk */
> + case 0xfd:
> + if (gtp_action_printk(gts,
> + (ULONGEST)stack[--sp],
> + (size_t) top))
> + goto out;
> + top = stack[--sp];
> + break;
> + /* trace_quick */
> + case 0x0d:
> + if (!gts->tpe->have_printk) {
> + if (gtp_action_memory_read
> + (gts, -1, (CORE_ADDR) top,
> + (size_t) ebuf[pc]))
> + goto out;
> + }
> + pc++;
> + break;
> + /* trace_quick_printk */
> + case 0xfe:
> + if (gtp_action_printk(gts, (ULONGEST) top,
> + (size_t) ebuf[pc++]))
> + goto out;
> + break;
> + /* log_not */
> + case 0x0e:
> + top = !top;
> + break;
> + /* bit_and */
> + case 0x0f:
> + top &= stack[--sp];
> + break;
> + /* bit_or */
> + case 0x10:
> + top |= stack[--sp];
> + break;
> + /* bit_xor */
> + case 0x11:
> + top ^= stack[--sp];
> + break;
> + /* bit_not */
> + case 0x12:
> + top = ~top;
> + break;
> + /* equal */
> + case 0x13:
> + top = (stack[--sp] == top);
> + break;
> + /* less_signed */
> + case 0x14:
> + top = (((LONGEST) stack[--sp])
> + < ((LONGEST) top));
> + break;
> + /* less_unsigned */
> + case 0x15:
> + top = (stack[--sp] < top);
> + break;
> + /* ext */
> + case 0x16:
> + arg = ebuf[pc++];
> + if (arg < (sizeof(LONGEST)*8)) {
> + LONGEST mask = 1 << (arg - 1);
> + top &= ((LONGEST) 1 << arg) - 1;
> + top = (top ^ mask) - mask;
> + }
> + break;
> + /* ref8 */
> + case 0x17:
> + if (probe_kernel_read
> + (cnv.u8.bytes,
> + (void *)(CORE_ADDR)top, 1))
> + goto code_error_out;
> + top = (ULONGEST) cnv.u8.val;
> + break;
> + /* ref16 */
> + case 0x18:
> + if (probe_kernel_read
> + (cnv.u16.bytes,
> + (void *)(CORE_ADDR)top, 2))
> + goto code_error_out;
> + top = (ULONGEST) cnv.u16.val;
> + break;
> + /* ref32 */
> + case 0x19:
> + if (probe_kernel_read
> + (cnv.u32.bytes,
> + (void *)(CORE_ADDR)top, 4))
> + goto code_error_out;
> + top = (ULONGEST) cnv.u32.val;
> + break;
> + /* ref64 */
> + case 0x1a:
> + if (probe_kernel_read
> + (cnv.u64.bytes,
> + (void *)(CORE_ADDR)top, 8))
> + goto code_error_out;
> + top = (ULONGEST) cnv.u64.val;
> + break;
> + /* if_goto */
> + case 0x20:
> + /* The not check sp code don't
> + support if_goto. */
> + goto code_error_out;
> + break;
> + /* goto */
> + case 0x21:
> + pc = (ebuf[pc] << 8) + (ebuf[pc + 1]);
> + break;
> + /* const8 */
> + case 0x22:
> + stack[sp++] = top;
> + top = ebuf[pc++];
> + break;
> + /* const16 */
> + case 0x23:
> + stack[sp++] = top;
> + top = ebuf[pc++];
> + top = (top << 8) + ebuf[pc++];
> + break;
> + /* const32 */
> + case 0x24:
> + stack[sp++] = top;
> + top = ebuf[pc++];
> + top = (top << 8) + ebuf[pc++];
> + top = (top << 8) + ebuf[pc++];
> + top = (top << 8) + ebuf[pc++];
> + break;
> + /* const64 */
> + case 0x25:
> + stack[sp++] = top;
> + top = ebuf[pc++];
> + top = (top << 8) + ebuf[pc++];
> + top = (top << 8) + ebuf[pc++];
> + top = (top << 8) + ebuf[pc++];
> + top = (top << 8) + ebuf[pc++];
> + top = (top << 8) + ebuf[pc++];
> + top = (top << 8) + ebuf[pc++];
> + top = (top << 8) + ebuf[pc++];
> + break;
> + /* reg */
> + case 0x26:
> + stack[sp++] = top;
> + arg = ebuf[pc++];
> + arg = (arg << 8) + ebuf[pc++];
> + top = gtp_action_reg_read(gts->regs, gts->tpe,
> + arg);
> + if (gts->tpe->reason != gtp_stop_normal)
> + goto error_out;
> + break;
> + /* end */
> + case 0x27:
> + if (gts->run)
> + *(gts->run) = (int)top;
> + goto out;
> + break;
> + /* dup */
> + case 0x28:
> + stack[sp++] = top;
> + break;
> + /* pop */
> + case 0x29:
> + top = stack[--sp];
> + break;
> + /* zero_ext */
> + case 0x2a:
> + arg = ebuf[pc++];
> + if (arg < (sizeof(LONGEST)*8))
> + top &= ((LONGEST) 1 << arg) - 1;
> + break;
> + /* swap */
> + case 0x2b:
> + stack[sp] = top;
> + top = stack[sp - 1];
> + stack[sp - 1] = stack[sp];
> + break;
> + /* getv */
> + case 0x2c:
> + arg = ebuf[pc++];
> + arg = (arg << 8) + ebuf[pc++];
> + gtp_action_x_getv;
> + break;
> + /* setv */
> + case 0x2d:
> + arg = ebuf[pc++];
> + arg = (arg << 8) + ebuf[pc++];
> + gtp_action_x_setv;
> + break;
> + /* tracev */
> + case 0x2e:
> + arg = ebuf[pc++];
> + arg = (arg << 8) + ebuf[pc++];
> + gtp_action_x_tracev;
> + break;
> + /* tracev_printk */
> + case 0xff:
> + arg = ebuf[pc++];
> + arg = (arg << 8) + ebuf[pc++];
> + gtp_action_x_tracev_printk;
> + break;
> + /* printf */
> + case 0x31: {
> + arg = ebuf[pc++];
> +
> + if (arg) {
> + void *argv = (void *)
> + (unsigned long)
> + top;
> +
> + /* pop */
> + top = stack[--sp];
> +
> + gtp_action_x_printf;
> + } else
> + snprintf(pbuf, psize,
> + (char *)(ebuf + pc));
> + psize -= strlen(pbuf);
> + pbuf += strlen(pbuf);
> +
> + pc += strlen((char *)ebuf + pc) + 1;
> + }
> + break;
> + }
> + }
> + } else {
> + /* The x execution code don't support printk so it doesn't have
> + printk ae support. */
> + while (pc < ae->u.exp.size) {
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_parse_x: cmd %x\n", ebuf[pc]);
> +#endif
> +
> + switch (ebuf[pc++]) {
> + /* add */
> + case 0x02:
> + if (sp)
> + top += stack[--sp];
> + else
> + goto code_error_out;
> + break;
> + /* sub */
> + case 0x03:
> + if (sp)
> + top = stack[--sp] - top;
> + else
> + goto code_error_out;
> + break;
> + /* mul */
> + case 0x04:
> + if (sp)
> + top *= stack[--sp];
> + else
> + goto code_error_out;
> + break;
> +#ifndef CONFIG_MIPS
> + /* div_signed */
> + case 0x05:
> + if (top && sp) {
> + LONGEST l = (LONGEST) stack[--sp];
> + do_div(l, (LONGEST) top);
> + top = l;
> + } else
> + goto code_error_out;
> + break;
> + /* div_unsigned */
> + case 0x06:
> + if (top && sp) {
> + ULONGEST ul = stack[--sp];
> + do_div(ul, top);
> + top = ul;
> + } else
> + goto code_error_out;
> + break;
> + /* rem_signed */
> + case 0x07:
> + if (top && sp) {
> + LONGEST l1 = (LONGEST) stack[--sp];
> + LONGEST l2 = (LONGEST) top;
> + top = do_div(l1, l2);
> + } else
> + goto code_error_out;
> + break;
> + /* rem_unsigned */
> + case 0x08:
> + if (top && sp) {
> + ULONGEST ul1 = stack[--sp];
> + ULONGEST ul2 = top;
> + top = do_div(ul1, ul2);
> + } else
> + goto code_error_out;
> + break;
> +#endif
> + /* lsh */
> + case 0x09:
> + if (sp)
> + top = stack[--sp] << top;
> + else
> + goto code_error_out;
> + break;
> + /* rsh_signed */
> + case 0x0a:
> + if (sp)
> + top = ((LONGEST) stack[--sp]) >> top;
> + else
> + goto code_error_out;
> + break;
> + /* rsh_unsigned */
> + case 0x0b:
> + if (sp)
> + top = stack[--sp] >> top;
> + else
> + goto code_error_out;
> + break;
> + /* trace */
> + case 0x0c:
> + if (sp > 1) {
> + if (gtp_action_memory_read
> + (gts, -1, (CORE_ADDR) stack[--sp],
> + (size_t) top)) {
> + /* gtp_action_memory_read will
> + set error status with itself
> + if it got error. */
> + goto out;
> + }
> + top = stack[--sp];
> + } else
> + goto code_error_out;
> + break;
> + /* trace_quick */
> + case 0x0d:
> + if (gtp_action_memory_read
> + (gts, -1, (CORE_ADDR) top,
> + (size_t) ebuf[pc++])) {
> + /* gtp_action_memory_read will set
> + error status with itself if it got
> + error. */
> + goto out;
> + }
> + break;
> + /* log_not */
> + case 0x0e:
> + top = !top;
> + break;
> + /* bit_and */
> + case 0x0f:
> + if (sp)
> + top &= stack[--sp];
> + else
> + goto code_error_out;
> + break;
> + /* bit_or */
> + case 0x10:
> + if (sp)
> + top |= stack[--sp];
> + else
> + goto code_error_out;
> + break;
> + /* bit_xor */
> + case 0x11:
> + if (sp)
> + top ^= stack[--sp];
> + else
> + goto code_error_out;
> + break;
> + /* bit_not */
> + case 0x12:
> + top = ~top;
> + break;
> + /* equal */
> + case 0x13:
> + if (sp)
> + top = (stack[--sp] == top);
> + else
> + goto code_error_out;
> + break;
> + /* less_signed */
> + case 0x14:
> + if (sp)
> + top = (((LONGEST) stack[--sp])
> + < ((LONGEST) top));
> + else
> + goto code_error_out;
> + break;
> + /* less_unsigned */
> + case 0x15:
> + if (sp)
> + top = (stack[--sp] < top);
> + else
> + goto code_error_out;
> + break;
> + /* ext */
> + case 0x16:
> + arg = ebuf[pc++];
> + if (arg < (sizeof(LONGEST)*8)) {
> + LONGEST mask = 1 << (arg - 1);
> + top &= ((LONGEST) 1 << arg) - 1;
> + top = (top ^ mask) - mask;
> + }
> + break;
> + /* ref8 */
> + case 0x17:
> + if (probe_kernel_read
> + (cnv.u8.bytes,
> + (void *)(CORE_ADDR)top, 1))
> + goto code_error_out;
> + top = (ULONGEST) cnv.u8.val;
> + break;
> + /* ref16 */
> + case 0x18:
> + if (probe_kernel_read
> + (cnv.u16.bytes,
> + (void *)(CORE_ADDR)top, 2))
> + goto code_error_out;
> + top = (ULONGEST) cnv.u16.val;
> + break;
> + /* ref32 */
> + case 0x19:
> + if (probe_kernel_read
> + (cnv.u32.bytes,
> + (void *)(CORE_ADDR)top, 4))
> + goto code_error_out;
> + top = (ULONGEST) cnv.u32.val;
> + break;
> + /* ref64 */
> + case 0x1a:
> + if (probe_kernel_read
> + (cnv.u64.bytes,
> + (void *)(CORE_ADDR)top, 8))
> + goto code_error_out;
> + top = (ULONGEST) cnv.u64.val;
> + break;
> + /* if_goto */
> + case 0x20:
> + if (top)
> + pc = (ebuf[pc] << 8)
> + + (ebuf[pc + 1]);
> + else
> + pc += 2;
> + /* pop */
> + if (sp)
> + top = stack[--sp];
> + else
> + goto code_error_out;
> + break;
> + /* goto */
> + case 0x21:
> + pc = (ebuf[pc] << 8) + (ebuf[pc + 1]);
> + break;
> + /* const8 */
> + case 0x22:
> + stack[sp++] = top;
> + top = ebuf[pc++];
> + break;
> + /* const16 */
> + case 0x23:
> + stack[sp++] = top;
> + top = ebuf[pc++];
> + top = (top << 8) + ebuf[pc++];
> + break;
> + /* const32 */
> + case 0x24:
> + stack[sp++] = top;
> + top = ebuf[pc++];
> + top = (top << 8) + ebuf[pc++];
> + top = (top << 8) + ebuf[pc++];
> + top = (top << 8) + ebuf[pc++];
> + break;
> + /* const64 */
> + case 0x25:
> + stack[sp++] = top;
> + top = ebuf[pc++];
> + top = (top << 8) + ebuf[pc++];
> + top = (top << 8) + ebuf[pc++];
> + top = (top << 8) + ebuf[pc++];
> + top = (top << 8) + ebuf[pc++];
> + top = (top << 8) + ebuf[pc++];
> + top = (top << 8) + ebuf[pc++];
> + top = (top << 8) + ebuf[pc++];
> + break;
> + /* reg */
> + case 0x26:
> + stack[sp++] = top;
> + arg = ebuf[pc++];
> + arg = (arg << 8) + ebuf[pc++];
> + top = gtp_action_reg_read(gts->regs, gts->tpe,
> + arg);
> + if (gts->tpe->reason != gtp_stop_normal)
> + goto error_out;
> + break;
> + /* end */
> + case 0x27:
> + if (gts->run)
> + *(gts->run) = (int)top;
> + goto out;
> + break;
> + /* dup */
> + case 0x28:
> + stack[sp++] = top;
> + break;
> + /* pop */
> + case 0x29:
> + if (sp)
> + top = stack[--sp];
> + else
> + goto code_error_out;
> + break;
> + /* zero_ext */
> + case 0x2a:
> + arg = ebuf[pc++];
> + if (arg < (sizeof(LONGEST)*8))
> + top &= ((LONGEST) 1 << arg) - 1;
> + break;
> + /* swap */
> + case 0x2b:
> + if (sp) {
> + stack[sp] = top;
> + top = stack[sp - 1];
> + stack[sp - 1] = stack[sp];
> + } else
> + goto code_error_out;
> + break;
> + /* getv */
> + case 0x2c:
> + arg = ebuf[pc++];
> + arg = (arg << 8) + ebuf[pc++];
> + if (GTP_VAR_NOT_GETV(arg))
> + goto code_error_out;
> + gtp_action_x_getv;
> + break;
> + /* setv */
> + case 0x2d:
> + arg = ebuf[pc++];
> + arg = (arg << 8) + ebuf[pc++];
> + if (GTP_VAR_NOT_SETV(arg))
> + goto code_error_out;
> + gtp_action_x_setv;
> + break;
> + /* tracev */
> + case 0x2e:
> + arg = ebuf[pc++];
> + arg = (arg << 8) + ebuf[pc++];
> + if (GTP_VAR_NOT_TRACEV(arg))
> + goto code_error_out;
> + gtp_action_x_tracev;
> + break;
> + /* printf */
> + case 0x31: {
> + arg = ebuf[pc++];
> +
> + if (arg) {
> + void *argv = (void *)
> + (unsigned long)
> + top;
> +
> + /* pop */
> + if (sp)
> + top = stack[--sp];
> + else
> + goto code_error_out;
> +
> + gtp_action_x_printf;
> + } else
> + snprintf(pbuf, psize,
> + (char *)(ebuf + pc));
> + psize -= strlen(pbuf);
> + pbuf += strlen(pbuf);
> +
> + pc += strlen((char *)ebuf + pc) + 1;
> + }
> + break;
> + }
> +
> + if (sp > STACK_MAX - 5) {
> + printk(KERN_WARNING "gtp_action_x: stack "
> + "overflow.\n");
> + gts->tpe->reason
> + = gtp_stop_agent_expr_stack_overflow;
> + goto error_out;
> + }
> + }
> + }
> +code_error_out:
> + gts->tpe->reason = gtp_stop_agent_expr_code_error;
> +error_out:
> + ret = -1;
> + printk(KERN_WARNING "gtp_action_x: tracepoint %d "
> + "action X get error in pc %u.\n",
> + (int)gts->tpe->num, pc);
> +out:
> + if (psize != GTP_PRINTF_MAX) {
> + unsigned long flags;
> +
> + local_irq_save(flags);
> + printk("%s", pbuf - (GTP_PRINTF_MAX - psize));
> + local_irq_restore(flags);
> + }
> + if (ae->u.exp.need_var_lock)
> + spin_unlock(&gtp_var_lock);
> + return ret;
> +}
> +
> +static int
> +gtp_kp_pre_handler(struct kprobe *p, struct pt_regs *regs)
> +{
> + struct gtp_trace_s gts;
> + struct action *ae;
> +
> + gts.tpe = container_of(p, struct gtp_entry, kp);
> + gts.regs = regs;
> + gts.next = NULL;
> + gts.printk_tmp = 0;
> + gts.printk_level = 8;
> + gts.printk_format = 0;
> + gts.printk_str = gts.tpe->printk_str;
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_kp_pre_handler: tracepoint %d\n",
> + (int)gts.tpe->num);
> +#endif
> +
> + if (gts.tpe->kpreg == 0)
> + return 0;
> +
> + /* Condition. */
> + if (gts.tpe->cond) {
> + int run;
> +
> + gts.run = &run;
> + if (gtp_action_x(&gts, gts.tpe->cond))
> + goto tpe_stop;
> + if (!run)
> + return 0;
> + }
> +
> + gts.run = NULL;
> +
> + /* Pass. */
> + if (!gts.tpe->nopass) {
> + if (atomic_dec_return(&gts.tpe->current_pass) < 0)
> + goto tpe_stop;
> + }
> +
> + atomic_inc(&gtp_frame_create);
> +
> + /* Handle actions. */
> + for (ae = gts.tpe->action_list; ae; ae = ae->next) {
> + switch (ae->type) {
> + case 'R':
> + if (gtp_action_r(&gts, ae))
> + goto tpe_stop;
> + break;
> + case 'X':
> + case 0xff:
> + if (gtp_action_x(&gts, ae))
> + goto tpe_stop;
> + break;
> + case 'M':
> + if (gtp_action_memory_read(&gts, ae->u.m.regnum,
> + ae->u.m.offset,
> + ae->u.m.size))
> + goto tpe_stop;
> + break;
> + }
> + }
> +
> + return 0;
> +
> +tpe_stop:
> + gts.tpe->kpreg = 0;
> + queue_work(gtp_wq, &gts.tpe->work);
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_kp_pre_handler: tracepoint %d stop.\n",
> + (int)gts.tpe->num);
> +#endif
> + return 0;
> +}
> +
> +static struct action *
> +gtp_action_alloc(char *pkg)
> +{
> + struct action *ret;
> +
> + ret = kmalloc(sizeof(struct action), GFP_KERNEL);
> + if (!ret)
> + goto out;
> +
> + memset(ret, '\0', sizeof(struct action));
> + ret->type = pkg[0];
> + ret->src = pkg;
> +
> +out:
> + return ret;
> +}
> +
> +static void
> +gtp_action_release(struct action *ae)
> +{
> + struct action *ae2;
> +
> + while (ae) {
> + ae2 = ae;
> + ae = ae->next;
> + /* Release ae2. */
> + switch (ae2->type) {
> + case 'X':
> + kfree(ae2->u.exp.buf);
> + break;
> + }
> + kfree(ae2->src);
> + kfree(ae2);
> + }
> +}
> +
> +static void
> +gtp_src_release(struct gtpsrc *src)
> +{
> + struct gtpsrc *src2;
> +
> + while (src) {
> + src2 = src;
> + src = src->next;
> + kfree(src2->src);
> + kfree(src2);
> + }
> +}
> +
> +static void
> +gtp_stop(struct work_struct *work)
> +{
> + struct gtp_entry *tpe = container_of(work,
> + struct gtp_entry, work);
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_stop: tracepoint %d\n", (int)tpe->num);
> +#endif
> +
> + unregister_kprobe(&tpe->kp);
> +}
> +
> +static struct gtp_entry *
> +gtp_list_add(ULONGEST num, ULONGEST addr)
> +{
> + struct gtp_entry *ret = kmalloc(sizeof(struct gtp_entry),
> + GFP_KERNEL);
> +
> + if (!ret)
> + goto out;
> + memset(ret, '\0', sizeof(struct gtp_entry));
> + ret->num = num;
> + ret->addr = addr;
> + ret->kp.addr = (kprobe_opcode_t *) (CORE_ADDR)addr;
> + ret->kp.pre_handler = gtp_kp_pre_handler;
> + INIT_WORK(&ret->work, gtp_stop);
> + ret->have_printk = 0;
> +
> + /* Add to gtp_list. */
> + ret->next = gtp_list;
> + gtp_list = ret;
> +
> +out:
> + return ret;
> +}
> +
> +static struct gtp_entry *
> +gtp_list_find(ULONGEST num)
> +{
> + struct gtp_entry *tpe;
> +
> + for (tpe = gtp_list; tpe; tpe = tpe->next) {
> + if (tpe->num == num)
> + return tpe;
> + }
> +
> + return NULL;
> +}
> +
> +static void
> +gtp_list_release(void)
> +{
> + struct gtp_entry *tpe;
> +
> + while (gtp_list) {
> + tpe = gtp_list;
> + gtp_list = gtp_list->next;
> + gtp_action_release(tpe->cond);
> + gtp_action_release(tpe->action_list);
> + gtp_src_release(tpe->src);
> + kfree(tpe);
> + }
> +
> + current_gtp = NULL;
> + current_gtp_action = NULL;
> + current_gtp_src = NULL;
> +}
> +
> +static void
> +gtp_frame_reset(void)
> +{
> + gtp_frame_r_start = gtp_frame;
> + gtp_frame_w_start = gtp_frame;
> + gtp_frame_end = gtp_frame + GTP_FRAME_SIZE;
> + gtp_frame_is_circular = 0;
> + gtp_frame_current = NULL;
> + gtp_frame_current_num = 0;
> + atomic_set(&gtp_frame_create, 0);
> + if (gtp_frame_file) {
> + vfree(gtp_frame_file);
> + gtp_frame_file = NULL;
> + gtp_frame_file_size = 0;
> + }
> +}
> +
> +static int
> +hex2int(char hex, int *i)
> +{
> + if ((hex >= '0') && (hex <= '9')) {
> + *i = hex - '0';
> + return 1;
> + }
> + if ((hex >= 'a') && (hex <= 'f')) {
> + *i = hex - 'a' + 10;
> + return 1;
> + }
> + if ((hex >= 'A') && (hex <= 'F')) {
> + *i = hex - 'A' + 10;
> + return 1;
> + }
> +
> + return 0;
> +}
> +
> +static char *
> +hex2ulongest(char *pkg, ULONGEST *u64)
> +{
> + int i;
> +
> + *u64 = 0;
> + while (hex2int(pkg[0], &i)) {
> + pkg++;
> + *u64 = (*u64) << 4;
> + *u64 |= i & 0xf;
> + }
> +
> + return pkg;
> +}
> +
> +static char *
> +string2hex(char *pkg, char *out)
> +{
> + char *ret = out;
> +
> + while (pkg[0]) {
> + sprintf(out, "%x", pkg[0]);
> + pkg++;
> + out += 2;
> + }
> +
> + return ret;
> +}
> +
> +static char *
> +hex2string(char *pkg, char *out)
> +{
> + char *ret = out;
> + int i, j;
> +
> + while (hex2int(pkg[0], &i) && hex2int(pkg[1], &j)) {
> + out[0] = i * 16 + j;
> + pkg += 2;
> + out += 1;
> + }
> + out[0] = '\0';
> +
> + return ret;
> +}
> +
> +static char *
> +gtp_strdup(char *begin, char *end)
> +{
> + int len;
> + char *ret;
> +
> + if (end)
> + len = end - begin;
> + else
> + len = strlen(begin);
> +
> + ret = kmalloc(len + 1, GFP_KERNEL);
> + if (ret == NULL)
> + return NULL;
> +
> + strncpy(ret, begin, len);
> + ret[len] = '\0';
> +
> + return ret;
> +}
> +
> +static void
> +gtpro_list_clear(void)
> +{
> + struct gtpro_entry *e;
> +
> + while (gtpro_list) {
> + e = gtpro_list;
> + gtpro_list = gtpro_list->next;
> + kfree(e);
> + }
> +}
> +
> +static struct gtpro_entry *
> +gtpro_list_add(CORE_ADDR start, CORE_ADDR end)
> +{
> + struct gtpro_entry *e;
> +
> + e = kmalloc(sizeof(struct gtpro_entry), GFP_KERNEL);
> + if (e == NULL)
> + goto out;
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtpro_list_add: %p %p\n", (void *)start, (void *)end);
> +#endif
> +
> + e->start = start;
> + e->end = end;
> +
> + e->next = gtpro_list;
> + gtpro_list = e;
> +
> +out:
> + return e;
> +}
> +
> +static struct gtp_var *
> +gtp_var_add(unsigned int num, uint64_t val, char *src)
> +{
> + struct gtp_var *var = kmalloc(sizeof(struct gtp_var), GFP_KERNEL);
> + if (!var)
> + goto out;
> +
> + var->num = num;
> + var->val = val;
> +
> + var->src = gtp_strdup(src, NULL);
> + if (var->src == NULL) {
> + kfree(var);
> + var = NULL;
> + goto out;
> + }
> +
> + var->next = gtp_var_list;
> + gtp_var_list = var;
> + gtp_var_head = min(var->num, gtp_var_head);
> + gtp_var_tail = max(var->num, gtp_var_tail);
> +
> +out:
> + return var;
> +}
> +
> +static struct gtp_var *
> +gtp_var_find(unsigned int num)
> +{
> + struct gtp_var *ret = NULL;
> +
> + if (num >= gtp_var_head && num <= gtp_var_tail) {
> + for (ret = gtp_var_list; ret; ret = ret->next) {
> + if (ret->num == num)
> + break;
> + }
> + }
> +
> + return ret;
> +}
> +
> +static void
> +gtp_var_release(void)
> +{
> + struct gtp_var *tve;
> +
> + gtp_var_head = GTP_VAR_SPECIAL_MIN;
> + gtp_var_tail = GTP_VAR_SPECIAL_MAX;
> + current_gtp_var = NULL;
> +
> + while (gtp_var_list != GTP_VAR_LIST_FIRST) {
> + tve = gtp_var_list;
> + gtp_var_list = gtp_var_list->next;
> + kfree(tve->src);
> + kfree(tve);
> + }
> +}
> +
> +static int
> +gtp_gdbrsp_qtstop(void)
> +{
> + struct gtp_entry *tpe;
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gdbrsp_qtstop\n");
> +#endif
> +
> +#ifdef FRAME_ALLOC_RECORD
> + printk(KERN_WARNING "frame_alloc_size = %llu, "
> + "frame_alloc_size_hole = %llu\n",
> + frame_alloc_size, frame_alloc_size_hole);
> + frame_alloc_size = 0;
> + frame_alloc_size_hole = 0;
> +#endif
> +
> + if (!gtp_start)
> + return -EBUSY;
> +
> + flush_workqueue(gtp_wq);
> +
> + for (tpe = gtp_list; tpe; tpe = tpe->next) {
> + if (tpe->kpreg) {
> + unregister_kprobe(&tpe->kp);
> + tpe->kpreg = 0;
> + }
> + }
> +
> + kfree(gtp_var_array);
> + gtp_var_array = NULL;
> +
> + gtp_start = 0;
> +
> + return 0;
> +}
> +
> +static int
> +gtp_gdbrsp_qtinit(void)
> +{
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gdbrsp_qtinit\n");
> +#endif
> +
> + if (gtp_start)
> + gtp_gdbrsp_qtstop();
> +
> + gtp_list_release();
> +
> + if (gtp_frame)
> + gtp_frame_reset();
> +
> + gtpro_list_clear();
> +
> + gtp_var_release();
> +
> + return 0;
> +}
> +
> +static int
> +gtp_gdbrsp_qtstart(void)
> +{
> + struct gtp_entry *tpe;
> + struct gtp_var *tve;
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gdbrsp_qtstart\n");
> +#endif
> +
> + if (gtp_start)
> + return -EBUSY;
> +
> + /* Check the tracepoint that have printk. */
> + for (tpe = gtp_list; tpe; tpe = tpe->next) {
> + if (tpe->have_printk) {
> + struct action *ae, *prev_ae = NULL;
> + struct gtpsrc *src, *srctail = NULL;
> +
> +restart:
> + for (ae = tpe->action_list; ae;
> + prev_ae = ae, ae = ae->next) {
> + switch (ae->type) {
> + case 'R':
> + /* Remove it. */
> + if (prev_ae)
> + prev_ae->next = ae->next;
> + else
> + tpe->action_list = ae->next;
> + kfree(ae->src);
> + kfree(ae);
> + if (prev_ae)
> + ae = prev_ae;
> + else
> + goto restart;
> + break;
> + case 'M':
> + printk(KERN_WARNING "qtstart: action "
> + "of tp %d is not right. "
> + "Please put global variable to "
> + "trace state variable "
> + "$printk_tmp before print it.\n",
> + (int)tpe->num);
> + return -EINVAL;
> + break;
> + }
> + }
> +
> + for (src = tpe->src; src; src = src->next) {
> + int i;
> + char str[strlen(src->src) >> 1];
> + char *var = NULL;
> + ULONGEST num;
> + char tmp[20];
> + struct gtpsrc *ksrc;
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gdbrsp_qtstart: action "
> + "%s\n", src->src);
> +#endif
> + /* Get the action in str. */
> + if (strncmp("cmd:0:", src->src,
> + strlen("cmd:0:")))
> + continue;
> + var = hex2ulongest(src->src + 6, &num);
> + if (var[0] == '\0')
> + return -EINVAL;
> + var++;
> + hex2string(var, str);
> + if (strlen(str) != num)
> + return -EINVAL;
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gdbrsp_qtstart: action "
> + "command %s\n", str);
> +#endif
> +
> + if (strncmp("collect ", str,
> + strlen("collect ")))
> + continue;
> + for (i = strlen("collect "); ; i++) {
> + if (str[i] != ' ') {
> + var = str + i;
> + break;
> + }
> + if (str[i] == '\0')
> + break;
> + }
> + if (!var) {
> + printk(KERN_WARNING "qtstart: cannot "
> + "get the var name "
> + "from tp %d "
> + "command %s.\n",
> + (int)tpe->num, str);
> + return -EINVAL;
> + }
> + if (strcmp(var, "$args") == 0
> + || strcmp(var, "$local") == 0) {
> + printk(KERN_WARNING "qtstart: cannot "
> + "print $args and "
> + "$local.\n");
> + return -EINVAL;
> + }
> + if (strcmp(var, "$reg") == 0)
> + continue;
> +
> + ksrc = kmalloc(sizeof(struct gtpsrc),
> + GFP_KERNEL);
> + if (ksrc == NULL)
> + return -ENOMEM;
> + ksrc->next = NULL;
> +
> + snprintf(tmp, 20, "gtp %d:", (int)tpe->num);
> + ksrc->src = kmalloc(strlen(tmp)
> + + strlen(var) + 2,
> + GFP_KERNEL);
> + if (ksrc->src == NULL) {
> + kfree(ksrc);
> + return -ENOMEM;
> + }
> + sprintf(ksrc->src, "%s%s=", tmp, var);
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gdbrsp_qtstart: new "
> + "printk var %s\n", ksrc->src);
> +#endif
> +
> + if (tpe->printk_str)
> + srctail->next = ksrc;
> + else
> + tpe->printk_str = ksrc;
> + srctail = ksrc;
> + }
> + }
> + }
> +
> + if (!gtp_frame) {
> + gtp_frame = vmalloc(GTP_FRAME_SIZE);
> + if (!gtp_frame)
> + return -ENOMEM;
> +
> + gtp_frame_reset();
> + }
> +
> + gtp_start = 1;
> +
> + gtp_var_array = kmalloc(sizeof(struct gtp_var *)
> + *(gtp_var_tail - gtp_var_head + 1),
> + GFP_KERNEL);
> + if (!gtp_var_array) {
> + gtp_gdbrsp_qtstop();
> + return -ENOMEM;
> + }
> + memset(gtp_var_array, '\0', sizeof(struct gtp_var *)
> + *(gtp_var_tail - gtp_var_head + 1));
> + for (tve = gtp_var_list; tve; tve = tve->next)
> + gtp_var_array[tve->num - gtp_var_head] = tve;
> +
> + for (tpe = gtp_list; tpe; tpe = tpe->next) {
> + if (!tpe->disable) {
> + int ret;
> +
> + if (!tpe->nopass)
> + atomic_set(&tpe->current_pass, tpe->pass);
> + ret = register_kprobe(&tpe->kp);
> + if (ret < 0) {
> + gtp_gdbrsp_qtstop();
> + return ret;
> + }
> + tpe->kpreg = 1;
> + }
> + tpe->reason = gtp_stop_normal;
> + }
> +
> + return 0;
> +}
> +
> +struct gtp_x_goto {
> + struct gtp_x_goto *next;
> + unsigned int addr;
> + int non_goto_done;
> +};
> +
> +static struct gtp_x_goto *
> +gtp_x_goto_find(struct gtp_x_goto *list, unsigned int pc)
> +{
> + struct gtp_x_goto *ret = NULL;
> +
> + for (ret = list; ret; ret = ret->next) {
> + if (ret->addr == pc)
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static struct gtp_x_goto *
> +gtp_x_goto_add(struct gtp_x_goto **list, unsigned int pc, int
> non_goto_done)
> +{
> + struct gtp_x_goto *ret;
> +
> + ret = kmalloc(sizeof(struct gtp_x_goto), GFP_KERNEL);
> + if (!ret)
> + goto out;
> +
> + ret->addr = pc;
> + ret->non_goto_done = non_goto_done;
> +
> + if (*list) {
> + ret->next = *list;
> + *list = ret;
> + } else {
> + ret->next = NULL;
> + *list = ret;
> + }
> +
> +out:
> + return ret;
> +}
> +
> +struct gtp_x_var {
> + struct gtp_x_var *next;
> + unsigned int num;
> + unsigned int flags;
> +};
> +
> +static int
> +gtp_x_var_add(struct gtp_x_var **list, unsigned int num, unsigned int flag)
> +{
> + struct gtp_x_var *curv;
> +
> + for (curv = *list; curv; curv = curv->next) {
> + if (curv->num == num)
> + break;
> + }
> +
> + if (!curv) {
> + curv = kmalloc(sizeof(struct gtp_x_var), GFP_KERNEL);
> + if (!curv)
> + return -ENOMEM;
> + curv->num = num;
> + curv->flags = 0;
> + if (*list) {
> + curv->next = *list;
> + *list = curv;
> + } else {
> + curv->next = NULL;
> + *list = curv;
> + }
> + }
> +
> + curv->flags |= flag;
> +
> + return 0;
> +}
> +
> +static int
> +gtp_check_x(struct gtp_entry *tpe, struct action *ae)
> +{
> + int ret = -EINVAL;
> + unsigned int pc = 0, sp = 0;
> + struct gtp_x_goto *glist = NULL, *gtmp;
> + struct gtp_x_var *vlist = NULL, *vtmp;
> + uint8_t *ebuf = ae->u.exp.buf;
> + int last_trace_pc = -1;
> + unsigned int stack_size = 0;
> +
> +reswitch:
> + while (pc < ae->u.exp.size) {
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_check_x: cmd %x\n", ebuf[pc]);
> +#endif
> + switch (ebuf[pc++]) {
> + /* add */
> + case 0x02:
> + /* sub */
> + case 0x03:
> + /* mul */
> + case 0x04:
> + /* lsh */
> + case 0x09:
> + /* rsh_signed */
> + case 0x0a:
> + /* rsh_unsigned */
> + case 0x0b:
> + /* bit_and */
> + case 0x0f:
> + /* bit_or */
> + case 0x10:
> + /* bit_xor */
> + case 0x11:
> + /* equal */
> + case 0x13:
> + /* less_signed */
> + case 0x14:
> + /* less_unsigned */
> + case 0x15:
> + /* pop */
> + case 0x29:
> + /* swap */
> + case 0x2b:
> + if (ae->type == 'X') {
> + if (sp < 1) {
> + printk(KERN_WARNING "gtp_check_x: "
> + "stack overflow "
> + "in %d.\n",
> + pc - 1);
> + goto release_out;
> + } else
> + sp--;
> + }
> + break;
> +
> + /* trace */
> + case 0x0c:
> + if (tpe->have_printk)
> + last_trace_pc = pc - 1;
> +
> + if (ae->type == 'X') {
> + if (sp < 2) {
> + printk(KERN_WARNING "gtp_check_x: "
> + "stack overflow "
> + "in %d.\n",
> + pc - 1);
> + goto release_out;
> + } else
> + sp -= 2;
> + }
> + break;
> +
> + /* log_not */
> + case 0x0e:
> + /* bit_not */
> + case 0x12:
> + /* ref8 */
> + case 0x17:
> + /* ref16 */
> + case 0x18:
> + /* ref32 */
> + case 0x19:
> + /* ref64 */
> + case 0x1a:
> + break;
> +
> + /* dup */
> + case 0x28:
> + if (ae->type == 'X') {
> + sp++;
> + if (stack_size < sp)
> + stack_size = sp;
> + }
> + break;
> +
> + /* const8 */
> + case 0x22:
> + if (ae->type == 'X') {
> + sp++;
> + if (stack_size < sp)
> + stack_size = sp;
> + }
> + /* ext */
> + case 0x16:
> + /* zero_ext */
> + case 0x2a:
> + if (pc >= ae->u.exp.size)
> + goto release_out;
> + pc++;
> + break;
> +
> + /* trace_quick */
> + case 0x0d:
> + if (tpe->have_printk)
> + last_trace_pc = pc - 1;
> +
> + if (pc >= ae->u.exp.size)
> + goto release_out;
> + pc++;
> + break;
> +
> + /* const16 */
> + case 0x23:
> + /* reg */
> + case 0x26:
> + if (pc + 1 >= ae->u.exp.size)
> + goto release_out;
> + pc += 2;
> + if (ae->type == 'X') {
> + sp++;
> + if (stack_size < sp)
> + stack_size = sp;
> + }
> + break;
> +
> + /* const32 */
> + case 0x24:
> + if (pc + 3 >= ae->u.exp.size)
> + goto release_out;
> + pc += 4;
> + if (ae->type == 'X') {
> + sp++;
> + if (stack_size < sp)
> + stack_size = sp;
> + }
> + break;
> +
> + /* const64 */
> + case 0x25:
> + if (pc + 7 >= ae->u.exp.size)
> + goto release_out;
> + pc += 8;
> + if (ae->type == 'X') {
> + sp++;
> + if (stack_size < sp)
> + stack_size = sp;
> + }
> + break;
> +
> + /* if_goto */
> + case 0x20:
> + if (tpe->have_printk)
> + goto release_out;
> +
> + if (pc + 1 >= ae->u.exp.size)
> + goto release_out;
> + gtmp = gtp_x_goto_find(glist, pc);
> + if (gtmp) {
> + if (gtmp->non_goto_done)
> + goto out;
> + else {
> + gtmp->non_goto_done = 1;
> + pc += 2;
> + }
> + } else {
> + if (!gtp_x_goto_add(&glist, pc, 0)) {
> + ret = -ENOMEM;
> + goto release_out;
> + }
> + pc = (ebuf[pc] << 8)
> + + (ebuf[pc + 1]);
> + }
> + /* Mark this action X need sp check when it exec. */
> + ae->type = 0xff;
> + break;
> +
> + /* goto */
> + case 0x21:
> + if (pc + 1 >= ae->u.exp.size)
> + goto release_out;
> + gtmp = gtp_x_goto_find(glist, pc);
> + if (gtmp)
> + goto out;
> + else {
> + if (!gtp_x_goto_add(&glist, pc, 1)) {
> + ret = -ENOMEM;
> + goto release_out;
> + }
> + pc = (ebuf[pc] << 8) + (ebuf[pc + 1]);
> + }
> + break;
> +
> + /* end */
> + case 0x27:
> + goto out;
> + break;
> +
> + /* getv */
> + case 0x2c: {
> + int arg;
> +
> + if (pc + 1 >= ae->u.exp.size)
> + goto release_out;
> + arg = ebuf[pc++];
> + arg = (arg << 8) + ebuf[pc++];
> +
> + if (GTP_VAR_NOT_GETV(arg)) {
> + printk(KERN_WARNING
> + "gtp_check_x: The tv %d cannot "
> + "get.\n", arg);
> + goto release_out;
> + }
> +
> + if (!GTP_VAR_IS_SPECIAL(arg)) {
> + if (gtp_x_var_add(&vlist, arg, 1)) {
> + ret = -ENOMEM;
> + goto release_out;
> + }
> + }
> + }
> + if (ae->type == 'X') {
> + sp++;
> + if (stack_size < sp)
> + stack_size = sp;
> + }
> + break;
> +
> + /* setv */
> + case 0x2d: {
> + int arg;
> +
> + if (pc + 1 >= ae->u.exp.size)
> + goto release_out;
> + arg = ebuf[pc++];
> + arg = (arg << 8) + ebuf[pc++];
> +
> + if (GTP_VAR_NOT_SETV(arg)) {
> + printk(KERN_WARNING
> + "gtp_check_x: The tv %d cannot "
> + "set.\n", arg);
> + goto release_out;
> + }
> +
> + if (arg == GTP_VAR_PRINTK_LEVEL_ID)
> + tpe->have_printk = 1;
> +
> + if (!GTP_VAR_IS_SPECIAL(arg)) {
> + if (gtp_x_var_add(&vlist, arg, 2)) {
> + ret = -ENOMEM;
> + goto release_out;
> + }
> + }
> + }
> + break;
> +
> + /* tracev */
> + case 0x2e: {
> + int arg;
> +
> + if (tpe->have_printk)
> + last_trace_pc = pc - 1;
> +
> + if (pc + 1 >= ae->u.exp.size)
> + goto release_out;
> + arg = ebuf[pc++];
> + arg = (arg << 8) + ebuf[pc++];
> +
> + if (GTP_VAR_NOT_TRACEV(arg)) {
> + printk(KERN_WARNING
> + "gtp_check_x: The tv %d cannot "
> + "trace.\n", arg);
> + goto release_out;
> + }
> +
> + if (!GTP_VAR_IS_SPECIAL(arg)) {
> + if (gtp_x_var_add(&vlist, arg, 4)) {
> + ret = -ENOMEM;
> + goto release_out;
> + }
> + }
> + }
> + break;
> +
> + /* printf */
> + case 0x31: {
> + int arg = ebuf[pc++];
> + if (arg && ae->type == 'X') {
> + if (sp < 1) {
> + printk(KERN_WARNING
> + "gtp_check_x: stack "
> + "overflow in %d.\n",
> + pc - 2);
> + goto release_out;
> + } else
> + sp--;
> + }
> + pc += strlen((char *)ebuf + pc) + 1;
> + }
> + break;
> +
> + /* div_signed */
> + case 0x05:
> + /* div_unsigned */
> + case 0x06:
> + /* rem_signed */
> + case 0x07:
> + /* rem_unsigned */
> + case 0x08:
> +#ifdef CONFIG_MIPS
> + /* XXX, mips don't have 64 bit div. */
> + goto release_out;
> +#endif
> + if (ae->type == 'X') {
> + if (sp < 1) {
> + printk(KERN_WARNING "gtp_check_x: "
> + "stack overflow "
> + "in %d.\n",
> + pc - 1);
> + goto release_out;
> + } else
> + sp--;
> + }
> + break;
> +
> + /* float */
> + case 0x01:
> + /* ref_float */
> + case 0x1b:
> + /* ref_double */
> + case 0x1c:
> + /* ref_long_double */
> + case 0x1d:
> + /* l_to_d */
> + case 0x1e:
> + /* d_to_l */
> + case 0x1f:
> + /* trace16 */
> + case 0x30:
> + default:
> + goto release_out;
> + break;
> + }
> + }
> + goto release_out;
> +
> +out:
> + for (gtmp = glist; gtmp; gtmp = gtmp->next) {
> + if (!gtmp->non_goto_done)
> + break;
> + }
> + if (gtmp) {
> + pc = gtmp->addr + 2;
> + gtmp->non_goto_done = 1;
> + goto reswitch;
> + }
> + if (stack_size >= STACK_MAX) {
> + printk(KERN_WARNING "gtp_check_x: stack overflow.");
> + goto release_out;
> + }
> + ret = 0;
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_check_x: Code is OK. sp_checked is %d. "
> + "stack_size is %d.\n",
> + (ae->type == 'X'), stack_size);
> +#endif
> +
> +release_out:
> + while (glist) {
> + gtmp = glist;
> + glist = glist->next;
> + kfree(gtmp);
> + }
> + while (vlist) {
> + vtmp = vlist;
> + vlist = vlist->next;
> + if ((vtmp->flags & 2)
> + && ((vtmp->flags & 1) || (vtmp->flags & 4)))
> + ae->u.exp.need_var_lock = 1;
> + kfree(vtmp);
> + }
> +
> + if (tpe->have_printk && last_trace_pc > -1) {
> + /* Set the last trace code to printk code. */
> + switch (ebuf[last_trace_pc]) {
> + /* trace */
> + case 0x0c:
> + ebuf[last_trace_pc] = 0xfd;
> + break;
> + /* trace_quick */
> + case 0x0d:
> + ebuf[last_trace_pc] = 0xfe;
> + break;
> + /* tracev */
> + case 0x2e:
> + ebuf[last_trace_pc] = 0xff;
> + break;
> + }
> + }
> +
> + return ret;
> +}
> +
> +static int
> +gtp_parse_x(struct gtp_entry *tpe, struct action *ae, char **pkgp)
> +{
> + ULONGEST size;
> + int ret = 0, i, h, l;
> + char *pkg = *pkgp;
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_parse_x: %s\n", pkg);
> +#endif
> +
> + if (pkg[0] == '\0') {
> + ret = -EINVAL;
> + goto out;
> + }
> + pkg = hex2ulongest(pkg, &size);
> + if (pkg[0] != ',') {
> + ret = -EINVAL;
> + goto out;
> + }
> + ae->u.exp.size = (unsigned int)size;
> + pkg++;
> +
> + ae->u.exp.buf = kmalloc(ae->u.exp.size, GFP_KERNEL);
> + if (!ae->u.exp.buf)
> + return -ENOMEM;
> +
> + for (i = 0; i < ae->u.exp.size
> + && hex2int(pkg[0], &h) && hex2int(pkg[1], &l);
> + i++) {
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_parse_x: %s %d %d\n", pkg, h, l);
> +#endif
> + ae->u.exp.buf[i] = (h << 4) | l;
> + pkg += 2;
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_parse_x: %x\n", ae->u.exp.buf[i]);
> +#endif
> + }
> + if (i != ae->u.exp.size) {
> + kfree(ae->u.exp.buf);
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + ae->u.exp.need_var_lock = 0;
> +
> + ret = gtp_check_x(tpe, ae);
> + if (ret < 0)
> + kfree(ae->u.exp.buf);
> +
> +out:
> + *pkgp = pkg;
> + return ret;
> +}
> +
> +static int
> +gtp_gdbrsp_qtdp(char *pkg)
> +{
> + int addnew = 1;
> + ULONGEST num, addr;
> + struct gtp_entry *tpe;
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gdbrsp_qtdp: %s\n", pkg);
> +#endif
> +
> + if (gtp_start)
> + return -EBUSY;
> +
> + if (pkg[0] == '-') {
> + pkg++;
> + addnew = 0;
> + }
> +
> + /* Get num and addr. */
> + if (pkg[0] == '\0')
> + return -EINVAL;
> + pkg = hex2ulongest(pkg, &num);
> + if (pkg[0] == '\0')
> + return -EINVAL;
> + pkg++;
> + pkg = hex2ulongest(pkg, &addr);
> + if (pkg[0] == '\0')
> + return -EINVAL;
> + pkg++;
> +
> + tpe = gtp_list_find(num);
> + if (addnew) {
> + if (tpe)
> + return -EINVAL;
> +
> + tpe = gtp_list_add(num, addr);
> + if (tpe == NULL)
> + return -ENOMEM;
> +
> + if (pkg[0] == '\0')
> + return -EINVAL;
> + if (pkg[0] == 'D')
> + tpe->disable = 1;
> + pkg++;
> +
> + /* Get step and pass. */
> + if (pkg[0] == '\0')
> + return -EINVAL;
> + pkg++;
> + pkg = hex2ulongest(pkg, &tpe->step);
> + if (pkg[0] == '\0')
> + return -EINVAL;
> + pkg++;
> + pkg = hex2ulongest(pkg, &tpe->pass);
> + if (tpe->pass == 0)
> + tpe->nopass = 1;
> + }
> +
> + if (tpe) {
> + /* Add action to tpe. */
> + int step_action = 0;
> +
> + if (pkg[0] == 'S') {
> + pkg++;
> + step_action = 1;
> + /* XXX: Still not support step. */
> + return 1;
> + }
> + while (pkg[0]) {
> + struct action *ae = NULL, *atail = NULL;
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gdbrsp_qtdp: %s\n", pkg);
> +#endif
> + switch (pkg[0]) {
> + case ':':
> + pkg++;
> + break;
> + case 'M': {
> + int is_neg = 0;
> + ULONGEST ulongtmp;
> +
> + ae = gtp_action_alloc(pkg);
> + if (!ae)
> + return -ENOMEM;
> + pkg++;
> + if (pkg[0] == '-') {
> + is_neg = 1;
> + pkg++;
> + }
> + pkg = hex2ulongest(pkg, &ulongtmp);
> + ae->u.m.regnum = (int)ulongtmp;
> + if (is_neg)
> + ae->u.m.regnum
> + = -ae->u.m.regnum;
> + if (pkg[0] == '\0') {
> + kfree(ae);
> + return -EINVAL;
> + }
> + pkg++;
> + pkg = hex2ulongest(pkg, &ulongtmp);
> + ae->u.m.offset = (CORE_ADDR)ulongtmp;
> + if (pkg[0] == '\0') {
> + kfree(ae);
> + return -EINVAL;
> + }
> + pkg++;
> + pkg = hex2ulongest(pkg, &ulongtmp);
> + ae->u.m.size = (size_t)ulongtmp;
> + }
> + break;
> + case 'R':
> + /* XXX: reg_mask is ignore. */
> + ae = gtp_action_alloc(pkg);
> + if (!ae)
> + return -ENOMEM;
> + pkg++;
> + pkg = hex2ulongest(pkg,
> + &ae->u.reg_mask);
> + break;
> + case 'X': {
> + int ret;
> +
> + ae = gtp_action_alloc(pkg);
> + if (!ae)
> + return -ENOMEM;
> + pkg++;
> + ret = gtp_parse_x(tpe, ae, &pkg);
> + if (ret < 0) {
> + kfree(ae);
> + return ret;
> + }
> +#ifdef GTP_DEBUG
> + if (ae && ae->u.exp.need_var_lock)
> + printk(GTP_DEBUG
> + "gtp_gdbrsp_qtdp: "
> + "ae need var lock.\n");
> +#endif
> + }
> + break;
> + case '-':
> + pkg++;
> + break;
> + default:
> + /* XXX: Not support. */
> + return 1;
> + }
> +
> + if (ae) {
> + /* Save the src. */
> + ae->src = gtp_strdup(ae->src, pkg);
> + if (ae->src == NULL) {
> + kfree(ae);
> + return -ENOMEM;
> + }
> + /* Add ae to tpe. */
> + if (ae->type == 'X' && addnew && !tpe->cond) {
> + tpe->cond = ae;
> + tpe->cond->next = NULL;
> + } else if (!tpe->action_list) {
> + tpe->action_list = ae;
> + atail = ae;
> + } else {
> + if (atail == NULL)
> + for (atail = tpe->action_list;
> + atail->next;
> + atail = atail->next)
> + ;
> + atail->next = ae;
> + atail = ae;
> + }
> + }
> + }
> + } else
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static int
> +gtp_gdbrsp_qtdpsrc(char *pkg)
> +{
> + ULONGEST num, addr;
> + struct gtpsrc *src, *srctail;
> + struct gtp_entry *tpe;
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gdbrsp_qtdpsrc: %s\n", pkg);
> +#endif
> +
> + if (gtp_start)
> + return -EBUSY;
> +
> + /* Get num and addr. */
> + if (pkg[0] == '\0')
> + return -EINVAL;
> + pkg = hex2ulongest(pkg, &num);
> + if (pkg[0] == '\0')
> + return -EINVAL;
> + pkg++;
> + pkg = hex2ulongest(pkg, &addr);
> + if (pkg[0] == '\0')
> + return -EINVAL;
> + pkg++;
> + tpe = gtp_list_find(num);
> + if (tpe == NULL)
> + return -EINVAL;
> +
> + src = kmalloc(sizeof(struct gtpsrc), GFP_KERNEL);
> + if (src == NULL)
> + return -ENOMEM;
> + src->next = NULL;
> + src->src = gtp_strdup(pkg, NULL);
> + if (src->src == NULL) {
> + kfree(src);
> + return -ENOMEM;
> + }
> +
> + if (tpe->src) {
> + for (srctail = tpe->src; srctail->next;
> + srctail = srctail->next)
> + ;
> + srctail->next = src;
> + } else
> + tpe->src = src;
> +
> + return 0;
> +}
> +
> +static int
> +gtp_gdbrsp_qtdisconnected(char *pkg)
> +{
> + ULONGEST setting;
> +
> + if (pkg[0] == '\0')
> + return -EINVAL;
> +
> + hex2ulongest(pkg, &setting);
> + gtp_disconnected_tracing = (int)setting;
> +
> + return 0;
> +}
> +
> +static int
> +gtp_gdbrsp_qtbuffer(char *pkg)
> +{
> + if (strncmp("circular:", pkg, 9) == 0) {
> + ULONGEST setting;
> +
> + pkg += 9;
> + if (pkg[0] == '\0')
> + return -EINVAL;
> + hex2ulongest(pkg, &setting);
> + gtp_circular = (int)setting;
> +
> + return 0;
> + }
> +
> + return 1;
> +}
> +
> +static int
> +gtp_frame_head_find_addr(char *cur, int inside, unsigned long lo,
> + unsigned long hi)
> +{
> + char *tmp;
> + int tfnum = gtp_frame_current_num;
> +
> + if (cur)
> + tmp = cur;
> + else
> + tmp = gtp_frame_r_start;
> +
> + do {
> + if (FID(tmp) == FID_HEAD) {
> + if (tfnum != gtp_frame_current_num) {
> + char *next;
> + struct pt_regs *regs = NULL;
> +
> + for (next = *(char **)(tmp + FID_SIZE); next;
> + next = *(char **)(next + FID_SIZE)) {
> + if (FID(next) == FID_REG) {
> + regs = (struct pt_regs *)
> + (next + FID_SIZE
> + + sizeof(char *));
> + break;
> + }
> + }
> + if (regs
> + && ((inside
> + && GTP_REGS_PC(regs) >= lo
> + && GTP_REGS_PC(regs) <= hi)
> + || (!inside
> + && (GTP_REGS_PC(regs) < lo
> + || GTP_REGS_PC(regs) > hi)))) {
> + gtp_frame_current_num = tfnum;
> + gtp_frame_current = tmp;
> + return 0;
> + }
> + }
> + tfnum++;
> + }
> +
> + tmp = gtp_frame_next(tmp);
> + if (!tmp)
> + break;
> +
> + if (tmp == gtp_frame_end)
> + tmp = gtp_frame;
> + } while (tmp != gtp_frame_w_start);
> +
> + return -1;
> +}
> +
> +static int
> +gtp_frame_head_find_trace(char *cur, ULONGEST trace)
> +{
> + char *tmp;
> + int tfnum = gtp_frame_current_num;
> +
> + if (cur)
> + tmp = cur;
> + else
> + tmp = gtp_frame_r_start;
> +
> + do {
> + if (FID(tmp) == FID_HEAD) {
> + if (tfnum != gtp_frame_current_num) {
> + if (trace == *(ULONGEST *) (tmp + FID_SIZE
> + + sizeof(char *))) {
> + gtp_frame_current_num = tfnum;
> + gtp_frame_current = tmp;
> + return 0;
> + }
> + }
> + tfnum++;
> + }
> +
> + tmp = gtp_frame_next(tmp);
> + if (!tmp)
> + break;
> +
> + if (tmp == gtp_frame_end)
> + tmp = gtp_frame;
> + } while (tmp != gtp_frame_w_start);
> +
> + return -1;
> +}
> +
> +static int
> +gtp_frame_head_find_num(int num)
> +{
> + char *tmp = gtp_frame_r_start;
> + int tfnum = 0;
> +
> + do {
> + if (FID(tmp) == FID_HEAD) {
> + if (tfnum == num) {
> + gtp_frame_current_num = num;
> + gtp_frame_current = tmp;
> + return 0;
> + }
> + tfnum++;
> + }
> +
> + tmp = gtp_frame_next(tmp);
> + if (!tmp)
> + break;
> +
> + if (tmp == gtp_frame_end)
> + tmp = gtp_frame;
> + } while (tmp != gtp_frame_w_start);
> +
> + return -1;
> +}
> +
> +static int
> +gtp_gdbrsp_qtframe(char *pkg)
> +{
> + int ret = -1;
> +
> + if (gtp_start)
> + return -EBUSY;
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gdbrsp_qtframe: %s\n", pkg);
> +#endif
> +
> + if (atomic_read(&gtp_frame_create) == 0)
> + goto out;
> +
> + if (strncmp(pkg, "pc:", 3) == 0) {
> + ULONGEST addr;
> +
> + pkg += 3;
> +
> + if (pkg[0] == '\0')
> + return -EINVAL;
> + hex2ulongest(pkg, &addr);
> +
> + ret = gtp_frame_head_find_addr(gtp_frame_current, 1,
> + (unsigned long)addr,
> + (unsigned long)addr);
> + } else if (strncmp(pkg, "tdp:", 4) == 0) {
> + ULONGEST trace;
> +
> + pkg += 4;
> +
> + if (pkg[0] == '\0')
> + return -EINVAL;
> + hex2ulongest(pkg, &trace);
> +
> + ret = gtp_frame_head_find_trace(gtp_frame_current, trace);
> + } else if (strncmp(pkg, "range:", 6) == 0) {
> + ULONGEST start, end;
> +
> + pkg += 6;
> +
> + if (pkg[0] == '\0')
> + return -EINVAL;
> + pkg = hex2ulongest(pkg, &start);
> + if (pkg[0] == '\0')
> + return -EINVAL;
> + pkg++;
> + hex2ulongest(pkg, &end);
> +
> + ret = gtp_frame_head_find_addr(gtp_frame_current, 1,
> + (unsigned long)start,
> + (unsigned long)end);
> + } else if (strncmp(pkg, "outside:", 8) == 0) {
> + ULONGEST start, end;
> +
> + pkg += 8;
> +
> + if (pkg[0] == '\0')
> + return -EINVAL;
> + pkg = hex2ulongest(pkg, &start);
> + if (pkg[0] == '\0')
> + return -EINVAL;
> + pkg++;
> + hex2ulongest(pkg, &end);
> +
> + ret = gtp_frame_head_find_addr(gtp_frame_current, 0,
> + (unsigned long)start,
> + (unsigned long)end);
> + } else {
> + ULONGEST num;
> +
> + if (pkg[0] == '\0')
> + return -EINVAL;
> + hex2ulongest(pkg, &num);
> +
> + if (((int) num) < 0) {
> + /* Return to current. */
> + gtp_frame_current = NULL;
> + gtp_frame_current_num = 0;
> +
> + return 0;
> + }
> + ret = gtp_frame_head_find_num((int) num);
> + }
> +
> +out:
> + if (ret) {
> + strcpy(gtp_rw_bufp, "F-1");
> + gtp_rw_bufp += 3;
> + gtp_rw_size += 3;
> + } else {
> + sprintf(gtp_rw_bufp, "F%xT%x",
> + gtp_frame_current_num,
> + (unsigned int)
> + *(ULONGEST *)(gtp_frame_current + FID_SIZE
> + + sizeof(char *)));
> + gtp_rw_size += strlen(gtp_rw_bufp);
> + gtp_rw_bufp += strlen(gtp_rw_bufp);
> + }
> + return 1;
> +}
> +
> +static int
> +gtp_gdbrsp_qtro(char *pkg)
> +{
> + ULONGEST start, end;
> +
> + gtpro_list_clear();
> +
> + while (pkg[0]) {
> + pkg = hex2ulongest(pkg, &start);
> + if (pkg[0] != ',')
> + return -EINVAL;
> + pkg++;
> + pkg = hex2ulongest(pkg, &end);
> + if (pkg[0])
> + pkg++;
> +
> + if (gtpro_list_add((CORE_ADDR)start, (CORE_ADDR)end) == NULL)
> + return -ENOMEM;
> + }
> +
> + return 0;
> +}
> +
> +static int
> +gtp_gdbrsp_qtdv(char *pkg)
> +{
> + ULONGEST num, val;
> + struct gtp_var *var;
> + char *src;
> +
> + pkg = hex2ulongest(pkg, &num);
> + if (GTP_VAR_IS_SPECIAL(num))
> + return 0;
> + if (pkg[0] != ':')
> + return -EINVAL;
> + pkg++;
> + src = pkg;
> + pkg = hex2ulongest(pkg, &val);
> + if (pkg[0] != ':')
> + return -EINVAL;
> + pkg++;
> +
> + var = gtp_var_find(num);
> + if (var)
> + return -EINVAL;
> +
> + if (!gtp_var_add((unsigned int)num, (uint64_t)val, src))
> + return -ENOMEM;
> +
> + return 0;
> +}
> +
> +static int
> +gtp_gdbrsp_QT(char *pkg)
> +{
> + int ret = 1;
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gdbrsp_QT: %s\n", pkg);
> +#endif
> +
> + if (strcmp("init", pkg) == 0)
> + ret = gtp_gdbrsp_qtinit();
> + else if (strcmp("Stop", pkg) == 0)
> + ret = gtp_gdbrsp_qtstop();
> + else if (strcmp("Start", pkg) == 0)
> + ret = gtp_gdbrsp_qtstart();
> + else if (strncmp("DP:", pkg, 3) == 0)
> + ret = gtp_gdbrsp_qtdp(pkg + 3);
> + else if (strncmp("DPsrc:", pkg, 6) == 0)
> + ret = gtp_gdbrsp_qtdpsrc(pkg + 6);
> + else if (strncmp("Disconnected:", pkg, 13) == 0)
> + ret = gtp_gdbrsp_qtdisconnected(pkg + 13);
> + else if (strncmp("Buffer:", pkg, 7) == 0)
> + ret = gtp_gdbrsp_qtbuffer(pkg + 7);
> + else if (strncmp("Frame:", pkg, 6) == 0)
> + ret = gtp_gdbrsp_qtframe(pkg + 6);
> + else if (strncmp("ro:", pkg, 3) == 0)
> + ret = gtp_gdbrsp_qtro(pkg + 3);
> + else if (strncmp("DV:", pkg, 3) == 0)
> + ret = gtp_gdbrsp_qtdv(pkg + 3);
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gdbrsp_QT: return %d\n", ret);
> +#endif
> +
> + return ret;
> +}
> +
> +static int
> +gtp_get_status(struct gtp_entry *tpe, char *buf)
> +{
> + int size = 0;
> + int tfnum = 0;
> + CORE_ADDR tmpaddr;
> +
> + if (!gtp_frame) {
> + sprintf(buf, "tnotrun:0;");
> + buf += 10;
> + size += 10;
> + } else if (!tpe || (tpe && tpe->reason == gtp_stop_normal)) {
> + sprintf(buf, "tstop:0;");
> + buf += 8;
> + size += 8;
> + } else {
> + char outtmp[100];
> +
> + switch (tpe->reason) {
> + case gtp_stop_frame_full:
> + sprintf(buf, "tfull:%lx;",
> + (unsigned long)tpe->num);
> + break;
> + case gtp_stop_efault:
> + sprintf(buf, "terror:%s:%lx;",
> + string2hex("read memory false", outtmp),
> + (unsigned long)tpe->num);
> + break;
> + case gtp_stop_access_wrong_reg:
> + sprintf(buf, "terror:%s:%lx;",
> + string2hex("access wrong register", outtmp),
> + (unsigned long)tpe->num);
> + break;
> + case gtp_stop_agent_expr_code_error:
> + sprintf(buf, "terror:%s:%lx;",
> + string2hex("agent expression code error",
> + outtmp),
> + (unsigned long)tpe->num);
> + break;
> + case gtp_stop_agent_expr_stack_overflow:
> + sprintf(buf, "terror:%s:%lx;",
> + string2hex("agent expression stack overflow",
> + outtmp),
> + (unsigned long)tpe->num);
> + break;
> + default:
> + buf[0] = '\0';
> + break;
> + }
> +
> + size += strlen(buf);
> + buf += strlen(buf);
> + }
> +
> + if (atomic_read(&gtp_frame_create)) {
> + char *tmp = gtp_frame_r_start;
> +
> + do {
> + if (FID(tmp) == FID_HEAD)
> + tfnum++;
> +
> + tmp = gtp_frame_next(tmp);
> + if (!tmp)
> + break;
> +
> + if (tmp == gtp_frame_end)
> + tmp = gtp_frame;
> + } while (tmp != gtp_frame_w_start);
> + }
> + sprintf(buf, "tframes:%x;", tfnum);
> + size += strlen(buf);
> + buf += strlen(buf);
> +
> + sprintf(buf, "tcreated:%x;", atomic_read(&gtp_frame_create));
> + size += strlen(buf);
> + buf += strlen(buf);
> +
> + sprintf(buf, "tsize:%x;", GTP_FRAME_SIZE);
> + size += strlen(buf);
> + buf += strlen(buf);
> +
> + spin_lock(&gtp_frame_lock);
> + if (gtp_frame_is_circular)
> + tmpaddr = 0;
> + else
> + tmpaddr = GTP_FRAME_SIZE - (gtp_frame_w_start - gtp_frame);
> + spin_unlock(&gtp_frame_lock);
> + sprintf(buf, "tfree:%lx;", (unsigned long)tmpaddr);
> + size += strlen(buf);
> + buf += strlen(buf);
> +
> + sprintf(buf, "circular:%x;", gtp_circular);
> + size += strlen(buf);
> + buf += strlen(buf);
> +
> + sprintf(buf, "disconn:%x", gtp_disconnected_tracing);
> + size += strlen(buf);
> + buf += strlen(buf);
> +
> + return size;
> +}
> +
> +static int
> +gtp_gdbrsp_qtstatus(void)
> +{
> + struct gtp_entry *tpe;
> + int tmp;
> +
> + for (tpe = gtp_list; tpe; tpe = tpe->next) {
> + if (tpe->reason != gtp_stop_normal)
> + break;
> + }
> +
> + if (gtp_start && tpe) /* Tpe is stop, stop all tpes. */
> + gtp_gdbrsp_qtstop();
> +
> + sprintf(gtp_rw_bufp, "T%x;", gtp_start ? 1 : 0);
> + gtp_rw_bufp += 3;
> + gtp_rw_size += 3;
> +
> + tmp = gtp_get_status(tpe, gtp_rw_bufp);
> + gtp_rw_bufp += tmp;
> + gtp_rw_size += tmp;
> +
> + return 1;
> +}
> +
> +static void
> +gtp_report_tracepoint(struct gtp_entry *gtp, char *buf)
> +{
> + sprintf(buf, "T%lx:%lx:%c:%lx:%lx",
> + (unsigned long)gtp->num,
> + (unsigned long)gtp->addr,
> + (gtp->disable ? 'D' : 'E'),
> + (unsigned long)gtp->step,
> + (unsigned long)gtp->pass);
> +}
> +
> +static void
> +gtp_report_action(struct gtp_entry *gtp, struct action *action, char *buf)
> +{
> + sprintf(buf, "A%lx:%lx:%s",
> + (unsigned long)gtp->num,
> + (unsigned long)gtp->addr,
> + action->src);
> +}
> +
> +static void
> +gtp_report_src(struct gtp_entry *gtp, struct gtpsrc *src, char *buf)
> +{
> + sprintf(buf, "Z%lx:%lx:%s",
> + (unsigned long)gtp->num,
> + (unsigned long)gtp->addr,
> + src->src);
> +}
> +
> +static void
> +gtp_current_set_check(void)
> +{
> + if (current_gtp_src == NULL)
> + current_gtp = current_gtp->next;
> +}
> +
> +static void
> +gtp_current_action_check(void)
> +{
> + if (current_gtp_action == NULL) {
> + current_gtp_src = current_gtp->src;
> + gtp_current_set_check();
> + }
> +}
> +
> +static int
> +gtp_gdbrsp_qtfp(void)
> +{
> + if (gtp_list) {
> + current_gtp = gtp_list;
> + gtp_report_tracepoint(current_gtp, gtp_rw_bufp);
> + gtp_rw_size += strlen(gtp_rw_bufp);
> + gtp_rw_bufp += strlen(gtp_rw_bufp);
> + current_gtp_action = current_gtp->action_list;
> + gtp_current_action_check();
> + } else {
> + gtp_rw_bufp[0] = 'l';
> + gtp_rw_size += 1;
> + gtp_rw_bufp += 1;
> + }
> +
> + return 1;
> +}
> +
> +static int
> +gtp_gdbrsp_qtsp(void)
> +{
> + if (current_gtp_action) {
> + gtp_report_action(current_gtp, current_gtp_action,
> + gtp_rw_bufp);
> + gtp_rw_size += strlen(gtp_rw_bufp);
> + gtp_rw_bufp += strlen(gtp_rw_bufp);
> + current_gtp_action = current_gtp_action->next;
> + gtp_current_action_check();
> + goto out;
> + }
> +
> + if (current_gtp_src) {
> + gtp_report_src(current_gtp, current_gtp_src, gtp_rw_bufp);
> + gtp_rw_size += strlen(gtp_rw_bufp);
> + gtp_rw_bufp += strlen(gtp_rw_bufp);
> + current_gtp_src = current_gtp_src->next;
> + gtp_current_set_check();
> + goto out;
> + }
> +
> + if (current_gtp) {
> + gtp_report_tracepoint(current_gtp, gtp_rw_bufp);
> + gtp_rw_size += strlen(gtp_rw_bufp);
> + gtp_rw_bufp += strlen(gtp_rw_bufp);
> + current_gtp_action = current_gtp->action_list;
> + gtp_current_action_check();
> + } else {
> + gtp_rw_bufp[0] = 'l';
> + gtp_rw_size += 1;
> + gtp_rw_bufp += 1;
> + }
> +out:
> + return 1;
> +}
> +
> +static void
> +gtp_report_var(void)
> +{
> + sprintf(gtp_rw_bufp, "%x:%s", current_gtp_var->num,
> + current_gtp_var->src);
> + gtp_rw_size += strlen(gtp_rw_bufp);
> + gtp_rw_bufp += strlen(gtp_rw_bufp);
> +}
> +
> +static int
> +gtp_gdbrsp_qtfsv(int f)
> +{
> + if (f)
> + current_gtp_var = gtp_var_list;
> +
> + if (current_gtp_var) {
> + gtp_report_var();
> + current_gtp_var = current_gtp_var->next;
> + } else {
> + gtp_rw_bufp[0] = 'l';
> + gtp_rw_size += 1;
> + gtp_rw_bufp += 1;
> + }
> +
> + return 1;
> +}
> +
> +static int
> +gtp_gdbrsp_qtv(char *pkg)
> +{
> + ULONGEST num;
> + struct gtp_var *var = NULL;
> + struct gtp_frame_var *vr = NULL;
> + uint64_t val;
> +
> + pkg = hex2ulongest(pkg, &num);
> +
> + if (gtp_start || !gtp_frame_current) {
> + if (!GTP_VAR_IS_SPECIAL(num)) {
> + var = gtp_var_find(num);
> + if (var)
> + val = var->val;
> + }
> + } else {
> + char *next;
> +
> + for (next = *(char **)(gtp_frame_current + FID_SIZE); next;
> + next = *(char **)(next + FID_SIZE)) {
> + if (FID(next) == FID_VAR) {
> + vr = (struct gtp_frame_var *)
> + (next + FID_SIZE + sizeof(char *));
> + if (vr->num == (unsigned int)num)
> + goto while_stop;
> + }
> + }
> + vr = NULL;
> +while_stop:
> + if (vr)
> + val = vr->val;
> + }
> +
> + if (var || vr) {
> + sprintf(gtp_rw_bufp, "V%08x%08x",
> + (unsigned int) (val >> 32),
> + (unsigned int) (val & 0xffffffff));
> + gtp_rw_size += strlen(gtp_rw_bufp);
> + gtp_rw_bufp += strlen(gtp_rw_bufp);
> + } else {
> + gtp_rw_bufp[0] = 'U';
> + gtp_rw_size += 1;
> + gtp_rw_bufp += 1;
> + }
> +
> + return 1;
> +}
> +
> +static int
> +gtp_gdbrsp_qT(char *pkg)
> +{
> + int ret = 1;
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gdbrsp_qT: %s\n", pkg);
> +#endif
> +
> + if (strcmp("Status", pkg) == 0)
> + ret = gtp_gdbrsp_qtstatus();
> + else if (strcmp("fP", pkg) == 0)
> + ret = gtp_gdbrsp_qtfp();
> + else if (strcmp("sP", pkg) == 0)
> + ret = gtp_gdbrsp_qtsp();
> + else if (strcmp("fV", pkg) == 0)
> + ret = gtp_gdbrsp_qtfsv(1);
> + else if (strcmp("sV", pkg) == 0)
> + ret = gtp_gdbrsp_qtfsv(0);
> + else if (strncmp("V:", pkg, 2) == 0)
> + ret = gtp_gdbrsp_qtv(pkg + 2);
> +
> + return ret;
> +}
> +
> +static uint8_t gtp_m_buffer[0xffff];
> +
> +static int
> +gtp_gdbrsp_m(char *pkg)
> +{
> + int i;
> + ULONGEST addr, len;
> +
> + /* Get add and len. */
> + if (pkg[0] == '\0')
> + return -EINVAL;
> + pkg = hex2ulongest(pkg, &addr);
> + if (pkg[0] != ',')
> + return -EINVAL;
> + pkg++;
> + pkg = hex2ulongest(pkg, &len);
> + if (len == 0)
> + return -EINVAL;
> + len &= 0xffff;
> + len = (ULONGEST) min((int)((GTP_RW_MAX - 4 - gtp_rw_size) / 2),
> + (int)len);
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gdbrsp_m: addr = 0x%lx len = %d\n",
> + (unsigned long) addr, (int) len);
> +#endif
> +
> + if (gtp_start || !gtp_frame_current) {
> + if (probe_kernel_read(gtp_m_buffer, (void *)(CORE_ADDR)addr,
> + (size_t)len))
> + return -EFAULT;
> + } else {
> + char *next;
> + int ret;
> +
> + /* The following part is for gtpro support.
> + It is not available because it make disassemble cannot
> + work when select a trace frame. */
> +#if 0
> + struct gtpro_entry *gtroe;
> +
> + memset(gtp_m_buffer, 0, len);
> +
> + /* Read the gtpro. */
> + for (gtroe = gtpro_list; gtroe; gtroe = gtroe->next) {
> + CORE_ADDR cur_start, cur_end;
> +
> + cur_start = max(gtroe->start, (CORE_ADDR)addr);
> + cur_end = min(gtroe->end, ((CORE_ADDR)(addr + len)));
> + if (cur_start < cur_end) {
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gdbrsp_m: ro read "
> + "start = 0x%lx end = 0x%lx\n",
> + (unsigned long) cur_start,
> + (unsigned long) cur_end);
> +#endif
> + if (probe_kernel_read(gtp_m_buffer,
> + (void *)cur_start,
> + (size_t)(cur_end
> + - cur_start)))
> + return -EFAULT;
> + }
> + }
> +#endif
> + ret = probe_kernel_read(gtp_m_buffer, (void *)(CORE_ADDR)addr,
> + (size_t)len);
> +
> + for (next = *(char **)(gtp_frame_current + FID_SIZE); next;
> + next = *(char **)(next + FID_SIZE)) {
> + if (FID(next) == FID_MEM) {
> + struct gtp_frame_mem *mr;
> + ULONGEST cur_start, cur_end;
> + uint8_t *buf;
> +
> + mr = (struct gtp_frame_mem *)
> + (next + FID_SIZE + sizeof(char *));
> + buf = next + GTP_FRAME_MEM_SIZE;
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gdbrsp_m: section "
> + "addr = 0x%lx size = %lu\n",
> + (unsigned long) mr->addr,
> + (unsigned long) mr->size);
> +#endif
> + cur_start = max(((ULONGEST)mr->addr), addr);
> + cur_end = min(((ULONGEST)mr->addr
> + + mr->size),
> + (addr + len));
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gdbrsp_m: read "
> + "start = 0x%lx end = 0x%lx\n",
> + (unsigned long) cur_start,
> + (unsigned long) cur_end);
> +#endif
> + if (cur_start < cur_end) {
> + memcpy(gtp_m_buffer,
> + buf + cur_start - mr->addr,
> + cur_end - cur_start);
> + ret = 0;
> + }
> + }
> + }
> +
> + if (ret)
> + return -EFAULT;
> + }
> +
> + for (i = 0; i < (int)len; i++) {
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_gdbrsp_m: %d %02x\n", i, gtp_m_buffer[i]);
> +#endif
> + sprintf(gtp_rw_bufp, "%02x", gtp_m_buffer[i]);
> + gtp_rw_bufp += 2;
> + gtp_rw_size += 2;
> + }
> +
> + return 1;
> +}
> +
> +static int
> +gtp_gdbrsp_g(void)
> +{
> + char *next;
> + struct pt_regs *regs;
> +
> + if (GTP_RW_MAX - 4 - gtp_rw_size < GTP_REG_ASCII_SIZE)
> + return -E2BIG;
> +
> + if (gtp_start || !gtp_frame_current) {
> + memset(gtp_rw_bufp, '0', GTP_REG_ASCII_SIZE);
> + goto out;
> + }
> +
> + /* Get the regs. */
> + regs = NULL;
> + for (next = *(char **)(gtp_frame_current + FID_SIZE); next;
> + next = *(char **)(next + FID_SIZE)) {
> + if (FID(next) == FID_REG) {
> + regs = (struct pt_regs *)
> + (next + FID_SIZE + sizeof(char *));
> + break;
> + }
> + }
> + if (regs)
> + gtp_regs2ascii(regs, gtp_rw_bufp);
> + else {
> + struct pt_regs pregs;
> + struct gtp_entry *tpe;
> +
> + memset(&pregs, '\0', sizeof(struct pt_regs));
> + tpe = gtp_list_find(*(ULONGEST *)(gtp_frame_current
> + + FID_SIZE + sizeof(char *)));
> + if (tpe)
> + GTP_REGS_PC(&pregs) = (unsigned long)tpe->addr;
> + gtp_regs2ascii(&pregs, gtp_rw_bufp);
> + }
> +out:
> + gtp_rw_bufp += GTP_REG_ASCII_SIZE;
> + gtp_rw_size += GTP_REG_ASCII_SIZE;
> +
> + return 1;
> +}
> +
> +static DEFINE_SEMAPHORE(gtp_rw_lock);
> +static DECLARE_WAIT_QUEUE_HEAD(gtp_rw_wq);
> +static unsigned int gtp_rw_count;
> +
> +static int
> +gtp_open(struct inode *inode, struct file *file)
> +{
> + int ret = 0;
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_open\n");
> +#endif
> +
> + down(&gtp_rw_lock);
> + if (gtp_rw_count == 0) {
> + gtp_read_ack = 0;
> + gtp_rw_buf = vmalloc(GTP_RW_MAX);
> + if (!gtp_rw_buf) {
> + ret = -ENOMEM;
> + goto out;
> + }
> + }
> + gtp_rw_count++;
> +
> +out:
> + up(&gtp_rw_lock);
> + return ret;
> +}
> +
> +static int
> +gtp_release(struct inode *inode, struct file *file)
> +{
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_release\n");
> +#endif
> +
> + down(&gtp_rw_lock);
> + gtp_rw_count--;
> + if (gtp_rw_count == 0) {
> + vfree(gtp_rw_buf);
> +
> + if (!gtp_disconnected_tracing) {
> + gtp_gdbrsp_qtstop();
> + gtp_gdbrsp_qtinit();
> + if (gtp_frame) {
> + vfree(gtp_frame);
> + gtp_frame = NULL;
> + }
> + }
> + }
> + up(&gtp_rw_lock);
> +
> + return 0;
> +}
> +
> +static long
> +gtp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> +{
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_ioctl: %x\n", cmd);
> +#endif
> +
> + return 0;
> +}
> +
> +static ssize_t
> +gtp_write(struct file *file, const char __user *buf, size_t size,
> + loff_t *ppos)
> +{
> + char *rsppkg = NULL;
> + int i, ret;
> + unsigned char csum = 0;
> +
> + if (down_interruptible(&gtp_rw_lock))
> + return -EINTR;
> +
> + if (size == 0) {
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_write: try write 0 size.\n");
> +#endif
> + goto error_out;
> + }
> +
> + size = min(size, (size_t) GTP_RW_MAX);
> + if (copy_from_user(gtp_rw_buf, buf, size)) {
> + size = -EFAULT;
> + goto error_out;
> + }
> +
> + if (gtp_rw_buf[0] == '+' || gtp_rw_buf[0] == '-'
> + || gtp_rw_buf[0] == '\3') {
> + if (gtp_rw_buf[0] == '+')
> + gtp_rw_size = 0;
> + size = 1;
> + goto out;
> + }
> +
> + if (size < 4) {
> + gtp_read_ack = '-';
> + goto out;
> + }
> + /* Check format and crc and get the rsppkg. */
> + for (i = 0; i < size - 2; i++) {
> + if (rsppkg == NULL) {
> + if (gtp_rw_buf[i] == '$')
> + rsppkg = gtp_rw_buf + i + 1;
> + } else {
> + if (gtp_rw_buf[i] == '#')
> + break;
> + else
> + csum += gtp_rw_buf[i];
> + }
> + }
> + if (rsppkg && gtp_rw_buf[i] == '#') {
> + /* Format is OK. Check crc. */
> + unsigned char c1, c2;
> +
> + gtp_rw_buf[i] = '\0';
> +
> + c1 = gtp_rw_buf[i+1];
> + c2 = gtp_rw_buf[i+2];
> + if (csum == (c1 << 4) + c2) {
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_write: crc error\n");
> +#endif
> + gtp_read_ack = '-';
> + goto out;
> + }
> + } else {
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_write: format error\n");
> +#endif
> + gtp_read_ack = '-';
> + goto out;
> + }
> + gtp_read_ack = '+';
> + size = i + 3;
> +
> + wake_up_interruptible_nr(&gtp_rw_wq, 1);
> +
> + up(&gtp_rw_lock);
> + if (down_interruptible(&gtp_rw_lock))
> + return -EINTR;
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_write: %s\n", rsppkg);
> +#endif
> +
> + /* Handle rsppkg and put return to gtp_rw_buf. */
> + gtp_rw_buf[0] = '$';
> + gtp_rw_bufp = gtp_rw_buf + 1;
> + gtp_rw_size = 0;
> + ret = 1;
> + switch (rsppkg[0]) {
> + case '?':
> + strcpy(gtp_rw_bufp, "S05");
> + gtp_rw_bufp += 3;
> + gtp_rw_size += 3;
> + break;
> + case 'g':
> + ret = gtp_gdbrsp_g();
> + break;
> + case 'm':
> + ret = gtp_gdbrsp_m(rsppkg + 1);
> + break;
> + case 'Q':
> + if (rsppkg[1] == 'T')
> + ret = gtp_gdbrsp_QT(rsppkg + 2);
> + break;
> + case 'q':
> + if (rsppkg[1] == 'T')
> + ret = gtp_gdbrsp_qT(rsppkg + 2);
> + else if (strncmp("qSupported", rsppkg, 10) == 0) {
> + strcpy(gtp_rw_bufp,
> + "ConditionalTracepoints+;"
> + "TracepointSource+;DisconnectedTracing+");
> + gtp_rw_size += strlen(gtp_rw_bufp);
> + gtp_rw_bufp += strlen(gtp_rw_bufp);
> + ret = 1;
> + }
> + break;
> + case 's':
> + case 'S':
> + case 'c':
> + case 'C':
> + ret = -1;
> + break;
> + }
> + if (ret == 0) {
> + strcpy(gtp_rw_bufp, "OK");
> + gtp_rw_bufp += 2;
> + gtp_rw_size += 2;
> + } else if (ret < 0) {
> + sprintf(gtp_rw_bufp, "E%02x", -ret);
> + gtp_rw_bufp += 3;
> + gtp_rw_size += 3;
> + }
> +
> + gtp_rw_bufp[0] = '#';
> + csum = 0;
> + for (i = 1; i < gtp_rw_size + 1; i++)
> + csum += gtp_rw_buf[i];
> + gtp_rw_bufp[1] = TOHEX(csum >> 4);
> + gtp_rw_bufp[2] = TOHEX(csum & 0x0f);
> + gtp_rw_bufp = gtp_rw_buf;
> + gtp_rw_size += 4;
> +
> +out:
> + wake_up_interruptible_nr(&gtp_rw_wq, 1);
> +error_out:
> + up(&gtp_rw_lock);
> + return size;
> +}
> +
> +static ssize_t
> +gtp_read(struct file *file, char __user *buf, size_t size,
> + loff_t *ppos)
> +{
> + int err;
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_read\n");
> +#endif
> +
> + if (size == 0)
> + goto out;
> +
> + if (down_interruptible(&gtp_rw_lock))
> + return -EINTR;
> +
> + if (gtp_read_ack) {
> + err = put_user(gtp_read_ack, buf);
> + if (err) {
> + size = -err;
> + goto out;
> + }
> + gtp_read_ack = 0;
> + size = 1;
> + goto out;
> + }
> +
> + size = min(gtp_rw_size, size);
> + if (size == 0)
> + goto out;
> + if (copy_to_user(buf, gtp_rw_bufp, size)) {
> + size = -EFAULT;
> + goto out;
> + }
> + gtp_rw_bufp += size;
> + gtp_rw_size -= size;
> +
> +out:
> + up(&gtp_rw_lock);
> + return size;
> +}
> +
> +static unsigned int
> +gtp_poll(struct file *file, poll_table *wait)
> +{
> + unsigned int mask = POLLOUT | POLLWRNORM;
> +
> +#ifdef GTP_DEBUG
> + printk(GTP_DEBUG "gtp_poll\n");
> +#endif
> +
> + down(&gtp_rw_lock);
> + poll_wait(file, &gtp_rw_wq, wait);
> + if (gtp_read_ack || gtp_rw_size)
> + mask |= POLLIN | POLLRDNORM;
> + up(&gtp_rw_lock);
> +
> + return mask;
> +}
> +
> +static char *
> +gtp_frame_file_realloc(size_t *real_size, size_t size, int is_end)
> +{
> + if (*real_size < gtp_frame_file_size + size) {
> + char *tmp;
> +
> + *real_size = gtp_frame_file_size + size;
> + if (!is_end)
> + *real_size += 100;
> +
> + tmp = vmalloc(*real_size);
> + if (!tmp) {
> + vfree(gtp_frame_file);
> + return NULL;
> + }
> +
> + memcpy(tmp, gtp_frame_file, gtp_frame_file_size);
> + vfree(gtp_frame_file);
> + gtp_frame_file = tmp;
> + }
> +
> + return gtp_frame_file + gtp_frame_file_size;
> +}
> +
> +static int
> +gtp_frame2file_m(size_t *real_sizep, uint32_t *data_size, char *frame)
> +{
> + struct gtp_frame_mem *mr;
> + uint8_t *buf;
> + ULONGEST addr;
> + size_t remaining;
> +
> + mr = (struct gtp_frame_mem *) (frame + FID_SIZE + sizeof(char *));
> + buf = frame + GTP_FRAME_MEM_SIZE;
> + addr = mr->addr;
> + remaining = mr->size;
> +
> + while (remaining > 0) {
> + uint16_t blocklen;
> + char *wbuf;
> + size_t sp;
> +
> + blocklen = remaining > 65535 ? 65535 : remaining;
> +
> + sp = 1 + sizeof(addr) + sizeof(blocklen) + blocklen;
> + wbuf = gtp_frame_file_realloc(real_sizep, sp, 0);
> + if (!wbuf)
> + return -1;
> +
> + wbuf[0] = 'M';
> + wbuf += 1;
> +
> + memcpy(wbuf, &addr, sizeof(addr));
> + wbuf += sizeof(addr);
> +
> + memcpy(wbuf, &blocklen, sizeof(blocklen));
> + wbuf += sizeof(blocklen);
> +
> + memcpy(wbuf, buf, blocklen);
> +
> + addr += blocklen;
> + remaining -= blocklen;
> + buf += blocklen;
> +
> + gtp_frame_file_size += sp;
> + *data_size += sp;
> + }
> +
> + return 0;
> +}
> +
> +static int
> +gtp_frame2file_v(size_t *real_sizep, uint32_t *data_size, char *frame)
> +{
> + struct gtp_frame_var *vr;
> + size_t sp = 1 + sizeof(unsigned int)
> + + sizeof(uint64_t);
> + char *wbuf;
> +
> + wbuf = gtp_frame_file_realloc(real_sizep, sp, 0);
> + if (!wbuf)
> + return -1;
> +
> + vr = (struct gtp_frame_var *) (frame + FID_SIZE + sizeof(char *));
> +
> + wbuf[0] = 'V';
> + wbuf += 1;
> +
> + memcpy(wbuf, &vr->num, sizeof(unsigned int));
> + wbuf += sizeof(unsigned int);
> +
> + memcpy(wbuf, &vr->val, sizeof(uint64_t));
> + wbuf += sizeof(uint64_t);
> +
> + gtp_frame_file_size += sp;
> + *data_size += sp;
> +
> + return 0;
> +}
> +
> +static int
> +gtp_frame2file(size_t *real_sizep, char *frame)
> +{
> + int16_t *tmp16p;
> + char *next;
> + char *wbuf;
> + uint32_t data_size;
> +
> + /* Head. */
> + tmp16p = (int16_t *)gtp_frame_file_realloc(real_sizep, 2, 0);
> + if (!tmp16p)
> + return -1;
> + *tmp16p = (int16_t)*(ULONGEST *)(frame + FID_SIZE + sizeof(char *));
> + gtp_frame_file_size += 2;
> + /* This part is for the data_size. */
> + wbuf = gtp_frame_file_realloc(real_sizep, 4, 0);
> + if (!wbuf)
> + return -1;
> + gtp_frame_file_size += 4;
> +
> + /* Body. */
> + data_size = 0;
> + for (next = *(char **)(frame + FID_SIZE); next;
> + next = *(char **)(next + FID_SIZE)) {
> + switch (FID(next)) {
> + case FID_REG:
> + wbuf = gtp_frame_file_realloc(real_sizep,
> + GTP_REG_BIN_SIZE + 1,
> + 0);
> + if (!wbuf)
> + return -1;
> + wbuf[0] = 'R';
> + gtp_regs2bin((struct pt_regs *)(next + FID_SIZE
> + + sizeof(char *)),
> + wbuf + 1);
> + gtp_frame_file_size += GTP_REG_BIN_SIZE + 1;
> + data_size += GTP_REG_BIN_SIZE + 1;
> + break;
> +
> + case FID_MEM:
> + if (gtp_frame2file_m(real_sizep, &data_size, next))
> + return -1;
> + break;
> +
> + case FID_VAR:
> + if (gtp_frame2file_v(real_sizep, &data_size, next))
> + return -1;
> + break;
> + }
> + }
> +
> + /* Set the data_size. */
> + memcpy(gtp_frame_file + gtp_frame_file_size - data_size - 4,
> + &data_size, 4);
> +
> + return 0;
> +}
> +
> +static ssize_t
> +gtpframe_read(struct file *file, char __user *buf, size_t size,
> + loff_t *ppos)
> +{
> + ssize_t ret = -ENOMEM;
> +
> + down(&gtp_rw_lock);
> +
> + if (gtp_start) {
> + ret = -EBUSY;
> + goto out;
> + }
> +
> + /* Set gtp_frame_file if need. */
> + if (!gtp_frame_file) {
> + size_t real_size;
> + char *wbuf;
> + struct gtp_entry *tpe;
> + struct gtp_var *tvar;
> + int tmpsize;
> + char tmpbuf[200];
> + char *frame;
> +
> + if (gtp_frame_is_circular)
> + real_size = GTP_FRAME_SIZE;
> + else
> + real_size = gtp_frame_w_start - gtp_frame;
> + real_size += 200;
> +
> + gtp_frame_file = vmalloc(real_size);
> + if (!gtp_frame_file)
> + goto out;
> + gtp_frame_file_size = 0;
> +
> + /* Head. */
> + wbuf = gtp_frame_file;
> + strcpy(wbuf, "\x7fTRACE0\n");
> + gtp_frame_file_size += 8;
> +
> + /* BUG: will be a new value. */
> + snprintf(tmpbuf, 200, "R %x\n", GTP_REG_BIN_SIZE);
> + wbuf = gtp_frame_file_realloc(&real_size, strlen(tmpbuf), 0);
> + if (!wbuf)
> + goto out;
> + strcpy(wbuf, tmpbuf);
> + gtp_frame_file_size += strlen(tmpbuf);
> +
> + strcpy(tmpbuf, "status 0;");
> + wbuf = gtp_frame_file_realloc(&real_size, strlen(tmpbuf), 0);
> + if (!wbuf)
> + goto out;
> + strcpy(wbuf, tmpbuf);
> + gtp_frame_file_size += strlen(tmpbuf);
> +
> + for (tpe = gtp_list; tpe; tpe = tpe->next) {
> + if (tpe->reason != gtp_stop_normal)
> + break;
> + }
> + tmpsize = gtp_get_status(tpe, tmpbuf);
> + wbuf = gtp_frame_file_realloc(&real_size, tmpsize, 0);
> + if (!wbuf)
> + goto out;
> + memcpy(wbuf, tmpbuf, tmpsize);
> + gtp_frame_file_size += tmpsize;
> +
> + wbuf = gtp_frame_file_realloc(&real_size, 1, 0);
> + if (!wbuf)
> + goto out;
> + wbuf[0] = '\n';
> + gtp_frame_file_size += 1;
> +
> + /* Tval. */
> + for (tvar = gtp_var_list; tvar; tvar = tvar->next) {
> + snprintf(tmpbuf, 200, "tsv %x:%s\n", tvar->num,
> + tvar->src);
> + wbuf = gtp_frame_file_realloc(&real_size,
> + strlen(tmpbuf), 0);
> + if (!wbuf)
> + goto out;
> + strcpy(wbuf, tmpbuf);
> + gtp_frame_file_size += strlen(tmpbuf);
> + }
> +
> + /* Tracepoint. */
> + for (tpe = gtp_list; tpe; tpe = tpe->next) {
> + struct action *ae;
> + struct gtpsrc *src;
> +
> + /* Tpe. */
> + gtp_report_tracepoint(tpe, tmpbuf);
> + wbuf = gtp_frame_file_realloc(&real_size,
> + strlen(tmpbuf) + 5, 0);
> + if (!wbuf)
> + goto out;
> + sprintf(wbuf, "tp %s\n", tmpbuf);
> + gtp_frame_file_size += strlen(tmpbuf) + 4;
> + /* Action. */
> + for (ae = tpe->action_list; ae; ae = ae->next) {
> + gtp_report_action(tpe, ae, tmpbuf);
> + wbuf = gtp_frame_file_realloc
> + (&real_size, strlen(tmpbuf) + 5, 0);
> + if (!wbuf)
> + goto out;
> + sprintf(wbuf, "tp %s\n", tmpbuf);
> + gtp_frame_file_size += strlen(tmpbuf) + 4;
> + }
> + /* Src. */
> + for (src = tpe->src; src; src = src->next) {
> + gtp_report_src(tpe, src, tmpbuf);
> + wbuf = gtp_frame_file_realloc
> + (&real_size, strlen(tmpbuf) + 5, 0);
> + if (!wbuf)
> + goto out;
> + sprintf(wbuf, "tp %s\n", tmpbuf);
> + gtp_frame_file_size += strlen(tmpbuf) + 4;
> + }
> + }
> +
> + wbuf = gtp_frame_file_realloc(&real_size, 1, 0);
> + if (!wbuf)
> + goto out;
> + wbuf[0] = '\n';
> + gtp_frame_file_size += 1;
> +
> + /* Frame. */
> + if (atomic_read(&gtp_frame_create) == 0)
> + goto end;
> + frame = gtp_frame_r_start;
> + do {
> + if (FID(frame) == FID_HEAD) {
> + if (gtp_frame2file(&real_size, frame))
> + goto out;
> + }
> +
> + frame = gtp_frame_next(frame);
> + if (!frame)
> + break;
> +
> + if (frame == gtp_frame_end)
> + frame = gtp_frame;
> + } while (frame != gtp_frame_w_start);
> +
> +end:
> + /* End. */
> + wbuf = gtp_frame_file_realloc(&real_size, tmpsize, 2);
> + if (!wbuf)
> + goto out;
> + wbuf[0] = '\0';
> + wbuf[1] = '\0';
> + gtp_frame_file_size += 2;
> + }
> +
> + /* Set buf. */
> + ret = size;
> + if (*ppos + ret > gtp_frame_file_size) {
> + ret = gtp_frame_file_size - *ppos;
> + if (ret <= 0) {
> + ret = 0;
> + goto out;
> + }
> + }
> + if (copy_to_user(buf, gtp_frame_file + *ppos, ret)) {
> + size = -EFAULT;
> + goto out;
> + }
> + *ppos += ret;
> +
> +out:
> + up(&gtp_rw_lock);
> + return ret;
> +}
> +
> +static const struct file_operations gtp_operations = {
> + .owner = THIS_MODULE,
> + .open = gtp_open,
> + .release = gtp_release,
> + .unlocked_ioctl = gtp_ioctl,
> + .compat_ioctl = gtp_ioctl,
> + .read = gtp_read,
> + .write = gtp_write,
> + .poll = gtp_poll,
> +};
> +
> +static const struct file_operations gtpframe_operations = {
> + .owner = THIS_MODULE,
> + .open = gtp_open,
> + .release = gtp_release,
> + .read = gtpframe_read,
> + .llseek = default_llseek,
> +};
> +
> +struct dentry *gtp_dir;
> +struct dentry *gtpframe_dir;
> +
> +static int __init gtp_init(void)
> +{
> + int ret = -ENOMEM;
> +
> + gtp_list = NULL;
> + gtp_read_ack = 0;
> + gtp_rw_bufp = NULL;
> + gtp_rw_size = 0;
> + gtp_start = 0;
> + gtp_disconnected_tracing = 0;
> + gtp_circular = 0;
> + gtp_var_list = GTP_VAR_LIST_FIRST;
> + gtp_var_head = GTP_VAR_SPECIAL_MIN;
> + gtp_var_tail = GTP_VAR_SPECIAL_MAX;
> + gtp_var_array = NULL;
> + current_gtp_var = NULL;
> + gtp_frame = NULL;
> + gtp_frame_r_start = NULL;
> + gtp_frame_w_start = NULL;
> + gtp_frame_end = NULL;
> + gtp_frame_is_circular = 0;
> + gtp_frame_current = NULL;
> + gtp_frame_current_num = 0;
> + atomic_set(&gtp_frame_create, 0);
> + gtp_rw_count = 0;
> + current_gtp = NULL;
> + current_gtp_action = NULL;
> + current_gtp_src = NULL;
> + gtpro_list = NULL;
> + gtp_frame_file = NULL;
> + gtp_frame_file_size = 0;
> + gtp_dir = NULL;
> + gtpframe_dir = NULL;
> +
> + gtp_wq = create_singlethread_workqueue("gtpd");
> + if (gtp_wq == NULL)
> + goto out;
> +
> + gtp_dir = debugfs_create_file("gtp", S_IFIFO | S_IRUSR | S_IWUSR, NULL,
> + NULL, &gtp_operations);
> + if (gtp_dir == NULL || gtp_dir == ERR_PTR(-ENODEV)) {
> + gtp_dir = NULL;
> + goto out;
> + }
> + gtpframe_dir = debugfs_create_file("gtpframe", S_IFIFO | S_IRUSR, NULL,
> + NULL, &gtpframe_operations);
> + if (gtpframe_dir == NULL || gtpframe_dir == ERR_PTR(-ENODEV)) {
> + gtpframe_dir = NULL;
> + goto out;
> + }
> +
> + ret = 0;
> +out:
> + if (ret < 0) {
> + if (gtp_wq)
> + destroy_workqueue(gtp_wq);
> +
> + if (gtp_dir != NULL)
> + debugfs_remove_recursive(gtp_dir);
> + if (gtpframe_dir != NULL)
> + debugfs_remove_recursive(gtpframe_dir);
> + }
> +
> + return 0;
> +}
> +
> +static void __exit gtp_exit(void)
> +{
> + if (gtp_dir != NULL)
> + debugfs_remove_recursive(gtp_dir);
> + if (gtpframe_dir != NULL)
> + debugfs_remove_recursive(gtpframe_dir);
> +
> + gtp_gdbrsp_qtstop();
> + gtp_gdbrsp_qtinit();
> + if (gtp_frame) {
> + vfree(gtp_frame);
> + gtp_frame = NULL;
> + }
> +
> + destroy_workqueue(gtp_wq);
> +}
> +
> +module_init(gtp_init)
> +module_exit(gtp_exit)
> +
> +MODULE_AUTHOR("Hui Zhu <teawater@xxxxxxxxx>");
> +MODULE_LICENSE("GPL");
> --- /dev/null
> +++ b/scripts/getgtprsp.pl
> @@ -0,0 +1,102 @@
> +#!/usr/bin/perl
> +
> +# This script to get the GDB tracepoint RSP package and save it
> +# to ./gtpstart and ./gtpstop file.
> +# GPL
> +# Copyright(C) Hui Zhu (teawater@xxxxxxxxx), 2010
> +
> +binmode STDIN, ":raw";
> +$| = 1;
> +
> +$status = 0;
> +$circular = 0;
> +$var_count = 0;
> +
> +while (1) {
> + sysread STDIN, $c, 1 or next;
> + if ($c eq '') {
> + next;
> + } elsif ($c eq '+' || $c eq '-') {
> + $c = '';
> + }
> +
> + sysread STDIN, $line, 1024 or next;
> + print '+';
> + $line = $c.$line;
> +
> + open(LOG, ">>./log");
> + print LOG $line."\n";
> + close (LOG);
> +
> + if ($status == 0) {
> + if ($line eq '$?#3f') {
> + print '$S05#b8';
> + } elsif ($line eq '$g#67') {
> + print '$00000000#80';
> + } elsif ($line =~ /^\$m/ || $line =~ /^\$p/) {
> + print '$00000000#80';
> + } elsif ($line eq '$qTStatus#49') {
> + print '$T0;tnotrun:0;tframes:0;tcreated:0;tsize:';
> + print '500000;tfree:500000;circular:0;disconn:0#d1';
> + } elsif ($line eq '$QTBuffer:circular:1#f9') {
> + print '$OK#9a';
> + $circular = 1;
> + } elsif ($line eq '$QTBuffer:circular:0#f8') {
> + print '$OK#9a';
> + $circular = 0;
> + } elsif ($line eq '$QTStop#4b') {
> + print '$OK#9a';
> + } elsif ($line =~ /^\$qSupported/) {
> + print '$ConditionalTracepoints+;TracepointSource+#1b';
> + } elsif ($line eq '$QTinit#59') {
> + $status = 1;
> + open(STARTFILE, ">./gtpstart");
> + print STARTFILE '$QTDisconnected:1#e3'."\n";
> + if ($circular) {
> + print STARTFILE '$QTBuffer:circular:1#f9'."\n";
> + } else {
> + print STARTFILE '$QTBuffer:circular:0#f8'."\n";
> + }
> + } elsif ($line eq '$qTfV#81') {
> + print '$8:0:1:64756d705f737461636b#f6';
> + } elsif ($line eq '$qTsV#8e') {
> + if ($var_count == 0) {
> + print '$7:0:1:7072696e746b5f666f726d6174#9b';
> + } elsif ($var_count == 1) {
> + print '$6:8:1:7072696e746b5f6c6576656c#3a';
> + } elsif ($var_count == 2) {
> + print '$5:0:1:7072696e746b5f746d70#28';
> + } elsif ($var_count == 3) {
> + print '$4:0:1:6370755f6964#f3';
> + } elsif ($var_count == 4) {
> + print '$3:0:1:636c6f636b#e1';
> + } elsif ($var_count == 5) {
> + print '$2:0:1:63757272656e745f7468726561';
> + print '645f696e666f#1f';
> + } elsif ($var_count == 6) {
> + print '$1:0:1:63757272656e745f7461736b#c7';
> + } else {
> + print '$l#6c';
> + }
> + $var_count++;
> + } else {
> + print '$#00';
> + }
> + }
> +
> + if ($status == 1) {
> + print '$OK#9a';
> +
> + print STARTFILE $line."\n";
> +
> + if ($line eq '$QTStart#b3') {
> + $status = 0;
> +
> + close(STARTFILE);
> +
> + open(STOPFILE, ">./gtpstop");
> + print STOPFILE '$QTStop#4b'."\n";
> + close(STOPFILE);
> + }
> + }
> +}
> --
Perfect , Thanks
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/