Debugging on s390
  Home
  Introduction
  Getting KGDB
  Documentation
  Using KGDB
  Credits
  Miscellaneous
  Troubleshooting
  FAQ
  Support
  About KGDB
  KGDB Pronew

 

Kgdb stub for Linux/s390 running under VM (ver 0.2.0)

It's an attempt at providing kernel source level debugging facilities for Linux running on the s390 platform under VM. It provides functionality similar to kgdb for x86. However, it does not require any additional patches to the kernel.

x3270 connects to the s390 VM and presents a console interface to the Linux guest machine. gdbstub.pl talks to VM via x3270 and uses VM's debugging commands (like cp trace, cp dispay etc.) to implement the gdb remote protocol. VM debugging commands can be used to extract and modify the state of the Linux guest without having to change the Linux kernel in any way.

B and C are usually the same machine.

It ONLY works for the 31-bit s390 platform. Every other line of code makes nasty assumptions of 32-bitness, so don't even think of running this on the 64-bit s390x platform. That would require a complete rewrite.

Requirements

Target:

  • Linux/s390 running under VM.
Host:
  • x86 or s390 machine running Linux
  • gdb-5.2 (either native or cross-hosted on x86) with the patch appended below. A prebuilt version (x86 binary) is available from downloads page.
  • x3270 (I use 3.2.16). It may be possible to use the text-based 3270 client, c3270, instead of x3270. The use of script-only clients is not advisable, since manual intervention is occasionally required.
  • The stub itself, which consists of 3 perl files - gdbstub.pl, cpinter.pl and cpstrings.pl.
    The latest release can be found  here. Check the CVS tree for the bleeding edge.
Basic Usage

(cross)Compile the kernel you want with -g -O2. Copy it to your target machine and boot with it. The host needs a copy of vmlinux with all debugging info intact.

You can't simply use a plain -O2 kernel from a standard kernel rpm on the target and a -g -O2 kernel on the host. Addresses turn out slightly different when you use -g. A pity, since this would have been quite useful
for debugging production servers running standard distribution kernels.

Edit the cpstrings.pl file to match the output of your VM configuration.

In the directory where you've unpacked the stub, create two fifos:

mkfifo ip op

Run the command:

sleep 1000000 >ip <op &

to keep x3270 happy.
Start x3270 as follows:

x3270 -script -port blah hostname <ip >op

Note that the console should be at least 80x25 in size. Now logon manually to your virtual machine and boot up on the kernel you compiled with -g.
Run the stub as follows:

perl -w ./gdbstub.pl >ip <op

If all goes well, it should force the target into CP and issue the command set run off

You should be able to observe this on the console. Once this is done, start gdb.

s390-ibm-linux-gdb /path/to/vmlinux

set remotetimeout 20  I need this because I need to debug a target over the net. you can add this to .gdbinit

set trust-readonly-sectio

ns 1 this is useful to avoid unnecessary traffic. gdb will memory. target remote stubhost:5513

where stubhost is where you're running gdbstub.pl (usually localhost) You should get something like:

[ganesh@blin1 linux-2.4.9-37]$ s390-ibm-linux-gdb vmlinux
GNU gdb 5.2
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "--host=i386-pc-linux-gnu --target=s390-ibm-linux"...
(gdb) set remotetimeout 20
(gdb) ta re linux-3:5513
Remote debugging using linux-3:5513
cpu_idle (unused=0x0) at process.c:76
76      }
warning: shared library handler failed to enable breakpoint
(gdb)

At this point you can set breakpoints, examine memory, registers, view backtraces etc.

Thread Debugging

You can now also view all the threads (tasks) running on the system. You can switch to a particular thread and do a backtrace. This may be useful in debugging deadlocks etc. There are two ways to do it. You might find one or the other easier depending on your particular situation.

Way 1

Helper Module: The first way requires you to compile a helper module called gdbmod and load it on the target machine. You can do this any time, even after the stub and gdb have been started, but you must do it before
issuing the first "info threads" command. The file gdbmod.c can be compiled after editing the sample Makefile to change things like path to the kernel source, cross compilation, etc.

[ganesh@blin1 gdbmod]$ make
s390-ibm-linux-gcc -D__KERNEL__ -I/home/ganesh/work/usr/src/linux-2.4.9-37/include -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -fno-omit-frame-pointer -fno-strict-aliasing -fno-common -Wno-unused -pipe -fno-strength-reduce -DMODULE -DEXPORT_SYMTAB -c gdbmod.c
[ganesh@blin1 gdbmod]$

Now copy it to the target machine.

[ganesh@blin1 gdbstub]$ scp -C gdbmod.o root@target:
gdbmod.o             100% |***************************************************|   106 KB    00:02
[ganesh@blin1 gdbstub]$

Load the module:

