--- Documentation/gtp/howto.txt | 1519 ++++++ Documentation/gtp/quickstart.txt | 250 + arch/arm/include/asm/gtp.h | 13 arch/mips/include/asm/gtp.h | 18 arch/x86/include/asm/gtp.h | 18 include/linux/perf_event.h | 3 kernel/events/core.c | 14 lib/Kconfig.debug | 10 lib/Makefile | 4 lib/gtp.c | 8916 +++++++++++++++++++++++++++++++++++++++ lib/gtp_rb.c | 498 ++ scripts/gtp/add-ons/hotcode.py | 588 ++ scripts/gtp/add-ons/pe.py | 729 +++ scripts/gtp/getgtprsp.pl | 137 scripts/gtp/getmod.py | 132 15 files changed, 12847 insertions(+), 2 deletions(-) --- /dev/null +++ b/Documentation/gtp/howto.txt @@ -0,0 +1,1519 @@ + Linux Kernel GDB tracepoint module (KGTP) + ========================================= + By Hui Zhu + https://code.google.com/p/kgtp/wiki/HOWTO + 2012-02-21 + +Table of contents +----------------- + +What is KGTP +Report issues about KGTP +Get info about GDB tracepoint +Install GDB for KGTP +Get KGTP through http +Get KGTP through svn +Config KGTP +Compile KGTP +Install KGTP +Uninstall KGTP +Use KGTP with DKMS +How to get new version GDB +Howto use + Exec it + Make GDB connect to gtp + If GDB on current machine + If GDB on remote machine + Add module symbols to GDB + How to use getmod.py + How to use getmod + Access memory directly + Get register 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 + Per_cpu trace state variables + Special trace state variables $current_task, $current_task_pid, + $current_thread_info, $cpu_id, $dump_stack, $printk_level, + $printk_format, $printk_tmp, $clock, $rdtsc, $hardirq_count, + $softirq_count and $irq_count + Special trace state variable $no_self_trace + Trace the function return with $kret + Use $ignore_error and $last_errno to ignore the error of tstart + Use $cooked_clock and $cooked_rdtsc the time without KGTP used + Use $xtime_sec and $xtime_nsec get the timespec + How to use performance counters + Show all the traced data of current frame + Get backtrace info(stack dump) 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 disconnects + Howto show a variable whose value has been optimized away + Linux kernel "Compile with almost no optimization" patch + Update your GCC + How to get the function pointer point to + If the debug info of the function pointer is not optimized out + If the debug info of the function pointer is optimized out + How to use /sys/kernel/debug/gtpframe + Offline debug + How to use /sys/kernel/debug/gtpframe_pipe + Get the frame info with cat + Get the frame info with getframe + Get the frame info with GDB + + + + +What is KGTP +------------ + +KGTP is a realtime and lightweight Linux Kernel GDB debugger and tracer. + +It makes Linux Kernel supply a GDB remote debug interface. Then GDB in current +machine or remote machine (see "Make GDB connect to gtp") can debug and trace +Linux through GDB tracepoint without stopping 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 using offline debug (See "Offline +debug"). +And it can work with Android +(See https://code.google.com/p/kgtp/wiki/HowToUseKGTPinAndroid). +It supports X86-32, X86-64, MIPS and ARM. + +For new user of KGTP, please go to see quickstart.txt. + + + + +Report issues about KGTP +------------------------ +You can post it in https://code.google.com/p/kgtp/issues/list or write Email +to teawater@gmail.com. + + + + +Get info about GDB tracepoint +----------------------------- +Please goto http://sourceware.org/gdb/current/onlinedocs/gdb/Tracepoints.html + + + + +Install GDB for KGTP +------------------------ +The GDB that older than 7.3 have some bugs of tracepoint. And some functions +of GDB are not very well. +So please goto https://code.google.com/p/gdbt/ to install GDB for KGTP. + + + + +Get KGTP through http +--------------------- +Please goto http://code.google.com/p/kgtp/downloads/list OR UPDATE to download +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/ Present for each release of KGTP. +kgtp-read-only/trunk/ Present for the main trunk of KGTP. + + + + +Config KGTP +----------- + +Before compiling KGTP, you can choose which kernel you want build with and +which compiler you want by making changes to the Makefile in your KGTP +repository. +For example: + +------------------------------------------- +KERNELDIR := /lib/modules/`uname -r`/build +CROSS_COMPILE := +------------------------------------------- + +KERELDIR is set to the directory which holds the kernel you want to build for. +By default, it is set to the kernel that you are running. +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 +--------- + + + + +Compile KGTP with old Linux Kernel +---------------------------------- + +Most of time, KGTP can auto select right options to build with old Linux Kernel. +But if you want config special options with yourself, you can read following +part. + +-------------------- +make AUTO=0 +-------------------- +With this option, KGTP will not auto select any build options. + +-------------------- +make AUTO=0 FRAME_SIMPLE=1 +-------------------- +With this option, KGTP will use simple frame instead of ring buffer. +The simple frame doesn't support gtpframe_pipe. Use it can make KGTP can build +with old Kernel that doesn't support ring buffer. + +-------------------- +make AUTO=0 CLOCK_CYCLE=1 +-------------------- +With this option, $clock will return rdtsc value instead of local_clock. + +-------------------- +make AUTO=0 USE_PROC=1 +-------------------- +With this option, KGTP will use procfs instead of debugfs. + +The options can use together, for example: +----------------------------------- +make AUTO=0 FRAME_SIMPLE=1 CLOCK_CYCLE=1 +----------------------------------- +This build command make KGTP build success with Linux Kernel 2.6.18. + + + + +Install KGTP +------------ + +------------------ +cd kgtp/ +sudo make install +------------------ + + + + +Uninstall KGTP +-------------- + +-------------------- +cd kgtp/ +sudo make uninstall +-------------------- + + + + +Use KGTP with DKMS +------------------ + +-------------------- +cd kgtp/ +sudo make dkms +-------------------- +This commands will copy the files of KGTP to the directory that DKMS need. +Then you can use DKMS commands to control KGTP. +Please goto http://linux.dell.com/dkms/manpage.html to see how to use DKMS. + + + + +How to get new version GDB +-------------------------- + +The old version GDB such as GDB 7.2 have some bugs about tracepoint that KGTP +need. So I suggest you update your version older than 7.2. +If you use UBUNTU, use can get the new version from +https://lkml.org/lkml/2011/6/4/65 + + + + +Howto use +--------- + +Exec it +------- + +If you have installed 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 +----------------------- + + +If GDB on current machine +------------------------- + +--------------------------------- +sudo gdb ./vmlinux +(gdb) target remote /sys/kernel/debug/gtp +Remote debugging using /sys/kernel/debug/gtp +0x0000000000000000 in ?? () +--------------------------------- +After that, you can begin to use GDB command trace the Linux Kernel. + + +If GDB on remote machine +------------------------ + +--------------------------------------------- +#Open the KGTP interface in current machine. +sudo su +nc -l 1234 /sys/kernel/debug/gtp +(nc -l -p 1234 /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 +--------------------------------------------- +After that, you can begin to use GDB command trace the Linux Kernel. + + + +Add module symbols to GDB +------------------------- + +Sometimes you need to add a Linux kernel module's symbols to GDB to debug it. +Add symbols with hand is not very easy, so KGTP package include an GDB python +script "getmod.py" and a program "getmod" can help you. + + +How to use getmod.py +-------------------- + +Connect to KGTP before use the getmod.py. +(gdb) source ~/kgtp/getmod.py +Then this script will auto load the Linux kernel module's symbols to GDB. + + +How to use getmod +----------------- +"getmod" is written by C so you can use it anywhere even if in an embedded +environment. +For example: + +-------------------------------------------------------------------------------- +#Following command save Linux Kernel module info to the file ~/tmp/mi in GDB +#command format. +sudo getmod >~/tmp/mi +#in gdb part: +(gdb) source ~/tmp/mi +add symbol table from file "/lib/modules/2.6.39-rc5+/kernel/fs/nls/nls_iso8859-1.ko" at + .text_addr = 0xf80de000 + .note.gnu.build-id_addr = 0xf80de088 + .exit.text_addr = 0xf80de074 + .init.text_addr = 0xf8118000 + .rodata.str1.1_addr = 0xf80de0ac + .rodata_addr = 0xf80de0c0 + __mcount_loc_addr = 0xf80de9c0 + .data_addr = 0xf80de9e0 + .gnu.linkonce.this_module_addr = 0xf80dea00 +#After this GDB command, all the Linux Kernel module info is loaded into GDB. +-------------------------------------------------------------------------------- + +If you use remote debug or offline debug, maybe you need change the base +directory. Following example is for it. + +-------------------------------------------------------------------------------- +#/lib/modules/2.6.39-rc5+/kernel is replaced to sudo ./getmod -r /home/teawater/kernel/b26 +sudo ./getmod -r /home/teawater/kernel/b26 >~/tmp/mi +-------------------------------------------------------------------------------- + + + +Access memory directly +---------------------- + +After connect the KGTP, you can access most of memory directly. +For example, you can access to "jiffies_64" with following command: +--------------- +p jiffies_64 +--------------- + +Or you can access to the first entry of "static LIST_HEAD(modules)" with +following command: +-------------------------------------------------------------------------------- +p *((struct module *)((char *)modules->next - ((size_t) &(((struct module *)0)->list)))) +-------------------------------------------------------------------------------- + + + +Get register info from Kernel +----------------------------- + +The following is an example that records the value of all registers +when "vfs_readdir" is called. + +-------------------------------------------------------------------------------- +(gdb) target remote /sys/kernel/debug/gtp +(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 , + buf=0xc0d09f90) at readdir.c:23 +23 readdir.c: No such file or directory. + in 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 +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 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 +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 +------------------------------------- + +The following is an example that records the value of "jiffies_64" when the +function "vfs_readdir" is called: + +-------------------------------------------------------------------------------- +(gdb) target remote /sys/kernel/debug/gtp +(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 , buf=0xd6dfdf90) + at readdir.c:24 +24 { +(gdb) p jiffies_64 +$1 = 4297248706 +(gdb) p file->f_path.dentry->d_iname +$1 = "b26", '\000' +-------------------------------------------------------------------------------- + + + +How to use use tracepoint condition +----------------------------------- + +http://sourceware.org/gdb/current/onlinedocs/gdb/Tracepoint-Conditions.html +Like breakpoints, we can set conditions on tracepoints. The speed of +tracepoints is faster than breakpoints because KGTP can do all the condition +checks. +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 +Tracepoints have special variables. The variables can be traced directly, +or used in tracepoint conditions. +Note that just GDB 7.2.1 and later versions support use trace state variables +directly, the old version of GDB 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 is created with initial value 0. +The following action uses $c to count how many irqs happened in the 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 +----------------------------------------------------------------------- + +Also, you can set a value of variable to trace state variable, but don't forget +covert variable to "uint64_t". + +----------------------------------------------------------------------- +>teval $c=(uint64_t)jiffies_64 +----------------------------------------------------------------------- + +You can get the current value of $c while the trace is running or stopped. + +---------------------------------- +(gdb) tstart +(gdb) info tvariables +$c 0 31554 +(gdb) p $c +$5 = 33652 +(gdb) tstop +(gdb) p $c +$9 = 105559 +---------------------------------- + +When using tfind, you can parse the trace frame buffer. If the value of a +trace state variable is collected, you can parse it out. + +------------------------------ +(gdb) tstop +(gdb) tfind +(gdb) info tvariables +$c 0 0 +(gdb) p $c +$6 = 0 +(gdb) tfind 100 +(gdb) p $c +$7 = 100 +------------------------------ + +If need, the tracepoint action that access the simple trace state variables will +auto lock the spin lock for trace state variables. So it can handle race +condition about trace state variables. +The following example is OK even if it running a machine that have more than +one CPU. +------------------------------------- +teval $c=$c+1 +------------------------------------- + + +Per_cpu trace state variables +----------------------------- + +Per_cpu trace state variables are special simple trace state variables. +When tracepoint action access to it, it will access to this CPU special trace +state variables. + +It have 2 advantages: +1. The tracepoint actions that access to per_cpu trace state variables don't +have the race conditon issue. So it don't need lock the spin lock for trace +state variables. It is faster than simple trace state variables on multi-core +machine. +2. Write the action that count some CPU special thing with it is easier than +simple trace state variables. + +To define per_cpu trace state variables, you need named it in +format: +"per_cpu_"+string+CPU_id +or +"pc_"+string+CPU_id +Following example will define a series of per_cpu trace state variables +in a 4 COREs CPU machine with string "count": +------------------------------------- +tvariable $pc_count0 +tvariable $pc_count1 +tvariable $pc_count2 +tvariable $pc_count3 +------------------------------------- + +You can use compatibility better way to do it: +----------------------------------------- +set $tmp=0 +while $tmp<$cpu_number + eval "tvariable $pc_count%d",$tmp + set $tmp=$tmp+1 +end +----------------------------------------- + +Tracepoint action can access anyone of a series of per_cpu trace state +variables. KGTP will auto access the one of CPU that it running on. +For example: +---------------------------------------- +trace vfs_read +actions +teval $pc_count0=$pc_count0+1 +end +---------------------------------------- +These GDB commands define a tracepoint that count the times that call vfs_read +of each CPU. + + +Special trace state variables $current_task, $current_task_pid, +$current_thread_info, $cpu_id, $dump_stack, $printk_level, $printk_format, +$printk_tmp, $clock, $rdtsc, $hardirq_count, $softirq_count and $irq_count +--------------------------------------------------------------------------- + +KGTP 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 connects to the KGTP. You can use them in tracepoint conditions +or actions. +Access $current_task in tracepoint condition and action will get that returns +of get_current(). +Access $current_task_pid in tracepoint condition and action will get that +returns of get_current()->pid. +Access $current_thread_info in tracepoint condition and action will get that +returns of current_thread_info(). +Access $cpu_id in tracepoint condition and action will get that returns of +smp_processor_id(). +Access $clock in tracepoint condition and action will get that returns of +local_clock() that return the timestamp in nanoseconds. +$rdtsc is only available on X86 and X86_64 architecture. Access it in anytime +will get current value of TSC with instruction RDTSC. +Access $hardirq_count in tracepoint condition and action will get that returns +of hardirq_count(). +Access $softirq_count in tracepoint condition and action will get that returns +of softirq_count(). +Access $irq_count in tracepoint condition and action will get that returns +of irq_count(). + +And KGTP has other special trace state variables $dump_stack, $printk_level, +$printk_format and $printk_tmp. All of them output their values directly, +as can be seen in "Howto let tracepoint output value directly". + +The following example counts in $c how many vfs_read calls that process 16663 +does and collects the struct thread_info of current task: + +-------------------------------------------------------------------------------- +(gdb) target remote /sys/kernel/debug/gtp +(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 +$current_thread_info 0 +$cpu_id 0 +(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 , {{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} +-------------------------------------------------------------------------------- + +Another example shows how much sys_read() executes 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 +$cpu_id 0 +$c0 0 3255 +$c1 0 1904 +-------------------------------------- + +sys_read() execute 3255 times in cpu0 and 1904 times in cpu1. + + +Special trace state variable $no_self_trace +-------------------------------------------- + +$no_self_trace is different with the special trace state variables in the +previous section. It is used to control the behavior of tracepoint. +If the action of a tracepoint include a command access to the $no_self_trace. +The tracepoint will not trace anything if the current_task is the a KGTP self +process (GDB, netcat, getframe or some others process that access to the +interface of KGTP). +For example, if we want trace vfs_read or something that have process context, +and we don't want trace the operation of KGTP self process. Add following +command to the action: + +-------------------------------------- +collect $no_self_trace +-------------------------------------- + +Please note that the code that doesn't have process context (Irq handler, +softirq) doesn't need set this variable. + + +Trace the function return with $kret +------------------------------------ + +Sometime, set the tracepoint to the end of function is hard because the Kernel +is compiled with optimization. At this time, you can get help from $kret. + +$kret is a special trace state variable like $no_self_trace. When you set +value of it inside the action of tracepoint, this tracepoint be set with +kretprobe instead of kprobe. Then it can trace the end of this function. + +Following part is an example: + +------------------------------------------------------------------------------ +target remote /sys/kernel/debug/gtp +#"*(function_name)" format can make certain that GDB send the first address +#of function to KGTP. +trace *(vfs_read) +action +teval $kret=0 +#Following part you can set commands that you want. +------------------------------------------------------------------------------ + + +Use $ignore_error and $last_errno to ignore the error of tstart +--------------------------------------------------------------- + +If KGTP got any error of tstart, this command will get fail. +But sometime we need ignore this error and let KGTP keep work. +For example: If you set tracepoint on the inline function spin_lock. +This tracepoint will be set to a lot of addresses that some of them cannot +be set kprobe. It will make tstart get fail. You can use "$ignore_error" +ignore this error. +And the last error number will available in "$last_errno". + +--------------------------------- +tvariable $ignore_error=1 +--------------------------------- +This command will open ignore. + +--------------------------------- +tvariable $ignore_error=0 +--------------------------------- +This command will close ignore. + + +Use $cooked_clock and $cooked_rdtsc the time without KGTP used +-------------------------------------------------------------- + +Access these two trace state variables can get the time without KGTP used. +Then we can get more close to really time that a part of code used even if the +actions of tracepoint is very complex. They will be introduce in +Cookbook (coming soon). + + +Use $xtime_sec and $xtime_nsec get the timespec +----------------------------------------------- + +Access these two trace state variables will return the time of day in +a timespec that use getnstimeofday. +$xtime_sec will access to the second part of a timespec. +$xtime_nsec will access to the nanosecond part of a timespec. + + + +How to use performance counters +------------------------------- + +Performance counters are special hardware registers available on most modern +CPUs. These registers count the number of certain types of hw events: such as +instructions executed, cachemisses suffered, or branches mis-predicted - without +slowing down the kernel or applications. These registers can also trigger +interrupts when a threshold number of events have passed - and can thus be +used to profile the code that runs on that CPU. + +The Linux Performance Counter subsystem called perf event can get the value of +performance counter. You can access it through KGTP perf event trace state +variables. + +Please goto read the file tools/perf/design.txt in Linux Kernel to get more +info about perf event. + + +Define a perf event trace state variable +---------------------------------------- + +Access an performance counter need define following trace state variable: +----------------------------------------------------------------------------- +"pe_cpu_"+tv_name Define the the CPU id of the performance counter. +"pe_type_"+tv_name Define the the type of the performance counter. +"pe_config_"+tv_name Define the the config of the performance counter. +"pe_en_"+tv_name This the switch to enable or disable the performance + counter. + The performance counter is disable in default. +"pe_val_"+tv_name Access this variable can get the value of the + performance counter. +----------------------------------------------------------------------------- + + +Define a per_cpu perf event trace state variable +------------------------------------------------ + +Define a per_cpu perf event trace state variable is same with define +"Per_cpu_trace_state_variables". + +------------------------------------------ +"pc_pe_"+perf_event type+string+CPU_id +------------------------------------------ + +Note that if you define a per_cpu perf event trace state variable, you will not +need define the "pe_cpu_" because KGTP already get it from per_cpu id. + + +The perf event type and config +------------------------------ + +The type of perf event can be: +----------------------------------------------------------- +0 PERF_TYPE_HARDWARE +1 PERF_TYPE_SOFTWARE +2 PERF_TYPE_TRACEPOINT +3 PERF_TYPE_HW_CACHE +4 PERF_TYPE_RAW +5 PERF_TYPE_BREAKPOINT +----------------------------------------------------------- + +If the type is 0(PERF_TYPE_HARDWARE), the config can be: +----------------------------------------------------------- +0 PERF_COUNT_HW_CPU_CYCLES +1 PERF_COUNT_HW_INSTRUCTIONS +2 PERF_COUNT_HW_CACHE_REFERENCES +3 PERF_COUNT_HW_CACHE_MISSES +4 PERF_COUNT_HW_BRANCH_INSTRUCTIONS +5 PERF_COUNT_HW_BRANCH_MISSES +6 PERF_COUNT_HW_BUS_CYCLES +7 PERF_COUNT_HW_STALLED_CYCLES_FRONTEND +8 PERF_COUNT_HW_STALLED_CYCLES_BACKEND +----------------------------------------------------------- + +If the type is 3(PERF_TYPE_HW_CACHE), the config need to divide to 3 parts: +First one is cache id, it need be << 0 before set to config: +----------------------------------------------------------- +0 PERF_COUNT_HW_CACHE_L1D +1 PERF_COUNT_HW_CACHE_L1I +2 PERF_COUNT_HW_CACHE_LL +3 PERF_COUNT_HW_CACHE_DTLB +4 PERF_COUNT_HW_CACHE_ITLB +5 PERF_COUNT_HW_CACHE_BPU +----------------------------------------------------------- +Second one is cache op id, it need be << 8 before set to config: +----------------------------------------------------------- +0 PERF_COUNT_HW_CACHE_OP_READ +1 PERF_COUNT_HW_CACHE_OP_WRITE +2 PERF_COUNT_HW_CACHE_OP_PREFETCH +----------------------------------------------------------- +Last one is cache op result id, it need be << 16 before set to config: +----------------------------------------------------------- +0 PERF_COUNT_HW_CACHE_RESULT_ACCESS +1 PERF_COUNT_HW_CACHE_RESULT_MISS +----------------------------------------------------------- +If you want get the perf count of PERF_COUNT_HW_CACHE_L1I(1), +PERF_COUNT_HW_CACHE_OP_WRITE(1) and PERF_COUNT_HW_CACHE_RESULT_MISS(1), +you can use: +----------------------------------------------------------- +tvariable $pe_config_cache=1 | (1 << 8) | (1 << 16) +----------------------------------------------------------- + +tools/perf/design.txt in Linux Kernel have more info about type and config +of perf event. + + +Enable and disable all the perf event in a CPU with $pc_pe_en +------------------------------------------------------------- + +I think the best way that count a part of code with performance counters is +enable all the count in the begin of the code and disable all of them in the +end. You can do it with "pe_en_". But if you have a lot of perf event trace +state variables. That will make the tracepoint action very big. $pc_pe_en is +for this issue. + +You can enable all the perf event trace state variables in current CPU with +following action: +-------------------- +teval $pc_pe_en=1 +-------------------- +Disable them with set $pc_pe_en to 0. +-------------------- +teval $pc_pe_en=0 +-------------------- + + +GDB scripts to help with set and get the perf event trace state variables +------------------------------------------------------------------------- + +Following is a GDB script define two commands dpe and spe to help define and +show the perf event trace state variables. +You can put it to the ~/.gdbinit or your tracepoint script. Then you can use +this two commands in GDB directly. +------------------------------------------------------------------------------- +define dpe + if ($argc < 2) + printf "Usage: dpe pe_type pe_config [enable]\n" + end + if ($argc >= 2) + set $tmp=0 + while $tmp<$cpu_number + eval "tvariable $pc_pe_type_%d%d_%d=%d",$arg0, $arg1, $tmp, $arg0 + eval "tvariable $pc_pe_config_%d%d_%d=%d",$arg0, $arg1, $tmp, $arg1 + eval "tvariable $pc_pe_val_%d%d_%d=0",$arg0, $arg1, $tmp + if ($argc >= 3) + eval "tvariable $pc_pe_en_%d%d_%d=%d",$arg0, $arg1, $tmp, $arg2 + end + set $tmp=$tmp+1 + end + end +end + +document dpe +Usage: dpe pe_type pe_config [enable] +end + +define spe + if ($argc != 2 && $argc != 3) + printf "Usage: spe pe_type pe_config [cpu_id]\n" + end + if ($argc == 2) + set $tmp=0 + while $tmp<$cpu_number + eval "printf \"$pc_pe_val_%%d%%d_%%d=%%ld\\n\",$arg0, $arg1, $tmp, $pc_pe_val_%d%d_%d", $arg0, $arg1, $tmp + set $tmp=$tmp+1 + end + end + if ($argc == 3) + eval "printf \"$pc_pe_val_%%d%%d_%%d=%%ld\\n\",$arg0, $arg1, $tmp, $pc_pe_val_%d%d_%d", $arg0, $arg1, $arg2 + end +end + +document spe +Usage: spe pe_type pe_config [cpu_id] +end +------------------------------------------------------------------------------- + +Following is an example to use it get the performance counters of function tcp_v4_rcv: +------------------------------------------------------------------------------- +#Connect to kgtp +target remote /sys/kernel/debug/gtp +#Define 3 pe tvs for PERF_COUNT_HW_CPU_CYCLES, PERF_COUNT_HW_CACHE_MISSES and PERF_COUNT_HW_BRANCH_MISSES. +dpe 0 0 +dpe 0 3 +dpe 0 5 +#enable the performance counters of this CPU in the begin of this function. +trace tcp_v4_rcv + action + teval $pc_pe_en=1 + end +#$kret make this hanler the end of function tcp_v4_rcv. +trace *(tcp_v4_rcv) + action + teval $kret=0 + #disable all performance counters of this CPU + teval $pc_pe_en=0 + #Access the per cpu perf event tv will access to the current cpu pe tv. + collect $pc_pe_val_00_0 + collect $pc_pe_val_03_0 + collect $pc_pe_val_05_0 + #Set all the pe tv to 0 + teval $pc_pe_val_00_0=0 + teval $pc_pe_val_03_0=0 + teval $pc_pe_val_05_0=0 + end +tstart +#Wait some time that current pc receive some tcp package. +tstop +tfind +spe 0 0 $cpu_id +$pc_pe_val_00_2=12676 +spe 0 3 $cpu_id +$pc_pe_val_03_2=7 +spe 0 5 $cpu_id +$pc_pe_val_05_2=97 +------------------------------------------------------------------------------- + + + +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", +jiffies_64 = 4319751455 +-------------------------------------------------------------------------------- + + + +Get backtrace info(stack dump) from Kernel +------------------------------------------ + +We can get a backtrace(stack dump) by collecting 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) target remote /sys/kernel/debug/gtp +(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 , buf=0xffff880005785f38) + at ./linux-2.6/fs/readdir.c:24 +24 { +(gdb) bt +#0 0xffffffff8113f7fd in vfs_readdir (file=0xffff880075f00780, filler=0xffffffff8113f630 , buf=0xffff880005785f38) + at ./linux-2.6/fs/readdir.c:24 +#1 0xffffffff8113fa14 in sys_getdents (fd=, 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 may understand that to get a value from Linux Kernel, +you need to use a tracepoint "collect" action to save the value to the +tracepoint frame and use the GDB command "tfind" to parse the value from the +frame data. +But we want get the value directly sometimes, so KGTP supports two ways to +output values directly. + + +Output stack dump directly +-------------------------- +KGTP has special trace state variable $dump_stack, "collect" it will let Linux +Kernel output stack dump directly. +Following example lets Linux Kernel show the stack dump of vfs_readdir: + +-------------------------------------------------------------------------------- +target remote /sys/kernel/debug/gtp +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] [] gtp_get_var+0x4a/0xa0 [gtp] +[22779.208076] [] gtp_collect_var+0x59/0xa0 [gtp] +[22779.208080] [] gtp_action_x+0x1bb4/0x1dc0 [gtp] +[22779.208084] [] ? _raw_spin_unlock+0x18/0x40 +[22779.208088] [] ? __find_get_block_slow+0xd2/0x160 +[22779.208091] [] ? delayacct_end+0x96/0xb0 +[22779.208100] [] ? __find_get_block+0x84/0x1d0 +[22779.208103] [] ? _raw_spin_unlock+0x18/0x40 +[22779.208106] [] ? find_revoke_record+0xa8/0xc0 +[22779.208109] [] ? jbd2_journal_cancel_revoke+0xd5/0xe0 +[22779.208112] [] ? __jbd2_journal_temp_unlink_buffer+0x2f/0x110 +[22779.208115] [] gtp_kp_pre_handler+0xcc/0x1c0 [gtp] +[22779.208118] [] kprobe_exceptions_notify+0x3d8/0x440 +[22779.208121] [] ? hw_breakpoint_exceptions_notify+0x14/0x180 +[22779.208124] [] ? sub_preempt_count+0x7b/0xb0 +[22779.208126] [] ? vfs_readdir+0x15/0xb0 +[22779.208128] [] ? vfs_readdir+0x14/0xb0 +[22779.208131] [] notifier_call_chain+0x43/0x60 +[22779.208134] [] __atomic_notifier_call_chain+0x38/0x50 +[22779.208137] [] atomic_notifier_call_chain+0x1f/0x30 +[22779.208140] [] notify_die+0x2d/0x30 +[22779.208142] [] do_int3+0x35/0xa0 +-------------------------------------------------------------------------------- + + +Switch collect to output the value directly +------------------------------------------- + +KGTP has 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" action +will save value to the tracepoint frame in the simple behavior. +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 is 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 output as an + unsigned decimal. + If not, it will be output 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 shows a count number, pid, jiffies_64 and the file name +that call vfs_readdir: + +-------------------------------------------------------------------------------- +target remote /sys/kernel/debug/gtp +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" means that it was output by tracepoint 1. + + +Use printf command in actions +----------------------------- + +Before you use this function, please go to https://code.google.com/p/gdbt/ +install GDB for KGTP first. + +Following example show the a count number, pid and the file name that call +vfs_readdir: + +-------------------------------------------------------------------------------- +target remote /sys/kernel/debug/gtp +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 with printk in Linux Kernel, please add kernel loglevel at the +beginning 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 disconnects +--------------------------------------------------- + +http://sourceware.org/gdb/current/onlinedocs/gdb/Starting-and-Stopping-Trace-Experiments.html +KGTP will stop and delete the trace frame when GDB disconnects with it by +default. + +---------------------------- +set disconnected-tracing on +---------------------------- +will open the KGTP disconnect-trace. After that, when GDB disconnects with +KGTP, KGTP will not stop trace. And after GDB reconnects to KGTP, it can keep +control of KGTP like nothing happened. + + + +Howto show a variable whose value has been optimized away +--------------------------------------------------------- + +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 co.patch include in the source of KGTP or goto +http://code.google.com/p/kgtp/downloads/detail?name=co.patch to get it. 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. This helps a lot with generating dwarf for previously +"optimized out" values. + + + +How to get the function pointer point to +---------------------------------------- + +If the debug info of the function pointer is not optimized out +-------------------------------------------------------------- +You can collect it directly and print what it point to. For example: +377 count = ret; +378 if (file->f_op->read) +379 ret = file->f_op->read(file, buf, count, pos); +(gdb) +(gdb) trace 379 +Tracepoint 1 at 0xffffffff81173ba5: file /home/teawater/kernel/linux/fs/read_write.c, line 379. +(gdb) actions +Enter actions for tracepoint 1, one per line. +End with a line saying just "end". +>collect file->f_op->read +>end +(gdb) tstart +(gdb) tstop +(gdb) tfind +(gdb) p file->f_op->read +$5 = (ssize_t (*)(struct file *, char *, size_t, loff_t *)) 0xffffffff81173190 +#Then you know file->f_op->read point to do_sync_read. + +If the debug info of the function pointer is optimized out +---------------------------------------------------------- +You can use tracepoint step to handle it. For example: +#Find out which instrunction that it is called. +(gdb) disassemble /rm vfs_read +379 ret = file->f_op->read(file, buf, count, pos); + 0xffffffff81173ba5 <+181>: 48 89 da mov %rbx,%rdx + 0xffffffff81173ba8 <+184>: 4c 89 e9 mov %r13,%rcx + 0xffffffff81173bab <+187>: 4c 89 e6 mov %r12,%rsi + 0xffffffff81173bae <+190>: 4c 89 f7 mov %r14,%rdi + 0xffffffff81173bb1 <+193>: ff d0 callq *%rax + 0xffffffff81173bb3 <+195>: 48 89 c3 mov %rax,%rbx +(gdb) trace *0xffffffff81173bb1 +Tracepoint 1 at 0xffffffff81173bb1: file /home/teawater/kernel/linux/fs/read_write.c, line 379. +(gdb) actions +Enter actions for tracepoint 1, one per line. +End with a line saying just "end". +>while-stepping 1 + >collect $reg + >end +>end +(gdb) tstart +(gdb) tstop +(gdb) tfind +#0 tty_read (file=0xffff88006ca74900, buf=0xb6b7dc
, count=8176, + ppos=0xffff88006e197f48) at tty_io.c:960 +960 { +#Then you know file->f_op->read point to tty_read. + + + +How to use /sys/kernel/debug/gtpframe +------------------------------------- + +This interface supplies trace frame in tfile format (GDB can parse it) when +KGTP is stop. +Please goto "Offline debug" to get more info about how to use it. + + + +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 to debug. + + +In the debugged 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 to save the value to the trace frame and parse later, you can use +file "/sys/kernel/debug/gtpframe" that has the trace frame. Copy it to the PC +that has GDB. +Please note that some "cp" cannot handle it very well, please use +"cat /sys/kernel/debug/gtpframe > ./gtpframe" to copy it. + +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 , buf=0xffff880001e5bf38) + at readdir.c:24 +24 { +-------------------------------------------------------------------------------- + + + +How to use /sys/kernel/debug/gtpframe_pipe +------------------------------------------ + +This interface supplies same format trace frame with "gtpframe". +But it can work when KGTP is running. And it is a consumer like "trace_pipe". + + +Get the frame info with cat +--------------------------- + +sudo cat /sys/kernel/debug/gtpframe_pipe > g +Then all the trace frame will be saved in file "g". + + +Get the frame info with getframe +-------------------------------- + +KGTP package include a program "getframe" can help you save the trace frame to +files. +Following part is the help of it: + +------------------------------------------------------------------- +getframe -h +Get the trace frame of KGTP and save them in current +directory with tfile format. +Usage: ./getframe [option] + + -g n Set the minimum free size limit to n G. + When free size of current disk is smaller than n G, + ./getframe will exit (-q) or wait some seconds (-w). + The default value of it is 2 G. + + -q Quit when current disk is smaller than + minimum free size limit (-g). + + -w n Wait n seconds when current disk is smaller + than minimum free size limit (-g). + + -e n Set the entry number of each tfile to n. + The default value of it is 1000. + + -h Display this information. +------------------------------------------------------------------- + + +Get the frame info with GDB +--------------------------- + +---------------------------------------------------- +#connect to the interface +(gdb) target tfile /sys/kernel/debug/gtpframe_pipe +#Get one trace frame entry +(gdb) tfind 0 +Found trace frame 0, tracepoint 1 +#Get the next one +(gdb) tfind +Target failed to find requested trace frame. +(gdb) tfind 0 +Found trace frame 0, tracepoint 1 +---------------------------------------------------- +This way is better to work with python to parse Kernel. +I will introduce them in example. --- /dev/null +++ b/Documentation/gtp/quickstart.txt @@ -0,0 +1,250 @@ + Linux Kernel GDB tracepoint module (KGTP) quick start + ===================================================== + By Hui Zhu + https://code.google.com/p/kgtp/wiki/Quickstart + 2011-09-12 + +Table of contents +----------------- +Ubuntu +Fedora + + + + +Ubuntu +------ + +Install GDB for KGTP +-------------------- + +This GDB's filename is different with the current GDB that you are using. +So please don't worry that it affect current GDB that your are using. + +For the Ubuntu 10.04 or later, running the following line at a terminal: +sudo add-apt-repository ppa:teawater/gdb-$(lsb_release -rs) +sudo apt-get update +sudo apt-get install gdb-release + +For the Ubuntu older than 10.04, please go to https://code.google.com/p/gdbt/ +get howto install GDB for KGTP from source. + + + +Install Linux kernel packages that KGTP need +-------------------------------------------- + +Please ignore this section if the Linux kernel of your system is built by +yourself. + +Install the Linux kernel debug image +------------------------------------ + +Add debug source to the sources list of Ubuntu +---------------------------------------------- + +Create an /etc/apt/sources.list.d/ddebs.list by running the following line at +a terminal: +echo "deb http://ddebs.ubuntu.com $(lsb_release -cs) main restricted universe multiverse" | \ +sudo tee -a /etc/apt/sources.list.d/ddebs.list + +Stable releases (not alphas and betas) require three more lines adding to the +same file, which is done by the following terminal command: +echo "deb http://ddebs.ubuntu.com $(lsb_release -cs)-updates main restricted universe multiverse +deb http://ddebs.ubuntu.com $(lsb_release -cs)-security main restricted universe multiverse +deb http://ddebs.ubuntu.com $(lsb_release -cs)-proposed main restricted universe multiverse" | \ +sudo tee -a /etc/apt/sources.list.d/ddebs.list + +Import the debug symbol archive signing key: +sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 428D7C01 + +Then run: +sudo apt-get update + +Get Linux kernel debug image +---------------------------- +sudo apt-get install linux-image-$(uname -r)-dbgsym + + +Install the Linux kernel headers +-------------------------------- + +Please ignore this section if the Linux kernel of your system is built by +yourself. +sudo apt-get install linux-headers-generic + + +Install the Linux kernel source +------------------------------- + +Install the source package: +sudo apt-get install linux-source + +Uncompress the source package: +sudo mkdir -p /build/buildd/ +sudo tar vxjf /usr/src/linux-source-$(uname -r | sed 's/-.*//').tar.bz2 -C /build/buildd/ +sudo mv /build/buildd/linux-source-$(uname -r | sed 's/-.*//') /build/buildd/linux-$(uname -r | sed 's/-.*//') + + + +Install GCC +----------- + +sudo apt-get install gcc + + + +Get and build KGTP +------------------ + +Install subversion: +sudo apt-get install subversion + +Get the source of KGTP with subversion and put it to directory "kgtp": +svn checkout https://kgtp.googlecode.com/svn/trunk kgtp + +Build KGTP: +cd kgtp +make + + + +Use KGTP +-------- + +Mount the sysfs and debugfs: +sudo mount -t sysfs none /sys/ +sudo mount -t debugfs none /sys/kernel/debug/ + +Insert the KGTP module to the current Linux Kernel: +cd kgtp +sudo insmod gtp.ko + +Use GDB connect to KGTP: +sudo gdb-release /usr/lib/debug/boot/vmlinux-$(uname -r) +(gdb) target remote /sys/kernel/debug/gtp + +Do a very simple trace: +(gdb) trace vfs_readdir +Tracepoint 1 at 0xc02289f0: file /build/buildd/linux-2.6.35/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 +vmlinux-2.6.35-30-generic +(gdb) tstop +(gdb) tfind +Found trace frame 0, tracepoint 1 +#0 vfs_readdir (file=0x0, filler=0x163d8ae3, buf=0x18c0) at readdir.c:23 +23 { + + + +End +--- + +Now, you can begin to rock and roll your Linux kernel with KGTP and GDB. +Please go to see gtp.txt to get more message about howto use KGTP. + + + + +Fedora +------ + +Install GDB for KGTP +-------------------- + +Please go to https://code.google.com/p/gdbt/ get howto install GDB for KGTP +from source. + + + +Install Linux kernel packages that KGTP need +-------------------------------------------- + +Please ignore this section if the Linux kernel of your system is built +by yourself. + + +Install the Linux kernel debug image +------------------------------------ + +sudo yum --enablerepo=fedora-debuginfo install kernel-debuginfo + + +Install the Linux kernel devel package +-------------------------------------- + +sudo yum install kernel-devel-$(uname -r) + + + +Install GCC +----------- + +sudo yum install gcc + + + +Get and build KGTP +------------------ + +Install subversion: +sudo yum install subversion + +Get the source of KGTP with subversion and put it to directory "kgtp": +svn checkout https://kgtp.googlecode.com/svn/trunk kgtp + +Build KGTP: +cd kgtp +make + + + +Use KGTP +-------- + +Mount the sysfs and debug fs: +sudo mount -t sysfs none /sys/ +sudo mount -t debugfs none /sys/kernel/debug/ + +Insert the KGTP module to the current Linux Kernel: +cd kgtp +sudo insmod gtp.ko + +Use GDB connect to KGTP: +sudo gdb-release /usr/lib/debug/lib/modules/$(uname -r)/vmlinux +(gdb) target remote /sys/kernel/debug/gtp + +Do a very simple trace: +(gdb) trace vfs_readdir +Tracepoint 1 at 0xffffffff8110ec9b: file 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 +co.patch getframe getmod.c gtp.mod.c gtp.txt perf_event.c +dkms.conf getframe.c getmod.py gtp.mod.o Makefile ring_buffer.c +dkms_others_install.sh getgtprsp.pl gtp.c gtp.o modules.order ring_buffer.h +dkms_others_uninstall.sh getmod gtp.ko gtp.patch Module.symvers +(gdb) tstop +(gdb) tfind +Found trace frame 0, tracepoint 1 +#0 vfs_readdir (file=0xffff880019d3df00, filler=0xffffffff8110eb16 , buf=0xffff880003b39f38) + at fs/readdir.c:23 +23 { + + + +End +--- + +Now, you can begin to rock and roll your Linux kernel with KGTP and GDB. +Please go to HOWTO to get more message about howto use KGTP. --- /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/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -1146,9 +1146,12 @@ extern void perf_output_copy(struct perf const void *buf, unsigned int len); extern int perf_swevent_get_recursion_context(void); extern void perf_swevent_put_recursion_context(int rctx); +extern int __perf_event_enable(void *info); +extern int __perf_event_disable(void *info); extern void perf_event_enable(struct perf_event *event); extern void perf_event_disable(struct perf_event *event); extern void perf_event_task_tick(void); +extern void perf_event_set(struct perf_event *event, u64 val); #else static inline void perf_event_task_sched_in(struct task_struct *prev, --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -1235,7 +1235,7 @@ retry: /* * Cross CPU call to disable a performance event */ -static int __perf_event_disable(void *info) +int __perf_event_disable(void *info) { struct perf_event *event = info; struct perf_event_context *ctx = event->ctx; @@ -1272,6 +1272,7 @@ static int __perf_event_disable(void *in return 0; } +EXPORT_SYMBOL_GPL(__perf_event_disable); /* * Disable a event. @@ -1683,7 +1684,7 @@ static void __perf_event_mark_enabled(st /* * Cross CPU call to enable a performance event */ -static int __perf_event_enable(void *info) +int __perf_event_enable(void *info) { struct perf_event *event = info; struct perf_event_context *ctx = event->ctx; @@ -1747,6 +1748,7 @@ unlock: return 0; } +EXPORT_SYMBOL_GPL(__perf_event_enable); /* * Enable a event. @@ -3065,6 +3067,14 @@ static void perf_event_reset(struct perf perf_event_update_userpage(event); } +void perf_event_set(struct perf_event *event, u64 val) +{ + (void)perf_event_read(event); + local64_set(&event->count, val); + perf_event_update_userpage(event); +} +EXPORT_SYMBOL_GPL(perf_event_set); + /* * Holding the top-level event's child_mutex means that any * descendant process that has inherited this event will block --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1268,6 +1268,16 @@ config ASYNC_RAID6_TEST If unsure, say N. +config GTP + tristate "GDB tracepoint support" + depends on X86 || ARM || MIPS + select KPROBES + select DEBUG_FS + ---help--- + Supply GDB tracepoint interface in /sys/kernel/debug/gtp. + See Documentation/trace/gtp.txt or + https://code.google.com/p/kgtp/wiki/HOWTO for more info. + source "samples/Kconfig" source "lib/Kconfig.kgdb" --- a/lib/Makefile +++ b/lib/Makefile @@ -123,6 +123,8 @@ obj-$(CONFIG_SIGNATURE) += digsig.o obj-$(CONFIG_CLZ_TAB) += clz_tab.o +obj-$(CONFIG_GTP) += gtp.o + hostprogs-y := gen_crc32table clean-files := crc32table.h @@ -133,3 +135,5 @@ quiet_cmd_crc32 = GEN $@ $(obj)/crc32table.h: $(obj)/gen_crc32table $(call cmd,crc32) + +gtp.o: gtp_rb.c --- /dev/null +++ b/lib/gtp.c @@ -0,0 +1,8916 @@ +/* + * 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) KGTP team (https://code.google.com/p/kgtp/), 2010, 2011, 2012 + * + */ + +/* If "* 10" means that this is not a release version. */ +#define GTP_VERSION (20120319) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GTP_RB + +/* If define GTP_FRAME_SIMPLE, KGTP will use simple frame allocer replace + ring buffer. */ +/* #define GTP_FRAME_SIMPLE */ + +/* If define GTP_CLOCK_CYCLE, $clock will return rdtscll. */ +/* #define GTP_CLOCK_CYCLE */ + +#ifdef GTP_FRAME_SIMPLE +/* This define is for simple frame alloc record, then we can get how many + memory are weste by FRAME_ALIGN. */ +/* #define FRAME_ALLOC_RECORD */ +#endif + +#define KERN_NULL + +#ifdef GTPDEBUG +#define GTP_DEBUG KERN_WARNING +#endif + +/* #define GTP_DEBUG_V */ + +#define GTP_RW_MAX 16384 +#define GTP_RW_BUFP_MAX (GTP_RW_MAX - 4 - gtp_rw_size) + +#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 +#define FID_PAGE_BEGIN 5 +#define FID_PAGE_END 6 + +/* GTP_FRAME_SIZE must align with FRAME_ALIGN_SIZE if use GTP_FRAME_SIMPLE. */ +#define GTP_FRAME_SIZE 5242880 +#if defined(GTP_FRAME_SIMPLE) || defined(GTP_RB) +#define FRAME_ALIGN_SIZE sizeof(unsigned int) +#define FRAME_ALIGN(x) ((x + FRAME_ALIGN_SIZE - 1) \ + & (~(FRAME_ALIGN_SIZE - 1))) +#endif +#ifdef GTP_FRAME_SIMPLE +#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)) +#endif +#ifdef GTP_RB +#define GTP_FRAME_HEAD_SIZE (FID_SIZE + sizeof(u64) + sizeof(ULONGEST)) +#define GTP_FRAME_PAGE_BEGIN_SIZE (FID_SIZE + sizeof(u64)) +#endif +#ifdef GTP_FTRACE_RING_BUFFER +#define GTP_FRAME_HEAD_SIZE (FID_SIZE + sizeof(ULONGEST)) +#endif +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) +#define GTP_FRAME_REG_SIZE (FID_SIZE + sizeof(struct pt_regs)) +#define GTP_FRAME_MEM_SIZE (FID_SIZE + sizeof(struct gtp_frame_mem)) +#define GTP_FRAME_VAR_SIZE (FID_SIZE + sizeof(struct gtp_frame_var)) +#endif +#include + +#define INT2CHAR(h) ((h) > 9 ? (h) + 'a' - 10 : (h) + '0') + +enum { + op_check_add = 0xe5, + op_check_sub, + op_check_mul, + op_check_div_signed, + op_check_div_unsigned, + op_check_rem_signed, + op_check_rem_unsigned, + op_check_lsh, + op_check_rsh_signed, + op_check_rsh_unsigned, + op_check_trace, + op_check_bit_and, + op_check_bit_or, + op_check_bit_xor, + op_check_equal, + op_check_less_signed, + op_check_less_unsigned, + op_check_pop, + op_check_swap, + op_check_if_goto, + op_check_printf, /* XXX: still not used. */ + + op_special_getv = 0xfa, + op_special_setv, + op_special_tracev, + + op_trace_printk = 0xfd, + op_trace_quick_printk, + op_tracev_printk, +}; + +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 { + int kpreg; + int no_self_trace; + int nopass; + int have_printk; + ULONGEST num; + struct action *cond; + struct action *action_list; + int step; + struct action *step_action_list; + atomic_t current_pass; + struct gtpsrc *printk_str; + enum gtp_stop_type reason; + struct tasklet_struct tasklet; + struct work_struct work; + struct gtp_entry *next; + struct kretprobe kp; + int disable; + int is_kretprobe; + ULONGEST addr; + ULONGEST pass; + struct gtpsrc *src; +}; + +#ifdef CONFIG_PERF_EVENTS +struct pe_tv_s { + struct pe_tv_s *pc_next; + int en; + struct perf_event *event; + int cpu; + u64 val; + u64 enabled; /* The perf inside timer */ + u64 running; /* The perf inside timer */ + char *name; + struct perf_event_attr attr; +}; +#endif + +enum pe_tv_id { + pe_tv_unknown = 0, + pe_tv_cpu, + pe_tv_type, + pe_tv_config, + pe_tv_en, + pe_tv_val, + pe_tv_enabled, + pe_tv_running, +}; + +struct gtp_var { + struct gtp_var *next; + unsigned int num; + uint64_t val; + char *src; + struct gtp_var **per_cpu; +#ifdef CONFIG_PERF_EVENTS + enum pe_tv_id ptid; + struct pe_tv_s *pts; +#endif +}; + +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 pid_t gtp_gtp_pid; +static unsigned int gtp_gtp_pid_count; +static pid_t gtp_gtpframe_pid; +static unsigned int gtp_gtpframe_pid_count; +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) +static pid_t gtp_gtpframe_pipe_pid; +#endif + +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 int gtp_cpu_number; + +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; + +enum { + GTP_VAR_SPECIAL_MIN = 1, + GTP_VAR_VERSION_ID = GTP_VAR_SPECIAL_MIN, + GTP_VAR_CPU_ID, + GTP_VAR_CURRENT_TASK_ID, + GTP_VAR_CURRENT_THREAD_INFO_ID, + GTP_VAR_CLOCK_ID, + GTP_VAR_COOKED_CLOCK_ID, +#ifdef CONFIG_X86 + GTP_VAR_RDTSC_ID, + GTP_VAR_COOKED_RDTSC_ID, +#endif +#ifdef GTP_RB + GTP_VAR_GTP_RB_DISCARD_PAGE_NUMBER, +#endif + GTP_VAR_PRINTK_TMP_ID, + GTP_VAR_PRINTK_LEVEL_ID, + GTP_VAR_PRINTK_FORMAT_ID, + GTP_VAR_DUMP_STACK_ID, + GTP_VAR_NO_SELF_TRACE_ID, + GTP_VAR_CPU_NUMBER_ID, + GTP_VAR_PC_PE_EN_ID, + GTP_VAR_KRET_ID, + GTP_VAR_XTIME_SEC_ID, + GTP_VAR_XTIME_NSEC_ID, + GTP_VAR_IGNORE_ERROR_ID, + GTP_VAR_LAST_ERRNO_ID, + GTP_VAR_HARDIRQ_COUNT_ID, + GTP_VAR_SOFTIRQ_COUNT_ID, + GTP_VAR_IRQ_COUNT_ID, + GTP_VAR_PIPE_TRACE_ID, + GTP_VAR_CURRENT_TASK_PID_ID, + GTP_VAR_SPECIAL_MAX = GTP_VAR_CURRENT_TASK_PID_ID, +}; + +#define PREV_VAR NULL + +static struct gtp_var gtp_var_version = { + .next = PREV_VAR, + .num = GTP_VAR_VERSION_ID, + .src = "0:1:6774705f76657273696f6e", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_version) + +static struct gtp_var gtp_var_cpu_id = { + .next = PREV_VAR, + .num = GTP_VAR_CPU_ID, + .src = "0:1:6370755f6964", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_cpu_id) + +static struct gtp_var gtp_var_current_task = { + .next = PREV_VAR, + .num = GTP_VAR_CURRENT_TASK_ID, + .src = "0:1:63757272656e745f7461736b", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_current_task) + +static struct gtp_var gtp_var_current_task_pid = { + .next = PREV_VAR, + .num = GTP_VAR_CURRENT_TASK_PID_ID, + .src = "0:1:63757272656e745f7461736b5f706964", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_current_task_pid) + +static struct gtp_var gtp_var_current_thread_info = { + .next = PREV_VAR, + .num = GTP_VAR_CURRENT_THREAD_INFO_ID, + .src = "0:1:63757272656e745f7468726561645f696e666f", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_current_thread_info) + +static struct gtp_var gtp_var_clock = { + .next = PREV_VAR, + .num = GTP_VAR_CLOCK_ID, + .src = "0:1:636c6f636b", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_clock) + +static struct gtp_var gtp_var_cooked_clock = { + .next = PREV_VAR, + .num = GTP_VAR_COOKED_CLOCK_ID, + .src = "0:1:636f6f6b65645f636c6f636b", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_cooked_clock) + +#ifdef CONFIG_X86 +static struct gtp_var gtp_var_rdtsc = { + .next = PREV_VAR, + .num = GTP_VAR_RDTSC_ID, + .src = "0:1:7264747363", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_rdtsc) +static struct gtp_var gtp_var_cooked_rdtsc = { + .next = PREV_VAR, + .num = GTP_VAR_COOKED_RDTSC_ID, + .src = "0:1:636f6f6b65645f7264747363", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_cooked_rdtsc) +#endif + +#ifdef GTP_RB +static struct gtp_var gtp_var_gtp_rb_discard_page_number = { + .next = PREV_VAR, + .num = GTP_VAR_GTP_RB_DISCARD_PAGE_NUMBER, + .src = "0:1:646973636172645f706167655f6e756d", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_gtp_rb_discard_page_number) +#endif + +static struct gtp_var gtp_var_printk_tmp = { + .next = PREV_VAR, + .num = GTP_VAR_PRINTK_TMP_ID, + .src = "0:1:7072696e746b5f746d70", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_printk_tmp) + +static struct gtp_var gtp_var_printk_level = { + .next = PREV_VAR, + .num = GTP_VAR_PRINTK_LEVEL_ID, + .src = "8:1:7072696e746b5f6c6576656c", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_printk_level) + +static struct gtp_var gtp_var_printk_format = { + .next = PREV_VAR, + .num = GTP_VAR_PRINTK_FORMAT_ID, + .src = "0:1:7072696e746b5f666f726d6174", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_printk_format) + +static struct gtp_var gtp_var_dump_stack = { + .next = PREV_VAR, + .num = GTP_VAR_DUMP_STACK_ID, + .src = "0:1:64756d705f737461636b", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_dump_stack) + +static struct gtp_var gtp_var_no_self_trace = { + .next = PREV_VAR, + .num = GTP_VAR_NO_SELF_TRACE_ID, + .src = "0:1:6e6f5f73656c665f7472616365", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_no_self_trace) + +static struct gtp_var gtp_var_pipe_trace = { + .next = PREV_VAR, + .num = GTP_VAR_PIPE_TRACE_ID, + .src = "0:1:706970655f7472616365", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_pipe_trace) + +static struct gtp_var gtp_var_cpu_number = { + .next = PREV_VAR, + .num = GTP_VAR_CPU_NUMBER_ID, + .src = "0:1:6370755f6e756d626572", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_cpu_number) + +static struct gtp_var gtp_var_pc_pe_en = { + .next = PREV_VAR, + .num = GTP_VAR_PC_PE_EN_ID, + .src = "0:1:70635f70655f656e", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_pc_pe_en) + +static struct gtp_var gtp_var_kret = { + .next = PREV_VAR, + .num = GTP_VAR_KRET_ID, + .src = "0:1:6b726574", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_kret) + +static struct gtp_var gtp_var_xtime_sec = { + .next = PREV_VAR, + .num = GTP_VAR_XTIME_SEC_ID, + .src = "0:1:7874696d655f736563", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_xtime_sec) + +static struct gtp_var gtp_var_xtime_nsec = { + .next = PREV_VAR, + .num = GTP_VAR_XTIME_NSEC_ID, + .src = "0:1:7874696d655f6e736563", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_xtime_nsec) + +static struct gtp_var gtp_var_ignore_error = { + .next = PREV_VAR, + .num = GTP_VAR_IGNORE_ERROR_ID, + .src = "0:1:69676e6f72655f6572726f72", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_ignore_error) + +static struct gtp_var gtp_var_last_errno = { + .next = PREV_VAR, + .num = GTP_VAR_LAST_ERRNO_ID, + .src = "0:1:6c6173745f6572726e6f", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_last_errno) + +static struct gtp_var gtp_var_hardirq_count = { + .next = PREV_VAR, + .num = GTP_VAR_HARDIRQ_COUNT_ID, + .src = "0:1:686172646972715f636f756e74", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_hardirq_count) + +static struct gtp_var gtp_var_softirq_count = { + .next = PREV_VAR, + .num = GTP_VAR_SOFTIRQ_COUNT_ID, + .src = "0:1:736f66746972715f636f756e74", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR +#define PREV_VAR (>p_var_softirq_count) + +static struct gtp_var gtp_var_irq_count = { + .next = PREV_VAR, + .num = GTP_VAR_IRQ_COUNT_ID, + .src = "0:1:6972715f636f756e74", + .per_cpu = NULL, +#ifdef CONFIG_PERF_EVENTS + .ptid = 0, + .pts = NULL, +#endif +}; +#undef PREV_VAR + +#define GTP_VAR_LIST_FIRST (>p_var_irq_count) + +#define GTP_VAR_IS_SPECIAL(x) ((x) >= GTP_VAR_SPECIAL_MIN \ + && (x) <= GTP_VAR_SPECIAL_MAX) +#ifdef GTP_RB +#define GTP_VAR_AUTO_TRACEV(x) ((x) == GTP_VAR_CPU_ID) +#endif +#if defined(GTP_FRAME_SIMPLE) || defined(GTP_FTRACE_RING_BUFFER) +#define GTP_VAR_AUTO_TRACEV(x) ((x) == GTP_VAR_CLOCK_ID \ + || (x) == GTP_VAR_CPU_ID) +#endif + +/* Current number in the frame. */ +static int gtp_frame_current_num; +/* Current tracepoint id. */ +static ULONGEST gtp_frame_current_tpe; +static atomic_t gtp_frame_create; +static char *gtp_frame_file; +static size_t gtp_frame_file_size; +static DECLARE_WAIT_QUEUE_HEAD(gtpframe_wq); +#ifdef GTP_FRAME_SIMPLE +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; +#endif +#ifdef GTP_FTRACE_RING_BUFFER +static struct ring_buffer *gtp_frame; +static struct ring_buffer_iter *gtp_frame_iter[NR_CPUS]; +static int gtp_frame_current_cpu; +static u64 gtp_frame_current_clock; +#endif + +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) +static DECLARE_WAIT_QUEUE_HEAD(gtpframe_pipe_wq); +static atomic_t gtpframe_pipe_wq_v; +static struct tasklet_struct gtpframe_pipe_wq_tasklet; +#endif + +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 DEFINE_PER_CPU(u64, rdtsc_current); +static DEFINE_PER_CPU(u64, rdtsc_offset); +#endif +static DEFINE_PER_CPU(u64, local_clock_current); +static DEFINE_PER_CPU(u64, local_clock_offset); + +static uint64_t gtp_start_last_errno; +static int gtp_start_ignore_error; + +static int gtp_pipe_trace; + +#ifdef GTP_RB +#include "gtp_rb.c" +#endif + +#define GTP_LOCAL_CLOCK local_clock() + +struct gtp_realloc_s { + char *buf; + size_t size; + size_t real_size; +}; + +static int +gtp_realloc_alloc(struct gtp_realloc_s *grs, size_t size) +{ + if (size) { + grs->buf = vmalloc(size); + if (!grs->buf) + return -ENOMEM; + } else + grs->buf = NULL; + + grs->size = 0; + grs->real_size = size; + + return 0; +} + +static char * +gtp_realloc(struct gtp_realloc_s *grs, size_t size, int is_end) +{ + char *tmp; + + if (unlikely((grs->real_size < grs->size + size) + || (is_end && grs->real_size != grs->size + size))) { + grs->real_size = grs->size + size; + if (!is_end) + grs->real_size += 100; + + tmp = vmalloc(grs->real_size); + if (!tmp) { + vfree(grs->buf); + memset(grs, 0, sizeof(struct gtp_realloc_s)); + return NULL; + } + + memcpy(tmp, grs->buf, grs->size); + if (grs->buf) + vfree(grs->buf); + grs->buf = tmp; + } + + grs->size += size; + return grs->buf + grs->size - size; +} + +static int +gtp_realloc_str(struct gtp_realloc_s *grs, char *str, int is_end) +{ + char *wbuf; + int str_len = strlen(str); + + wbuf = gtp_realloc(grs, str_len, is_end); + if (wbuf == NULL) + return -ENOMEM; + + memcpy(wbuf, str, str_len); + + return 0; +} + +static inline void +gtp_realloc_reset(struct gtp_realloc_s *grs) +{ + grs->size = 0; +} + +static inline int +gtp_realloc_is_alloced(struct gtp_realloc_s *grs) +{ + return (grs->buf != NULL); +} + +static inline int +gtp_realloc_is_empty(struct gtp_realloc_s *grs) +{ + return (grs->size == 0); +} + +static inline void +gtp_realloc_sub_size(struct gtp_realloc_s *grs, size_t size) +{ + grs->size -= size; +} + +#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)®s->sp; + break; + case 5: + ret = regs->bp; + break; + case 6: + ret = regs->si; + break; + case 7: + ret = regs->di; + break; + case 8: + if (tpe->step) + ret = regs->ip; + else + 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: + if (tpe->step) + ret = regs->ip; + else + 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_V + printk(GTP_DEBUG_V "gtp_regs2ascii: ax = 0x%x\n", + (unsigned int) regs->ax); + printk(GTP_DEBUG_V "gtp_regs2ascii: cx = 0x%x\n", + (unsigned int) regs->cx); + printk(GTP_DEBUG_V "gtp_regs2ascii: dx = 0x%x\n", + (unsigned int) regs->dx); + printk(GTP_DEBUG_V "gtp_regs2ascii: bx = 0x%x\n", + (unsigned int) regs->bx); + printk(GTP_DEBUG_V "gtp_regs2ascii: sp = 0x%x\n", + (unsigned int) regs->sp); + printk(GTP_DEBUG_V "gtp_regs2ascii: bp = 0x%x\n", + (unsigned int) regs->bp); + printk(GTP_DEBUG_V "gtp_regs2ascii: si = 0x%x\n", + (unsigned int) regs->si); + printk(GTP_DEBUG_V "gtp_regs2ascii: di = 0x%x\n", + (unsigned int) regs->di); + printk(GTP_DEBUG_V "gtp_regs2ascii: ip = 0x%x\n", + (unsigned int) regs->ip); + printk(GTP_DEBUG_V "gtp_regs2ascii: flags = 0x%x\n", + (unsigned int) regs->flags); + printk(GTP_DEBUG_V "gtp_regs2ascii: cs = 0x%x\n", + (unsigned int) regs->cs); + printk(GTP_DEBUG_V "gtp_regs2ascii: ss = 0x%x\n", + (unsigned int) regs->ss); + printk(GTP_DEBUG_V "gtp_regs2ascii: ds = 0x%x\n", + (unsigned int) regs->ds); + printk(GTP_DEBUG_V "gtp_regs2ascii: es = 0x%x\n", + (unsigned int) regs->es); + printk(GTP_DEBUG_V "gtp_regs2ascii: fs = 0x%x\n", + (unsigned int) regs->fs); + printk(GTP_DEBUG_V "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_V + printk(GTP_DEBUG_V "gtp_regs2ascii: ax = 0x%lx\n", regs->ax); + printk(GTP_DEBUG_V "gtp_regs2ascii: bx = 0x%lx\n", regs->bx); + printk(GTP_DEBUG_V "gtp_regs2ascii: cx = 0x%lx\n", regs->cx); + printk(GTP_DEBUG_V "gtp_regs2ascii: dx = 0x%lx\n", regs->dx); + printk(GTP_DEBUG_V "gtp_regs2ascii: si = 0x%lx\n", regs->si); + printk(GTP_DEBUG_V "gtp_regs2ascii: di = 0x%lx\n", regs->di); + printk(GTP_DEBUG_V "gtp_regs2ascii: bp = 0x%lx\n", regs->bp); + printk(GTP_DEBUG_V "gtp_regs2ascii: sp = 0x%lx\n", regs->sp); + printk(GTP_DEBUG_V "gtp_regs2ascii: r8 = 0x%lx\n", regs->r8); + printk(GTP_DEBUG_V "gtp_regs2ascii: r9 = 0x%lx\n", regs->r9); + printk(GTP_DEBUG_V "gtp_regs2ascii: r10 = 0x%lx\n", regs->r10); + printk(GTP_DEBUG_V "gtp_regs2ascii: r11 = 0x%lx\n", regs->r11); + printk(GTP_DEBUG_V "gtp_regs2ascii: r12 = 0x%lx\n", regs->r12); + printk(GTP_DEBUG_V "gtp_regs2ascii: r13 = 0x%lx\n", regs->r13); + printk(GTP_DEBUG_V "gtp_regs2ascii: r14 = 0x%lx\n", regs->r14); + printk(GTP_DEBUG_V "gtp_regs2ascii: r15 = 0x%lx\n", regs->r15); + printk(GTP_DEBUG_V "gtp_regs2ascii: ip = 0x%lx\n", regs->ip); + printk(GTP_DEBUG_V "gtp_regs2ascii: flags = 0x%lx\n", regs->flags); + printk(GTP_DEBUG_V "gtp_regs2ascii: cs = 0x%lx\n", regs->cs); + printk(GTP_DEBUG_V "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_V_V + printk(GTP_DEBUG_V_V "gtp_regs2ascii: ax = 0x%x\n", + (unsigned int) regs->ax); + printk(GTP_DEBUG_V_V "gtp_regs2ascii: cx = 0x%x\n", + (unsigned int) regs->cx); + printk(GTP_DEBUG_V_V "gtp_regs2ascii: dx = 0x%x\n", + (unsigned int) regs->dx); + printk(GTP_DEBUG_V_V "gtp_regs2ascii: bx = 0x%x\n", + (unsigned int) regs->bx); + printk(GTP_DEBUG_V_V "gtp_regs2ascii: sp = 0x%x\n", + (unsigned int) regs->sp); + printk(GTP_DEBUG_V_V "gtp_regs2ascii: bp = 0x%x\n", + (unsigned int) regs->bp); + printk(GTP_DEBUG_V_V "gtp_regs2ascii: si = 0x%x\n", + (unsigned int) regs->si); + printk(GTP_DEBUG_V_V "gtp_regs2ascii: di = 0x%x\n", + (unsigned int) regs->di); + printk(GTP_DEBUG_V_V "gtp_regs2ascii: ip = 0x%x\n", + (unsigned int) regs->ip); + printk(GTP_DEBUG_V_V "gtp_regs2ascii: flags = 0x%x\n", + (unsigned int) regs->flags); + printk(GTP_DEBUG_V_V "gtp_regs2ascii: cs = 0x%x\n", + (unsigned int) regs->cs); + printk(GTP_DEBUG_V_V "gtp_regs2ascii: ss = 0x%x\n", + (unsigned int) regs->ss); + printk(GTP_DEBUG_V_V "gtp_regs2ascii: ds = 0x%x\n", + (unsigned int) regs->ds); + printk(GTP_DEBUG_V_V "gtp_regs2ascii: es = 0x%x\n", + (unsigned int) regs->es); + printk(GTP_DEBUG_V_V "gtp_regs2ascii: fs = 0x%x\n", + (unsigned int) regs->fs); + printk(GTP_DEBUG_V_V "gtp_regs2ascii: gs = 0x%x\n", + (unsigned int) regs->gs); +#endif + memcpy(buf, ®s->ax, 4); + buf += 4; + memcpy(buf, ®s->cx, 4); + buf += 4; + memcpy(buf, ®s->dx, 4); + buf += 4; + memcpy(buf, ®s->bx, 4); + buf += 4; + memcpy(buf, ®s->sp, 4); + buf += 4; + memcpy(buf, ®s->bp, 4); + buf += 4; + memcpy(buf, ®s->si, 4); + buf += 4; + memcpy(buf, ®s->di, 4); + buf += 4; + memcpy(buf, ®s->ip, 4); + buf += 4; + memcpy(buf, ®s->flags, 4); + buf += 4; + memcpy(buf, ®s->cs, 4); + buf += 4; + memcpy(buf, ®s->ss, 4); + buf += 4; + memcpy(buf, ®s->ds, 4); + buf += 4; + memcpy(buf, ®s->es, 4); + buf += 4; + memcpy(buf, ®s->fs, 4); + buf += 4; + memcpy(buf, ®s->gs, 4); + buf += 4; +#else +#ifdef GTP_DEBUG_V + printk(GTP_DEBUG_V "gtp_regs2ascii: ax = 0x%lx\n", regs->ax); + printk(GTP_DEBUG_V "gtp_regs2ascii: bx = 0x%lx\n", regs->bx); + printk(GTP_DEBUG_V "gtp_regs2ascii: cx = 0x%lx\n", regs->cx); + printk(GTP_DEBUG_V "gtp_regs2ascii: dx = 0x%lx\n", regs->dx); + printk(GTP_DEBUG_V "gtp_regs2ascii: si = 0x%lx\n", regs->si); + printk(GTP_DEBUG_V "gtp_regs2ascii: di = 0x%lx\n", regs->di); + printk(GTP_DEBUG_V "gtp_regs2ascii: bp = 0x%lx\n", regs->bp); + printk(GTP_DEBUG_V "gtp_regs2ascii: sp = 0x%lx\n", regs->sp); + printk(GTP_DEBUG_V "gtp_regs2ascii: r8 = 0x%lx\n", regs->r8); + printk(GTP_DEBUG_V "gtp_regs2ascii: r9 = 0x%lx\n", regs->r9); + printk(GTP_DEBUG_V "gtp_regs2ascii: r10 = 0x%lx\n", regs->r10); + printk(GTP_DEBUG_V "gtp_regs2ascii: r11 = 0x%lx\n", regs->r11); + printk(GTP_DEBUG_V "gtp_regs2ascii: r12 = 0x%lx\n", regs->r12); + printk(GTP_DEBUG_V "gtp_regs2ascii: r13 = 0x%lx\n", regs->r13); + printk(GTP_DEBUG_V "gtp_regs2ascii: r14 = 0x%lx\n", regs->r14); + printk(GTP_DEBUG_V "gtp_regs2ascii: r15 = 0x%lx\n", regs->r15); + printk(GTP_DEBUG_V "gtp_regs2ascii: ip = 0x%lx\n", regs->ip); + printk(GTP_DEBUG_V "gtp_regs2ascii: flags = 0x%lx\n", regs->flags); + printk(GTP_DEBUG_V "gtp_regs2ascii: cs = 0x%lx\n", regs->cs); + printk(GTP_DEBUG_V "gtp_regs2ascii: ss = 0x%lx\n", regs->ss); +#endif + memcpy(buf, ®s->ax, 8); + buf += 8; + memcpy(buf, ®s->bx, 8); + buf += 8; + memcpy(buf, ®s->cx, 8); + buf += 8; + memcpy(buf, ®s->dx, 8); + buf += 8; + memcpy(buf, ®s->si, 8); + buf += 8; + memcpy(buf, ®s->di, 8); + buf += 8; + memcpy(buf, ®s->bp, 8); + buf += 8; + memcpy(buf, ®s->sp, 8); + buf += 8; + memcpy(buf, ®s->r8, 8); + buf += 8; + memcpy(buf, ®s->r9, 8); + buf += 8; + memcpy(buf, ®s->r10, 8); + buf += 8; + memcpy(buf, ®s->r11, 8); + buf += 8; + memcpy(buf, ®s->r12, 8); + buf += 8; + memcpy(buf, ®s->r13, 8); + buf += 8; + memcpy(buf, ®s->r14, 8); + buf += 8; + memcpy(buf, ®s->r15, 8); + buf += 8; + memcpy(buf, ®s->ip, 8); + buf += 8; + memcpy(buf, ®s->flags, 4); + buf += 4; + memcpy(buf, ®s->cs, 4); + buf += 4; + memcpy(buf, ®s->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_V + { + int i; + + for (i = 0; i < 32; i++) + printk(GTP_DEBUG_V "gtp_gdbrsp_g: r%d = 0x%lx\n", i, + regs->regs[i]); + } + printk(GTP_DEBUG_V "gtp_gdbrsp_g: status = 0x%lx\n", + regs->cp0_status); + printk(GTP_DEBUG_V "gtp_gdbrsp_g: lo = 0x%lx\n", regs->lo); + printk(GTP_DEBUG_V "gtp_gdbrsp_g: hi = 0x%lx\n", regs->hi); + printk(GTP_DEBUG_V "gtp_gdbrsp_g: badvaddr = 0x%lx\n", + regs->cp0_badvaddr); + printk(GTP_DEBUG_V "gtp_gdbrsp_g: cause = 0x%lx\n", regs->cp0_cause); + printk(GTP_DEBUG_V "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_V + { + int i; + + for (i = 0; i < 32; i++) + printk(GTP_DEBUG_V "gtp_gdbrsp_g: r%d = 0x%lx\n", i, + regs->regs[i]); + } + printk(GTP_DEBUG_V "gtp_gdbrsp_g: status = 0x%lx\n", + regs->cp0_status); + printk(GTP_DEBUG_V "gtp_gdbrsp_g: lo = 0x%lx\n", regs->lo); + printk(GTP_DEBUG_V "gtp_gdbrsp_g: hi = 0x%lx\n", regs->hi); + printk(GTP_DEBUG_V "gtp_gdbrsp_g: badvaddr = 0x%lx\n", + regs->cp0_badvaddr); + printk(GTP_DEBUG_V "gtp_gdbrsp_g: cause = 0x%lx\n", regs->cp0_cause); + printk(GTP_DEBUG_V "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, ®s->regs[i], REGSIZE); + buf += REGSIZE; + } + } + memcpy(buf, ®s->cp0_status, REGSIZE); + buf += REGSIZE; + memcpy(buf, ®s->lo, REGSIZE); + buf += REGSIZE; + memcpy(buf, ®s->hi, REGSIZE); + buf += REGSIZE; + memcpy(buf, ®s->cp0_badvaddr, REGSIZE); + buf += REGSIZE; + memcpy(buf, ®s->cp0_cause, REGSIZE); + buf += REGSIZE; + memcpy(buf, ®s->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_V + printk(GTP_DEBUG_V "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_V + printk(GTP_DEBUG_V "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_V + printk(GTP_DEBUG_V "gtp_gdbrsp_g: r%d = 0x%lx\n", + i, regs->uregs[i]); +#endif + memcpy(buf, ®s->uregs[i], 4); + buf += 4; + } + + /* f0-f7 fps */ + memset(buf, '\0', 100); + buf += 100; + +#ifdef GTP_DEBUG_V + printk(GTP_DEBUG_V "gtp_gdbrsp_g: cpsr = 0x%lx\n", regs->uregs[16]); +#endif + memcpy(buf, ®s->uregs[16], 4); + buf += 4; +} +#endif + +#ifdef CONFIG_PERF_EVENTS +static DEFINE_PER_CPU(int, pc_pe_list_all_disabled); +static DEFINE_PER_CPU(struct pe_tv_s *, pc_pe_list); + +static void +pc_pe_list_disable(void) +{ + struct pe_tv_s *ppl; + + if (__get_cpu_var(pc_pe_list_all_disabled)) + return; + + for (ppl = __get_cpu_var(pc_pe_list); ppl; ppl = ppl->pc_next) { + if (ppl->en) + __perf_event_disable(ppl->event); + } +} + +static void +pc_pe_list_enable(void) +{ + struct pe_tv_s *ppl; + + if (__get_cpu_var(pc_pe_list_all_disabled)) + return; + + for (ppl = __get_cpu_var(pc_pe_list); ppl; ppl = ppl->pc_next) { + if (ppl->en) + __perf_event_enable(ppl->event); + } +} + +static void +gtp_pc_pe_en(int enable) +{ + struct pe_tv_s *ppl = __get_cpu_var(pc_pe_list); + + for (ppl = __get_cpu_var(pc_pe_list); ppl; ppl = ppl->pc_next) + ppl->en = enable; + + __get_cpu_var(pc_pe_list_all_disabled) = !enable; +} + +static void +gtp_pe_set_en(struct pe_tv_s *pts, int enable) +{ + if (pts->event->cpu != smp_processor_id()) { + if (enable) + perf_event_enable(pts->event); + else + perf_event_disable(pts->event); + } + pts->en = enable; +} +#else +static void +gtp_pc_pe_en(int enable) +{ +} +#endif /* CONFIG_PERF_EVENTS */ + +#ifdef GTP_FRAME_SIMPLE +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_end; + break; + default: + return NULL; + break; + } + + return frame; +} +#endif + +#ifdef GTP_FRAME_SIMPLE +#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(>p_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; + if (tmp == gtp_frame_end) + gtp_frame_r_start = gtp_frame; + else + gtp_frame_r_start = tmp; + } + } + + ret = gtp_frame_w_start; + gtp_frame_w_start += size; + +out: + spin_unlock(>p_frame_lock); + return ret; +} +#endif + +struct gtp_trace_s { + struct gtp_entry *tpe; + struct pt_regs *regs; +#ifdef GTP_FRAME_SIMPLE + /* Next part set it to prev part. */ + char **next; +#endif +#ifdef GTP_FTRACE_RING_BUFFER + /* NULL means doesn't have head. */ + char *next; +#endif +#ifdef GTP_RB + /* rb of current cpu. */ + struct gtp_rb_s *next; + u64 id; +#endif + int step; + struct kretprobe_instance *ri; + int *run; + struct timespec xtime; + 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 + +#ifdef GTP_FTRACE_RING_BUFFER +#define GTP_FRAME_RINGBUFFER_ALLOC(size) \ + do { \ + rbe = ring_buffer_lock_reserve(gtp_frame, size); \ + if (rbe == NULL) { \ + gts->tpe->reason = gtp_stop_frame_full; \ + return -1; \ + } \ + tmp = ring_buffer_event_data(rbe); \ + } while (0) +#endif + +static struct gtp_var *gtp_gtp_var_array_find(unsigned int num); +static int gtp_collect_var(struct gtp_trace_s *gts, + struct gtp_var *tve); + +static int +gtp_action_head(struct gtp_trace_s *gts) +{ + char *tmp; + ULONGEST *trace_nump; +#ifdef GTP_FTRACE_RING_BUFFER + struct ring_buffer_event *rbe; +#endif + +#ifdef GTP_RB + gts->next = (struct gtp_rb_s *)this_cpu_ptr(gtp_rb); +#endif + + /* Get the head. */ +#ifdef GTP_FTRACE_RING_BUFFER + GTP_FRAME_RINGBUFFER_ALLOC(GTP_FRAME_HEAD_SIZE); +#endif +#if defined(GTP_FRAME_SIMPLE) || defined(GTP_RB) +#ifdef GTP_RB + GTP_RB_LOCK(gts->next); + tmp = gtp_rb_alloc(gts->next, GTP_FRAME_HEAD_SIZE, 0); +#endif +#ifdef GTP_FRAME_SIMPLE + tmp = gtp_frame_alloc(GTP_FRAME_HEAD_SIZE); +#endif + if (!tmp) { + gts->tpe->reason = gtp_stop_frame_full; + return -1; + } +#endif + + FID(tmp) = FID_HEAD; + tmp += FID_SIZE; + +#ifdef GTP_RB + gts->id = gtp_rb_clock(); + *(u64 *)tmp = gts->id; + tmp += sizeof(u64); +#endif + +#ifdef GTP_FRAME_SIMPLE + gts->next = (char **)tmp; + *(gts->next) = NULL; + tmp += sizeof(char *); +#endif + + trace_nump = (ULONGEST *)tmp; + *trace_nump = gts->tpe->num; + +#ifdef GTP_FTRACE_RING_BUFFER + ring_buffer_unlock_commit(gtp_frame, rbe); + gts->next = (char *)1; +#endif + +#ifdef GTP_FRAME_SIMPLE + /* Trace $cpu_id and $clock. */ + { + struct gtp_var *tve; + + tve = gtp_gtp_var_array_find(GTP_VAR_CLOCK_ID); + if (!tve) { + gts->tpe->reason = gtp_stop_agent_expr_code_error; + return -1; + } + if (gtp_collect_var(gts, tve)) + return -1; + tve = gtp_gtp_var_array_find(GTP_VAR_CPU_ID); + if (!tve) { + gts->tpe->reason = gtp_stop_agent_expr_code_error; + return -1; + } + if (gtp_collect_var(gts, tve)) + return -1; + } +#endif + + atomic_inc(>p_frame_create); + + return 0; +} + +static int +gtp_action_printk(struct gtp_trace_s *gts, ULONGEST addr, size_t size) +{ + unsigned int printk_format = gts->printk_format; + char *pbuf = __get_cpu_var(gtp_printf); + + if (gts->printk_str == NULL) { + gts->tpe->reason = gtp_stop_agent_expr_code_error; + printk(KERN_WARNING "gtp_action_printk: id:%d addr:%p " + "printk doesn't have var name. Please " + "check actions of it.\n", + (int)gts->tpe->num, (void *)(CORE_ADDR)gts->tpe->addr); + 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 addr:%p " + "read %p %u get error.\n", + (int)gts->tpe->num, + (void *)(CORE_ADDR)gts->tpe->addr, + (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 addr:%p " + "size %d cannot printk.\n", + (int)gts->tpe->num, + (void *)(CORE_ADDR)gts->tpe->addr, + (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 addr:%p" + "size %d cannot printk.\n", + (int)gts->tpe->num, + (void *)(CORE_ADDR)gts->tpe->addr, + (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 addr:%p " + "size %d cannot printk.\n", + (int)gts->tpe->num, + (void *)(CORE_ADDR)gts->tpe->addr, + (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 addr:%p " + "printk format %u is not support.\n", + (int)gts->tpe->num, (void *)(CORE_ADDR)gts->tpe->addr, + 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; +#ifdef GTP_FTRACE_RING_BUFFER + struct ring_buffer_event *rbe; +#endif + + 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) { + if (gtp_action_head(gts)) + return -1; + } + +#ifdef GTP_FTRACE_RING_BUFFER + GTP_FRAME_RINGBUFFER_ALLOC(GTP_FRAME_MEM_SIZE + size); +#endif +#if defined(GTP_FRAME_SIMPLE) || defined(GTP_RB) +#ifdef GTP_RB + tmp = gtp_rb_alloc(gts->next, GTP_FRAME_MEM_SIZE + size, gts->id); +#endif +#ifdef GTP_FRAME_SIMPLE + tmp = gtp_frame_alloc(GTP_FRAME_MEM_SIZE + size); +#endif + if (!tmp) { + gts->tpe->reason = gtp_stop_frame_full; + return -1; + } +#ifdef GTP_FRAME_SIMPLE + *gts->next = tmp; +#endif +#endif + + FID(tmp) = FID_MEM; + tmp += FID_SIZE; + +#ifdef GTP_FRAME_SIMPLE + gts->next = (char **)tmp; + *gts->next = NULL; + tmp += sizeof(char *); +#endif + + fm = (struct gtp_frame_mem *)tmp; + fm->addr = addr; + fm->size = size; + tmp += sizeof(struct gtp_frame_mem); + +#ifdef GTP_DEBUG_V + printk(GTP_DEBUG_V "gtp_action_memory_read: id:%d addr:%p %p %u\n", + (int)gts->tpe->num, (void *)(CORE_ADDR)gts->tpe->addr, + (void *)addr, (unsigned int)size); +#endif + + if (probe_kernel_read(tmp, (void *)addr, size)) { + gts->tpe->reason = gtp_stop_efault; +#ifdef GTP_FRAME_SIMPLE + memset(tmp, 0, size); +#endif +#ifdef GTP_FTRACE_RING_BUFFER + ring_buffer_discard_commit(gtp_frame, rbe); +#endif +#ifdef GTP_RB + GTP_RB_RELEASE(gts->next); +#endif + printk(KERN_WARNING "gtp_action_memory_read: id:%d addr:%p " + "read %p %u get error.\n", + (int)gts->tpe->num, (void *)(CORE_ADDR)gts->tpe->addr, + (void *)addr, (unsigned int)size); + return -1; + } + +#ifdef GTP_FTRACE_RING_BUFFER + ring_buffer_unlock_commit(gtp_frame, rbe); +#endif + + return 0; +} + +static int +gtp_action_r(struct gtp_trace_s *gts, struct action *ae) +{ + struct pt_regs *regs; + char *tmp; +#ifdef GTP_FTRACE_RING_BUFFER + struct ring_buffer_event *rbe; +#endif + + if (gts->next == NULL) { + if (gtp_action_head(gts)) + return -1; + } + +#ifdef GTP_FTRACE_RING_BUFFER + GTP_FRAME_RINGBUFFER_ALLOC(GTP_FRAME_REG_SIZE); +#endif +#if defined(GTP_FRAME_SIMPLE) || defined(GTP_RB) +#ifdef GTP_RB + tmp = gtp_rb_alloc(gts->next, GTP_FRAME_REG_SIZE, gts->id); +#endif +#ifdef GTP_FRAME_SIMPLE + tmp = gtp_frame_alloc(GTP_FRAME_REG_SIZE); +#endif + if (!tmp) { + gts->tpe->reason = gtp_stop_frame_full; + return -1; + } +#ifdef GTP_FRAME_SIMPLE + *gts->next = tmp; +#endif +#endif + + FID(tmp) = FID_REG; + tmp += FID_SIZE; + +#ifdef GTP_FRAME_SIMPLE + gts->next = (char **)tmp; + *gts->next = NULL; + tmp += sizeof(char *); +#endif + + regs = (struct pt_regs *)tmp; + + memcpy(regs, gts->regs, sizeof(struct pt_regs)); +#ifdef CONFIG_X86_32 + regs->sp = (unsigned long)®s->sp; +#endif /* CONFIG_X86_32 */ + + if (gts->ri) + GTP_REGS_PC(regs) = (CORE_ADDR)gts->ri->ret_addr; +#ifdef CONFIG_X86 + else if (!gts->step) + GTP_REGS_PC(regs) -= 1; +#endif /* CONFIG_X86 */ + +#ifdef GTP_FTRACE_RING_BUFFER + ring_buffer_unlock_commit(gtp_frame, rbe); +#endif + + return 0; +} + +static struct gtp_var * +gtp_gtp_var_array_find(unsigned int num) +{ + struct gtp_var *ret; + +#ifdef GTP_DEBUG_V + printk(GTP_DEBUG_V "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; + + ret = gtp_var_array[num - gtp_var_head]; + if (ret->per_cpu) + ret = ret->per_cpu[smp_processor_id()]; + + return ret; +} + +static uint64_t +gtp_get_var_special(struct gtp_trace_s *gts, unsigned int num) +{ + uint64_t ret; + + switch (num) { + case GTP_VAR_CURRENT_TASK_ID: + if (gts->ri) + ret = (uint64_t)(CORE_ADDR)gts->ri->task; + else + ret = (uint64_t)(CORE_ADDR)get_current(); + break; + case GTP_VAR_CURRENT_TASK_PID_ID: + if (gts->ri) + ret = (uint64_t)(CORE_ADDR)gts->ri->task->pid; + else + ret = (uint64_t)(CORE_ADDR)get_current()->pid; + break; + case GTP_VAR_CURRENT_THREAD_INFO_ID: + ret = (uint64_t)(CORE_ADDR)current_thread_info(); + break; + case GTP_VAR_CLOCK_ID: + ret = (uint64_t)GTP_LOCAL_CLOCK; + break; + case GTP_VAR_COOKED_CLOCK_ID: + ret = (uint64_t)(__get_cpu_var(local_clock_current) + - __get_cpu_var(local_clock_offset)); + break; +#ifdef CONFIG_X86 + case GTP_VAR_RDTSC_ID: + { + unsigned long long a; + rdtscll(a); + ret = (uint64_t)a; + } + break; + case GTP_VAR_COOKED_RDTSC_ID: + ret = (uint64_t)(__get_cpu_var(rdtsc_current) + - __get_cpu_var(rdtsc_offset)); + break; +#endif + case GTP_VAR_CPU_ID: + ret = (uint64_t)(CORE_ADDR)smp_processor_id(); + break; + case GTP_VAR_CPU_NUMBER_ID: + ret = (uint64_t)gtp_cpu_number; + break; + case GTP_VAR_PRINTK_TMP_ID: + ret = gts->printk_tmp; + break; + case GTP_VAR_DUMP_STACK_ID: + printk(KERN_NULL "gtp %d %p:", (int)gts->tpe->num, + (void *)(CORE_ADDR)gts->tpe->addr); + dump_stack(); + ret = 0; + break; + case GTP_VAR_XTIME_SEC_ID: + if (gts->xtime.tv_sec == 0 && gts->xtime.tv_nsec == 0) + getnstimeofday(>s->xtime); + ret = (uint64_t)gts->xtime.tv_sec; + break; + case GTP_VAR_XTIME_NSEC_ID: + if (gts->xtime.tv_sec == 0 && gts->xtime.tv_nsec == 0) + getnstimeofday(>s->xtime); + ret = (uint64_t)gts->xtime.tv_nsec; + break; + case GTP_VAR_HARDIRQ_COUNT_ID: + ret = (uint64_t)hardirq_count(); + break; + case GTP_VAR_SOFTIRQ_COUNT_ID: + ret = (uint64_t)softirq_count(); + break; + case GTP_VAR_IRQ_COUNT_ID: + ret = (uint64_t)irq_count(); + break; + default: + ret = 0; + break; + } + + return ret; +} + +static void +gtp_set_var_special(struct gtp_trace_s *gts, unsigned int num, ULONGEST val) +{ + switch (num) { + case GTP_VAR_PRINTK_TMP_ID: + gts->printk_tmp = val; + break; + case GTP_VAR_PRINTK_LEVEL_ID: + gts->printk_level = (unsigned int)val; + break; + case GTP_VAR_PRINTK_FORMAT_ID: + gts->printk_format = (unsigned int)val; + break; + case GTP_VAR_PC_PE_EN_ID: + gtp_pc_pe_en((int)val); + break; + } +} + +static int +gtp_collect_var_special(struct gtp_trace_s *gts, unsigned int num) +{ + struct gtp_frame_var *fvar; + char *tmp; +#ifdef GTP_FTRACE_RING_BUFFER + struct ring_buffer_event *rbe; +#endif + + if (gts->next == NULL) { + if (gtp_action_head(gts)) + return -1; + } + + if (GTP_VAR_AUTO_TRACEV(num)) + return 0; + +#ifdef GTP_FTRACE_RING_BUFFER + GTP_FRAME_RINGBUFFER_ALLOC(GTP_FRAME_VAR_SIZE); +#endif +#if defined(GTP_FRAME_SIMPLE) || defined(GTP_RB) +#ifdef GTP_RB + tmp = gtp_rb_alloc(gts->next, GTP_FRAME_VAR_SIZE, gts->id); +#endif +#ifdef GTP_FRAME_SIMPLE + tmp = gtp_frame_alloc(GTP_FRAME_VAR_SIZE); +#endif + if (!tmp) { + gts->tpe->reason = gtp_stop_frame_full; + return -1; + } +#ifdef GTP_FRAME_SIMPLE + *gts->next = tmp; +#endif +#endif + + FID(tmp) = FID_VAR; + tmp += FID_SIZE; + +#ifdef GTP_FRAME_SIMPLE + gts->next = (char **)tmp; + *gts->next = NULL; + tmp += sizeof(char *); +#endif + + fvar = (struct gtp_frame_var *) tmp; + fvar->num = num; + fvar->val = gtp_get_var_special(gts, num); + +#ifdef GTP_FTRACE_RING_BUFFER + ring_buffer_unlock_commit(gtp_frame, rbe); +#endif + + return 0; +} + +uint64_t +gtp_get_var(struct gtp_trace_s *gts, struct gtp_var *tve) +{ +#ifdef CONFIG_PERF_EVENTS + if (tve->ptid == pe_tv_val || tve->ptid == pe_tv_enabled + || tve->ptid == pe_tv_running) { + tve->pts->val = perf_event_read_value(tve->pts->event, + &(tve->pts->enabled), + &(tve->pts->running)); + switch (tve->ptid) { + case pe_tv_val: + return (uint64_t)(tve->pts->val); + break; + case pe_tv_enabled: + return (uint64_t)(tve->pts->enabled); + break; + case pe_tv_running: + return (uint64_t)(tve->pts->running); + break; + default: + return 0; + break; + } + } +#endif + + return tve->val; +} + +static int +gtp_collect_var(struct gtp_trace_s *gts, struct gtp_var *tve) +{ + struct gtp_frame_var *fvar; + char *tmp; +#ifdef GTP_FTRACE_RING_BUFFER + struct ring_buffer_event *rbe; +#endif + + if (gts->next == NULL) { + if (gtp_action_head(gts)) + return -1; + } + +#ifdef GTP_FTRACE_RING_BUFFER + GTP_FRAME_RINGBUFFER_ALLOC(GTP_FRAME_VAR_SIZE); +#endif +#if defined(GTP_FRAME_SIMPLE) || defined(GTP_RB) +#ifdef GTP_RB + tmp = gtp_rb_alloc(gts->next, GTP_FRAME_VAR_SIZE, gts->id); +#endif +#ifdef GTP_FRAME_SIMPLE + tmp = gtp_frame_alloc(GTP_FRAME_VAR_SIZE); +#endif + if (!tmp) { + gts->tpe->reason = gtp_stop_frame_full; + return -1; + } +#ifdef GTP_FRAME_SIMPLE + *gts->next = tmp; +#endif +#endif + + FID(tmp) = FID_VAR; + tmp += FID_SIZE; + +#ifdef GTP_FRAME_SIMPLE + gts->next = (char **)tmp; + *gts->next = NULL; + tmp += sizeof(char *); +#endif + + fvar = (struct gtp_frame_var *) tmp; + fvar->num = tve->num; + fvar->val = gtp_get_var(gts, tve); + +#ifdef GTP_FTRACE_RING_BUFFER + ring_buffer_unlock_commit(gtp_frame, rbe); +#endif + + return 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 = __get_cpu_var(gtp_printf); + ULONGEST *stack = __get_cpu_var(action_x_stack); + + if (unlikely(ae->u.exp.need_var_lock)) + spin_lock(>p_var_lock); + + while (1) { +#ifdef GTP_DEBUG_V + printk(GTP_DEBUG_V "gtp_parse_x: cmd %x\n", ebuf[pc]); +#endif + + switch (ebuf[pc++]) { + /* add */ + case 0x02: + top += stack[--sp]; + break; + + case op_check_add: + if (sp) + top += stack[--sp]; + else + goto code_error_out; + break; + + /* sub */ + case 0x03: + top = stack[--sp] - top; + break; + + case op_check_sub: + if (sp) + top = stack[--sp] - top; + else + goto code_error_out; + break; + + /* mul */ + case 0x04: + top *= stack[--sp]; + break; + + case op_check_mul: + if (sp) + top *= stack[--sp]; + else + goto code_error_out; + 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; + + case op_check_div_signed: + 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) { + ULONGEST ul = stack[--sp]; + do_div(ul, top); + top = ul; + } else + goto code_error_out; + break; + + case op_check_div_unsigned: + 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) { + LONGEST l1 = (LONGEST) stack[--sp]; + LONGEST l2 = (LONGEST) top; + top = do_div(l1, l2); + } else + goto code_error_out; + break; + + case op_check_rem_signed: + 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) { + ULONGEST ul1 = stack[--sp]; + ULONGEST ul2 = top; + top = do_div(ul1, ul2); + } else + goto code_error_out; + break; + + case op_check_rem_unsigned: + 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: + top = stack[--sp] << top; + break; + + case op_check_lsh: + if (sp) + top = stack[--sp] << top; + else + goto code_error_out; + break; + + /* rsh_signed */ + case 0x0a: + top = ((LONGEST) stack[--sp]) >> top; + break; + + case op_check_rsh_signed: + if (sp) + top = ((LONGEST) stack[--sp]) >> top; + else + goto code_error_out; + break; + + /* rsh_unsigned */ + case 0x0b: + top = stack[--sp] >> top; + break; + + case op_check_rsh_unsigned: + if (sp) + top = stack[--sp] >> top; + else + goto code_error_out; + 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; + + case op_check_trace: + 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_printk */ + case op_trace_printk: + 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 op_trace_quick_printk: + 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; + + case op_check_bit_and: + if (sp) + top &= stack[--sp]; + else + goto code_error_out; + break; + + /* bit_or */ + case 0x10: + top |= stack[--sp]; + break; + + case op_check_bit_or: + if (sp) + top |= stack[--sp]; + else + goto code_error_out; + break; + + /* bit_xor */ + case 0x11: + top ^= stack[--sp]; + break; + + case op_check_bit_xor: + if (sp) + top ^= stack[--sp]; + else + goto code_error_out; + break; + + /* bit_not */ + case 0x12: + top = ~top; + break; + + /* equal */ + case 0x13: + top = (stack[--sp] == top); + break; + + case op_check_equal: + if (sp) + top = (stack[--sp] == top); + else + goto code_error_out; + break; + + /* less_signed */ + case 0x14: + top = (((LONGEST) stack[--sp]) + < ((LONGEST) top)); + break; + + case op_check_less_signed: + if (sp) + top = (((LONGEST) stack[--sp]) + < ((LONGEST) top)); + else + goto code_error_out; + break; + + /* less_unsigned */ + case 0x15: + top = (stack[--sp] < top); + break; + + case op_check_less_unsigned: + 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 */ + top = stack[--sp]; + break; + + case op_check_if_goto: + 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: + top = stack[--sp]; + break; + + case op_check_pop: + 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: + stack[sp] = top; + top = stack[sp - 1]; + stack[sp - 1] = stack[sp]; + break; + + case op_check_swap: + 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++]; + + stack[sp++] = top; + + top = gtp_get_var(gts, gtp_gtp_var_array_find(arg)); + break; + + /* getv_sepecial */ + case op_special_getv: + arg = ebuf[pc++]; + arg = (arg << 8) + ebuf[pc++]; + stack[sp++] = top; + top = gtp_get_var_special(gts, arg); + break; + + /* setv */ + case 0x2d: { + struct gtp_var *tve; + + arg = ebuf[pc++]; + arg = (arg << 8) + ebuf[pc++]; + + tve = gtp_gtp_var_array_find(arg); +#ifdef CONFIG_PERF_EVENTS + if (tve->ptid == pe_tv_en) + gtp_pe_set_en(tve->pts, (int)top); + else if (tve->ptid == pe_tv_val) + perf_event_set(tve->pts->event, + (u64)top); +#endif + tve->val = (uint64_t)top; + } + break; + + /* setv_sepecial */ + case op_special_setv: + arg = ebuf[pc++]; + arg = (arg << 8) + ebuf[pc++]; + gtp_set_var_special(gts, arg, top); + break; + + /* tracev */ + case 0x2e: + arg = ebuf[pc++]; + arg = (arg << 8) + ebuf[pc++]; + + if (gtp_collect_var(gts, gtp_gtp_var_array_find(arg))) { + /* gtp_collect_var will set error + status with itself if it got error. */ + goto out; + } + break; + + /* tracev_special */ + case op_special_tracev: + arg = ebuf[pc++]; + arg = (arg << 8) + ebuf[pc++]; + gtp_collect_var_special(gts, arg); + break; + + /* tracev_printk */ + case op_tracev_printk: { + uint64_t u64; + arg = ebuf[pc++]; + arg = (arg << 8) + ebuf[pc++]; + + if (!GTP_VAR_IS_SPECIAL(arg)) + u64 = gtp_get_var(gts, + gtp_gtp_var_array_find + (arg)); + else + u64 = gtp_get_var_special(gts, arg); + if (gtp_action_printk(gts, u64, 0)) { + /* gtp_collect_var will set error status + with itself if it got error. */ + goto out; + } + } + break; + } + + if (ae->type != 'X' && unlikely(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 addr:%p" + "action X get error in pc %u.\n", + (int)gts->tpe->num, (void *)(CORE_ADDR)gts->tpe->addr, pc); +out: + if (unlikely(psize != GTP_PRINTF_MAX)) { + unsigned long flags; + + local_irq_save(flags); + printk("%s", pbuf - (GTP_PRINTF_MAX - psize)); + local_irq_restore(flags); + } + if (unlikely(ae->u.exp.need_var_lock)) + spin_unlock(>p_var_lock); + return ret; +} + +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) +static void +gtp_handler_wakeup(void) +{ +#ifdef GTP_FTRACE_RING_BUFFER + FID_TYPE eid = FID_END; + ring_buffer_write(gtp_frame, FID_SIZE, &eid); +#endif + + if (atomic_read(>pframe_pipe_wq_v) > 0) { + atomic_dec(>pframe_pipe_wq_v); + add_preempt_count(HARDIRQ_OFFSET); + tasklet_schedule(>pframe_pipe_wq_tasklet); + sub_preempt_count(HARDIRQ_OFFSET); + } +} +#endif + +static void +gtp_handler(struct gtp_trace_s *gts) +{ + struct action *ae; + +#ifdef GTP_DEBUG_V + printk(GTP_DEBUG_V "gtp_handler: tracepoint %d %p\n", + (int)gts->tpe->num, (void *)(CORE_ADDR)gts->tpe->addr); +#endif + + if (gts->tpe->kpreg == 0) + return; + +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) + if (!gtp_pipe_trace && get_current()->pid == gtp_gtpframe_pipe_pid) + return; +#endif + + if (gts->tpe->no_self_trace + && (get_current()->pid == gtp_gtp_pid + || get_current()->pid == gtp_gtpframe_pid)) { + return; + } + + if (gts->tpe->have_printk) { + gts->printk_level = 8; + gts->printk_str = gts->tpe->printk_str; + } + + /* Condition. */ + if (gts->tpe->cond) { + int run; + + gts->run = &run; + if (gtp_action_x(gts, gts->tpe->cond)) + goto tpe_stop; + if (!run) + return; + } + + gts->run = NULL; + + /* Pass. */ + if (!gts->tpe->nopass) { + if (atomic_dec_return(>s->tpe->current_pass) < 0) + goto tpe_stop; + } + + /* Handle actions. */ + if (gts->step) + ae = gts->tpe->step_action_list; + else + ae = gts->tpe->action_list; + for (; 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; + } + } + +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) + if (gts->next) { +#ifdef GTP_RB + GTP_RB_UNLOCK(gts->next); +#endif + gtp_handler_wakeup(); + } +#endif + + return; + +tpe_stop: +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) + if (gts->next) { +#ifdef GTP_RB + GTP_RB_UNLOCK(gts->next); +#endif + gtp_handler_wakeup(); + } +#endif + gts->tpe->kpreg = 0; + add_preempt_count(HARDIRQ_OFFSET); + tasklet_schedule(>s->tpe->tasklet); + sub_preempt_count(HARDIRQ_OFFSET); +#ifdef GTP_DEBUG_V + printk(GTP_DEBUG_V "gtp_handler: tracepoint %d %p stop.\n", + (int)gts->tpe->num, (void *)(CORE_ADDR)gts->tpe->addr); +#endif + return; +} + +static DEFINE_PER_CPU(int, gtp_handler_began); + +#ifdef CONFIG_X86 +static int gtp_access_cooked_rdtsc; +#endif +static int gtp_access_cooked_clock; +#ifdef CONFIG_PERF_EVENTS +static int gtp_have_pc_pe; +#endif + +static void +gtp_handler_begin(void) +{ + if (!__get_cpu_var(gtp_handler_began)) { +#ifdef CONFIG_X86 + if (gtp_access_cooked_rdtsc) { + u64 a; + + rdtscll(a); + __get_cpu_var(rdtsc_current) = a; + } +#endif + + if (gtp_access_cooked_clock) + __get_cpu_var(local_clock_current) = GTP_LOCAL_CLOCK; + +#ifdef CONFIG_PERF_EVENTS + if (gtp_have_pc_pe) + pc_pe_list_disable(); +#endif + + __get_cpu_var(gtp_handler_began) = 1; + } +} + +static void +gtp_handler_end(void) +{ + if (__get_cpu_var(gtp_handler_began)) { +#ifdef CONFIG_PERF_EVENTS + if (gtp_have_pc_pe) + pc_pe_list_enable(); +#endif + + if (gtp_access_cooked_clock) { + __get_cpu_var(local_clock_offset) += GTP_LOCAL_CLOCK + - __get_cpu_var(local_clock_current); + __get_cpu_var(local_clock_current) = 0; + } + +#ifdef CONFIG_X86 + if (gtp_access_cooked_rdtsc) { + u64 a; + + rdtscll(a); + __get_cpu_var(rdtsc_offset) += a + - __get_cpu_var(rdtsc_current); + __get_cpu_var(rdtsc_current) = 0; + } +#endif + + __get_cpu_var(gtp_handler_began) = 0; + } +} + +static inline void +gtp_kp_pre_handler_1(struct kprobe *p, struct pt_regs *regs) +{ + struct kretprobe *kp; + struct gtp_trace_s gts; + + memset(>s, 0, sizeof(struct gtp_trace_s)); + kp = container_of(p, struct kretprobe, kp); + gts.tpe = container_of(kp, struct gtp_entry, kp); + gts.regs = regs; + + gtp_handler(>s); +} + +static inline void +gtp_kp_post_handler_1(struct kprobe *p, struct pt_regs *regs, + unsigned long flags) +{ + struct kretprobe *kp; + struct gtp_entry *tpe; + struct gtp_trace_s gts; + + kp = container_of(p, struct kretprobe, kp); + tpe = container_of(kp, struct gtp_entry, kp); + + memset(>s, 0, sizeof(struct gtp_trace_s)); + gts.tpe = tpe; + gts.regs = regs; + gts.step = 1; + + gtp_handler(>s); +} + +static inline void +gtp_kp_ret_handler_1(struct kretprobe_instance *ri, struct pt_regs *regs) +{ + struct gtp_trace_s gts; + + memset(>s, 0, sizeof(struct gtp_trace_s)); + gts.tpe = container_of(ri->rp, struct gtp_entry, kp); + gts.regs = regs; + gts.ri = ri; + + gtp_handler(>s); +} + +static int +gtp_kp_pre_handler_plus_step(struct kprobe *p, struct pt_regs *regs) +{ + gtp_handler_begin(); + + gtp_kp_pre_handler_1(p, regs); + + return 0; +} + +static int +gtp_kp_pre_handler_plus(struct kprobe *p, struct pt_regs *regs) +{ + gtp_handler_begin(); + + gtp_kp_pre_handler_1(p, regs); + + gtp_handler_end(); + + return 0; +} + +static int +gtp_kp_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + gtp_kp_pre_handler_1(p, regs); + + return 0; +} + +/* Only available when tpe->step is true. */ + +static void +gtp_kp_post_handler_plus(struct kprobe *p, struct pt_regs *regs, + unsigned long flags) +{ + gtp_kp_post_handler_1(p, regs, flags); + + gtp_handler_end(); +} + +/* Only available when tpe->step is true. */ + +static void +gtp_kp_post_handler(struct kprobe *p, struct pt_regs *regs, + unsigned long flags) +{ + gtp_kp_post_handler_1(p, regs, flags); +} + +static int +gtp_kp_ret_handler_plus(struct kretprobe_instance *ri, struct pt_regs *regs) +{ + gtp_handler_begin(); + + gtp_kp_ret_handler_1(ri, regs); + + gtp_handler_end(); + + return 0; +} + +static int +gtp_kp_ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs) +{ + gtp_kp_ret_handler_1(ri, regs); + + 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': + case 0xff: + 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 %p\n", (int)tpe->num, + (void *)(CORE_ADDR)tpe->addr); +#endif + + if (tpe->is_kretprobe) + unregister_kretprobe(&tpe->kp); + else + unregister_kprobe(&tpe->kp.kp); +} + +static struct gtp_entry * +gtp_list_add(ULONGEST num, ULONGEST addr) +{ + struct gtp_entry *ret = kcalloc(1, 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.kp.addr = (kprobe_opcode_t *) (CORE_ADDR)addr; + 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, ULONGEST addr) +{ + struct gtp_entry *tpe; + + for (tpe = gtp_list; tpe; tpe = tpe->next) { + if (tpe->num == num && tpe->addr == addr) + return tpe; + } + + return NULL; +} + +/* If more than one gtp entry have same num, return NULL. */ + +static struct gtp_entry * +gtp_list_find_without_addr(ULONGEST num) +{ + struct gtp_entry *tpe, *ret = NULL; + + for (tpe = gtp_list; tpe; tpe = tpe->next) { + if (tpe->num == num) { + if (ret) + return NULL; + else + ret = tpe; + } + } + + return ret; +} + +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; +} + +#ifdef GTP_FTRACE_RING_BUFFER +static void +gtp_frame_iter_open(void) +{ + int cpu; + + + for_each_online_cpu(cpu) + gtp_frame_iter[cpu] = ring_buffer_read_prepare(gtp_frame, cpu); + ring_buffer_read_prepare_sync(); + for_each_online_cpu(cpu) { + ring_buffer_read_start(gtp_frame_iter[cpu]); + } +} + +static void +gtp_frame_iter_reset(void) +{ + int cpu; + + for_each_online_cpu(cpu) + ring_buffer_iter_reset(gtp_frame_iter[cpu]); + gtp_frame_current_num = -1; +} + +static int +gtp_frame_iter_peek_head(void) +{ + int cpu; + int ret = -1; + u64 min = 0; + + for_each_online_cpu(cpu) { + struct ring_buffer_event *rbe; + char *tmp; + u64 ts; + + while (1) { + rbe = ring_buffer_iter_peek(gtp_frame_iter[cpu], &ts); + if (rbe == NULL) + break; + tmp = ring_buffer_event_data(rbe); + if (FID(tmp) == FID_HEAD) + break; + ring_buffer_read(gtp_frame_iter[cpu], NULL); + } + + if (rbe) { + if ((min && ts < min) || !min) { + min = ts; + ret = cpu; + } + } + } + + if (ret < 0) + gtp_frame_current_num = -1; + else + gtp_frame_current_num++; + return ret; +} + +static void +gtp_frame_iter_close(void) +{ + int cpu; + + for_each_online_cpu(cpu) { + if (gtp_frame_iter[cpu]) { + ring_buffer_read_finish(gtp_frame_iter[cpu]); + gtp_frame_iter[cpu] = NULL; + } + } +} +#endif + +static void +gtp_frame_reset(void) +{ + gtp_frame_current_num = -1; +#ifdef GTP_FRAME_SIMPLE + 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; +#endif +#ifdef GTP_FTRACE_RING_BUFFER + gtp_frame_iter_close(); + if (gtp_frame) + ring_buffer_reset(gtp_frame); +#endif +#ifdef GTP_RB + gtp_rb_reset(); +#endif + atomic_set(>p_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; + + if (u64) + *u64 = 0; + while (hex2int(pkg[0], &i)) { + pkg++; + if (u64) { + *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; +} + +#ifdef CONFIG_PERF_EVENTS +static struct gtp_var * +gtp_var_add(unsigned int num, uint64_t val, char *src, + struct gtp_var **per_cpu, int per_cpu_id, + enum pe_tv_id ptid, struct pe_tv_s *pts) +#else +static struct gtp_var * +gtp_var_add(unsigned int num, uint64_t val, char *src, + struct gtp_var **per_cpu, int per_cpu_id) +#endif +{ + struct gtp_var *var = kcalloc(1, 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->per_cpu = per_cpu; + if (per_cpu) + var->per_cpu[per_cpu_id] = var; + +#ifdef CONFIG_PERF_EVENTS + var->ptid = ptid; + var->pts = pts; +#endif + + 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; + + if (tve->per_cpu) { + struct gtp_var *tve1; + + for (tve1 = gtp_var_list; tve1; tve1 = tve1->next) { + if (tve1->per_cpu == tve->per_cpu) + tve1->per_cpu = NULL; + } + + kfree(tve->per_cpu); + } + +#ifdef CONFIG_PERF_EVENTS + if (tve->pts) { + struct gtp_var *tve1; + + for (tve1 = gtp_var_list; tve1; tve1 = tve1->next) { + if (tve1->pts == tve->pts) { + tve1->pts = NULL; + tve1->ptid = pe_tv_unknown; + } + } + + if (tve->pts->event) + perf_event_release_kernel(tve->pts->event); + kfree(tve->pts); + } +#endif + + kfree(tve->src); + kfree(tve); + } + + gtp_start_ignore_error = 0; +} + +static int +gtp_gdbrsp_qtstop(void) +{ + struct gtp_entry *tpe; +#ifdef CONFIG_PERF_EVENTS + struct gtp_var *tve; +#endif + +#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) { + if (tpe->is_kretprobe) + unregister_kretprobe(&tpe->kp); + else + unregister_kprobe(&tpe->kp.kp); + tpe->kpreg = 0; + } + tasklet_kill(&tpe->tasklet); + } + +#ifdef CONFIG_PERF_EVENTS + for (tve = gtp_var_list; tve; tve = tve->next) { + if (tve->pts == NULL) + continue; + if (tve->pts->event == NULL) + continue; + + tve->pts->val = perf_event_read_value(tve->pts->event, + &(tve->pts->enabled), + &(tve->pts->running)); + perf_event_release_kernel(tve->pts->event); + tve->pts->event = NULL; + } +#endif + + kfree(gtp_var_array); + gtp_var_array = NULL; + +#ifdef GTP_FTRACE_RING_BUFFER + if (gtp_frame) { + gtp_frame_iter_open(); + gtp_frame_iter_reset(); + } +#endif + + gtp_start = 0; +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) + if (atomic_read(>pframe_pipe_wq_v) > 0) { + atomic_dec(>pframe_pipe_wq_v); + tasklet_schedule(>pframe_pipe_wq_tasklet); + } + tasklet_kill(>pframe_pipe_wq_tasklet); +#endif + wake_up_interruptible_nr(>pframe_wq, 1); + + 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(); + +#ifdef GTP_RB + if (!GTP_RB_PAGE_IS_EMPTY) +#elif defined(GTP_FRAME_SIMPLE) || defined(GTP_FTRACE_RING_BUFFER) + if (gtp_frame) +#endif + gtp_frame_reset(); + + gtpro_list_clear(); + + gtp_var_release(); + +#ifdef CONFIG_X86 + gtp_access_cooked_rdtsc = 0; +#endif + gtp_access_cooked_clock = 0; +#ifdef CONFIG_PERF_EVENTS + gtp_have_pc_pe = 0; +#endif + + return 0; +} + +struct gtp_x_loop { + struct gtp_x_loop *next; + unsigned int addr; + int non_goto_done; +}; + +static struct gtp_x_loop * +gtp_x_loop_find(struct gtp_x_loop *list, unsigned int pc) +{ + struct gtp_x_loop *ret = NULL; + + for (ret = list; ret; ret = ret->next) { + if (ret->addr == pc) + break; + } + + return ret; +} + +static struct gtp_x_loop * +gtp_x_loop_add(struct gtp_x_loop **list, unsigned int pc, int non_goto_done) +{ + struct gtp_x_loop *ret; + + ret = kmalloc(sizeof(struct gtp_x_loop), GFP_KERNEL); + if (!ret) + goto out; + + ret->addr = pc; + ret->non_goto_done = non_goto_done; + + ret->next = *list; + *list = ret; + +out: + return ret; +} + +struct gtp_x_if_goto { + struct gtp_x_if_goto *next; + unsigned int ip; + unsigned int sp; +}; + +static struct gtp_x_if_goto * +gtp_x_if_goto_add(struct gtp_x_if_goto **list, unsigned int pc, unsigned int sp) +{ + struct gtp_x_if_goto *ret; + + ret = kmalloc(sizeof(struct gtp_x_loop), GFP_KERNEL); + if (!ret) + goto out; + + ret->ip = pc; + ret->sp = sp; + + ret->next = *list; + *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_simple(struct gtp_entry *tpe, struct action *ae) +{ + int ret = -EINVAL; + unsigned int pc = 0, sp = 0; + struct gtp_x_if_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 sp_max = 0; + +reswitch: + while (pc < ae->u.exp.size) { +#ifdef GTP_DEBUG_V + printk(GTP_DEBUG_V "gtp_check_x_simple: 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 (sp < 1) { + printk(KERN_WARNING "gtp_check_x_simple: stack " + "overflow in %d.\n", + pc - 1); + goto release_out; + } else { + if (ebuf[pc - 1] != 0x2b) + sp--; + } + break; + + /* trace */ + case 0x0c: + if (tpe->have_printk) + last_trace_pc = pc - 1; + + if (sp < 2) { + printk(KERN_WARNING "gtp_check_x_simple: 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: + sp++; + if (sp_max < sp) + sp_max = sp; + break; + + /* const8 */ + case 0x22: + sp++; + if (sp_max < sp) + sp_max = 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; + + sp++; + if (sp_max < sp) + sp_max = sp; + break; + + /* const32 */ + case 0x24: + if (pc + 3 >= ae->u.exp.size) + goto release_out; + pc += 4; + + sp++; + if (sp_max < sp) + sp_max = sp; + break; + + /* const64 */ + case 0x25: + if (pc + 7 >= ae->u.exp.size) + goto release_out; + pc += 8; + + sp++; + if (sp_max < sp) + sp_max = sp; + break; + + /* if_goto */ + case 0x20: + if (tpe->have_printk) { + printk(KERN_WARNING "If_goto action doesn't" + "support printk.\n"); + goto release_out; + } + if (pc + 1 >= ae->u.exp.size) + goto release_out; + + { + unsigned int dpc = (ebuf[pc] << 8) + + ebuf[pc + 1]; + + if (dpc < pc) { + /* This action X include loop. */ + ae->type = 0xff; + ret = 0; + goto release_out; + } + + if (!gtp_x_if_goto_add(&glist, dpc, sp)) { + ret = -ENOMEM; + goto release_out; + } + } + + pc += 2; + break; + + /* goto */ + case 0x21: + if (pc + 1 >= ae->u.exp.size) + goto release_out; + + { + unsigned int dpc = (ebuf[pc] << 8) + + ebuf[pc + 1]; + + if (dpc < pc) { + /* This action X include loop. */ + ae->type = 0xff; + ret = 0; + goto release_out; + } + + pc = dpc; + } + 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_IS_SPECIAL(arg)) { + if (gtp_x_var_add(&vlist, arg, 1)) { + ret = -ENOMEM; + goto release_out; + } + } else { + if (arg == GTP_VAR_NO_SELF_TRACE_ID) { + tpe->no_self_trace = 1; + ret = 1; + goto release_out; + } + + if (arg == GTP_VAR_COOKED_CLOCK_ID) + gtp_access_cooked_clock = 1; +#ifdef CONFIG_X86 + else if (arg == GTP_VAR_COOKED_RDTSC_ID) + gtp_access_cooked_rdtsc = 1; +#endif + ebuf[pc - 3] = op_special_getv; + } + } + sp++; + if (sp_max < sp) + sp_max = 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_IS_SPECIAL(arg)) { + if (gtp_x_var_add(&vlist, arg, 2)) { + ret = -ENOMEM; + goto release_out; + } + } else { + if (arg == GTP_VAR_NO_SELF_TRACE_ID) { + tpe->no_self_trace = 1; + ret = 1; + goto release_out; + } else if (arg == GTP_VAR_KRET_ID) { + /* XXX: still not set it + value to maxactive. */ + tpe->is_kretprobe = 1; + ret = 1; + goto release_out; + } + + if (arg == GTP_VAR_PRINTK_LEVEL_ID) + tpe->have_printk = 1; + + ebuf[pc - 3] = op_special_setv; + } + } + 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_IS_SPECIAL(arg)) { + if (gtp_x_var_add(&vlist, arg, 4)) { + ret = -ENOMEM; + goto release_out; + } + } else { + if (arg == GTP_VAR_NO_SELF_TRACE_ID) { + tpe->no_self_trace = 1; + ret = 1; + goto release_out; + } + if (arg == GTP_VAR_COOKED_CLOCK_ID) + gtp_access_cooked_clock = 1; +#ifdef CONFIG_X86 + else if (arg == GTP_VAR_COOKED_RDTSC_ID) + gtp_access_cooked_rdtsc = 1; +#endif + ebuf[pc - 3] = op_special_tracev; + } + } + 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 (sp < 1) { + printk(KERN_WARNING "gtp_check_x_simple: 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: +#ifdef GTP_DEBUG + printk(GTP_DEBUG "sp_max = %d\n", sp_max); +#endif + if (sp_max >= STACK_MAX) { + printk(KERN_WARNING "gtp_check_x_simple: stack overflow, " + "current %d, max %d.\n", + sp_max, STACK_MAX); + goto release_out; + } + if (glist) { + pc = glist->ip; + sp = glist->sp; + gtmp = glist; + glist = glist->next; + kfree(gtmp); + goto reswitch; + } + ret = 0; +#ifdef GTP_DEBUG + printk(GTP_DEBUG "gtp_check_x_simple: Code is OK. sp_max is %d.\n", + sp_max); +#endif + +release_out: + while (glist) { + gtmp = glist; + glist = glist->next; + kfree(gtmp); + } + while (vlist) { + struct gtp_var *var; + + vtmp = vlist; + vlist = vlist->next; + + /* Get the var of vtmp. */ + var = gtp_var_find(vtmp->num); + if (var == NULL) { + printk(KERN_WARNING "gtp_check_x_simple: cannot find " + "tvar %d.\n", vtmp->num); + ret = -EINVAL; + } else { + if (var->per_cpu == NULL) { + 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] = op_trace_printk; + break; + /* trace_quick */ + case 0x0d: + ebuf[last_trace_pc] = op_trace_quick_printk; + break; + /* tracev */ + case 0x2e: + ebuf[last_trace_pc] = op_tracev_printk; + break; + case op_special_tracev: + ebuf[last_trace_pc] = op_tracev_printk; + break; + } + } + + return ret; +} + +static int +gtp_check_x_loop(struct gtp_entry *tpe, struct action *ae) +{ + int ret = -EINVAL; + unsigned int pc = 0; + struct gtp_x_loop *glist = NULL, *gtmp; + struct gtp_x_var *vlist = NULL, *vtmp; + uint8_t *ebuf = ae->u.exp.buf; + + printk(KERN_WARNING "Action of tracepoint %d have loop.\n", + (int)tpe->num); + + tpe->have_printk = 0; + +reswitch: + while (pc < ae->u.exp.size) { +#ifdef GTP_DEBUG + printk(GTP_DEBUG "gtp_check_x_loop: cmd %x\n", ebuf[pc]); +#endif + switch (ebuf[pc++]) { + /* add */ + case 0x02: + ebuf[pc - 1] = op_check_add; + break; + /* sub */ + case 0x03: + ebuf[pc - 1] = op_check_sub; + break; + /* mul */ + case 0x04: + ebuf[pc - 1] = op_check_mul; + break; + /* lsh */ + case 0x09: + ebuf[pc - 1] = op_check_lsh; + break; + /* rsh_signed */ + case 0x0a: + ebuf[pc - 1] = op_check_rsh_signed; + break; + /* rsh_unsigned */ + case 0x0b: + ebuf[pc - 1] = op_check_rsh_unsigned; + break; + /* bit_and */ + case 0x0f: + ebuf[pc - 1] = op_check_bit_and; + break; + /* bit_or */ + case 0x10: + ebuf[pc - 1] = op_check_bit_or; + break; + /* bit_xor */ + case 0x11: + ebuf[pc - 1] = op_check_bit_xor; + break; + /* equal */ + case 0x13: + ebuf[pc - 1] = op_check_equal; + break; + /* less_signed */ + case 0x14: + ebuf[pc - 1] = op_check_less_signed; + break; + /* less_unsigned */ + case 0x15: + ebuf[pc - 1] = op_check_less_unsigned; + break; + /* pop */ + case 0x29: + ebuf[pc - 1] = op_check_pop; + break; + /* swap */ + case 0x2b: + ebuf[pc - 1] = op_check_swap; + break; + + /* trace */ + case 0x0c: + ebuf[pc - 1] = op_check_trace; + break; + + /* log_not */ + case 0x0e: + /* bit_not */ + case 0x12: + /* ref8 */ + case 0x17: + /* ref16 */ + case 0x18: + /* ref32 */ + case 0x19: + /* ref64 */ + case 0x1a: + /* dup */ + case 0x28: + break; + + /* const8 */ + case 0x22: + /* ext */ + case 0x16: + /* zero_ext */ + case 0x2a: + /* trace_quick */ + case 0x0d: + 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; + break; + + /* const32 */ + case 0x24: + if (pc + 3 >= ae->u.exp.size) + goto release_out; + pc += 4; + break; + + /* const64 */ + case 0x25: + if (pc + 7 >= ae->u.exp.size) + goto release_out; + pc += 8; + break; + + /* if_goto */ + case 0x20: + case op_check_if_goto: + ebuf[pc - 1] = op_check_if_goto; + + if (pc + 1 >= ae->u.exp.size) + goto release_out; + + gtmp = gtp_x_loop_find(glist, pc); + if (gtmp) { + if (gtmp->non_goto_done) + goto out; + else { + gtmp->non_goto_done = 1; + pc += 2; + } + } else { + if (!gtp_x_loop_add(&glist, pc, 0)) { + ret = -ENOMEM; + goto release_out; + } + pc = (ebuf[pc] << 8) + ebuf[pc + 1]; + } + break; + + /* goto */ + case 0x21: + if (pc + 1 >= ae->u.exp.size) + goto release_out; + + gtmp = gtp_x_loop_find(glist, pc); + if (gtmp) + goto out; + else { + if (!gtp_x_loop_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_IS_SPECIAL(arg)) { + if (gtp_x_var_add(&vlist, arg, 1)) { + ret = -ENOMEM; + goto release_out; + } + } else { + if (arg == GTP_VAR_NO_SELF_TRACE_ID) { + tpe->no_self_trace = 1; + ret = 1; + goto release_out; + } + + if (arg == GTP_VAR_COOKED_CLOCK_ID) + gtp_access_cooked_clock = 1; +#ifdef CONFIG_X86 + else if (arg == GTP_VAR_COOKED_RDTSC_ID) + gtp_access_cooked_rdtsc = 1; +#endif + ebuf[pc - 3] = op_special_getv; + } + } + 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_IS_SPECIAL(arg)) { + if (gtp_x_var_add(&vlist, arg, 2)) { + ret = -ENOMEM; + goto release_out; + } + } else { + if (arg == GTP_VAR_NO_SELF_TRACE_ID) { + tpe->no_self_trace = 1; + ret = 1; + goto release_out; + } else if (arg == GTP_VAR_KRET_ID) { + /* XXX: still not set it + value to maxactive. */ + tpe->is_kretprobe = 1; + ret = 1; + goto release_out; + } + + if (arg == GTP_VAR_PRINTK_LEVEL_ID) { + printk(KERN_WARNING "Loop " + "action doesn't" + "support printk.\n"); + goto release_out; + } + + ebuf[pc - 3] = op_special_setv; + } + } + break; + + /* tracev */ + case 0x2e: { + int arg; + + if (pc + 1 >= ae->u.exp.size) + goto release_out; + arg = ebuf[pc++]; + arg = (arg << 8) + ebuf[pc++]; + + if (!GTP_VAR_IS_SPECIAL(arg)) { + if (gtp_x_var_add(&vlist, arg, 4)) { + ret = -ENOMEM; + goto release_out; + } + } else { + if (arg == GTP_VAR_NO_SELF_TRACE_ID) { + tpe->no_self_trace = 1; + ret = 1; + goto release_out; + } + if (arg == GTP_VAR_COOKED_CLOCK_ID) + gtp_access_cooked_clock = 1; +#ifdef CONFIG_X86 + else if (arg == GTP_VAR_COOKED_RDTSC_ID) + gtp_access_cooked_rdtsc = 1; +#endif + ebuf[pc - 3] = op_special_tracev; + } + } + break; + + /* div_signed */ + case 0x05: +#ifdef CONFIG_MIPS + /* XXX, mips don't have 64 bit div. */ + printk(KERN_WARNING "MIPS don't have 64 bit div.\n"); + goto release_out; +#endif + ebuf[pc - 1] = op_check_div_signed; + break; + /* div_unsigned */ + case 0x06: +#ifdef CONFIG_MIPS + /* XXX, mips don't have 64 bit div. */ + printk(KERN_WARNING "MIPS don't have 64 bit div.\n"); + goto release_out; +#endif + ebuf[pc - 1] = op_check_div_unsigned; + break; + /* rem_signed */ + case 0x07: +#ifdef CONFIG_MIPS + /* XXX, mips don't have 64 bit div. */ + printk(KERN_WARNING "MIPS don't have 64 bit div.\n"); + goto release_out; +#endif + ebuf[pc - 1] = op_check_rem_signed; + break; + /* rem_unsigned */ + case 0x08: +#ifdef CONFIG_MIPS + /* XXX, mips don't have 64 bit div. */ + printk(KERN_WARNING "MIPS don't have 64 bit div.\n"); + goto release_out; +#endif + ebuf[pc - 1] = op_check_rem_unsigned; + 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; + } + ret = 0; + +release_out: + while (glist) { + gtmp = glist; + glist = glist->next; + kfree(gtmp); + } + while (vlist) { + struct gtp_var *var; + + vtmp = vlist; + vlist = vlist->next; + + /* Get the var of vtmp. */ + var = gtp_var_find(vtmp->num); + if (var == NULL) { + printk(KERN_WARNING "gtp_check_x_loop: cannot find " + "tvar %d.\n", vtmp->num); + ret = -EINVAL; + } else { + if (var->per_cpu == NULL) { + if ((vtmp->flags & 2) + && ((vtmp->flags & 1) || (vtmp->flags & 4))) + ae->u.exp.need_var_lock = 1; + } + } + kfree(vtmp); + } + + return ret; +} + +static int +gtp_check_x(struct gtp_entry *tpe, struct action *ae) +{ + int ret = gtp_check_x_simple(tpe, ae); + + if (ret != 0 || ae->type == 'X') + return ret; + + return gtp_check_x_loop(tpe, ae); +} + +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) +static void +gtpframe_pipe_wq_wake_up(unsigned long data) +{ + wake_up_interruptible_nr(>pframe_pipe_wq, 1); +} +#endif + +static void +gtp_wq_add_work(unsigned long data) +{ + queue_work(gtp_wq, (struct work_struct *)data); +} + +static int +gtp_gdbrsp_qtstart(void) +{ + int cpu; + struct gtp_entry *tpe; + struct gtp_var *tve; + +#ifdef GTP_DEBUG + printk(GTP_DEBUG "gtp_gdbrsp_qtstart\n"); +#endif + + if (gtp_start) + return -EBUSY; + +#ifdef GTP_FTRACE_RING_BUFFER + if (!tracing_is_on()) { + printk(KERN_WARNING "qtstart: Ring buffer is off. Please use " + "command " + "\"echo 1 > /sys/kernel/debug/tracing/tracing_on\" " + "open it.\n"); + return -EIO; + } +#endif + + for (tpe = gtp_list; tpe; tpe = tpe->next) { + int ret; + struct action *ae, *prev_ae = NULL; + + /* Check cond. */ + if (tpe->cond) { + ret = gtp_check_x(tpe, tpe->cond); + if (ret > 0) { + kfree(tpe->cond->u.exp.buf); + kfree(tpe->cond); + tpe->cond = NULL; + } else if (ret < 0) + return ret; + } + + /* Check X. */ + for (ae = tpe->action_list; ae; ae = ae->next) { +re_check: + if (ae->type == 'X' || ae->type == 0xff) { + ret = gtp_check_x(tpe, ae); + if (ret > 0) { + struct action *old_ae = ae; + + /* Remove ae from action_list. */ + ae = ae->next; + if (prev_ae) + prev_ae->next = ae; + else + tpe->action_list = ae; + + kfree(old_ae->u.exp.buf); + kfree(old_ae); + + if (ae) + goto re_check; + else + break; + } else if (ret < 0) + return ret; + } + + prev_ae = ae; + } + + /* Check the tracepoint that have printk. */ + 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 %p is not right. " + "Please put global variable to " + "trace state variable " + "$printk_tmp before print it.\n", + (int)tpe->num, + (void *)(CORE_ADDR)tpe->addr); + 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[30]; + 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 %p" + "command %s.\n", + (int)tpe->num, + (void *)(CORE_ADDR)tpe->addr, + 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, 30, "gtp %d %p:", (int)tpe->num, + (void *)(CORE_ADDR)tpe->addr); + 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; + } + } + } + +#ifdef GTP_RB + if (GTP_RB_PAGE_IS_EMPTY) { + if (gtp_rb_page_alloc(GTP_FRAME_SIZE) != 0) { + gtp_rb_page_free(); + return -ENOMEM; + } +#endif +#if defined(GTP_FRAME_SIMPLE) || defined(GTP_FTRACE_RING_BUFFER) + if (!gtp_frame) { +#ifdef GTP_FRAME_SIMPLE + gtp_frame = vmalloc(GTP_FRAME_SIZE); +#endif +#ifdef GTP_FTRACE_RING_BUFFER + gtp_frame = ring_buffer_alloc(GTP_FRAME_SIZE, + gtp_circular ? RB_FL_OVERWRITE + : 0); +#endif + if (!gtp_frame) + return -ENOMEM; +#endif + + gtp_frame_reset(); + } + + for_each_online_cpu(cpu) { +#ifdef CONFIG_X86 + per_cpu(rdtsc_current, cpu) = 0; + per_cpu(rdtsc_offset, cpu) = 0; +#endif + per_cpu(local_clock_current, cpu) = 0; + per_cpu(local_clock_offset, cpu) = 0; + per_cpu(gtp_handler_began, cpu) = 0; + } + + 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; + +#ifdef CONFIG_PERF_EVENTS + /* Clear pc_pe_list. */ + for_each_online_cpu(cpu) { + per_cpu(pc_pe_list, cpu) = NULL; + per_cpu(pc_pe_list_all_disabled, cpu) = 1; + } + for (tve = gtp_var_list; tve; tve = tve->next) { + if (tve->ptid == pe_tv_unknown) + continue; + if (tve->pts->event) + continue; + + /* Get event. */ + tve->pts->event = + perf_event_create_kernel_counter(&(tve->pts->attr), + tve->pts->cpu, + NULL, NULL, NULL); + if (IS_ERR(tve->pts->event)) { + int ret = PTR_ERR(tve->pts->event); + + printk(KERN_WARNING "gtp_gdbrsp_qtstart:" + "create perf_event CPU%d %d %d got error.\n", + (int)tve->pts->cpu, (int)tve->pts->attr.type, + (int)tve->pts->attr.config); + tve->pts->event = NULL; + gtp_gdbrsp_qtstop(); + return ret; + } + + /* Add event to pc_pe_list. */ + if (tve->pts->cpu >= 0) { + struct pe_tv_s *ppl = per_cpu(pc_pe_list, + tve->pts->cpu); + if (ppl == NULL) { + per_cpu(pc_pe_list, tve->pts->cpu) = tve->pts; + tve->pts->pc_next = NULL; + } else { + tve->pts->pc_next = ppl; + per_cpu(pc_pe_list, + tve->pts->cpu) = tve->pts; + } + if (tve->pts->en) + per_cpu(pc_pe_list_all_disabled, tve->pts->cpu) + = 0; + } + } +#endif + +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) + tasklet_init(>pframe_pipe_wq_tasklet, gtpframe_pipe_wq_wake_up, 0); +#endif + + gtp_start_last_errno = 0; + + for (tpe = gtp_list; tpe; tpe = tpe->next) { + tpe->reason = gtp_stop_normal; + if (!tpe->disable && tpe->addr != 0) { + int ret; + + if (!tpe->nopass) + atomic_set(&tpe->current_pass, tpe->pass); + + tasklet_init(&tpe->tasklet, gtp_wq_add_work, + (unsigned long)&tpe->work); + + if (tpe->is_kretprobe) { + if (gtp_access_cooked_clock +#ifdef CONFIG_X86 + || gtp_access_cooked_rdtsc +#endif +#ifdef CONFIG_PERF_EVENTS + || gtp_have_pc_pe +#endif + ) + tpe->kp.handler = + gtp_kp_ret_handler_plus; + else + tpe->kp.handler = gtp_kp_ret_handler; + ret = register_kretprobe(&tpe->kp); + } else { + if (gtp_access_cooked_clock +#ifdef CONFIG_X86 + || gtp_access_cooked_rdtsc +#endif +#ifdef CONFIG_PERF_EVENTS + || gtp_have_pc_pe +#endif + ) { + if (tpe->step) { + tpe->kp.kp.pre_handler = + gtp_kp_pre_handler_plus_step; + tpe->kp.kp.post_handler = + gtp_kp_post_handler_plus; + } else + tpe->kp.kp.pre_handler = + gtp_kp_pre_handler_plus; + ret = register_kprobe(&tpe->kp.kp); + } else { + tpe->kp.kp.pre_handler = + gtp_kp_pre_handler; + if (tpe->step) + tpe->kp.kp.post_handler = + gtp_kp_post_handler; + ret = register_kprobe(&tpe->kp.kp); + } + } + if (ret < 0) { + printk(KERN_WARNING "gtp_gdbrsp_qtstart:" + "register tracepoint %d %p got error.\n", + (int)tpe->num, (void *)(CORE_ADDR)tpe->addr); + if (gtp_start_ignore_error) { + gtp_start_last_errno = (uint64_t)ret; + continue; + } else { + gtp_gdbrsp_qtstop(); + return ret; + } + } + tpe->kpreg = 1; + } + } + + return 0; +} + +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; + +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, addr); + if (addnew) { + ULONGEST ulongtmp; + + 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. */ + if (pkg[0] == '\0') + return -EINVAL; + pkg++; + pkg = hex2ulongest(pkg, &ulongtmp); + if (pkg[0] == '\0') + return -EINVAL; + if (ulongtmp > 1) { + printk(KERN_WARNING "KGTP only support one step.\n"); + return -EINVAL; + } + tpe->step = (int)ulongtmp; + + /* Get pass. */ + 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') { + if (tpe->step == 0) + return -EINVAL; + pkg++; + step_action = 1; + } else if (tpe->step_action_list) + step_action = 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) { + kfree(ae); + ae = NULL; + + if (ret < 0) + return ret; + } + } + 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' || ae->type == 0xff) + && addnew && !tpe->cond) { + tpe->cond = ae; + tpe->cond->next = NULL; + } else if (!step_action && !tpe->action_list) { + tpe->action_list = ae; + atail = ae; + } else if (step_action + && !tpe->step_action_list) { + tpe->step_action_list = ae; + atail = ae; + } else { + if (atail == NULL) { + if (step_action) + atail = + tpe->step_action_list; + else + atail = + tpe->action_list; + for (; 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, addr); + 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; +#ifdef GTP_FTRACE_RING_BUFFER + if (gtp_frame) + ring_buffer_change_overwrite(gtp_frame, (int)setting); +#endif + + return 0; + } + + return 1; +} + +static int +gtp_frame_head_find_num(int num) +{ +#ifdef GTP_FRAME_SIMPLE + int tfnum = 0; + char *tmp = gtp_frame_r_start; + + do { + if (tmp == gtp_frame_end) + tmp = gtp_frame; + + 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; + } while (tmp != gtp_frame_w_start); +#endif +#ifdef GTP_FTRACE_RING_BUFFER + if (gtp_frame_current_num >= num) + gtp_frame_iter_reset(); + + while (1) { + int cpu; + + cpu = gtp_frame_iter_peek_head(); + if (cpu < 0) + break; + + if (num == gtp_frame_current_num) + return cpu; + + ring_buffer_read(gtp_frame_iter[cpu], NULL); + } +#endif +#ifdef GTP_RB + if (num < gtp_frame_current_num) + gtp_rb_read_reset(); + + while (1) { + if (gtp_frame_current_num == num) + return 0; + + if (gtp_rb_read() != 0) + break; + } +#endif + + return -1; +} + +static int +gtp_frame_head_find_addr(int inside, unsigned long lo, + unsigned long hi) +{ +#ifdef GTP_FRAME_SIMPLE + int tfnum = gtp_frame_current_num; + char *tmp; + + if (gtp_frame_current) + tmp = gtp_frame_current; + else + tmp = gtp_frame_r_start; + + do { + if (tmp == gtp_frame_end) + tmp = gtp_frame; + + 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; + } while (tmp != gtp_frame_w_start); +#endif +#ifdef GTP_FTRACE_RING_BUFFER + while (1) { + int cpu; + struct ring_buffer_event *rbe; + char *tmp; + struct pt_regs *regs = NULL; + + cpu = gtp_frame_iter_peek_head(); + if (cpu < 0) + break; + + while (1) { + ring_buffer_read(gtp_frame_iter[cpu], NULL); + rbe = ring_buffer_iter_peek(gtp_frame_iter[cpu], NULL); + if (rbe == NULL) + break; + + tmp = ring_buffer_event_data(rbe); + if (FID(tmp) == FID_HEAD) + break; + if (FID(tmp) == FID_REG) { + regs = (struct pt_regs *)(tmp + FID_SIZE); + 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)))) + return gtp_frame_head_find_num(gtp_frame_current_num); + } +#endif +#ifdef GTP_RB + struct gtp_rb_walk_s rbws; + + if (gtp_frame_current_num < 0) { + if (gtp_rb_read() != 0) + return -1; + } + + rbws.flags = GTP_RB_WALK_PASS_PAGE | GTP_RB_WALK_CHECK_END + | GTP_RB_WALK_CHECK_ID | GTP_RB_WALK_CHECK_TYPE; + rbws.type = FID_REG; + + while (1) { + char *tmp; + + rbws.end = gtp_frame_current_rb->w; + rbws.id = gtp_frame_current_id; + tmp = gtp_rb_walk(&rbws, gtp_frame_current_rb->rp); + if (rbws.reason == gtp_rb_walk_type) { + struct pt_regs *regs + = (struct pt_regs *)(tmp + FID_SIZE); + + if ((inside && GTP_REGS_PC(regs) >= lo + && GTP_REGS_PC(regs) <= hi) + || (!inside && (GTP_REGS_PC(regs) < lo + || GTP_REGS_PC(regs) > hi))) { + return 0; + } + } + + if (gtp_rb_read() != 0) + break; + } +#endif + + return -1; +} + +static int +gtp_frame_head_find_trace(ULONGEST trace) +{ +#ifdef GTP_FRAME_SIMPLE + int tfnum = gtp_frame_current_num; + char *tmp; + + if (gtp_frame_current) + tmp = gtp_frame_current; + else + tmp = gtp_frame_r_start; + + do { + if (tmp == gtp_frame_end) + tmp = gtp_frame; + + 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; + } while (tmp != gtp_frame_w_start); +#endif +#ifdef GTP_FTRACE_RING_BUFFER + while (1) { + int cpu; + struct ring_buffer_event *rbe; + char *tmp; + + cpu = gtp_frame_iter_peek_head(); + if (cpu < 0) + break; + + rbe = ring_buffer_iter_peek(gtp_frame_iter[cpu], NULL); + if (rbe == NULL) { + /* It will not happen, just for safe. */ + return -1; + } + tmp = ring_buffer_event_data(rbe); + if (trace == *(ULONGEST *) (tmp + FID_SIZE)) + return cpu; + + ring_buffer_read(gtp_frame_iter[cpu], NULL); + } +#endif +#ifdef GTP_RB + if (gtp_frame_current_num < 0) { + if (gtp_rb_read() != 0) + return -1; + } + + while (1) { + if (gtp_frame_current_tpe == trace) + return 0; + + if (gtp_rb_read() != 0) + break; + } +#endif + + return -1; +} + +static int +gtp_gdbrsp_qtframe(char *pkg) +{ + int ret = -1; +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) + int old_num = gtp_frame_current_num; +#endif + + if (gtp_start) + return -EBUSY; + + if (gtp_gtpframe_pipe_pid >= 0) + return -EBUSY; + +#ifdef GTP_DEBUG + printk(GTP_DEBUG "gtp_gdbrsp_qtframe: %s\n", pkg); +#endif + + if (atomic_read(>p_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(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(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(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(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. */ +#ifdef GTP_FRAME_SIMPLE + gtp_frame_current = NULL; + gtp_frame_current_num = -1; +#endif +#ifdef GTP_FTRACE_RING_BUFFER + gtp_frame_iter_reset(); +#endif +#ifdef GTP_RB + gtp_rb_read_reset(); +#endif + + return 0; + } + ret = gtp_frame_head_find_num((int) num); + } + +out: + if (ret < 0) { +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) + /* Set frame back to old_num. */ + if (old_num < 0) +#ifdef GTP_FTRACE_RING_BUFFER + gtp_frame_iter_reset(); +#endif +#ifdef GTP_RB + gtp_rb_read_reset(); +#endif + else + gtp_frame_head_find_num(old_num); +#endif + snprintf(gtp_rw_bufp, GTP_RW_BUFP_MAX, "F-1"); + gtp_rw_bufp += 3; + gtp_rw_size += 3; + } else { +#ifdef GTP_FRAME_SIMPLE + gtp_frame_current_tpe = *(ULONGEST *)(gtp_frame_current + + FID_SIZE + + sizeof(char *)); +#endif +#ifdef GTP_FTRACE_RING_BUFFER + struct ring_buffer_event *rbe; + char *tmp; + + rbe = ring_buffer_read(gtp_frame_iter[ret], + >p_frame_current_clock); + if (rbe == NULL) { + /* It will not happen, just for safe. */ + ret = -1; + goto out; + } + gtp_frame_current_cpu = ret; + tmp = ring_buffer_event_data(rbe); + gtp_frame_current_tpe = *(ULONGEST *)(tmp + FID_SIZE); +#endif + snprintf(gtp_rw_bufp, GTP_RW_BUFP_MAX, "F%xT%x", + gtp_frame_current_num, + (unsigned int) gtp_frame_current_tpe); + 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; + char *src_no_val; + int src_no_val_size; + int per_cpu_id = 0; + struct gtp_var **per_cpu = NULL; + int per_cpu_alloced = 0; + int ret = -EINVAL; +#ifdef CONFIG_PERF_EVENTS + enum pe_tv_id ptid = pe_tv_unknown; + struct pe_tv_s *pts = NULL; + int pts_alloced = 0; +#endif + + pkg = hex2ulongest(pkg, &num); + if (pkg[0] != ':') + goto error_out; + pkg++; + src = pkg; + pkg = hex2ulongest(pkg, &val); + if (pkg[0] != ':') + goto error_out; + + if (GTP_VAR_IS_SPECIAL(num)) { + /* Change the value of special tv. */ + var = gtp_var_find(num); + if (var) + var->val = val; + if (num == GTP_VAR_IGNORE_ERROR_ID) + gtp_start_ignore_error = (int)val; + else if (num == GTP_VAR_PIPE_TRACE_ID) + gtp_pipe_trace = (int)val; + + return 0; + } + + /* src_no_val is not include the val but the ':' after it. */ + src_no_val = pkg; + src_no_val_size = strlen(src_no_val); + + pkg++; + + var = gtp_var_find(num); + if (var) + goto error_out; + + /* Check if this is a "pc_" or "per_cpu_" trace state variable. */ + if (strncasecmp(pkg, "0:70635f", 8) == 0 + || strncasecmp(pkg, "0:7065725f6370755f", 18) == 0) { + int name_size; + char *id_s; + int mul = 1; + struct gtp_var *tve; + + if (strncasecmp(pkg, "0:70635f", 8) == 0) + pkg += 8; + else + pkg += 18; + name_size = strlen(pkg); + + /* Get the cpu id of this variable. */ + if (name_size % 2 != 0) + goto error_out; + for (id_s = pkg + name_size - 2; id_s > pkg; id_s -= 2) { + int i, j; + + if (!hex2int(id_s[0], &i)) + goto error_out; + if (!hex2int(id_s[1], &j)) + goto error_out; + j |= (i << 4); + if (j < 0x30 || j > 0x39) + break; + j -= 0x30; + per_cpu_id += mul * j; + mul *= 10; + /* src_no_val_size will not include the cpu id. */ + src_no_val_size -= 2; + } + if (mul == 1) + goto error_out; + if (per_cpu_id >= gtp_cpu_number) { + printk(KERN_WARNING "gtp_gdbrsp_qtdv: id %d is bigger " + "than cpu number %d.\n", + per_cpu_id, gtp_cpu_number); + goto error_out; + } + + /* Find the per cpu array per_cpu. */ + for (tve = gtp_var_list; tve; tve = tve->next) { + if (tve->per_cpu) { + char *gtp_var_src; + /* Let gtp_var_src point after the value. */ + gtp_var_src = hex2ulongest(tve->src, NULL); + + if (strncmp(gtp_var_src, src_no_val, + src_no_val_size) == 0) { + per_cpu = tve->per_cpu; + break; + } + } + } + if (per_cpu == NULL) { + per_cpu = kcalloc(gtp_cpu_number, + sizeof(struct gtp_var *), + GFP_KERNEL); + if (per_cpu == NULL) { + ret = -ENOMEM; + goto error_out; + } + per_cpu_alloced = 1; +#ifdef GTP_DEBUG + printk(GTP_DEBUG "gtp_gdbrsp_qtdv: Create a " + "new per_cpu list for %s and set var " + "to cpu %d.\n", + src_no_val, per_cpu_id); +#endif + } else { +#ifdef GTP_DEBUG + printk(GTP_DEBUG "gtp_gdbrsp_qtdv: Find a " + "per_cpu list for %s and set var " + "to cpu %d.\n", + src_no_val, per_cpu_id); +#endif + } + } else { + /* Remove first "0:" for following code. */ + if (strlen(pkg) <= 2) + goto error_out; + pkg += 2; + } + + /* Check if this is a "pe_" OR "perf_event_" trace state variable. */ + if (strncasecmp(pkg, "70655f", 6) == 0 + || strncasecmp(pkg, "706572665f6576656e745f", 22) == 0) { +#ifdef CONFIG_PERF_EVENTS + struct gtp_var *tve; + + if (strncasecmp(pkg, "70655f", 6) == 0) + pkg += 6; + else + pkg += 22; + + if (strncasecmp(pkg, "6370755f", 8) == 0) { + /* "cpu_" */ + pkg += 8; + ptid = pe_tv_cpu; + } else if (strncasecmp(pkg, "747970655f", 10) == 0) { + /* "type_" */ + pkg += 10; + ptid = pe_tv_type; + } else if (strncasecmp(pkg, "636f6e6669675f", 14) == 0) { + /* "config_" */ + pkg += 14; + ptid = pe_tv_config; + } else if (strncasecmp(pkg, "656e5f", 6) == 0) { + /* "en_" */ + pkg += 6; + ptid = pe_tv_en; + } else if (strncasecmp(pkg, "76616c5f", 8) == 0) { + /* "val_" */ + pkg += 8; + ptid = pe_tv_val; + } else if (strncasecmp(pkg, "656e61626c65645f", 16) == 0) { + /* "enabled_" */ + pkg += 16; + ptid = pe_tv_enabled; + } else if (strncasecmp(pkg, "72756e6e696e675f", 16) == 0) { + /* "running_" */ + pkg += 16; + ptid = pe_tv_running; + } else + goto pe_format_error; + + if (strlen(pkg) <= 0) + goto pe_format_error; + + /* Find the pe_tv that name is pkg. */ + for (tve = gtp_var_list; tve; tve = tve->next) { + if (tve->ptid != pe_tv_unknown) { + if (strcmp(tve->pts->name, pkg) == 0) + break; + } + } + + if (tve) + pts = tve->pts; + else { + pts = kcalloc(1, sizeof(struct pe_tv_s), GFP_KERNEL); + if (pts == NULL) { + ret = -ENOMEM; + goto error_out; + } + pts_alloced = 1; + /* Init the value in pts to default value. */ + pts->name = gtp_strdup(pkg, NULL); + if (per_cpu) + pts->cpu = per_cpu_id; + else + pts->cpu = -1; + pts->en = 0; + pts->attr.type = PERF_TYPE_HARDWARE; + pts->attr.config = PERF_COUNT_HW_CPU_CYCLES; + pts->attr.disabled = 1; + pts->attr.pinned = 1; + pts->attr.size = sizeof(struct perf_event_attr); + } + + /* Set current val to pts. */ + switch (ptid) { + case pe_tv_cpu: + pts->cpu = (int)(LONGEST)val; + break; + case pe_tv_type: + pts->attr.type = val; + break; + case pe_tv_config: + pts->attr.config = val; + break; + case pe_tv_en: + if (val) { + pts->attr.disabled = 0; + pts->en = 1; + } else { + pts->attr.disabled = 1; + pts->en = 0; + } + break; + case pe_tv_val: + case pe_tv_enabled: + case pe_tv_running: + break; + default: + goto pe_format_error; + break; + } + + gtp_have_pc_pe = 1; +#else + printk(KERN_WARNING "Current Kernel doesn't open " + "CONFIG_PERF_EVENTS\n"); + ret = -ENXIO; + goto error_out; +#endif + } + +#ifdef CONFIG_PERF_EVENTS + if (!gtp_var_add((unsigned int)num, (uint64_t)val, src, + per_cpu, per_cpu_id, ptid, pts)) { +#else + if (!gtp_var_add((unsigned int)num, (uint64_t)val, src, + per_cpu, per_cpu_id)) { +#endif + ret = -ENOMEM; + goto error_out; + } + + return 0; + +#ifdef CONFIG_PERF_EVENTS +pe_format_error: + printk(KERN_WARNING "The format of this perf event " + "trace state variables is not right.\n"); +#endif + +error_out: +#ifdef CONFIG_PERF_EVENTS + if (pts_alloced) + kfree(pts); +#endif + if (per_cpu_alloced) + kfree(per_cpu); + return ret; +} + +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 bufmax) +{ + int size = 0; + int tfnum = 0; + CORE_ADDR tmpaddr; + +#ifdef GTP_RB + if (GTP_RB_PAGE_IS_EMPTY) { +#endif +#if defined(GTP_FRAME_SIMPLE) || defined(GTP_FTRACE_RING_BUFFER) + if (!gtp_frame) { +#endif + snprintf(buf, bufmax, "tnotrun:0;"); + buf += 10; + size += 10; + bufmax -= 10; + } else if (!tpe || (tpe && tpe->reason == gtp_stop_normal)) { + snprintf(buf, bufmax, "tstop:0;"); + buf += 8; + size += 8; + bufmax -= 8; + } else { + char outtmp[100]; + + switch (tpe->reason) { + case gtp_stop_frame_full: + snprintf(buf, bufmax, "tfull:%lx;", + (unsigned long)tpe->num); + break; + case gtp_stop_efault: + snprintf(buf, bufmax, "terror:%s:%lx;", + string2hex("read memory false", outtmp), + (unsigned long)tpe->num); + break; + case gtp_stop_access_wrong_reg: + snprintf(buf, bufmax, "terror:%s:%lx;", + string2hex("access wrong register", outtmp), + (unsigned long)tpe->num); + break; + case gtp_stop_agent_expr_code_error: + snprintf(buf, bufmax, "terror:%s:%lx;", + string2hex("agent expression code error", + outtmp), + (unsigned long)tpe->num); + break; + case gtp_stop_agent_expr_stack_overflow: + snprintf(buf, bufmax, "terror:%s:%lx;", + string2hex("agent expression stack overflow", + outtmp), + (unsigned long)tpe->num); + break; + default: + buf[0] = '\0'; + break; + } + + size += strlen(buf); + bufmax -= strlen(buf); + buf += strlen(buf); + } + + if (atomic_read(>p_frame_create)) { +#ifdef GTP_FRAME_SIMPLE + char *tmp = gtp_frame_r_start; + + do { + if (tmp == gtp_frame_end) + tmp = gtp_frame; + + if (FID(tmp) == FID_HEAD) + tfnum++; + + tmp = gtp_frame_next(tmp); + if (!tmp) + break; + } while (tmp != gtp_frame_w_start); +#endif +#ifdef GTP_FTRACE_RING_BUFFER + if (gtp_start) { + /* XXX: It is just the number of entries. */ + tfnum = (int)ring_buffer_entries(gtp_frame); + } else { + int old_num = gtp_frame_current_num; + int cpu; + + gtp_frame_iter_reset(); + + for_each_online_cpu(cpu) { + char *tmp; + struct ring_buffer_event *rbe; + + while (1) { + rbe = ring_buffer_read + (gtp_frame_iter[cpu], NULL); + if (rbe == NULL) + break; + tmp = ring_buffer_event_data(rbe); + if (FID(tmp) == FID_HEAD) + tfnum++; + } + } + + if (old_num == -1) + gtp_frame_iter_reset(); + else if (old_num >= 0) { + gtp_frame_head_find_num(old_num); + ring_buffer_read + (gtp_frame_iter[gtp_frame_current_cpu], + NULL); + } + } +#endif +#ifdef GTP_RB + int cpu; + struct gtp_rb_walk_s rbws; + + rbws.flags = GTP_RB_WALK_PASS_PAGE | GTP_RB_WALK_CHECK_END; + + for_each_online_cpu(cpu) { + struct gtp_rb_s *rb + = (struct gtp_rb_s *)per_cpu_ptr(gtp_rb, cpu); + void *tmp; + unsigned long flags; + + GTP_RB_LOCK_IRQ(rb, flags); + rbws.end = rb->w; + tmp = rb->r; + while (1) { + tmp = gtp_rb_walk(&rbws, tmp); + if (rbws.reason != gtp_rb_walk_new_entry) + break; + tfnum++; + tmp += FRAME_ALIGN(GTP_FRAME_HEAD_SIZE); + } + GTP_RB_UNLOCK_IRQ(rb, flags); + } +#endif + } + + snprintf(buf, bufmax, "tframes:%x;", tfnum); + size += strlen(buf); + bufmax -= strlen(buf); + buf += strlen(buf); + + snprintf(buf, bufmax, "tcreated:%x;", atomic_read(>p_frame_create)); + size += strlen(buf); + bufmax -= strlen(buf); + buf += strlen(buf); + +#ifdef GTP_FRAME_SIMPLE + snprintf(buf, bufmax, "tsize:%x;", GTP_FRAME_SIZE); +#endif +#ifdef GTP_FTRACE_RING_BUFFER + if (gtp_frame) + snprintf(buf, bufmax, "tsize:%lx;", + ring_buffer_size(gtp_frame)); + else + snprintf(buf, bufmax, "tsize:%x;", + GTP_FRAME_SIZE * num_online_cpus()); +#endif +#ifdef GTP_RB + snprintf(buf, bufmax, "tsize:%lx;", + gtp_rb_page_count * GTP_RB_DATA_MAX * num_online_cpus()); +#endif + size += strlen(buf); + bufmax -= strlen(buf); + buf += strlen(buf); + +#ifdef GTP_FRAME_SIMPLE + spin_lock(>p_frame_lock); + if (gtp_frame_is_circular) + tmpaddr = 0; + else + tmpaddr = GTP_FRAME_SIZE - (gtp_frame_w_start - gtp_frame); + spin_unlock(>p_frame_lock); +#endif +#ifdef GTP_FTRACE_RING_BUFFER + /* XXX: Ftrace ring buffer don't have interface to get the size of free + buffer. */ + tmpaddr = 0; +#endif +#ifdef GTP_RB + if (atomic_read(>p_frame_create)) { + int cpu; + + tmpaddr = 0; + for_each_online_cpu(cpu) { + struct gtp_rb_s *rb + = (struct gtp_rb_s *)per_cpu_ptr(gtp_rb, cpu); + void *tmp; + unsigned long flags; + + GTP_RB_LOCK_IRQ(rb, flags); + tmpaddr += GTP_RB_END(rb->w) - rb->w; + for (tmp = GTP_RB_NEXT(rb->w); + GTP_RB_HEAD(tmp) != GTP_RB_HEAD(rb->r); + tmp = GTP_RB_NEXT(tmp)) + tmpaddr += GTP_RB_DATA_MAX; + tmpaddr += rb->r - GTP_RB_DATA(rb->r); + GTP_RB_UNLOCK_IRQ(rb, flags); + } + } else { + tmpaddr = gtp_rb_page_count * GTP_RB_DATA_MAX + * num_online_cpus(); + } +#endif + snprintf(buf, bufmax, "tfree:%lx;", (unsigned long)tmpaddr); + size += strlen(buf); + bufmax -= strlen(buf); + buf += strlen(buf); + + snprintf(buf, bufmax, "circular:%x;", gtp_circular); + size += strlen(buf); + bufmax -= strlen(buf); + buf += strlen(buf); + + snprintf(buf, bufmax, "disconn:%x", gtp_disconnected_tracing); + size += strlen(buf); + bufmax -= 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(); + + snprintf(gtp_rw_bufp, GTP_RW_BUFP_MAX, "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_MAX); + gtp_rw_bufp += tmp; + gtp_rw_size += tmp; + + return 1; +} + +#define GTP_REPORT_TRACEPOINT_MAX (1 + 16 + 1 + 16 + 1 + 1 + 1 + \ + 20 + 1 + 16 + 1) + +static void +gtp_report_tracepoint(struct gtp_entry *gtp, char *buf, int bufmax) +{ + snprintf(buf, bufmax, "T%lx:%lx:%c:%d:%lx", (unsigned long)gtp->num, + (unsigned long)gtp->addr, (gtp->disable ? 'D' : 'E'), + gtp->step, (unsigned long)gtp->pass); +} + +static int +gtp_report_action_max(struct gtp_entry *gtp, struct action *action) +{ + return 1 + 16 + 1 + 16 + 1 + strlen(action->src) + 1; +} + +static void +gtp_report_action(struct gtp_entry *gtp, struct action *action, char *buf, + int bufmax) +{ + snprintf(buf, bufmax, "A%lx:%lx:%s", (unsigned long)gtp->num, + (unsigned long)gtp->addr, action->src); +} + +static int +gtp_report_src_max(struct gtp_entry *gtp, struct gtpsrc *src) +{ + return 1 + 16 + 1 + 16 + 1 + strlen(src->src) + 1; +} + +static void +gtp_report_src(struct gtp_entry *gtp, struct gtpsrc *src, char *buf, int bufmax) +{ + snprintf(buf, bufmax, "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_BUFP_MAX); + 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 { + if (GTP_RW_BUFP_MAX > 1) { + 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_BUFP_MAX); + 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_BUFP_MAX); + 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_BUFP_MAX); + 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 { + if (GTP_RW_BUFP_MAX > 1) { + gtp_rw_bufp[0] = 'l'; + gtp_rw_size += 1; + gtp_rw_bufp += 1; + } + } +out: + return 1; +} + +static void +gtp_report_var(void) +{ + snprintf(gtp_rw_bufp, GTP_RW_BUFP_MAX, "%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 { + if (GTP_RW_BUFP_MAX > 1) { + 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 = 0; + + pkg = hex2ulongest(pkg, &num); + + if (num == GTP_VAR_CPU_NUMBER_ID) { + val = (uint64_t)gtp_cpu_number; + goto output_value; + } else if (num == GTP_VAR_LAST_ERRNO_ID) { + val = (uint64_t)gtp_start_last_errno; + goto output_value; + } else if (num == GTP_VAR_IGNORE_ERROR_ID) { + val = (uint64_t)gtp_start_ignore_error; + goto output_value; + } else if (num == GTP_VAR_PIPE_TRACE_ID) { + val = (uint64_t)gtp_pipe_trace; + goto output_value; + } else if (num == GTP_VAR_VERSION_ID) { + val = (uint64_t)GTP_VERSION; + goto output_value; + } +#ifdef GTP_RB + else if (num == GTP_VAR_GTP_RB_DISCARD_PAGE_NUMBER) { + val = (uint64_t)atomic_read(>p_rb_discard_page_number); + goto output_value; + } +#endif + +#ifdef GTP_FRAME_SIMPLE + if (gtp_start || !gtp_frame_current) { +#elif defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) + if (gtp_start || gtp_frame_current_num < 0) { +#endif + if (num == GTP_VAR_CLOCK_ID) { + val = (uint64_t)GTP_LOCAL_CLOCK; + goto output_value; +#ifdef CONFIG_X86 + } else if (num == GTP_VAR_RDTSC_ID) { + unsigned long long a; + rdtscll(a); + val = (uint64_t)a; + goto output_value; +#endif + } else if (num == GTP_VAR_XTIME_SEC_ID + || num == GTP_VAR_XTIME_NSEC_ID) { + struct timespec time; + + getnstimeofday(&time); + if (num == GTP_VAR_XTIME_SEC_ID) + val = (uint64_t)time.tv_sec; + else + val = (uint64_t)time.tv_nsec; + + goto output_value; + } + + if (GTP_VAR_IS_SPECIAL(num)) + goto out; + var = gtp_var_find(num); + if (var == NULL) + goto out; +#ifdef CONFIG_PERF_EVENTS + if (var->ptid == pe_tv_val + || var->ptid == pe_tv_enabled + || var->ptid == pe_tv_running) { + if (gtp_start) + var->pts->val = + perf_event_read_value(var->pts->event, + &(var->pts->enabled), + &(var->pts->running)); + switch (var->ptid) { + case pe_tv_val: + val = (uint64_t)(var->pts->val); + break; + case pe_tv_enabled: + val = (uint64_t)(var->pts->enabled); + break; + case pe_tv_running: + val = (uint64_t)(var->pts->running); + break; + default: + break; + } + goto out; + } +#endif + val = var->val; + } else { +#ifdef GTP_FRAME_SIMPLE + 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; + } + } +#endif +#ifdef GTP_FTRACE_RING_BUFFER + int is_first = 1; + struct ring_buffer_event *rbe; + char *tmp; + + /* Handle $cpu_id and $clock. */ + if (GTP_VAR_AUTO_TRACEV(num)) { + if (num == GTP_VAR_CLOCK_ID) + val = gtp_frame_current_clock; + else if (num == GTP_VAR_CPU_ID) + val = gtp_frame_current_cpu; + goto output_value; + } +re_find: + while (1) { + rbe = ring_buffer_iter_peek + (gtp_frame_iter[gtp_frame_current_cpu], NULL); + if (rbe == NULL) + break; + tmp = ring_buffer_event_data(rbe); + if (FID(tmp) == FID_HEAD) + break; + if (FID(tmp) == FID_VAR) { + vr = (struct gtp_frame_var *)(tmp + FID_SIZE); + if (vr->num == (unsigned int)num) + goto while_stop; + } + ring_buffer_read(gtp_frame_iter[gtp_frame_current_cpu], + NULL); + } + if (is_first) { + gtp_frame_head_find_num(gtp_frame_current_num); + ring_buffer_read(gtp_frame_iter[gtp_frame_current_cpu], + NULL); + is_first = 0; + goto re_find; + } +#endif +#ifdef GTP_RB + struct gtp_rb_walk_s rbws; + char *tmp; + + /* Handle $cpu_id. */ + if (GTP_VAR_AUTO_TRACEV(num)) { + val = gtp_frame_current_rb->cpu; + goto output_value; + } + + rbws.flags = GTP_RB_WALK_PASS_PAGE | GTP_RB_WALK_CHECK_END + | GTP_RB_WALK_CHECK_ID | GTP_RB_WALK_CHECK_TYPE; + rbws.end = gtp_frame_current_rb->w; + rbws.id = gtp_frame_current_id; + rbws.type = FID_VAR; + tmp = gtp_frame_current_rb->rp; + + while (1) { + tmp = gtp_rb_walk(&rbws, tmp); + if (rbws.reason != gtp_rb_walk_type) + break; + + vr = (struct gtp_frame_var *)(tmp + FID_SIZE); + if (vr->num == (unsigned int)num) + goto while_stop; + + tmp += FRAME_ALIGN(GTP_FRAME_VAR_SIZE); + } +#endif + vr = NULL; +while_stop: + if (vr) + val = vr->val; + } + +out: + if (var || vr) { +output_value: + snprintf(gtp_rw_bufp, GTP_RW_BUFP_MAX, "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 { + if (GTP_RW_BUFP_MAX > 1) { + 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; +} + +#ifdef GTP_RB +static char *gtp_traceframe_info; +static unsigned int gtp_traceframe_info_len; + +static int gtp_modules_traceframe_info_need_get; +static char *gtp_modules_traceframe_info; +static unsigned int gtp_modules_traceframe_info_len; + +static int +gtp_modules_traceframe_info_get(void) +{ + struct module *mod; + struct gtp_realloc_s grs; + int ret = 0; + + gtp_realloc_alloc(&grs, 0); + + if (gtp_modules_traceframe_info_len > 0) { + vfree(gtp_modules_traceframe_info); + gtp_modules_traceframe_info_len = 0; + } + + mutex_lock(&module_mutex); + list_for_each_entry_rcu(mod, &(THIS_MODULE->list), list) { + if (__module_address((unsigned long)mod)) { + char buf[70]; + + snprintf(buf, 70, + "\n", + (ULONGEST)mod->module_core, + (ULONGEST)mod->core_text_size); + ret = gtp_realloc_str(&grs, buf, 0); + if (ret) + goto out; + } + } + gtp_modules_traceframe_info = grs.buf; + gtp_modules_traceframe_info_len = grs.size; +out: + mutex_unlock(&module_mutex); + return ret; +} + +static int +gtp_traceframe_info_get(void) +{ + struct gtp_realloc_s grs; + int ret; + struct gtp_rb_walk_s rbws; + char *tmp; + + if (gtp_traceframe_info_len > 0) { + vfree(gtp_traceframe_info); + gtp_traceframe_info_len = 0; + } + /* 40 is size for "\n\n" */ + ret = gtp_realloc_alloc(&grs, 40); + if (ret != 0) + return ret; + + ret = gtp_realloc_str(&grs, "\n", 0); + if (ret != 0) + return ret; + + rbws.flags = GTP_RB_WALK_PASS_PAGE + | GTP_RB_WALK_CHECK_END + | GTP_RB_WALK_CHECK_ID + | GTP_RB_WALK_CHECK_TYPE; + rbws.end = gtp_frame_current_rb->w; + rbws.id = gtp_frame_current_id; + rbws.type = FID_MEM; + tmp = gtp_frame_current_rb->rp; + + while (1) { + struct gtp_frame_mem *mr; + char buf[70]; + + tmp = gtp_rb_walk(&rbws, tmp); + if (rbws.reason != gtp_rb_walk_type) + break; + mr = (struct gtp_frame_mem *) (tmp + FID_SIZE); + snprintf(buf, 70, + "\n", + (ULONGEST)mr->addr, (ULONGEST)mr->size); + ret = gtp_realloc_str(&grs, buf, 0); + if (ret != 0) + return ret; + tmp += FRAME_ALIGN(GTP_FRAME_MEM_SIZE + mr->size); + } + + if (gtp_modules_traceframe_info_need_get) { + int ret = gtp_modules_traceframe_info_get(); + if (ret != 0) + return ret; + gtp_modules_traceframe_info_need_get = 0; + } + if (gtp_modules_traceframe_info_len > 0) { + tmp = gtp_realloc(&grs, gtp_modules_traceframe_info_len, 0); + if (tmp == NULL) + return -ENOMEM; + memcpy(tmp, gtp_modules_traceframe_info, + gtp_modules_traceframe_info_len); + } + + ret = gtp_realloc_str(&grs, "\n", 1); + if (ret != 0) + return ret; + + gtp_traceframe_info = grs.buf; + gtp_traceframe_info_len = grs.size; + + return 0; +} + +static int +gtp_gdbrsp_qxfer_traceframe_info_read(char *pkg) +{ + ULONGEST offset, len; + + if (gtp_start || gtp_frame_current_num < 0) + return -EINVAL; + + pkg = hex2ulongest(pkg, &offset); + if (pkg[0] != ',') + return -EINVAL; + pkg++; + pkg = hex2ulongest(pkg, &len); + if (len == 0) + return -EINVAL; + + if (GTP_RW_BUFP_MAX < 10) + return -EINVAL; + + if (offset == 0) { + int ret = gtp_traceframe_info_get(); + if (ret != 0) + return ret; + } + + if (len > GTP_RW_BUFP_MAX - 1) + len = GTP_RW_BUFP_MAX - 1; + + if (len >= gtp_traceframe_info_len - offset) { + len = gtp_traceframe_info_len - offset; + gtp_rw_bufp[0] = 'l'; + gtp_rw_size += 1; + gtp_rw_bufp += 1; + } else { + if (GTP_RW_BUFP_MAX > 1) { + gtp_rw_bufp[0] = 'm'; + gtp_rw_size += 1; + gtp_rw_bufp += 1; + } + } + + memcpy(gtp_rw_bufp, gtp_traceframe_info + offset, len); + gtp_rw_size += len; + gtp_rw_bufp += len; + + return 1; +} +#endif + +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_BUFP_MAX / 2), + (int)len); + +#ifdef GTP_DEBUG + printk(GTP_DEBUG "gtp_gdbrsp_m: addr = 0x%lx len = %d\n", + (unsigned long) addr, (int) len); +#endif + +#ifdef GTP_FRAME_SIMPLE + if (gtp_start || !gtp_frame_current) { +#elif defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) + if (gtp_start || gtp_frame_current_num < 0) { +#endif + if (probe_kernel_read(gtp_m_buffer, (void *)(CORE_ADDR)addr, + (size_t)len)) + return -EFAULT; + } else { +#ifdef GTP_FRAME_SIMPLE + char *next; +#endif + int ret; + + /* XXX: Issue 1: 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); +#ifdef GTP_FRAME_SIMPLE + 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; + } + } + } +#endif +#ifdef GTP_FTRACE_RING_BUFFER + gtp_frame_head_find_num(gtp_frame_current_num); + ring_buffer_read(gtp_frame_iter[gtp_frame_current_cpu], NULL); + + while (1) { + struct ring_buffer_event *rbe; + char *tmp; + + rbe = ring_buffer_iter_peek + (gtp_frame_iter[gtp_frame_current_cpu], NULL); + if (rbe == NULL) + break; + tmp = ring_buffer_event_data(rbe); + if (FID(tmp) == FID_HEAD) + break; + if (FID(tmp) == FID_MEM) { + struct gtp_frame_mem *mr; + ULONGEST cur_start, cur_end; + uint8_t *buf; + + mr = (struct gtp_frame_mem *) + (tmp + FID_SIZE); + buf = tmp + 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; + } + } + ring_buffer_read(gtp_frame_iter[gtp_frame_current_cpu], + NULL); + } +#endif +#ifdef GTP_RB + { + struct gtp_rb_walk_s rbws; + char *tmp; + + rbws.flags = GTP_RB_WALK_PASS_PAGE + | GTP_RB_WALK_CHECK_END + | GTP_RB_WALK_CHECK_ID + | GTP_RB_WALK_CHECK_TYPE; + rbws.end = gtp_frame_current_rb->w; + rbws.id = gtp_frame_current_id; + rbws.type = FID_MEM; + tmp = gtp_frame_current_rb->rp; + + while (1) { + struct gtp_frame_mem *mr; + ULONGEST cur_start, cur_end; + uint8_t *buf; + + tmp = gtp_rb_walk(&rbws, tmp); + if (rbws.reason != gtp_rb_walk_type) + break; + + mr = (struct gtp_frame_mem *) (tmp + FID_SIZE); + buf = tmp + 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; + } + + tmp += FRAME_ALIGN(GTP_FRAME_MEM_SIZE + + mr->size); + } + } +#endif + 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) +{ +#ifdef GTP_FRAME_SIMPLE + char *next; +#endif + struct pt_regs *regs; + + if (GTP_RW_BUFP_MAX < GTP_REG_ASCII_SIZE) + return -E2BIG; + +#ifdef GTP_FRAME_SIMPLE + if (gtp_start || !gtp_frame_current) { +#elif defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) + if (gtp_start || gtp_frame_current_num < 0) { +#endif + memset(gtp_rw_bufp, '0', GTP_REG_ASCII_SIZE); + goto out; + } + + /* Get the regs. */ + regs = NULL; +#ifdef GTP_FRAME_SIMPLE + 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; + } + } +#endif +#ifdef GTP_FTRACE_RING_BUFFER + { + int is_first = 1; + struct ring_buffer_event *rbe; + char *tmp; + +re_find: + while (1) { + rbe = ring_buffer_iter_peek + (gtp_frame_iter[gtp_frame_current_cpu], NULL); + if (rbe == NULL) + break; + tmp = ring_buffer_event_data(rbe); + if (FID(tmp) == FID_HEAD) + break; + if (FID(tmp) == FID_REG) { + regs = (struct pt_regs *)(tmp + FID_SIZE); + is_first = 0; + break; + } + ring_buffer_read(gtp_frame_iter[gtp_frame_current_cpu], + NULL); + } + if (is_first) { + gtp_frame_head_find_num(gtp_frame_current_num); + ring_buffer_read(gtp_frame_iter[gtp_frame_current_cpu], + NULL); + is_first = 0; + goto re_find; + } + } +#endif +#ifdef GTP_RB + { + struct gtp_rb_walk_s rbws; + char *tmp; + + rbws.flags = GTP_RB_WALK_PASS_PAGE | GTP_RB_WALK_CHECK_END + | GTP_RB_WALK_CHECK_ID | GTP_RB_WALK_CHECK_TYPE; + rbws.end = gtp_frame_current_rb->w; + rbws.id = gtp_frame_current_id; + rbws.type = FID_REG; + tmp = gtp_rb_walk(&rbws, gtp_frame_current_rb->rp); + if (rbws.reason == gtp_rb_walk_type) + regs = (struct pt_regs *)(tmp + FID_SIZE); + } +#endif + 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_without_addr(gtp_frame_current_tpe); + 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 unsigned int gtp_frame_count; + +static void +gtp_frame_count_release(void) +{ + gtp_frame_count--; + if (gtp_frame_count == 0) { + if (!gtp_disconnected_tracing) { + gtp_gdbrsp_qtstop(); + gtp_gdbrsp_qtinit(); +#ifdef GTP_RB + if (!GTP_RB_PAGE_IS_EMPTY) + gtp_rb_page_free(); +#endif +#if defined(GTP_FRAME_SIMPLE) || defined(GTP_FTRACE_RING_BUFFER) + if (gtp_frame) { +#ifdef GTP_FRAME_SIMPLE + vfree(gtp_frame); +#endif +#ifdef GTP_FTRACE_RING_BUFFER + ring_buffer_free(gtp_frame); +#endif + gtp_frame = NULL; + } +#endif + } + } +} + +static int +gtp_open(struct inode *inode, struct file *file) +{ + int ret = 0; + + down(>p_rw_lock); + if (gtp_gtp_pid >= 0) { + if (get_current()->pid != gtp_gtp_pid) { + ret = -EBUSY; + goto out; + } + } + + 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++; + + gtp_frame_count++; + + gtp_gtp_pid_count++; + if (gtp_gtp_pid < 0) + gtp_gtp_pid = get_current()->pid; + +out: + up(>p_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(>p_rw_lock); + gtp_rw_count--; + if (gtp_rw_count == 0) + vfree(gtp_rw_buf); + + gtp_frame_count_release(); + + gtp_gtp_pid_count--; + if (gtp_gtp_pid_count == 0) + gtp_gtp_pid = -1; + + up(>p_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(>p_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_t(size_t, size, 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. */ + int c1, c2; + + gtp_rw_buf[i] = '\0'; + + if (!hex2int(gtp_rw_buf[i+1], &c1) + || !hex2int(gtp_rw_buf[i+2], &c2) + || 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(>p_rw_wq, 1); + + up(>p_rw_lock); + if (down_interruptible(>p_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 '?': + snprintf(gtp_rw_bufp, GTP_RW_BUFP_MAX, "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) { +#ifdef GTP_RB + snprintf(gtp_rw_bufp, GTP_RW_BUFP_MAX, + "ConditionalTracepoints+;" + "TracepointSource+;DisconnectedTracing+;" + "qXfer:traceframe-info:read+;"); +#endif +#if defined(GTP_FRAME_SIMPLE) || defined(GTP_FTRACE_RING_BUFFER) + snprintf(gtp_rw_bufp, GTP_RW_BUFP_MAX, + "ConditionalTracepoints+;" + "TracepointSource+;DisconnectedTracing+;"); +#endif + gtp_rw_size += strlen(gtp_rw_bufp); + gtp_rw_bufp += strlen(gtp_rw_bufp); + ret = 1; + } +#ifdef GTP_RB + else if (strncmp("qXfer:traceframe-info:read::", + rsppkg, 28) == 0) + ret = gtp_gdbrsp_qxfer_traceframe_info_read(rsppkg + + 28); +#endif + break; + case 's': + case 'S': + case 'c': + case 'C': + ret = -1; + break; + } + if (ret == 0) { + snprintf(gtp_rw_bufp, GTP_RW_BUFP_MAX, "OK"); + gtp_rw_bufp += 2; + gtp_rw_size += 2; + } else if (ret < 0) { + snprintf(gtp_rw_bufp, GTP_RW_BUFP_MAX, "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] = INT2CHAR(csum >> 4); + gtp_rw_bufp[2] = INT2CHAR(csum & 0x0f); + gtp_rw_bufp = gtp_rw_buf; + gtp_rw_size += 4; + +out: + wake_up_interruptible_nr(>p_rw_wq, 1); +error_out: + up(>p_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(>p_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(>p_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(>p_rw_lock); + poll_wait(file, >p_rw_wq, wait); + if (gtp_read_ack || gtp_rw_size) + mask |= POLLIN | POLLRDNORM; + up(>p_rw_lock); + + return mask; +} + +static int +gtp_frame2file_r(struct gtp_realloc_s *grs, uint32_t *data_size, char *frame) +{ + char *wbuf; + + wbuf = gtp_realloc(grs, GTP_REG_BIN_SIZE + 1, 0); + if (!wbuf) + return -1; + + wbuf[0] = 'R'; +#ifdef GTP_FRAME_SIMPLE + gtp_regs2bin((struct pt_regs *)(frame + FID_SIZE + sizeof(char *)), + wbuf + 1); +#endif +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) + gtp_regs2bin((struct pt_regs *)(frame + FID_SIZE), wbuf + 1); +#endif + + *data_size += GTP_REG_BIN_SIZE + 1; + + return 0; +} + +static int +gtp_frame2file_m(struct gtp_realloc_s *grs, uint32_t *data_size, char *frame) +{ + struct gtp_frame_mem *mr; + uint8_t *buf; + ULONGEST addr; + size_t remaining; + +#ifdef GTP_FRAME_SIMPLE + mr = (struct gtp_frame_mem *) (frame + FID_SIZE + sizeof(char *)); +#endif +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) + mr = (struct gtp_frame_mem *) (frame + FID_SIZE); +#endif + 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_realloc(grs, 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; + + *data_size += sp; + } + + return 0; +} + +static int +gtp_frame2file_v(struct gtp_realloc_s *grs, 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_realloc(grs, sp, 0); + if (!wbuf) + return -1; + +#ifdef GTP_FRAME_SIMPLE + vr = (struct gtp_frame_var *) (frame + FID_SIZE + sizeof(char *)); +#endif +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) + vr = (struct gtp_frame_var *) (frame + FID_SIZE); +#endif + + 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); + + *data_size += sp; + + return 0; +} + +static int +#ifdef GTP_FRAME_SIMPLE +gtp_frame2file(struct gtp_realloc_s *grs, char *frame) +#endif +#ifdef GTP_FTRACE_RING_BUFFER +gtp_frame2file(struct gtp_realloc_s *grs, int cpu) +#endif +#ifdef GTP_RB +/* gtp_frame_current_rb will step inside this function. */ +gtp_frame2file(struct gtp_realloc_s *grs) +#endif +{ + int16_t *tmp16p; + char *next; + char *wbuf; + uint32_t data_size; +#ifdef GTP_FTRACE_RING_BUFFER + struct ring_buffer_event *rbe; + u64 clock; +#endif +#ifdef GTP_RB + struct gtp_rb_walk_s rbws; +#endif + + /* Head. */ + tmp16p = (int16_t *)gtp_realloc(grs, 2, 0); + if (!tmp16p) + return -1; +#ifdef GTP_FRAME_SIMPLE + *tmp16p = (int16_t)*(ULONGEST *)(frame + FID_SIZE + sizeof(char *)); +#endif +#ifdef GTP_FTRACE_RING_BUFFER + rbe = ring_buffer_read(gtp_frame_iter[cpu], &clock); + if (rbe == NULL) { + /* It will not happen, just for safe. */ + return -1; + } + next = ring_buffer_event_data(rbe); + *tmp16p = (int16_t)*(ULONGEST *)(next + FID_SIZE); +#endif +#ifdef GTP_RB + *tmp16p = (int16_t)gtp_frame_current_tpe; +#endif + /* This part is for the data_size. */ + wbuf = gtp_realloc(grs, 4, 0); + if (!wbuf) + return -1; + + /* Body. */ + data_size = 0; + +#ifdef GTP_FTRACE_RING_BUFFER + { + /* Handle $cpu_id and $clock. */ + struct gtp_frame_var *vr; + char frame[GTP_FRAME_VAR_SIZE]; + + vr = (struct gtp_frame_var *) (frame + FID_SIZE); + vr->num = GTP_VAR_CLOCK_ID; + vr->val = clock; + if (gtp_frame2file_v(grs, &data_size, frame)) + return -1; + vr->num = GTP_VAR_CPU_ID; + vr->val = cpu; + if (gtp_frame2file_v(grs, &data_size, frame)) + return -1; + } +#endif + +#ifdef GTP_RB + { + /* Handle $cpu_id. */ + struct gtp_frame_var *vr; + char tmp[GTP_FRAME_VAR_SIZE]; + + vr = (struct gtp_frame_var *) (tmp + FID_SIZE); + vr->num = GTP_VAR_CPU_ID; + vr->val = gtp_frame_current_rb->cpu; + if (gtp_frame2file_v(grs, &data_size, tmp)) + return -1; + } +#endif + +#ifdef GTP_FRAME_SIMPLE + for (next = *(char **)(frame + FID_SIZE); next; + next = *(char **)(next + FID_SIZE)) { +#elif defined(GTP_FTRACE_RING_BUFFER) + while (1) { + rbe = ring_buffer_iter_peek(gtp_frame_iter[cpu], NULL); + if (rbe == NULL) + break; + next = ring_buffer_event_data(rbe); +#endif +#ifdef GTP_RB + rbws.flags = GTP_RB_WALK_PASS_PAGE | GTP_RB_WALK_CHECK_END + | GTP_RB_WALK_CHECK_ID | GTP_RB_WALK_STEP; + rbws.end = gtp_frame_current_rb->w; + rbws.id = gtp_frame_current_id; + rbws.step = 0; + next = gtp_rb_walk(&rbws, gtp_frame_current_rb->rp); + rbws.step = 1; + while (rbws.reason == gtp_rb_walk_step) { +#endif + switch (FID(next)) { + case FID_REG: + if (gtp_frame2file_r(grs, &data_size, next)) + return -1; + break; + case FID_MEM: + if (gtp_frame2file_m(grs, &data_size, next)) + return -1; + break; + case FID_VAR: + if (gtp_frame2file_v(grs, &data_size, next)) + return -1; + break; +#ifdef GTP_FTRACE_RING_BUFFER + case FID_HEAD: + goto out; + break; +#endif + } +#ifdef GTP_FTRACE_RING_BUFFER + ring_buffer_read(gtp_frame_iter[cpu], NULL); +#endif +#ifdef GTP_RB + next = gtp_rb_walk(&rbws, next); +#endif + } + +#ifdef GTP_FTRACE_RING_BUFFER +out: +#endif +#ifdef GTP_RB + gtp_frame_current_rb->rp = next; +#endif + /* Set the data_size. */ + memcpy(grs->buf + grs->size - data_size - 4, + &data_size, 4); + + return 0; +} + +static int +gtp_frame_file_header(struct gtp_realloc_s *grs, int is_end) +{ + char *wbuf; + struct gtp_entry *tpe; + struct gtp_var *tvar; + int tmpsize; + int ret = -1; + + /* Head. */ + wbuf = gtp_realloc(grs, 8, 0); + strcpy(wbuf, "\x7fTRACE0\n"); + + /* BUG: will be a new value. */ + wbuf = gtp_realloc(grs, 100, 0); + if (!wbuf) + goto out; + snprintf(wbuf, 100, "R %x\n", GTP_REG_BIN_SIZE); + gtp_realloc_sub_size(grs, 100 - strlen(wbuf)); + + if (gtp_realloc_str(grs, "status 0;", 0)) + goto out; + + wbuf = gtp_realloc(grs, 300, 0); + if (!wbuf) + goto out; + for (tpe = gtp_list; tpe; tpe = tpe->next) { + if (tpe->reason != gtp_stop_normal) + break; + } + tmpsize = gtp_get_status(tpe, wbuf, 300); + gtp_realloc_sub_size(grs, 300 - tmpsize); + + if (gtp_realloc_str(grs, "\n", 0)) + goto out; + + /* Tval. */ + for (tvar = gtp_var_list; tvar; tvar = tvar->next) { + wbuf = gtp_realloc(grs, 200, 0); + if (!wbuf) + goto out; + snprintf(wbuf, 200, "tsv %x:%s\n", tvar->num, tvar->src); + gtp_realloc_sub_size(grs, 200 - strlen(wbuf)); + } + + /* Tracepoint. */ + for (tpe = gtp_list; tpe; tpe = tpe->next) { + struct action *ae; + struct gtpsrc *src; + + /* Tpe. */ + if (gtp_realloc_str(grs, "tp ", 0)) + goto out; + wbuf = gtp_realloc(grs, GTP_REPORT_TRACEPOINT_MAX, 0); + if (!wbuf) + goto out; + gtp_report_tracepoint(tpe, wbuf, GTP_REPORT_TRACEPOINT_MAX); + gtp_realloc_sub_size(grs, + GTP_REPORT_TRACEPOINT_MAX - strlen(wbuf)); + if (gtp_realloc_str(grs, "\n", 0)) + goto out; + /* Action. */ + for (ae = tpe->action_list; ae; ae = ae->next) { + if (gtp_realloc_str(grs, "tp ", 0)) + goto out; + tmpsize = gtp_report_action_max(tpe, ae); + wbuf = gtp_realloc(grs, tmpsize, 0); + if (!wbuf) + goto out; + gtp_report_action(tpe, ae, wbuf, tmpsize); + gtp_realloc_sub_size(grs, tmpsize - strlen(wbuf)); + if (gtp_realloc_str(grs, "\n", 0)) + goto out; + } + /* Src. */ + for (src = tpe->src; src; src = src->next) { + if (gtp_realloc_str(grs, "tp ", 0)) + goto out; + tmpsize = gtp_report_src_max(tpe, src); + wbuf = gtp_realloc(grs, tmpsize, 0); + if (!wbuf) + goto out; + gtp_report_src(tpe, src, wbuf, tmpsize); + gtp_realloc_sub_size(grs, tmpsize - strlen(wbuf)); + if (gtp_realloc_str(grs, "\n", 0)) + goto out; + } + } + + if (gtp_realloc_str(grs, "\n", is_end)) + goto out; + + ret = 0; +out: + return ret; +} + +static ssize_t +gtpframe_read(struct file *file, char __user *buf, size_t size, + loff_t *ppos) +{ + ssize_t ret = -ENOMEM; +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) + /* -2 means don't need set the frame back old number. */ + int old_num = -2; +#endif + +recheck: + down(>p_rw_lock); + if (gtp_start) { + up(>p_rw_lock); + if (wait_event_interruptible(gtpframe_wq, + !gtp_start) == -ERESTARTSYS) + return -EINTR; +#ifdef GTP_DEBUG + printk(GTP_DEBUG "gtpframe_read: goto recheck\n"); +#endif + goto recheck; + } + + /* Set gtp_frame_file if need. */ + if (!gtp_frame_file) { + char *wbuf; +#ifdef GTP_FRAME_SIMPLE + char *frame; +#endif + struct gtp_realloc_s gr; + +#ifdef GTP_FRAME_SIMPLE + if (gtp_frame_is_circular) + gr.real_size = GTP_FRAME_SIZE; + else + gr.real_size = gtp_frame_w_start - gtp_frame; +#endif +#ifdef GTP_FTRACE_RING_BUFFER + gr.real_size = + ring_buffer_entries(gtp_frame) * GTP_FRAME_HEAD_SIZE; +#endif +#ifdef GTP_RB + if (atomic_read(>p_frame_create) != 0) { + int cpu; + + for_each_online_cpu(cpu) { + + struct gtp_rb_s *rb + = (struct gtp_rb_s *)per_cpu_ptr(gtp_rb, cpu); + void *tmp; + unsigned long flags; + + GTP_RB_LOCK_IRQ(rb, flags); + gr.real_size = GTP_RB_END(rb->r) - rb->r; + for (tmp = GTP_RB_NEXT(rb->r); + GTP_RB_HEAD(tmp) != GTP_RB_HEAD(rb->w); + tmp = GTP_RB_NEXT(tmp)) + gr.real_size += GTP_RB_DATA_MAX; + gr.real_size += rb->w - GTP_RB_DATA(rb->w); + GTP_RB_UNLOCK_IRQ(rb, flags); + } + } +#endif + gr.real_size += 200; + ret = gtp_realloc_alloc(&gr, gr.real_size); + if (ret != 0) + goto out; + + if (gtp_frame_file_header(&gr, 0)) + goto out; + + /* Frame. */ + if (atomic_read(>p_frame_create) == 0) + goto end; +#ifdef GTP_FRAME_SIMPLE + frame = gtp_frame_r_start; + do { + if (frame == gtp_frame_end) + frame = gtp_frame; + + if (FID(frame) == FID_HEAD) { + if (gtp_frame2file(&gr, frame)) + goto out; + } + + frame = gtp_frame_next(frame); + if (!frame) + break; + } while (frame != gtp_frame_w_start); +#endif +#ifdef GTP_FTRACE_RING_BUFFER + old_num = gtp_frame_current_num; + gtp_frame_iter_reset(); + while (1) { + int cpu; + + cpu = gtp_frame_iter_peek_head(); + if (cpu < 0) + break; + + if (gtp_frame2file(&gr, cpu)) + goto out; + } +#endif +#ifdef GTP_RB + old_num = gtp_frame_current_num; + gtp_rb_read_reset(); + while (1) { + if (gtp_rb_read() != 0) + break; + gtp_frame2file(&gr); + } +#endif + +end: + /* End. */ + wbuf = gtp_realloc(&gr, 2, 1); + if (!wbuf) + goto out; + wbuf[0] = '\0'; + wbuf[1] = '\0'; + + gtp_frame_file = gr.buf; + gtp_frame_file_size = gr.size; + } + + /* 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: +#ifdef GTP_FTRACE_RING_BUFFER + if (old_num == -1) + gtp_frame_iter_reset(); + else if (old_num >= 0) { + gtp_frame_head_find_num(old_num); + ring_buffer_read(gtp_frame_iter[gtp_frame_current_cpu], NULL); + } +#endif +#ifdef GTP_RB + if (old_num == -1) + gtp_rb_reset(); + else if (old_num >= 0) + gtp_frame_head_find_num(old_num); +#endif + up(>p_rw_lock); + return ret; +} + +static int +gtpframe_open(struct inode *inode, struct file *file) +{ +recheck: + down(>p_rw_lock); +#ifdef GTP_RB + if (GTP_RB_PAGE_IS_EMPTY) { +#elif defined(GTP_FRAME_SIMPLE) || defined(GTP_FTRACE_RING_BUFFER) + if (!gtp_frame) { +#endif + up(>p_rw_lock); +#ifdef GTP_RB + if (wait_event_interruptible(gtpframe_wq, + !GTP_RB_PAGE_IS_EMPTY) + == -ERESTARTSYS) +#elif defined(GTP_FRAME_SIMPLE) || defined(GTP_FTRACE_RING_BUFFER) + if (wait_event_interruptible(gtpframe_wq, + gtp_frame) == -ERESTARTSYS) +#endif + return -EINTR; +#ifdef GTP_DEBUG + printk(GTP_DEBUG "gtpframe_open: goto recheck\n"); +#endif + goto recheck; + } + + if (gtp_gtpframe_pipe_pid >= 0) { + up(>p_rw_lock); + return -EBUSY; + } + + if (gtp_gtpframe_pid >= 0) { + if (get_current()->pid != gtp_gtpframe_pid) { + up(>p_rw_lock); + return -EBUSY; + } + } + + gtp_frame_count++; + + gtp_gtpframe_pid_count++; + if (gtp_gtpframe_pid < 0) + gtp_gtpframe_pid = get_current()->pid; + + up(>p_rw_lock); + return 0; +} + +static int +gtpframe_release(struct inode *inode, struct file *file) +{ + down(>p_rw_lock); + gtp_frame_count_release(); + + gtp_gtpframe_pid_count--; + if (gtp_gtpframe_pid_count == 0) + gtp_gtpframe_pid = -1; + up(>p_rw_lock); + + return 0; +} + +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) +struct gtpframe_pipe_s { + loff_t begin; + struct gtp_realloc_s *grs; + int llseek_move; +#ifdef GTP_RB + void **page; + u64 *page_id; +#endif +}; + +static int +gtpframe_pipe_open(struct inode *inode, struct file *file) +{ + int ret = -ENOMEM; + struct gtpframe_pipe_s *gps = NULL; + + down(>p_rw_lock); + + if (gtp_frame_current_num >= 0 || gtp_gtpframe_pipe_pid >= 0) { + ret = -EBUSY; + goto out; + } + gtp_gtpframe_pipe_pid = get_current()->pid; + +recheck: +#ifdef GTP_RB + if (GTP_RB_PAGE_IS_EMPTY) { +#elif defined(GTP_FTRACE_RING_BUFFER) + if (!gtp_frame) { +#endif + up(>p_rw_lock); + atomic_inc(>pframe_pipe_wq_v); +#ifdef GTP_RB + if (wait_event_interruptible(gtpframe_pipe_wq, + !GTP_RB_PAGE_IS_EMPTY) == -ERESTARTSYS) { +#elif defined(GTP_FTRACE_RING_BUFFER) + if (wait_event_interruptible(gtpframe_pipe_wq, + gtp_frame) == -ERESTARTSYS) { +#endif + ret = -EINTR; + goto out; + } +#ifdef GTP_DEBUG + printk(GTP_DEBUG "gtpframe_pipe_open: goto recheck\n"); +#endif + down(>p_rw_lock); + goto recheck; + } + + gps = kcalloc(1, sizeof(struct gtpframe_pipe_s), GFP_KERNEL); + if (gps == NULL) + goto out; + gps->grs = kcalloc(1, sizeof(struct gtp_realloc_s), GFP_KERNEL); + if (gps->grs == NULL) + goto out; +#ifdef GTP_RB + gps->page = kcalloc(gtp_cpu_number, sizeof(void *), GFP_KERNEL); + if (gps->page == NULL) + goto out; + gps->page_id = kcalloc(gtp_cpu_number, sizeof(u64), GFP_KERNEL); + if (gps->page_id == NULL) + goto out; +#endif + + file->private_data = gps; + + gtp_frame_count++; + + ret = 0; +out: + if (ret) { + gtp_gtpframe_pipe_pid = -1; + if (gps) { + kfree(gps->grs); +#ifdef GTP_RB + kfree(gps->page); + kfree(gps->page_id); +#endif + kfree(gps); + } + } + up(>p_rw_lock); + return ret; +} + +static int +gtpframe_pipe_release(struct inode *inode, struct file *file) +{ + struct gtpframe_pipe_s *gps = file->private_data; + + down(>p_rw_lock); + gtp_frame_count_release(); + + gtp_gtpframe_pipe_pid = -1; + + up(>p_rw_lock); + + if (gps) { +#ifdef GTP_RB + int cpu; + + for_each_online_cpu(cpu) { + struct gtp_rb_s *rb + = (struct gtp_rb_s *)per_cpu_ptr(gtp_rb, cpu); + if (gps->page[cpu]) + gtp_rb_put_page(rb, gps->page[cpu], 0); + } + + kfree(gps->page); + kfree(gps->page_id); +#endif + if (gps->grs) { + if (gps->grs->buf) + vfree(gps->grs->buf); + kfree(gps->grs); + } + kfree(gps); + } + + return 0; +} + +#ifdef GTP_RB +static int +gtpframe_pipe_peek(struct gtpframe_pipe_s *gps) +{ + int cpu; + u64 min_id = ULLONG_MAX; + int ret = -1; + struct gtp_rb_walk_s rbws; + + rbws.flags = 0; + + for_each_online_cpu(cpu) { + struct gtp_rb_s *rb + = (struct gtp_rb_s *)per_cpu_ptr(gtp_rb, cpu); + + if (gps->page_id[cpu] == 0) { + /* Get new page. */ + if (gps->page[cpu] == NULL) { +get_new_page: + gps->page[cpu] = gtp_rb_get_page(rb); + if (gps->page[cpu] == NULL) + continue; + } + /* Get new entry. */ + gps->page[cpu] = gtp_rb_walk(&rbws, gps->page[cpu]); + if (rbws.reason != gtp_rb_walk_new_entry) { + /* Put the page back and get a new page. */ + gtp_rb_put_page(rb, gps->page[cpu], 1); + goto get_new_page; + } + /* Get id. */ + gps->page_id[cpu] = *(u64 *)(gps->page[cpu] + FID_SIZE); + } + + if (gps->page_id[cpu] < min_id) { + min_id = gps->page_id[cpu]; + ret = cpu; + } + } + + return ret; +} +#else +static int +gtpframe_pipe_peek(void) +{ + u64 min = 0; + u64 ts; + int cpu; + struct ring_buffer_event *rbe; + char *next; + int ret = -1; + + for_each_online_cpu(cpu) { + while (1) { + rbe = ring_buffer_peek(gtp_frame, cpu, &ts, NULL); + if (rbe == NULL) + break; + next = ring_buffer_event_data(rbe); + if (FID(next) == FID_HEAD) + break; + ring_buffer_consume(gtp_frame, cpu, &ts, NULL); + } + + if (rbe) { + if ((min && ts < min) || !min) { + min = ts; + ret = cpu; + } + } + } + + return ret; +} +#endif + +static int +#ifdef GTP_RB +gtpframe_pipe_get_entry(struct gtpframe_pipe_s *gps) +#endif +#ifdef GTP_FTRACE_RING_BUFFER +gtpframe_pipe_get_entry(struct gtp_realloc_s *grs) +#endif +{ + int cpu; + int16_t *tmp16p; + uint32_t data_size; +#ifdef GTP_FTRACE_RING_BUFFER + char *next; + struct ring_buffer_event *rbe; + u64 ts; +#endif + +#ifdef GTP_RB + struct gtp_rb_walk_s rbws; + struct gtp_realloc_s *grs = gps->grs; +#endif + /* Because this function only be called when gtp_realloc_is_empty, + so grs don't need reset. */ + +#ifdef GTP_RB +#define GTP_PIPE_PEEK (cpu = gtpframe_pipe_peek(gps)) +#endif +#ifdef GTP_FTRACE_RING_BUFFER +recheck: +#define GTP_PIPE_PEEK (cpu = gtpframe_pipe_peek()) +#endif + GTP_PIPE_PEEK; + if (cpu < 0) { + /* Didn't get the buffer that have event. + Wait and recheck.*/ + atomic_inc(>pframe_pipe_wq_v); + if (wait_event_interruptible(gtpframe_pipe_wq, + GTP_PIPE_PEEK >= 0) + == -ERESTARTSYS) + return -EINTR; + } +#undef GTP_PIPE_PEEK + + /* Head. */ +#ifdef GTP_FTRACE_RING_BUFFER + rbe = ring_buffer_consume(gtp_frame, cpu, &ts, NULL); + if (rbe == NULL) + goto recheck; + next = ring_buffer_event_data(rbe); + if (FID(next) != FID_HEAD) + goto recheck; +#endif + tmp16p = (int16_t *)gtp_realloc(grs, 2, 0); + if (!tmp16p) + return -ENOMEM; +#ifdef GTP_RB + *tmp16p = (int16_t)*(ULONGEST *)(gps->page[cpu] + FID_SIZE + + sizeof(u64)); + gps->page[cpu] += FRAME_ALIGN(GTP_FRAME_HEAD_SIZE); +#endif +#ifdef GTP_FTRACE_RING_BUFFER + *tmp16p = (int16_t)*(ULONGEST *)(next + FID_SIZE); +#endif + /* This part is for the data_size. */ + if (gtp_realloc(grs, 4, 0) == NULL) + return -ENOMEM; + data_size = 0; + +#ifdef GTP_RB + { + /* Handle $cpu_id. */ + struct gtp_frame_var *vr; + char frame[GTP_FRAME_VAR_SIZE]; + + vr = (struct gtp_frame_var *) (frame + FID_SIZE); + vr->num = GTP_VAR_CPU_ID; + vr->val = cpu; + if (gtp_frame2file_v(grs, &data_size, frame)) + return -ENOMEM; + } +#endif +#ifdef GTP_FTRACE_RING_BUFFER + { + /* Handle $cpu_id and $clock. */ + struct gtp_frame_var *vr; + char frame[GTP_FRAME_VAR_SIZE]; + + + vr = (struct gtp_frame_var *) (frame + FID_SIZE); + vr->num = GTP_VAR_CLOCK_ID; + vr->val = ts; + if (gtp_frame2file_v(grs, &data_size, frame)) + return -ENOMEM; + vr->num = GTP_VAR_CPU_ID; + vr->val = cpu; + if (gtp_frame2file_v(grs, &data_size, frame)) + return -ENOMEM; + } +#endif + +#ifdef GTP_RB + rbws.flags = GTP_RB_WALK_CHECK_ID | GTP_RB_WALK_STEP; + rbws.id = gps->page_id[cpu]; +re_walk: + rbws.step = 0; + gps->page[cpu] = gtp_rb_walk(&rbws, gps->page[cpu]); + rbws.step = 1; + while (rbws.reason == gtp_rb_walk_step) { + switch (FID(gps->page[cpu])) { + case FID_REG: + if (gtp_frame2file_r(grs, &data_size, gps->page[cpu])) + return -ENOMEM; + break; + + case FID_MEM: + if (gtp_frame2file_m(grs, &data_size, gps->page[cpu])) + return -ENOMEM; + break; + + case FID_VAR: + if (gtp_frame2file_v(grs, &data_size, gps->page[cpu])) + return -ENOMEM; + break; + } + gps->page[cpu] = gtp_rb_walk(&rbws, gps->page[cpu]); + } + if (rbws.reason == gtp_rb_walk_end_page + || rbws.reason == gtp_rb_walk_error) { + /* Put this page back. */ + gtp_rb_put_page((struct gtp_rb_s *)per_cpu_ptr(gtp_rb, cpu), + gps->page[cpu], 1); + gps->page[cpu] = gtp_rb_get_page((struct gtp_rb_s *)per_cpu_ptr + (gtp_rb, cpu)); + if (gps->page[cpu]) + goto re_walk; + } + gps->page_id[cpu] = 0; +#endif +#ifdef GTP_FTRACE_RING_BUFFER + while (1) { +#define GTP_PIPE_CONSUME (rbe = ring_buffer_consume(gtp_frame, cpu, NULL, NULL)) + GTP_PIPE_CONSUME; + if (rbe == NULL) { + if (!gtp_start) + break; + + atomic_inc(>pframe_pipe_wq_v); + if (wait_event_interruptible(gtpframe_pipe_wq, + GTP_PIPE_CONSUME + != NULL) + == -ERESTARTSYS) + return -EINTR; + continue; + } +#undef GTP_PIPE_CONSUME + next = ring_buffer_event_data(rbe); + switch (FID(next)) { + case FID_REG: + if (gtp_frame2file_r(grs, &data_size, next)) + return -ENOMEM; + break; + + case FID_MEM: + if (gtp_frame2file_m(grs, &data_size, next)) + return -ENOMEM; + break; + + case FID_VAR: + if (gtp_frame2file_v(grs, &data_size, next)) + return -ENOMEM; + break; + + case FID_HEAD: + case FID_END: + goto while_out; + break; + } + } +while_out: +#endif + /* Set the data_size. */ + memcpy(grs->buf + grs->size - data_size - 4, &data_size, 4); + + return 0; +} + +static ssize_t +gtpframe_pipe_read(struct file *file, char __user *buf, size_t size, + loff_t *ppos) +{ + ssize_t ret = -ENOMEM; + struct gtpframe_pipe_s *gps = file->private_data; + loff_t entry_offset; + +#ifdef GTP_DEBUG + printk(GTP_DEBUG "gtpframe_pipe_read: size=%u *ppos=%lld\n", + size, *ppos); +#endif + + if (!gtp_realloc_is_alloced(gps->grs)) { + ret = gtp_realloc_alloc(gps->grs, 200); + if (ret != 0) + goto out; + } else if (*ppos < gps->begin + || *ppos >= (gps->begin + gps->grs->size)) { + gtp_realloc_reset(gps->grs); + + if (gps->llseek_move) { + /* clear user will return NULL. + Then GDB tfind got a fail. */ + if (size > 2) + size = 2; + if (clear_user(buf, size)) { + ret = -EFAULT; + goto out; + } + gps->begin = 0; + gps->llseek_move = 0; + ret = size; + goto out; + } + } + + if (gtp_realloc_is_empty(gps->grs)) { + if (*ppos == 0) { + + if (gtp_frame_file_header(gps->grs, 1)) + goto out; +#ifdef GTP_DEBUG + printk(GTP_DEBUG "gtpframe_pipe_read: Get header.\n"); +#endif + } else { +#ifdef GTP_RB + ret = gtpframe_pipe_get_entry(gps); +#endif +#ifdef GTP_FTRACE_RING_BUFFER + ret = gtpframe_pipe_get_entry(gps->grs); +#endif + if (ret < 0) + goto out; +#ifdef GTP_DEBUG + printk(GTP_DEBUG "gtpframe_pipe_read: Get entry.\n"); +#endif + } + gps->begin = *ppos; + } + +#ifdef GTP_DEBUG + printk(GTP_DEBUG "gtpframe_pipe_read: gps->begin=%lld " + "gps->grs->size=%u\n", + gps->begin, gps->grs->size); +#endif + + entry_offset = *ppos - gps->begin; + ret = size; + if (entry_offset + size > gps->grs->size) + ret = gps->grs->size - entry_offset; + if (copy_to_user(buf, gps->grs->buf + entry_offset, ret)) { + ret = -EFAULT; + goto out; + } + *ppos += ret; + +out: + return ret; +} + +static loff_t +gtpframe_pipe_llseek(struct file *file, loff_t offset, int origin) +{ + struct gtpframe_pipe_s *gps = file->private_data; + loff_t ret = default_llseek(file, offset, origin); + + if (ret < 0) + return ret; + + /* True means that GDB tfind to next frame entry. */ + if (ret >= gps->begin + gps->grs->size && gps->begin) + gps->llseek_move = 1; + + return ret; +} +#endif + +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 = gtpframe_open, + .release = gtpframe_release, + .read = gtpframe_read, + .llseek = default_llseek, +}; + +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) +static const struct file_operations gtpframe_pipe_operations = { + .owner = THIS_MODULE, + .open = gtpframe_pipe_open, + .release = gtpframe_pipe_release, + .read = gtpframe_pipe_read, + .llseek = gtpframe_pipe_llseek, +}; +#endif + +struct dentry *gtp_dir; +struct dentry *gtpframe_dir; +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) +struct dentry *gtpframe_pipe_dir; +#endif + +static int +gtp_modules_load_del_notify(struct notifier_block *self, unsigned long val, + void *data) +{ + if (val == MODULE_STATE_COMING) + return 0; + + down(>p_rw_lock); + gtp_modules_traceframe_info_need_get = 1; + up(>p_rw_lock); + + return 0; +} + +static struct notifier_block gtp_modules_load_del_nb = { + .notifier_call = gtp_modules_load_del_notify, +}; + +static int __init gtp_init(void) +{ + int ret = -ENOMEM; + + gtp_gtp_pid = -1; + gtp_gtp_pid_count = 0; + gtp_gtpframe_pid = -1; + gtp_gtpframe_pid_count = 0; +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) + gtp_gtpframe_pipe_pid = -1; +#endif + 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; +#if defined(GTP_FRAME_SIMPLE) || defined(GTP_FTRACE_RING_BUFFER) + gtp_frame = NULL; +#endif + gtp_frame_current_num = -1; + gtp_frame_current_tpe = 0; +#ifdef GTP_FRAME_SIMPLE + gtp_frame_r_start = NULL; + gtp_frame_w_start = NULL; + gtp_frame_end = NULL; + gtp_frame_current = NULL; + gtp_frame_is_circular = 0; +#endif +#ifdef GTP_FTRACE_RING_BUFFER + { + int cpu; + + for_each_online_cpu(cpu) + gtp_frame_iter[cpu] = NULL; + } + gtp_frame_current_cpu = 0; +#endif +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) + atomic_set(>pframe_pipe_wq_v, 0); +#endif + atomic_set(>p_frame_create, 0); + gtp_rw_count = 0; + gtp_frame_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; +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) + gtpframe_pipe_dir = NULL; +#endif + { + int cpu; + + gtp_cpu_number = 0; + for_each_online_cpu(cpu) { + if (cpu > gtp_cpu_number) + gtp_cpu_number = cpu; + } + gtp_cpu_number++; + } + gtp_start_last_errno = 0; + gtp_start_ignore_error = 0; + gtp_pipe_trace = 0; +#ifdef GTP_RB + gtp_traceframe_info = NULL; + gtp_traceframe_info_len = 0; +#endif + +#ifdef GTP_RB + ret = gtp_rb_init(); + if (ret != 0) + goto out; +#endif + + 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, >p_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, >pframe_operations); + if (gtpframe_dir == NULL || gtpframe_dir == ERR_PTR(-ENODEV)) { + gtpframe_dir = NULL; + goto out; + } +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) + gtpframe_pipe_dir = debugfs_create_file("gtpframe_pipe", + S_IFIFO | S_IRUSR, NULL, NULL, + >pframe_pipe_operations); + if (gtpframe_pipe_dir == NULL + || gtpframe_pipe_dir == ERR_PTR(-ENODEV)) { + gtpframe_pipe_dir = NULL; + goto out; + } +#endif + + gtp_modules_traceframe_info_need_get = 1; + gtp_modules_traceframe_info = NULL; + gtp_modules_traceframe_info_len = 0; + if (register_module_notifier(>p_modules_load_del_nb)) + goto out; + + ret = 0; +out: + if (ret < 0) { + if (gtp_wq) + destroy_workqueue(gtp_wq); + + if (gtp_dir) + debugfs_remove_recursive(gtp_dir); + if (gtpframe_dir) + debugfs_remove_recursive(gtpframe_dir); +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) + if (gtpframe_pipe_dir) + debugfs_remove_recursive(gtpframe_pipe_dir); +#endif + +#ifdef GTP_RB + gtp_rb_release(); +#endif + } + + return ret; +} + +static void __exit gtp_exit(void) +{ + unregister_module_notifier(>p_modules_load_del_nb); + + if (gtp_dir) + debugfs_remove_recursive(gtp_dir); + if (gtpframe_dir) + debugfs_remove_recursive(gtpframe_dir); +#if defined(GTP_FTRACE_RING_BUFFER) || defined(GTP_RB) + if (gtpframe_pipe_dir) + debugfs_remove_recursive(gtpframe_pipe_dir); +#endif + + gtp_gdbrsp_qtstop(); + gtp_gdbrsp_qtinit(); +#ifdef GTP_RB + if (!GTP_RB_PAGE_IS_EMPTY) + gtp_rb_page_free(); +#endif +#if defined(GTP_FRAME_SIMPLE) || defined(GTP_FTRACE_RING_BUFFER) + if (gtp_frame) { +#ifdef GTP_FRAME_SIMPLE + vfree(gtp_frame); +#endif +#ifdef GTP_FTRACE_RING_BUFFER + ring_buffer_free(gtp_frame); +#endif + gtp_frame = NULL; + } +#endif + + destroy_workqueue(gtp_wq); + +#ifdef GTP_RB + gtp_rb_release(); +#endif +} + +module_init(gtp_init) +module_exit(gtp_exit) + +MODULE_AUTHOR("Hui Zhu "); +MODULE_LICENSE("GPL"); --- /dev/null +++ b/lib/gtp_rb.c @@ -0,0 +1,498 @@ +/* + * Ring buffer of 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) KGTP team (https://code.google.com/p/kgtp/), 2011, 2012 + * + */ + +#define ADDR_SIZE sizeof(size_t) +#define GTP_RB_HEAD(addr) ((void *)((size_t)(addr) & PAGE_MASK)) +#define GTP_RB_DATA(addr) (GTP_RB_HEAD(addr) + ADDR_SIZE) +#define GTP_RB_END(addr) (GTP_RB_HEAD(addr) + PAGE_SIZE - ADDR_SIZE) +#define GTP_RB_PREV(addr) (*(void **)GTP_RB_HEAD(addr)) +#define GTP_RB_NEXT(addr) (*(void **)GTP_RB_END(addr)) +#define GTP_RB_DATA_MAX (PAGE_SIZE - ADDR_SIZE - ADDR_SIZE - FID_SIZE \ + - sizeof(u64)) + +struct gtp_rb_s { + spinlock_t lock; + void *w; + void *prev_w; + void *r; + void *rp; + u64 rp_id; + int cpu; +}; + +static struct gtp_rb_s __percpu *gtp_rb; +static atomic64_t gtp_rb_count; +static unsigned int gtp_rb_page_count; +static atomic_t gtp_rb_discard_page_number; + +static int +gtp_rb_init(void) +{ + int cpu; + + gtp_rb = alloc_percpu(struct gtp_rb_s); + if (!gtp_rb) + return -ENOMEM; + + for_each_online_cpu(cpu) { + struct gtp_rb_s *rb + = (struct gtp_rb_s *)per_cpu_ptr(gtp_rb, cpu); + memset(rb, 0, sizeof(struct gtp_rb_s)); + rb->lock = __SPIN_LOCK_UNLOCKED(rb->lock); + rb->cpu = cpu; + } + gtp_rb_page_count = 0; + atomic_set(>p_rb_discard_page_number, 0); + + return 0; +} + +static void +gtp_rb_release(void) +{ + if (gtp_rb) { + free_percpu(gtp_rb); + gtp_rb = NULL; + } +} + +static void +gtp_rb_reset(void) +{ + int cpu; + + for_each_online_cpu(cpu) { + struct gtp_rb_s *rb + = (struct gtp_rb_s *)per_cpu_ptr(gtp_rb, cpu); + rb->w = GTP_RB_DATA(rb->w); + rb->r = rb->w; + rb->rp = NULL; + rb->rp_id = 0; + } + + atomic64_set(>p_rb_count, 0); + atomic_set(>p_rb_discard_page_number, 0); +} + +static inline u64 +gtp_rb_clock(void) +{ + return atomic64_inc_return(>p_rb_count); +} + +#define GTP_RB_PAGE_IS_EMPTY (gtp_rb_page_count == 0) + +static int +gtp_rb_page_alloc(int size) +{ + int cpu; + + for_each_online_cpu(cpu) { + struct gtp_rb_s *rb + = (struct gtp_rb_s *)per_cpu_ptr(gtp_rb, cpu); + void *last = NULL, *next = NULL; + struct page *page; + int current_size; + + gtp_rb_page_count = 0; + current_size = size; + + while (1) { + if (current_size > 0) + current_size -= PAGE_SIZE; + else + break; + + page = alloc_pages_node(cpu_to_node(cpu), + GFP_KERNEL, 0); + if (!page) + return -1; + gtp_rb_page_count++; + rb->w = GTP_RB_DATA(page_address(page)); + GTP_RB_NEXT(rb->w) = next; + if (next) + GTP_RB_PREV(next) = rb->w; + next = rb->w; + if (!last) + last = rb->w; + } + + GTP_RB_NEXT(last) = next; + GTP_RB_PREV(next) = last; + rb->r = rb->w; + + if (gtp_rb_page_count < 3) + return -1; + } + + return 0; +} + +static void +gtp_rb_page_free(void) +{ + int cpu; + + for_each_online_cpu(cpu) { + struct gtp_rb_s *rb + = (struct gtp_rb_s *)per_cpu_ptr(gtp_rb, cpu); + void *need_free = NULL; + int is_first = 1; + + for (rb->r = rb->w = GTP_RB_DATA(rb->w); + is_first || rb->w != rb->r; + rb->w = GTP_RB_NEXT(rb->w)) { + if (need_free) + free_page((unsigned long)need_free); + need_free = GTP_RB_HEAD(rb->w); + is_first = 0; + } + if (need_free) + free_page((unsigned long)need_free); + } + + gtp_rb_page_count = 0; +} + +#define GTP_RB_LOCK(r) spin_lock(&r->lock); +#define GTP_RB_UNLOCK(r) spin_unlock(&r->lock); +#define GTP_RB_LOCK_IRQ(r, flags) spin_lock_irqsave(&r->lock, flags); +#define GTP_RB_UNLOCK_IRQ(r, flags) spin_unlock_irqrestore(&r->lock, flags); +#define GTP_RB_RELEASE(r) (r->prev_w = r->w) + +static void * +gtp_rb_alloc(struct gtp_rb_s *rb, size_t size, u64 id) +{ + void *ret; + + size = FRAME_ALIGN(size); + + if (size > GTP_RB_DATA_MAX) { + printk(KERN_WARNING "gtp_rb_alloc: The size %zu is too big" + "for the KGTP ring buffer. " + "The max size that KGTP ring buffer " + "support is %lu (Need sub some size for " + "inside structure).\n", size, GTP_RB_DATA_MAX); + return NULL; + } + + rb->prev_w = rb->w; + + if (rb->w + size > GTP_RB_END(rb->w)) { + /* Don't have enough size in current page, insert a + FID_PAGE_END and try to get next page. */ + if (GTP_RB_END(rb->w) - rb->w >= FID_SIZE) + FID(rb->w) = FID_PAGE_END; + + if (GTP_RB_HEAD(GTP_RB_NEXT(rb->w)) == GTP_RB_HEAD(rb->r)) { + if (gtp_circular) { + rb->r = GTP_RB_NEXT(rb->r); + atomic_inc(>p_rb_discard_page_number); + } else + return NULL; + } + rb->w = GTP_RB_NEXT(rb->w); + + if (id) { + /* Need insert a FID_PAGE_BEGIN. */ + FID(rb->w) = FID_PAGE_BEGIN; + *((u64 *)(rb->w + FID_SIZE)) = id; + rb->w += FRAME_ALIGN(GTP_FRAME_PAGE_BEGIN_SIZE); + } + } + + ret = rb->w; + rb->w += size; + + return ret; +} + +enum gtp_rb_walk_reason { + gtp_rb_walk_end = 0, + gtp_rb_walk_end_page, + gtp_rb_walk_end_entry, + gtp_rb_walk_new_entry, + gtp_rb_walk_type, + gtp_rb_walk_step, + gtp_rb_walk_error, +}; + +/* Check *end. */ +#define GTP_RB_WALK_CHECK_END 0x1 +/* When to the end of a page, goto next one. */ +#define GTP_RB_WALK_PASS_PAGE 0x2 +/* When to the end of a entry, goto next one. */ +#define GTP_RB_WALK_PASS_ENTRY 0x4 +/* Check with id and FID_PAGE_BEGIN to make sure this is the current frame. */ +#define GTP_RB_WALK_CHECK_ID 0x8 +/* Return ff type is same in buffer. */ +#define GTP_RB_WALK_CHECK_TYPE 0x10 +/* Return ff type is same in buffer. */ +#define GTP_RB_WALK_STEP 0x20 + +struct gtp_rb_walk_s { + unsigned int flags; + /* Reason for return. */ + enum gtp_rb_walk_reason reason; + /* GTP_RB_WALK_CHECK_END */ + void *end; + /* GTP_RB_WALK_CHECK_ID */ + u64 id; + /* GTP_RB_WALK_CHECK_TYPE */ + FID_TYPE type; + /* GTP_RB_WALK_STEP */ + int step; +}; + +static void * +gtp_rb_walk(struct gtp_rb_walk_s *s, void *ret) +{ + int step; + void *page_end = GTP_RB_END(ret); + + if (s->flags & GTP_RB_WALK_STEP) + step = 0; + + while (1) { + FID_TYPE fid; + + if ((s->flags & GTP_RB_WALK_CHECK_END) && ret == s->end) { + s->reason = gtp_rb_walk_end; + break; + } + + if (ret == page_end || page_end - ret < FID_SIZE + || FID(ret) == FID_PAGE_END) { + if (!(s->flags & GTP_RB_WALK_PASS_PAGE)) { + s->reason = gtp_rb_walk_end_page; + break; + } + ret = GTP_RB_NEXT(ret); + page_end = GTP_RB_END(ret); + continue; + } + + fid = FID(ret); + + if ((s->flags & GTP_RB_WALK_CHECK_TYPE) && s->type == fid) { + s->reason = gtp_rb_walk_type; + break; + } + + if ((s->flags & GTP_RB_WALK_STEP) + && (fid == FID_REG || fid == FID_MEM || fid == FID_VAR)) { + if (step >= s->step) { + s->reason = gtp_rb_walk_step; + break; + } + step++; + } + + switch (fid) { + case FID_HEAD: + if (!(s->flags & GTP_RB_WALK_PASS_ENTRY)) { + s->reason = gtp_rb_walk_new_entry; + goto out; + } + ret += FRAME_ALIGN(GTP_FRAME_HEAD_SIZE); + break; + case FID_REG: + ret += FRAME_ALIGN(GTP_FRAME_REG_SIZE); + break; + case FID_MEM: { + struct gtp_frame_mem *gfm; + + gfm = (struct gtp_frame_mem *) (ret + FID_SIZE); + ret += FRAME_ALIGN(GTP_FRAME_MEM_SIZE + + gfm->size); + } + break; + case FID_VAR: + ret += FRAME_ALIGN(GTP_FRAME_VAR_SIZE); + break; + case FID_PAGE_BEGIN: + if ((s->flags & GTP_RB_WALK_CHECK_ID) + && s->id != *(u64 *)(ret + FID_SIZE)) { + s->reason = gtp_rb_walk_end_entry; + goto out; + } + ret += FRAME_ALIGN(GTP_FRAME_PAGE_BEGIN_SIZE); + break; + default: + printk(KERN_WARNING + "Walk in gtp ring buffer got error id 0x%x " + "in 0x%p.\n", + fid, ret); + s->reason = gtp_rb_walk_error; + goto out; + break; + } + } + +out: + return ret; +} + +static struct gtp_rb_s *gtp_frame_current_rb; +static u64 gtp_frame_current_id; + +static void +gtp_rb_read_reset(void) +{ + int cpu; + + for_each_online_cpu(cpu) { + struct gtp_rb_s *rb + = (struct gtp_rb_s *)per_cpu_ptr(gtp_rb, cpu); + + rb->rp = rb->r; + rb->rp_id = 0; + } + gtp_frame_current_num = -1; + gtp_frame_current_rb = NULL; +} + +static int +gtp_rb_read(void) +{ + int cpu; + u64 min_id = ULLONG_MAX; + struct gtp_rb_walk_s rbws; + + gtp_frame_current_rb = NULL; + + rbws.flags = GTP_RB_WALK_PASS_PAGE | GTP_RB_WALK_CHECK_END; + + for_each_online_cpu(cpu) { + struct gtp_rb_s *rb + = (struct gtp_rb_s *)per_cpu_ptr(gtp_rb, cpu); + + if (rb->rp == NULL) + rb->rp = rb->r; + + if (rb->rp_id == 0) { + rbws.end = rb->w; + rb->rp = gtp_rb_walk(&rbws, rb->rp); + if (rbws.reason != gtp_rb_walk_new_entry) + continue; + rb->rp_id = *(u64 *)(rb->rp + FID_SIZE); + } + if (rb->rp_id < min_id) { + min_id = rb->rp_id; + gtp_frame_current_rb = rb; + } + } + + if (gtp_frame_current_rb == NULL) { + gtp_rb_read_reset(); + return -1; + } + + gtp_frame_current_rb->rp_id = 0; + gtp_frame_current_id = *(u64 *)(gtp_frame_current_rb->rp + FID_SIZE); + gtp_frame_current_tpe = *(ULONGEST *)(gtp_frame_current_rb->rp + + FID_SIZE + sizeof(u64)); + gtp_frame_current_rb->rp += FRAME_ALIGN(GTP_FRAME_HEAD_SIZE); + + gtp_frame_current_num += 1; + + return 0; +} + +static void * +gtp_rb_get_page(struct gtp_rb_s *rb) +{ + void *ret = NULL; + unsigned long flags; + + GTP_RB_LOCK_IRQ(rb, flags); + + if (GTP_RB_HEAD(rb->r) == GTP_RB_HEAD(rb->w)) { + if (rb->r == rb->w) + goto out; + /* Move rb->w to next page. */ + if (GTP_RB_END(rb->w) - rb->w >= FID_SIZE) + FID(rb->w) = FID_PAGE_END; + rb->w = GTP_RB_NEXT(rb->w); + } + + ret = rb->r; + { + /* Move this page out of ring. */ + void *prev = GTP_RB_PREV(rb->r); + void *next = GTP_RB_NEXT(rb->r); + + GTP_RB_NEXT(prev) = next; + GTP_RB_PREV(next) = prev; + rb->r = next; + } + +out: + GTP_RB_UNLOCK_IRQ(rb, flags); + return ret; +} + +static void +gtp_rb_put_page(struct gtp_rb_s *rb, void *page, int page_is_empty) +{ + void *prev, *next; + unsigned long flags; + + GTP_RB_LOCK_IRQ(rb, flags); + + if (page_is_empty) { + page = GTP_RB_DATA(page); + if (rb->w == GTP_RB_DATA(rb->w)) { + /* Set page before rb->w and set it as rb->w. + If need, set it as rb->r. */ + prev = GTP_RB_PREV(rb->w); + next = rb->w; + if (rb->r == rb->w) + rb->r = page; + rb->w = page; + } else { + /* Set page after rb->w. */ + prev = GTP_RB_DATA(rb->w); + next = GTP_RB_NEXT(rb->w); + } + } else { + if (rb->r == GTP_RB_DATA(rb->r)) { + /* Current rb->r page is point to the begin of a page. + Set page before rb->r and set it as rb->r. */ + prev = GTP_RB_PREV(rb->r); + next = rb->r; + } else { + /* Current rb->r page is not point to the begin of a + page, give up this data. + Set page after rb->r and set it as rb->r. */ + prev = GTP_RB_DATA(rb->r); + next = GTP_RB_NEXT(rb->r); + } + rb->r = page; + } + + GTP_RB_NEXT(prev) = GTP_RB_DATA(page); + GTP_RB_PREV(next) = GTP_RB_DATA(page); + GTP_RB_PREV(page) = prev; + GTP_RB_NEXT(page) = next; + + GTP_RB_UNLOCK_IRQ(rb, flags); +} --- /dev/null +++ b/scripts/gtp/add-ons/hotcode.py @@ -0,0 +1,588 @@ +#!/usr/bin/python + +# This script is used to find the hotcode in some tasks +# GPL +# Copyright(C) Hui Zhu (teawater@gmail.com), 2012 + +import gdb +import tempfile +import os +import signal + +debug_dir = "/usr/lib/debug/" + +from operator import itemgetter +def dict_sort(d, reverse=False): + #proposed in PEP 265, using the itemgetter + return sorted(d.iteritems(), key=itemgetter(1), reverse=True) + +def sigint_handler(num, e): + hotcode_show() + try: + s = raw_input('Continue? (yes)') + except: + s = 'y' + finally: + if s[0:1] != 'n' and s[0:1] != 'N': + return; + #gdb.execute("inferior 1") + try: + gdb.execute("tfind -1", True, False) + gdb.execute("target remote /sys/kernel/debug/gtp", True, False) + gdb.execute("set disconnected-tracing off", True, False) + except: + print "Try to stop GTP got error, please use command \"sudo rmmod gtp.ko\" stop it." + exit(1); + +def add_inferior(): + fid = gdb.execute("add-inferior", False, True) + if fid.find("Added inferior ") != 0: + return -1 + fid = int(fid[len("Added inferior "):]) + return fid + +def get_addr_range_list(fun): + buf = gdb.execute("info line "+fun, False, True) + line_list = buf.split(os.linesep) + ret = [] + begin = -1 + end = -1 + for line in line_list: + addr_begin = line.find("starts at address ") + if addr_begin >= 0: + line = line[addr_begin + len("starts at address "):] + addr_end = line.find(" <"+fun) + if addr_end >= 0: + begin = int(line[:addr_end], 0) + line = line[addr_end + len(" <"+fun):] + addr_begin = line.find("ends at ") + if addr_begin >= 0: + line = line[addr_begin + len("ends at "):] + addr_end = line.find(" <"+fun) + if addr_end > 0: + end = int(line[:addr_end], 0) + if begin != -1: + ret.append([begin, end]) + begin = -1 + end = -1 + + if len(ret) > 0: + buf = gdb.execute("disassemble "+fun, False, True) + line_list = buf.split(os.linesep) + line_list.reverse() + end = 0 + for line in line_list: + addr_begin = line.find("0x") + if addr_begin >= 0: + line = line[addr_begin:] + addr_end = line.find(" <+") + if addr_end > 0: + end = int(line[:addr_end], 0) + 1 + break + if end != 0: + offset = 0 + for c in ret: + if c[1] < end: + if offset == 0 or offset > (end - c[1]): + offset = end - c[1] + for c in ret: + c[1] += offset + + return ret + +#0 inferior_id 1 dir_name 2 kernel_list 3 user_list +#list 0 function_list 1 file_list 2 line_list +task_list = {} +no_task = False +kernel_hotcode_list = ({},{},{}) + +#info[0] line_num, info[1] file_name, info[2] function_name +def add_info_to_code_list(info, code_list): + #function_list + if info[2] in code_list[0]: + code_list[0][info[2]] += 1 + else: + code_list[0][info[2]] = 1 + #file_list + if info[1] in code_list[1]: + code_list[1][info[1]] += 1 + else: + code_list[1][info[1]] = 1 + #line_list + line = str(info[1]) + ":" + str(info[0]) + if line in code_list[2]: + code_list[2][line] += 1 + else: + code_list[2][line] = 1 + +def task_list_add_line(is_user, pid, info): + if no_task: + #function_list + add_info_to_code_list (info, kernel_hotcode_list) + else: + if is_user: + add_info_to_code_list (info, task_list[pid][3]) + else: + add_info_to_code_list (info, task_list[pid][2]) + +show_line_number = 20 + +def hotcode_show_code_list(string, code_list): + if len(code_list) > 0: + print "\t", string + i = 1 + for c in dict_sort(code_list): + print "\t", c[0], "\t\t", c[1] + i += 1 + if i > show_line_number: + break + print + +def hotcode_show(): + if no_task: + hotcode_show_code_list("Hotest function", kernel_hotcode_list[0]) + hotcode_show_code_list("Hotest file", kernel_hotcode_list[1]) + hotcode_show_code_list("Hotest line", kernel_hotcode_list[2]) + else: + for pid in task_list: + print "task", str(pid), task_list[pid][1] + print "Kernel hotcode:" + hotcode_show_code_list("Hotest function", task_list[pid][2][0]) + hotcode_show_code_list("Hotest file", task_list[pid][2][1]) + hotcode_show_code_list("Hotest line", task_list[pid][2][2]) + print "User hotcode:" + hotcode_show_code_list("Hotest function", task_list[pid][3][0]) + hotcode_show_code_list("Hotest file", task_list[pid][3][1]) + hotcode_show_code_list("Hotest line", task_list[pid][3][2]) + print + +gdb.execute("set target-async on", True, False) +gdb.execute("set pagination off", True, False) +gdb.execute("set confirm off", True, False) +gdb.execute("set circular-trace-buffer on", True, False) +gdb.execute("set debug-file-directory "+debug_dir, True, False) +try: + gdb.execute("kill", True, False) +except: + pass + +trace_user = True +trace_kernel = True +while 1: + tmp = "Both" + try: + tmp = raw_input('Which part of code you want trace?(User/Kernel/[Both])') + except: + continue + if tmp[0:1] == 'U' or tmp[0:1] == 'u': + trace_kernel = False + elif tmp[0:1] == 'K' or tmp[0:1] == 'k': + trace_user = False + break + +#Get which task pid why want to trace +print("Please input the pid of tasks that you want to trace - one per line (use empty to end input).") +print("If not set any task, will trace all code in the Kernel.") +while 1: + pid = -1 + try: + pid = input('id:') + except: + pass + if pid <= 0: + break + if pid in task_list: + print("This pid already in the list.") + continue + user_dir = "" + fid = 0 + if trace_user: + try: + orig_user_dir = user_dir = os.path.realpath("/proc/"+str(pid)+"/exe") + except: + #maybe this is the kernel task + print "Cannot get the user code info of this pid, will not parse the user level code symbol" + task_list[pid] = (fid, user_dir, {}, {}) + continue + if os.path.exists(debug_dir+user_dir): + user_dir = debug_dir+user_dir + while 1: + tmp = "" + try: + tmp = raw_input('Please input the debug binary of task if you want to change it:['+user_dir+']') + except: + continue + if tmp != "": + user_dir = os.path.realpath(tmp) + break + if not os.path.exists(user_dir): + print "Cannot get the user code info of this pid, will not parse the user level code symbol" + task_list[pid] = (fid, user_dir, {}, {}) + continue + print "Use "+user_dir+" as debug binary." + fid = add_inferior() + if fid < 0: + print "Try to load task got error." + continue + gdb.execute("inferior "+str(fid)) + pfile = open("/proc/"+str(pid)+"/maps", "r") + tmplist = pfile.read().split(os.linesep) + pfile.close() + for c in tmplist: + c_list = c.split(" ") + filename = c_list[-1].strip() + if filename != orig_user_dir and os.path.exists(filename) and len(c_list) > 2 and len(c_list[1]) > 3 and c_list[1][2] == 'x': + addr = "0x"+c_list[0][0:c.find('-')] + gdb.execute("file "+filename) + info_files = gdb.execute("info files", True, True) + info_files_list = info_files.split(os.linesep) + text_offset = "0x0" + for line in info_files_list: + line_list = line.split(" is ") + if len(line_list) == 2 and line_list[1].strip() == ".text": + line_list[0] = line_list[0].strip() + text_offset = line_list[0][0:line_list[0].find(' - ')] + print ("add-symbol-file "+filename+" ("+addr+"+"+text_offset+")") + gdb.execute("add-symbol-file "+filename+" ("+addr+"+"+text_offset+")") + gdb.execute("file "+user_dir) + gdb.execute("inferior 1") + task_list[pid] = (fid, user_dir, ({},{},{}), ({},{},{})) + +def get_ignore_str(function): + ret = "" + try: + s = raw_input('Do you want to ignore function \"'+function+'\"? (yes)') + except: + s = 'y' + if s[0:1] != 'n' and s[0:1] != 'N': + r_list = get_addr_range_list(function) + for r in r_list: + if ret != "": + ret += " && " + else: + ret += "&& (" + #(regs->ip < r[0] || regs->ip > r[1]) + ret += "($pc_ip0 < "+str(r[0])+" || $pc_ip0 > "+str(r[1])+")" + if ret != "": + ret += ")" + return ret + +ignore_str = "" +if len(task_list) == 0: + trace_user = False + trace_kernel = True + no_task = True + +try: + show_line_number = input('Show line number (0 meas all)?['+str(show_line_number)+']') +except: + show_line_number = 20 + +#Set tracepoint +gdb.execute("target remote /sys/kernel/debug/gtp", True, False) + +try: + gdb.execute("tstop", True, False) + gdb.execute("delete", True, False) +except: + pass + + +def getmod(): + #following code is get from ../getmod.py + #use the code directly because sys.argv = [''] inside GDB + def format_file(name): + tmp = "" + for c in name: + if c == "_": + c = "-" + tmp += c + return tmp + + #Check if the target is available + if str(gdb.selected_thread()) == "None": + raise gdb.error("Please connect to Linux Kernel before use the script.") + + #Output the help + print "Use GDB command \"set $mod_search_dir=dir\" to set an directory for search the modules." + + ignore_gtp_ko = gdb.parse_and_eval("$ignore_gtp_ko") + if ignore_gtp_ko.type.code == gdb.TYPE_CODE_INT: + ignore_gtp_ko = int(ignore_gtp_ko) + else: + ignore_gtp_ko = 1 + + #Get the mod_search_dir + mod_search_dir_list = [] + #Get dir from $mod_search_dir + tmp_dir = gdb.parse_and_eval("$mod_search_dir") + if tmp_dir.type.code == gdb.TYPE_CODE_ARRAY: + tmp_dir = str(tmp_dir) + tmp_dir = tmp_dir[1:len(tmp_dir)] + tmp_dir = tmp_dir[0:tmp_dir.index("\"")] + mod_search_dir_list.append(tmp_dir) + #Get dir that same with current vmlinux + tmp_dir = str(gdb.execute("info files", False, True)) + tmp_dir = tmp_dir[tmp_dir.index("Symbols from \"")+len("Symbols from \""):len(tmp_dir)] + tmp_dir = tmp_dir[0:tmp_dir.index("\"")] + tmp_dir = tmp_dir[0:tmp_dir.rindex("/")] + mod_search_dir_list.append(tmp_dir) + #Get the dir of current Kernel + tmp_dir = "/lib/modules/" + str(os.uname()[2]) + if os.path.isdir(tmp_dir): + mod_search_dir_list.append(tmp_dir) + #Let user choice dir + mod_search_dir = "" + while mod_search_dir == "": + for i in range(0, len(mod_search_dir_list)): + print str(i)+". "+mod_search_dir_list[i] + try: + s = input('Select a directory for search the modules [0]:') + except SyntaxError: + s = 0 + except: + continue + if s < 0 or s >= len(mod_search_dir_list): + continue + mod_search_dir = mod_search_dir_list[s] + + mod_list_offset = long(gdb.parse_and_eval("((size_t) &(((struct module *)0)->list))")) + mod_list = long(gdb.parse_and_eval("(&modules)")) + mod_list_current = mod_list + + while 1: + mod_list_current = long(gdb.parse_and_eval("((struct list_head *) "+str(mod_list_current)+")->next")) + + #check if need break the loop + if mod_list == mod_list_current: + break + + mod = mod_list_current - mod_list_offset + + #get mod_name + mod_name = str(gdb.parse_and_eval("((struct module *)"+str(mod)+")->name")) + mod_name = mod_name[mod_name.index("\"")+1:len(mod_name)] + mod_name = mod_name[0:mod_name.index("\"")] + if mod_name == "fglrx": + contiue + mod_name += ".ko" + mod_name = format_file(mod_name) + + #get mod_dir_name + mod_dir_name = "" + for root, dirs, files in os.walk(mod_search_dir): + for afile in files: + tmp_file = format_file(afile) + if tmp_file == mod_name: + mod_dir_name = os.path.join(root,afile) + break + if mod_dir_name != "": + break + + command = " " + + #Add module_core to command + command += str(gdb.parse_and_eval("((struct module *)"+str(mod)+")->module_core")) + + #Add each sect_attrs->attrs to command + #get nsections + nsections = int(gdb.parse_and_eval("((struct module *)"+str(mod)+")->sect_attrs->nsections")) + sect_attrs = long(gdb.parse_and_eval("(u64)((struct module *)"+str(mod)+")->sect_attrs")) + for i in range(0, nsections): + command += " -s" + tmp = str(gdb.parse_and_eval("((struct module_sect_attrs *)"+str(sect_attrs)+")->attrs["+str(i)+"].name")) + tmp = tmp[tmp.index("\"")+1:len(tmp)] + tmp = tmp[0:tmp.index("\"")] + command += " "+tmp + tmp = str(gdb.parse_and_eval("((struct module_sect_attrs *)"+str(sect_attrs)+")->attrs["+str(i)+"].address")) + command += " "+tmp + + if mod_dir_name == "": + print "Cannot find out",mod_name,"from directory." + print "Please use following command load the symbols from it:" + print "add-symbol-file some_dir/"+mod_name+command + else: + if ignore_gtp_ko and mod_name == "gtp.ko": + pass + else: + #print "add-symbol-file "+mod_dir_name+command + gdb.execute("add-symbol-file "+mod_dir_name+command, False, False) + +if trace_kernel: + try: + s = raw_input('Do you load the symbol from LKM? [no]') + except: + s = 'n' + if s[0:1] == 'y' or s[0:1] == 'Y': + getmod() + +cpu_number = int(gdb.parse_and_eval("$cpu_number")) +tempfilename = tempfile.mktemp() +tempfile = open(tempfilename, "w") +if no_task: + #Setup first tracepoint + ignore_str += get_ignore_str("arch_local_irq_enable") + ignore_str += get_ignore_str("intel_idle") + # GDB have bug with long conditon so close them + #ignore_str += get_ignore_str("__do_softirq") + #ignore_str += get_ignore_str("_raw_spin_unlock_irqrestore") + + for i in range(0, cpu_number): + tempfile.write("tvariable $pc_ip"+str(i)+"\n") + tempfile.write("tvariable $pc_cs"+str(i)+"\n") + tempfile.write("trace handle_irq\n") + tempfile.write("commands\n") + tempfile.write("teval $pc_ip0=(u64)regs->ip\n") + tempfile.write("teval $pc_cs0=(u64)regs->cs\n") + tempfile.write("end\n") + #Setup second tracepoint + tempfile.write("trace handle_irq\n") + cond_str = " (($pc_cs0 & 3) == 0)" + tempfile.write("condition $bpnum "+cond_str+ignore_str+"\n") + tempfile.write("commands\n") + tempfile.write("collect $no_self_trace\n") + tempfile.write("collect $pc_ip0\n") + tempfile.write("end\n") + tempfile.write("trace smp_apic_timer_interrupt\n") + tempfile.write("commands\n") + tempfile.write("teval $pc_ip0=(u64)regs->ip\n") + tempfile.write("teval $pc_cs0=(u64)regs->cs\n") + tempfile.write("end\n") + #Setup second tracepoint + tempfile.write("trace smp_apic_timer_interrupt\n") + cond_str = " (($pc_cs0 & 3) == 0)" + tempfile.write("condition $bpnum "+cond_str+ignore_str+"\n") + tempfile.write("commands\n") + tempfile.write("collect $no_self_trace\n") + tempfile.write("collect $pc_ip0\n") + tempfile.write("end\n") +else: + pid_str = "" + for pid in task_list: + if pid_str != "": + pid_str += " || " + else: + pid_str += "(" + pid_str += "($current_task_pid == "+str(pid)+") " + if pid_str != "": + pid_str += ")" + cond_str = "" + if not trace_user: + if pid_str != "": + cond_str += " && " + cond_str += " ((regs->cs & 3) == 0)" + elif not trace_kernel: + if pid_str != "": + cond_str += "&&" + cond_str += " ((regs->cs & 3) == 3)" + tempfile.write("trace handle_irq\n") + tempfile.write("condition $bpnum "+pid_str+cond_str+"\n") + tempfile.write("commands\n") + tempfile.write("collect regs->ip\n") + if trace_user and trace_kernel: + tempfile.write("collect regs->cs\n") + tempfile.write("collect $current_task_pid\n") + tempfile.write("end\n") + tempfile.write("trace smp_apic_timer_interrupt\n") + tempfile.write("condition $bpnum "+pid_str+cond_str+"\n") + tempfile.write("commands\n") + tempfile.write("collect regs->ip\n") + if trace_user and trace_kernel: + tempfile.write("collect regs->cs\n") + tempfile.write("collect $current_task_pid\n") + tempfile.write("end\n") +tempfile.close() +tempfile = open(tempfilename, "r") +print "Tracepoint command:" +print tempfile.read() +tempfile.close() +gdb.execute("source "+tempfilename, True, False) +os.remove(tempfilename) +gdb.execute("set disconnected-tracing on", True, False) +gdb.execute("tstart") +gdb.execute("kill", True, False) + +signal.signal(signal.SIGINT, sigint_handler); +signal.siginterrupt(signal.SIGINT, False); + +#Connect to pipe +gdb.execute("target tfile /sys/kernel/debug/gtpframe_pipe") + +def get_line_from_sym(sym): + sym = sym.rstrip(os.linesep) + + #Get line_num and file_name + begin = sym.find("Line ") + end = sym.find("\" starts at address") + line_num = None + file_name = None + if begin >= 0 and end > 0 and begin + len("Line ") < end: + line = sym[begin + len("Line "):end] + line = line.split(" of \"") + if len(line) == 2: + line_num = line[0] + file_name = line[1] + sym = sym[end:] + + #Get function_name + begin = sym.find("<") + end = sym.find(">") + if begin >= 0 and end > 0 and begin + 1 < end: + function_name = sym[begin + 1:end] + end = function_name.rfind("+") + if end > 0: + function_name = function_name[:end] + sym = gdb.execute("info symbol "+function_name, True, True).rstrip(os.linesep) + begin = sym.rfind(" of ") + if begin > 0: + begin += len(" of ") + function_name = sym[begin:] + ":" + function_name + else: + function_name = None + return (line_num, file_name, function_name) + +if no_task: + while 1: + try: + gdb.execute("tfind 0", False, True) + cpu_id = long(gdb.parse_and_eval("$cpu_id")); + sym = gdb.execute("info line *($pc_ip"+str(cpu_id)+" - 1)", True, True) + line = get_line_from_sym(sym) + task_list_add_line(False, 0, line) + except gdb.error, x: + print("Drop one entry because", x) + except gdb.MemoryError, x: + print("Drop one entry because", x) + try: + gdb.execute("tfind 1", False, True) + except: + pass +else: + while 1: + try: + gdb.execute("tfind 0", False, True) + is_user = False + pid = long(gdb.parse_and_eval("$current_task_pid")) + if not pid in task_list: + raise gdb.error ("Cannot find inferior for pid "+ str(pid) +", drop one entry.") + if trace_user and long(gdb.parse_and_eval("regs->cs & 3")) == 3: + is_user = True + ip = long(gdb.parse_and_eval("regs->ip - 1")) + gdb.execute("inferior "+str(task_list[pid][0]), False, True) + sym = gdb.execute("info line *"+str(ip), True, True) + else: + sym = gdb.execute("info line *(regs->ip - 1)", True, True) + line = get_line_from_sym(sym) + if is_user: + gdb.execute("inferior 1", False, True) + task_list_add_line(is_user, pid, line) + except gdb.error, x: + print("Drop one entry because", x) + gdb.execute("inferior 1", False, True) + except gdb.MemoryError, x: + print("Drop one entry because", x) + gdb.execute("inferior 1", False, True) + try: + gdb.execute("tfind 1", False, True) + except: + pass --- /dev/null +++ b/scripts/gtp/add-ons/pe.py @@ -0,0 +1,729 @@ +#!/usr/bin/python + +# This script is used to show the performance counters in graph mode +# GPL +# Copyright(C) Hui Zhu (teawater@gmail.com), 2011 + + +pe_list = [] +#0 type, 1 config, 2 name +#typt and config can get from https://code.google.com/p/kgtp/wiki/HOWTO#How_to_use_performance_counters +pe_list.append(["0","0", "CPU_CYCLES"]) +pe_list.append(["0","1", "INSTRUCTIONS"]) +pe_list.append(["0","2", "CACHE_REFERENCES"]) +pe_list.append(["0","3", "CACHE_MISSES"]) +pe_list.append(["0","4", "BRANCH_INSTRUCTIONS"]) +pe_list.append(["0","5", "BRANCH_MISSES"]) +pe_list.append(["0","6", "BUS_CYCLES"]) +pe_list.append(["3","0", "L1D_READ_ACCESS"]) +pe_list.append(["3","1", "L1I_READ_ACCESS"]) + +#sleep time +sleep_sec = 1 + +#0 text 1 gtk +gui_type = 1 + +in_gdb = False + + +pe_list_type = 0 +pe_list_config = 1 +pe_list_name = 2 +pe_list_prev = 3 +pe_list_qtv = 4 + +if in_gdb: + import gdb +else: + import os + + +class kgtp: + fd = -1 + retry_count = 3 + buf_max = 1024 + tvariable = {} + tvariable_next_number = 0 + + def __init__(self): + #Open fd + try: + self.fd = os.open("/sys/kernel/debug/gtp", os.O_RDWR) + except: + print "Please do not forget insmod and sudo." + exit(0) + + def __del__(self): + if self.fd >= 0: + os.close(self.fd) + + def read_fd(self): + try: + buf = os.read(self.fd, self.buf_max) + except: + return False + return buf + + def write_fd(self, msg): + try: + buf = os.write(self.fd, msg) + except: + return False + return True + + def read(self): + for i in range(0, self.retry_count): + if i != 0: + self.write_fd("-") + + buf = self.read_fd() + if buf == False: + continue + buf_len = len(buf) + if buf_len < 4: + continue + + csum = 0 + for i in range(0, buf_len - 2): + if i == 0: + if buf[i] != "$": + retry = True + break + elif buf[i] == '#': + break + else: + csum += ord(buf[i]) + if i == 0 or buf[i] != "#": + continue + if int("0x"+buf[i+1:i+3], 16) != (csum & 0xff): + continue + buf = buf[1:i] + self.write_fd("+") + + #print "KGTP read: "+buf + return buf + + print "KGTP read got error" + return False + + def write(self, msg): + for i in range(0, self.retry_count): + if i != 0: + self.write_fd("-") + + csum = 0 + for c in msg: + csum += ord(c) + msg = "$"+msg+"#"+"%02x" % (csum & 0xff) + + if self.write_fd(msg) == False: + continue + if self.read_fd() != "+": + continue + + #print "KGTP write: "+msg + return True + + print "KGTP write got error" + return False + + def simple_cmd(self, cmd): + if gtp.write(cmd) == False: + return False + if gtp.read() != "OK": + return False + return True + + def tvariable_init(self): + tvariable = {} + tvariable_next_number = 0 + + if gtp.write("qTfV") == False: + return False + ret = gtp.read() + while 1: + if ret == False: + return False + if ret == "l": + return True + ret = ret.split(":") + if len(ret) < 4: + print "KGTP GDBRSP package format error" + return False + if len(ret[3]) % 2 != 0: + print "KGTP GDBRSP package format error" + return False + + #Get name + letter = "" + name = "" + for c in ret[3]: + letter += c + if len(letter) == 2: + name += chr(int("0x"+letter, 16)) + letter = "" + + number = int("0x"+ret[0], 16) + self.tvariable[name] = number + if (number >= self.tvariable_next_number): + self.tvariable_next_number = number + 1 + + if gtp.write("qTsV") == False: + return False + ret = gtp.read() + + def tvariable_val(self, number): + return self.tvariable_val_raw("qTV:"+"%x" % number) + + def tvariable_val_raw(self, buf): + if gtp.write(buf) == False: + return + ret = gtp.read() + if ret == False: + return + if ret[0] != "V": + return + + return long("0x"+ret[1:], 16) + + def tvariable_add(self, name, val): + if self.tvariable_next_number == 0: + print "Must call tvariable_init before add tvariable" + return + + buf = "QTDV:" + "%x" % self.tvariable_next_number + ":" + "%x" % val + ":0:" + for c in name: + buf += "%02x" % ord(c) + if gtp.write(buf) == False: + return + if gtp.read() != "OK": + print "Get something wrong when add tvariable to KGTP" + return + + self.tvariable_next_number += 1 + return (self.tvariable_next_number - 1) + + def qtinit(self): + return self.simple_cmd("QTinit") + + def tstart(self): + return self.simple_cmd("QTStart") + + def tstop(self): + return self.simple_cmd("QTStop") + + +def each_entry(callback): + global pe_list, cpu_number + for i in range(0, cpu_number): + for e in pe_list: + callback(i, e) + + +def init_pe(i, e): + if (len(e) < pe_list_prev + 1): + e.append([]) + e[pe_list_prev].append(0) + if (len(e) < pe_list_qtv + 1): + e.append([]) + + if in_gdb: + gdb.execute("tvariable $pc_pe_type_"+e[pe_list_type]+e[pe_list_config]+"_"+str(i)+"="+e[pe_list_type], True, False) + gdb.execute("tvariable $pc_pe_config_"+e[pe_list_type]+e[pe_list_config]+"_"+str(i)+"="+e[pe_list_config], True, False) + gdb.execute("tvariable $pc_pe_val_"+e[pe_list_type]+e[pe_list_config]+"_"+str(i)+"=0", True, False) + gdb.execute("tvariable $pc_pe_en_"+e[pe_list_type]+e[pe_list_config]+"_"+str(i)+"=1", True, False) + else: + if gtp.tvariable_add("pc_pe_type_"+e[pe_list_type]+e[pe_list_config]+"_"+str(i), int(e[pe_list_type])) == None: + exit(0) + if gtp.tvariable_add("pc_pe_config_"+e[pe_list_type]+e[pe_list_config]+"_"+str(i), int(e[pe_list_config])) == None: + exit(0) + number = gtp.tvariable_add("pc_pe_val_"+e[pe_list_type]+e[pe_list_config]+"_"+str(i), 0) + if number == None: + exit(0) + if gtp.tvariable_add("pc_pe_en_"+e[pe_list_type]+e[pe_list_config]+"_"+str(i), 1) == None: + exit(0) + e[pe_list_qtv].append("qTV:"+"%x" % number) + +def init_kgtp(): + global cpu_number + + if in_gdb: + cpu_number = int(gdb.parse_and_eval("$cpu_number")) + #Set the empty tracepoint + gdb.execute("delete tracepoints", False, False) + gdb.execute("trace *0", True, False) + else: + cpu_number = gtp.tvariable_val(gtp.tvariable["cpu_number"]) + if cpu_number == None: + exit(0) + + #Set the pe + each_entry(init_pe) + +import signal +def sigint_handler(num, e): + if in_gdb: + gdb.execute("tstop", True, False) + else: + gtp.tstop() + exit(0) + + +if in_gdb: + #close pagination + gdb.execute("set pagination off", True, False); + #Connect to KGTP if need + if str(gdb.selected_thread()) == "None": + gdb.execute("target remote /sys/kernel/debug/gtp", True, False) +else: + gtp = kgtp() + if gtp.qtinit == False: + exit(0) + if gtp.tvariable_init() == False: + exit(0) + +#Init the status to KGTP +cpu_number = 0 +init_kgtp() +signal.signal(signal.SIGINT, sigint_handler) + + +#start +if in_gdb: + gdb.execute("tstart", True, False) +else: + gtp.tstart() + + +#text gui --------------------------------------------------------------------- +#pe_list will be set to:type, config, name, prev_value_list +if gui_type == 0: + import time + def output_pe(i, e): + if in_gdb: + current_value = long(gdb.parse_and_eval("$pc_pe_val_"+e[pe_list_type]+e[pe_list_config]+"_"+str(i))) + else: + current_value = gtp.tvariable_val_raw(e[pe_list_qtv][i]) + if current_value == None: + print "Fail when get val from KGTP" + exit(0) + print "cpu"+str(i),e[pe_list_name],current_value-e[pe_list_prev][i] + e[pe_list_prev][i] = current_value + + while 1: + each_entry(output_pe) + print + time.sleep(sleep_sec) + + +#gtk gui ---------------------------------------------------------------------- +#pe_list will be set to:0 type, 1 config, 2 name, 3 prev_value_list, +# 4 value_list, 5 x_list, 6 button_list, +# 7 button_color_list, 8 line_color_list +if gui_type == 1: + #This script need python-gtk2 + import gtk + import glib + + pe_list_value = 5 + pe_list_x = 6 + pe_list_button = 7 + pe_list_bcolor = 8 + pe_list_lcolor = 9 + + pe_color = (0xffb0ff, 0x006000) + + class PyApp(gtk.Window): + #Init ---------------------------------------------------------- + def __init__(self): + global pe_list, cpu_number + + super(PyApp, self).__init__() + + self.max_value = 0 + self.prev_width = 0 + self.prev_height = 0 + self.y_ratio = 0 + self.entry_width = 10 + self.logfd = False + + #Set the pe + each_entry(self.pe_init_callback) + + #Set the color + num = len(pe_list) * cpu_number + block = (pe_color[0] - pe_color[1]) / float(num) + color = pe_color[1] + for i in range(0, cpu_number): + for e in pe_list: + e[pe_list_bcolor].append(gtk.gdk.Color("#"+ "%06x" % int(color))) + e[pe_list_lcolor].append((((int(color) >> 16) / float(0xff) * 1), ((int(color) >> 8 & 0xff) / float(0xff) * 1), ((int(color) & 0xff) / float(0xff) * 1))) + color += block + + #Set window + self.set_title("KGTP") + self.connect("destroy", gtk.main_quit) + gtk.Window.maximize(self) + + #menubar + mb = gtk.MenuBar() + #file + filemenu = gtk.Menu() + filem = gtk.MenuItem("File") + filem.set_submenu(filemenu) + save = gtk.CheckMenuItem("Save log to a CSV file") + save.connect("activate", self.mb_save) + save.set_active(False) + exit = gtk.MenuItem("Exit") + exit.connect("activate", gtk.main_quit) + filemenu.append(save) + filemenu.append(gtk.SeparatorMenuItem()) + filemenu.append(exit) + mb.append(filem) + #set + setmenu = gtk.Menu() + setm = gtk.MenuItem("Settings") + setm.set_submenu(setmenu) + show_buttons = gtk.CheckMenuItem("Show buttons") + show_buttons.set_active(True) + show_buttons.connect("activate", self.show_buttons) + setmenu.append(show_buttons) + mb.append(setm) + + #Widget + #Creat self.darea + self.darea = gtk.DrawingArea() + self.darea.connect("expose-event", self.expose) + self.darea.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color("#FFFFFF")) + #Creat all ToggleButton for each pe + each_entry(self.pe_gtk_creat_button) + #Creat button_hboxes + self.button_hboxes = self.pe_gtk_creat_button_hboxes_first() + + #Add mb and widget to window + self.vbox = gtk.VBox(False, 0) + self.vbox.pack_start(mb, False, False, 0) + self.vbox.pack_start(self.darea, True, True, 0) + for e in self.button_hboxes: + self.vbox.pack_start(e, False, False, 0) + self.add(self.vbox) + + #First show to get the right size + self.show_all() + size = self.pe_gtk_get_size() + + #Reset the button_hboxes + each_entry(self.pe_gtk_remove_creat_button_hboxes) + for e in self.button_hboxes: + self.vbox.remove(e) + self.button_hboxes = self.pe_gtk_creat_button_hboxes_second(size) + for e in self.button_hboxes: + self.vbox.pack_start(e, False, False, 0) + self.show_all() + + #Reset the value of each button + each_entry(self.button_reset) + + #Add timer + glib.timeout_add(int(sleep_sec * 1000), self.timer_cb) + #Remove the first entry because it already record a big value + glib.timeout_add(int(sleep_sec * 1100), self.timer_remove_first_record) + + def __del__(self): + if self.logfd: + self.logfd.close() + self.logfd = False + + def pe_init_callback(self, i, e): + if (len(e) < pe_list_value + 1): + e.append([]) + e[pe_list_value].append([]) + if (len(e) < pe_list_x + 1): + e.append([]) + e[pe_list_x].append([]) + if (len(e) < pe_list_button + 1): + e.append([]) + if (len(e) < pe_list_button + 1): + e.append([]) + if (len(e) < pe_list_bcolor + 1): + e.append([]) + if (len(e) < pe_list_lcolor + 1): + e.append([]) + + def pe_gtk_creat_button(self, i, e): + e[pe_list_button].append(gtk.ToggleButton(e[pe_list_name]+":"+str(18446744073709551615))) + self.set_button_color(e[pe_list_button][i], e[pe_list_bcolor][i]) + e[pe_list_button][i].connect("clicked", self.button_click) + + def pe_gtk_creat_button_hboxes_first(self): + global pe_list, cpu_number + + hboxes = [] + self.label_list = [] + for i in range(0, cpu_number): + hboxes.append(gtk.HBox(False, 0)) + self.label_list.append(gtk.Label("CPU"+str(i))) + hboxes[i].pack_start(self.label_list[i], False, False, 0) + for e in pe_list: + hboxes[i].pack_start(e[pe_list_button][i], False, False, 0) + + return hboxes + + def pe_gtk_get_size(self): + global pe_list, cpu_number + + #0 label size 1 button size + size = ([],[]) + for i in range(0, cpu_number): + size[0].append(self.label_list[i].allocation.width) + size[1].append([]) + for e in pe_list: + size[1][i].append(e[pe_list_button][i].allocation.width) + + return size + + def pe_gtk_remove_creat_button_hboxes(self, i, e): + self.button_hboxes[i].remove(e[pe_list_button][i]) + + def pe_gtk_creat_button_hboxes_second(self, size): + global pe_list, cpu_number + + hboxes = [] + hbox_id = -1 + for i in range(0, cpu_number): + keep_going = True + prev_entry_id = 0 + while keep_going == True: + width = self.allocation.width + keep_going = False + hbox_id += 1 + hboxes.append(gtk.HBox(False, 0)) + width -= size[0][i] + hboxes[hbox_id].pack_start(gtk.Label("CPU"+str(i)), False, False, 0) + for j in range(prev_entry_id, len(pe_list)): + if width - size[1][i][j] <= 0: + prev_entry_id = j + keep_going = True + break + width -= size[1][i][j] + 200 + hboxes[hbox_id].pack_start(pe_list[j][pe_list_button][i], False, False, 0) + + return hboxes + + def button_reset(self, i, e): + e[pe_list_button][i].set_label(e[pe_list_name]+":0") + + #Dialog ------------------------------------------------------- + def dialog_error(self, msg): + md = gtk.MessageDialog(self,gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg) + md.run() + md.destroy() + + #Menubar ------------------------------------------------------- + def show_buttons(self, widget): + if widget.active: + for e in self.button_hboxes: + e.show() + else: + for e in self.button_hboxes: + e.hide() + + def log_write_name(self, i, e): + self.logfd.write("CPU"+str(i)+" "+e[pe_list_name]+",") + + def mb_save(self, widget): + if widget.active: + md = gtk.FileChooserDialog(title="Save log to a CSV file", action=gtk.FILE_CHOOSER_ACTION_SAVE, buttons = (gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OK, gtk.RESPONSE_OK)) + md.set_do_overwrite_confirmation(True) + md.set_current_name("pe.csv") + if md.run() == gtk.RESPONSE_OK: + try: + self.logfd = open(md.get_filename(), "w") + each_entry(self.log_write_name) + self.logfd.write("\n") + except: + self.dialog_error("Try to open file "+md.get_filename()+" got error") + widget.set_active(False) + if self.logfd: + self.logfd.close() + self.logfd = False + else: + widget.set_active(False) + md.destroy() + else: + if self.logfd: + self.logfd.close() + self.logfd = False + + #Button -------------------------------------------------------- + def refind_max_value(self, i, e): + if e[pe_list_button][i].get_active(): + return + for i in e[pe_list_value][i]: + if i > self.max_value: + self.max_value = i + self.y_ratio = 0 + + def set_button_color(self, button, color): + style = button.get_style().copy() + style.bg[gtk.STATE_NORMAL] = color + style.bg[gtk.STATE_ACTIVE] = color + style.bg[gtk.STATE_PRELIGHT] = color + style.bg[gtk.STATE_SELECTED] = color + style.bg[gtk.STATE_INSENSITIVE] = color + button.set_style(style) + + def button_click(self, widget): + if widget.get_active(): + self.set_button_color(widget, gtk.gdk.Color("#FFFFFF")) + else: + color = False + for i in range(0, cpu_number): + for e in pe_list: + if e[pe_list_button][i] == widget: + color = e[pe_list_bcolor][i] + break + if color: + break + if color: + self.set_button_color(widget, color) + each_entry(self.refind_max_value) + self.darea.queue_draw() + + #Timer --------------------------------------------------------- + def write_csv(self, msg): + try: + self.logfd.write(msg) + except: + self.dialog_error("Writ CSV file got error") + widget.set_active(False) + self.logfd.close() + self.logfd = False + + def pe_gtk_add(self, i, e): + if in_gdb: + current_value = long(gdb.parse_and_eval("$pc_pe_val_"+e[pe_list_type]+e[pe_list_config]+"_"+str(i))) + else: + current_value = gtp.tvariable_val_raw(e[pe_list_qtv][i]) + if current_value == None: + print "Fail when get val from KGTP" + exit(0) + this_value = current_value-e[pe_list_prev][i] + e[pe_list_value][i].append(this_value) + if this_value > self.max_value and not e[pe_list_button][i].get_active(): + self.max_value = this_value + self.y_ratio = 0 + e[pe_list_x][i].append(-1) + e[pe_list_prev][i] = current_value + e[pe_list_button][i].set_label(e[pe_list_name]+":"+str(this_value)) + if self.logfd: + write_csv(str(this_value)+",") + + def timer_cb(self): + each_entry(self.pe_gtk_add) + if self.logfd: + write_csv("\n") + self.darea.queue_draw() + return True + + def timer_remove_first_record(self): + if len(pe_list[0][pe_list_value][0]) >= 1: + self.pe_remove_entry_num = 1 + each_entry(self.pe_remove_entry) + return False + else: + return True + + #DrawingArea --------------------------------------------------- + def pe_gtk_line(self, i, e): + if len(e[pe_list_value][i]) < 2: + return + if e[pe_list_button][i].get_active(): + return + + self.cr.set_source_rgb(e[pe_list_lcolor][i][0], e[pe_list_lcolor][i][1], e[pe_list_lcolor][i][2]) + x = 0 + for num in range(0, len(e[pe_list_value][i])): + if e[pe_list_value][i][num] > self.line_max: + self.line_max = e[pe_list_value][i][num] + if self.height_change or e[pe_list_x][i][num] < 0: + e[pe_list_x][i][num] = self.prev_height - e[pe_list_value][i][num] * self.y_ratio + if num == 0: + self.cr.move_to(x, e[pe_list_x][i][num]) + else: + self.cr.line_to(x, e[pe_list_x][i][num]) + x += self.entry_width + self.cr.stroke() + + def pe_remove_entry(self, i, e): + del(e[pe_list_value][i][0:self.pe_remove_entry_num]) + del(e[pe_list_x][i][0:self.pe_remove_entry_num]) + + def expose(self, widget, event): + self.cr = widget.window.cairo_create() + + #y + if self.prev_height != self.darea.allocation.height: + self.height_change = True + self.prev_height = self.darea.allocation.height + else: + self.height_change = False + if self.max_value > 0 and (self.height_change or self.y_ratio == 0): + self.max_value += 100 - self.max_value % 100 + self.y_ratio = float(self.prev_height)/self.max_value + self.height_change = True + + #x + x_size = len(pe_list[0][pe_list_value][0]) + entry_number = 0 + if self.entry_width * x_size > self.darea.allocation.width: + entry_number = self.darea.allocation.width // self.entry_width + self.pe_remove_entry_num = x_size - entry_number + each_entry(self.pe_remove_entry) + + #dash + self.cr.set_source_rgb(0, 0, 0) + self.cr.set_dash((1, 5)) + #dash line for x + if entry_number == 0: + entry_number = self.darea.allocation.width // self.entry_width + x = 0 + while x < self.darea.allocation.width: + x += self.entry_width * 10 + self.cr.move_to(x, 0) + self.cr.line_to(x, self.prev_height) + #dash line for y + self.cr.move_to(0, 10) + self.cr.show_text(str(self.max_value)) + + self.cr.move_to(0, self.darea.allocation.height/4*3) + self.cr.show_text(str(self.max_value/4*3)) + self.cr.line_to(self.darea.allocation.width, self.darea.allocation.height/4*3) + + self.cr.move_to(0, self.darea.allocation.height/2) + self.cr.show_text(str(self.max_value/2)) + self.cr.line_to(self.darea.allocation.width, self.darea.allocation.height/2) + + self.cr.move_to(0, self.darea.allocation.height/4) + self.cr.show_text(str(self.max_value/4)) + self.cr.line_to(self.darea.allocation.width, self.darea.allocation.height/4) + + self.cr.stroke() + self.cr.set_dash(()) + + self.line_max = 0 + each_entry(self.pe_gtk_line) + if self.line_max > 0 and self.line_max * 2 < self.max_value: + self.max_value = self.line_max + self.y_ratio = 0 + + self.height_change = False + + PyApp() + gtk.main() + if in_gdb: + gdb.execute("tstop", True, False) + else: + gtp.tstop() + exit(0) --- /dev/null +++ b/scripts/gtp/getgtprsp.pl @@ -0,0 +1,137 @@ +#!/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@gmail.com), 2010, 2011 + +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 eq '$k#6b') { + exit; + } 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 '$18:0:1:6972715f636f756e74#ca'; + } elsif ($line eq '$qTsV#8e') { + if ($var_count == 0) { + print '$17:0:1:736f66746972715f636f756e74#a6'; + } elsif ($var_count == 1) { + print '$16:0:1:686172646972715f636f756e74#70'; + } elsif ($var_count == 2) { + print '$15:0:1:6c6173745f6572726e6f#59'; + } elsif ($var_count == 3) { + print '$14:0:1:69676e6f72655f6572726f72#38'; + } elsif ($var_count == 4) { + print '$13:0:1:7874696d655f6e736563#35'; + } elsif ($var_count == 5) { + print '$12:0:1:7874696d655f736563#99'; + } elsif ($var_count == 6) { + print '$11:0:1:6b726574#48'; + } elsif ($var_count == 7) { + print '$10:0:1:70635f70655f656e#4e'; + } elsif ($var_count == 8) { + print '$f:0:1:6370755f6e756d626572#29'; + } elsif ($var_count == 9) { + print '$e:0:1:6e6f5f73656c665f7472616365#ca'; + } elsif ($var_count == 10) { + print '$d:0:1:64756d705f737461636b#22'; + } elsif ($var_count == 11) { + print '$c:0:1:7072696e746b5f666f726d6174#c7'; + } elsif ($var_count == 12) { + print '$b:8:1:7072696e746b5f6c6576656c#66'; + } elsif ($var_count == 13) { + print '$a:0:1:7072696e746b5f746d70#54'; + } elsif ($var_count == 14) { + print '$9:0:1:646973636172645f706167655f6e756d#ab'; + } elsif ($var_count == 15) { + print '$8:0:1:636f6f6b65645f7264747363#01'; + } elsif ($var_count == 16) { + print '$7:0:1:7264747363#57'; + } elsif ($var_count == 17) { + print '$6:0:1:636f6f6b65645f636c6f636b#8d'; + } elsif ($var_count == 18) { + print '$5:0:1:636c6f636b#e3'; + } elsif ($var_count == 19) { + print '$4:0:1:63757272656e745f7468726561645f696e666f#21'; + } elsif ($var_count == 20) { + print '$3:0:1:63757272656e745f7461736b#c9'; + } elsif ($var_count == 21) { + print '$2:0:1:6370755f6964#f1'; + } elsif ($var_count == 22) { + print '$1:0:1:6774705f76657273696f6e#6b'; + } elsif ($var_count == 23) { + print '$19:0:1:706970655f7472616365#cb'; + } 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); + } + } +} --- /dev/null +++ b/scripts/gtp/getmod.py @@ -0,0 +1,132 @@ +#!/usr/bin/python + +# This script is used by GDB to load the symbols from Linux kernel modules +# GPL +# Copyright(C) KGTP team (https://code.google.com/p/kgtp/), 2011 + +#Set special mod_search_dir +#set $mod_search_dir="dir" +#Clear special mod_search_dir +#set $mod_search_dir=(void)1 +#Not ignore gtp.ko +#set $ignore_gtp_ko=0 + +import gdb +import os + +def format_file(name): + tmp = "" + for c in name: + if c == "_": + c = "-" + tmp += c + return tmp + +#Check if the target is available +if str(gdb.selected_thread()) == "None": + raise gdb.error("Please connect to Linux Kernel before use the script.") + +#Output the help +print "Use GDB command \"set $mod_search_dir=dir\" to set an directory for search the modules." + +ignore_gtp_ko = gdb.parse_and_eval("$ignore_gtp_ko") +if ignore_gtp_ko.type.code == gdb.TYPE_CODE_INT: + ignore_gtp_ko = int(ignore_gtp_ko) +else: + ignore_gtp_ko = 1 + +#Get the mod_search_dir +mod_search_dir_list = [] +#Get dir from $mod_search_dir +tmp_dir = gdb.parse_and_eval("$mod_search_dir") +if tmp_dir.type.code == gdb.TYPE_CODE_ARRAY: + tmp_dir = str(tmp_dir) + tmp_dir = tmp_dir[1:len(tmp_dir)] + tmp_dir = tmp_dir[0:tmp_dir.index("\"")] + mod_search_dir_list.append(tmp_dir) +#Get dir that same with current vmlinux +tmp_dir = str(gdb.execute("info files", False, True)) +tmp_dir = tmp_dir[tmp_dir.index("Symbols from \"")+len("Symbols from \""):len(tmp_dir)] +tmp_dir = tmp_dir[0:tmp_dir.index("\"")] +tmp_dir = tmp_dir[0:tmp_dir.rindex("/")] +mod_search_dir_list.append(tmp_dir) +#Get the dir of current Kernel +tmp_dir = "/lib/modules/" + str(os.uname()[2]) +if os.path.isdir(tmp_dir): + mod_search_dir_list.append(tmp_dir) +#Let user choice dir +mod_search_dir = "" +while mod_search_dir == "": + for i in range(0, len(mod_search_dir_list)): + print str(i)+". "+mod_search_dir_list[i] + try: + s = input('Select a directory for search the modules [0]:') + except SyntaxError: + s = 0 + except: + continue + if s < 0 or s >= len(mod_search_dir_list): + continue + mod_search_dir = mod_search_dir_list[s] + +mod_list_offset = long(gdb.parse_and_eval("((size_t) &(((struct module *)0)->list))")) +mod_list = long(gdb.parse_and_eval("(&modules)")) +mod_list_current = mod_list + +while 1: + mod_list_current = long(gdb.parse_and_eval("((struct list_head *) "+str(mod_list_current)+")->next")) + + #check if need break the loop + if mod_list == mod_list_current: + break + + mod = mod_list_current - mod_list_offset + + #get mod_name + mod_name = str(gdb.parse_and_eval("((struct module *)"+str(mod)+")->name")) + mod_name = mod_name[mod_name.index("\"")+1:len(mod_name)] + mod_name = mod_name[0:mod_name.index("\"")] + mod_name += ".ko" + mod_name = format_file(mod_name) + + #get mod_dir_name + mod_dir_name = "" + for root, dirs, files in os.walk(mod_search_dir): + for afile in files: + tmp_file = format_file(afile) + if tmp_file == mod_name: + mod_dir_name = os.path.join(root,afile) + break + if mod_dir_name != "": + break + + command = " " + + #Add module_core to command + command += str(gdb.parse_and_eval("((struct module *)"+str(mod)+")->module_core")) + + #Add each sect_attrs->attrs to command + #get nsections + nsections = int(gdb.parse_and_eval("((struct module *)"+str(mod)+")->sect_attrs->nsections")) + sect_attrs = long(gdb.parse_and_eval("(u64)((struct module *)"+str(mod)+")->sect_attrs")) + for i in range(0, nsections): + command += " -s" + tmp = str(gdb.parse_and_eval("((struct module_sect_attrs *)"+str(sect_attrs)+")->attrs["+str(i)+"].name")) + tmp = tmp[tmp.index("\"")+1:len(tmp)] + tmp = tmp[0:tmp.index("\"")] + command += " "+tmp + tmp = str(gdb.parse_and_eval("((struct module_sect_attrs *)"+str(sect_attrs)+")->attrs["+str(i)+"].address")) + command += " "+tmp + + if mod_dir_name == "": + print "Cannot find out",mod_name,"from directory." + print "Please use following command load the symbols from it:" + print "add-symbol-file some_dir/"+mod_name+command + else: + if ignore_gtp_ko and mod_name == "gtp.ko": + print "gtp.ko is ignored. You can use command \"set $ignore_gtp_ko=0\" to close this ignore." + print "Or you can use following command load the symbols from it:" + print "add-symbol-file "+mod_dir_name+command + else: + #print "add-symbol-file "+mod_dir_name+command + gdb.execute("add-symbol-file "+mod_dir_name+command, False, False)