[ganesh@blin1 gdbstub]$ ssh root@target "/sbin/insmod /root/gdbmod.o;/sbin/lsmod"
Module                  Size  Used by
gdbmod                107616   0  (unused)
netiucv                18096   1  (autoclean)
af_packet              16560   0  (autoclean)
[ganesh@blin1 gdbstub]$

Grab a copy of the target's /proc/ksyms and dump it in the directory where the perl stub resides:

[ganesh@blin1 gdbstub]$ ssh root@target cat /proc/ksyms >ksyms
[ganesh@blin1 gdbstub]$

Now you can do an "info threads" from gdb and you should get (after about half a minute if the target is across the net) a list of threads running on the target. Use the "thread" and the "bt" commands to switch to threads and backtrace. Setting variables/registers in the context of a thread is not supported. Look but don't touch.

[ganesh@blin1 gdb]$ ~/work/gdb-build/gdb/gdb
GNU gdb 5.2
[.....]
(gdb) ta re localhost:5513
cpu_idle (unused=0x0) at process.c:76
76      }


warning: shared library handler failed to enable breakpoint
(gdb) info threads
  22 Thread 575 (mingetty)  schedule_timeout (timeout=2147483647) at sched.c:425
  21 Thread 568 (xfs)  schedule_timeout (timeout=282645) at sched.c:453
  20 Thread 516 (crond)  schedule_timeout (timeout=231187) at sched.c:453
  19 Thread 497 (xinetd)  schedule_timeout (timeout=2147483647) at sched.c:425
  18 Thread 464 (sshd)  schedule_timeout (timeout=2147483647) at sched.c:425
  17 Thread 427 (klogd)  0x0001f8d4 in do_syslog (type=0, buf=0x406b38 "", len=4095) at printk.c:178
  16 Thread 422 (syslogd)  schedule_timeout (timeout=2147483647) at sched.c:425
  15 Thread 110 (kjournald)  interruptible_sleep_on (q=0xc6d468) at sched.c:838
  14 Thread 109 (kjournald)  interruptible_sleep_on (q=0xc6d668) at sched.c:838
  13 Thread 108 (kjournald)  interruptible_sleep_on (q=0xc6d268) at sched.c:838
  12 Thread 107 (kjournald)  interruptible_sleep_on (q=0xc6d068) at sched.c:838
  11 Thread 106 (kjournald)  interruptible_sleep_on (q=0xf6a868) at sched.c:838
  10 Thread 10 (mdrecoveryd)  md_thread (arg=0x0) at md.c:3010
  9 Thread 9 (kupdated)  schedule_timeout (timeout=228645) at sched.c:453
  8 Thread 8 (bdflush)  interruptible_sleep_on (q=0x200c30) at sched.c:838
  7 Thread 7 (kreclaimd)  interruptible_sleep_on (q=0x1f9a0c) at sched.c:838
  6 Thread 6 (kswapd)  schedule_timeout (timeout=228682) at sched.c:453
  5 Thread 5 (ksoftirqd_CPU1)  ksoftirqd (__bind_cpu=0x0) at softirq.c:387
  4 Thread 4 (ksoftirqd_CPU0)  ksoftirqd (__bind_cpu=0x0) at softirq.c:387
  3 Thread 3 (keventd)  context_thread (dummy=0x0) at context.c:99
  2 Thread 2 (kmcheck)  __down_interruptible (sem=0x321020) at semaphore.c:128
  1 Thread 1 (init)  schedule_timeout (timeout=228644) at sched.c:453
(gdb) thread 17
[Switching to thread 17 (Thread 427)]#0  0x0001f8d4 in do_syslog (type=0, buf=0x406b38 "", len=4095)
    at printk.c:178
178                     error = wait_event_interruptible(log_wait, (log_start - log_end));
(gdb) bt 3
#0  0x0001f8d4 in do_syslog (type=0, buf=0x406b38 "", len=4095) at printk.c:178
#1  0x0007a6aa in kmsg_read (file=0x0, buf=0x0, count=0, ppos=0x0) at kmsg.c:35
#2  0x0004d5c8 in sys_read (fd=0, buf=0x406b38 "", count=4095) at read_write.c:162
(More stack frames follow...)
(gdb)
 

Way 2
The other way takes longer and is more painful than it needs to be, but it has the advantage of not requiring a helper module. You need to add two user-defined commands to your .gdbinit

define addcontext
        maintenance packet Qa,$arg0,$arg1,,
end
define findthreads
        set $init_thread = init_tasks[0]
        set $athread = $init_thread->next_task
        while $athread != $init_thread
                set $frameptr = (unsigned long *)(((unsigned long *)$athread->thread.ksp)[0])
                printf "%s %x\n", (char *)$athread->comm, $frameptr
                set $athread = $athread->next_task
        end
end

(gdb) findthreads
init 7f5d80
kmcheck 7f1eb8
keventd df7f08
ksoftirqd_CPU0 df5f38
ksoftirqd_CPU1 df3f38
kswapd de5e48
kreclaimd de3ec0
bdflush de1eb8
kupdated ddfeb8
mdrecoveryd ddbf18
kjournald f00bea0
kjournald f003ea0
kjournald efffea0
kjournald eff9ea0
kjournald eff5ea0
syslogd e975d80
klogd eb11d80
sshd e7ffd80
xinetd e733d80
crond 1003e28


xfs e567d80
mingetty e4dfd08
(gdb) addcontext syslogd e975d80
sending: "Qa,syslogd,e975d80,,"
received: "OK"
(gdb) info threads
  1 Thread 65536 (syslogd)  schedule_timeout (timeout=2147483647) at sched.c:425
(gdb) thread 1
[Switching to thread 1 (Thread 65536)]#0  schedule_timeout (timeout=2147483647) at sched.c:425
425                     goto out;
(gdb) bt 3
#0  schedule_timeout (timeout=2147483647) at sched.c:425
#1  0x00063178 in do_select (n=1, fds=0xe975ed8, timeout=0xe975ef0) at select.c:223
#2  0x00063548 in sys_select (n=1, inp=0x7ffff800, outp=0x0, exp=0x0, tvp=0x0) at select.c:318
(More stack frames follow...)
(gdb) addcontext klogd eb11d80
sending: "Qa,klogd,eb11d80,,"
received: "OK"
(gdb) info threads
  2 Thread 65537 (klogd)  0x0001f8d4 in do_syslog (type=129048040, buf=0x406b38 "", len=4095)
    at printk.c:178
* 1 Thread 65536 (syslogd)  schedule_timeout (timeout=2147483647) at sched.c:425
(gdb) thread 2
[Switching to thread 2 (Thread 65537)]#0  0x0001f8d4 in do_syslog (type=129048040, buf=0x406b38 "",
    len=4095) at printk.c:178
178                     error = wait_event_interruptible(log_wait, (log_start - log_end));
(gdb) bt 3
#0  0x0001f8d4 in do_syslog (type=129048040, buf=0x406b38 "", len=4095) at printk.c:178
#1  0x0007a6aa in kmsg_read (file=0x7b11de8, buf=0x7b11de8 "", count=0, ppos=0x0) at kmsg.c:35
#2  0x0004d5c8 in sys_read (fd=129048040, buf=0x406b38 "", count=4095) at read_write.c:162
(More stack frames follow...)
(gdb) The program is running.  Exit anyway? (y or n) y
 

This would be much easier if gdb's pseudo-language could actually evaluate arguments before passing them. Then we could simply call addcontext from the findthreads loop instead of having to do it manually.  The addcontext command can also be used to backtrace arbitrary frame pointers, such as those printed on the console after a panic.

Tips

  • Don't do unlimited backtraces, esp. if you're debugging a very remote system. Do a bt 5 or something.
  • Avoid using the "n" command and single stepping (maybe fixable in future).  Set breakpoints where necessary.
  • Hit ^C on gdb to force the machine into debugger. Alternatively, PA1 or  #cp entered from the console will have the same effect.
  • Sometimes it may happen that the machine has entered CP but no sign of it is seen on the console - the status remains at VM Read/Running and no breakpoint address or CP message is seen on the screen. If you suspect
  • that the machine is in CP, hit Enter on the console. This is usually sufficient to get the ball rolling.
  • When the machine is in CP, you can issue any CP commands from the console without affecting the debugger. Just don't change state (registers, memory).
  • Don't resume the VM manually from the console while the stub is active.
  • You can kill the stub and gdb at any time, do a #cpu all cp tr end to get rid of breakpoints and restart the stub and gdb.
  • Yes you can debug modules but it's complicated and I can't explain it all here. Look at kgdb.sourceforge.net for instructions on debugging modules. The procedure outlined f or x86 will work here as well.


gdb patch

the stub uses "hardware" breakpoints, so decrementing PC is not necessary. I'm sure there's a better way to do this.

--- ./gdb/s390-tdep.c.ORG Wed Jun  5 01:59:18 2002
+++ ./gdb/s390-tdep.c Wed Jun  5 01:59:39 2002
@@ -1787,7 +1787,7 @@
   /* Amount PC must be decremented by after a breakpoint.
      This is often the number of bytes in BREAKPOINT
      but not always.  */
-  set_gdbarch_decr_pc_after_break (gdbarch, 2);
+  set_gdbarch_decr_pc_after_break (gdbarch, 0);
   set_gdbarch_pop_frame (gdbarch, s390_pop_frame);
   /* Stack grows downward.  */
   set_gdbarch_inner_than (gdbarch, core_addr_lessthan);