Add data window feature to show current kernel status
on separate consoles, including: 1) registers, 2) back
trace and 3) watch data windows.
The data window would help kernel developer to understand
current hardware/ kernel status, and on single-stepping, the
modified fields in the data window would be highlighted.
Signed-off-by: Houcheng Lin <[email protected]>
---
Documentation/gdb-kernel-debugging.txt | 9 ++
scripts/gdb/linux/dw.py | 234 +++++++++++++++++++++++++++++++++
scripts/gdb/linux/lx-dw.py | 53 ++++++++
scripts/gdb/vmlinux-gdb.py | 1 +
scripts/lx-dw.sh | 4 +
5 files changed, 301 insertions(+)
create mode 100644 scripts/gdb/linux/dw.py
create mode 100644 scripts/gdb/linux/lx-dw.py
create mode 100755 scripts/lx-dw.sh
diff --git a/Documentation/gdb-kernel-debugging.txt b/Documentation/gdb-kernel-debugging.txt
index 7050ce8..b74ecff 100644
--- a/Documentation/gdb-kernel-debugging.txt
+++ b/Documentation/gdb-kernel-debugging.txt
@@ -139,6 +139,14 @@ Examples of using the Linux-provided gdb helpers
start_comm = "swapper/2\000\000\000\000\000\000"
}
+ o Enable the debugging data window feature in GDB, then run lx-dw.sh script
+ to create three xterm console on desktop to display the CPU registers,
+ back trace and variables.
+ (gdb) lx-dw
+ (gdb) lx-add p $lx_current().comm
+ (gdb) lx-add x/8x $rsp
+ (gdb) lx-add x/8i $rip
+
List of commands and functions
------------------------------
@@ -153,6 +161,7 @@ this is just a snapshot of the initial version:
function lx_task_by_pid -- Find Linux task by PID and return the task_struct variable
function lx_thread_info -- Calculate Linux thread_info from task variable
lx-dmesg -- Print Linux kernel log buffer
+ lx-dw -- Enable GDB data window feature
lx-lsmod -- List currently loaded modules
lx-symbols -- (Re-)load symbols of Linux kernel and currently loaded modules
diff --git a/scripts/gdb/linux/dw.py b/scripts/gdb/linux/dw.py
new file mode 100644
index 0000000..9d78bb3
--- /dev/null
+++ b/scripts/gdb/linux/dw.py
@@ -0,0 +1,234 @@
+#
+# gdb data window feature for Linux kernel debugging
+#
+#
+# Authors:
+# Houcheng Lin <[email protected]>
+#
+# This work is licensed under the terms of the GNU GPL version 2.
+#
+
+from __future__ import with_statement
+from __future__ import print_function
+
+import gdb
+
+
+class CmdWindow:
+ def __init__(self, file, cmd, DecoClass):
+ self.cmd = cmd
+ self.file = file
+ self.decowin = DecoClass(file)
+ def refresh(self, events):
+ try:
+ regstr = gdb.execute(self.cmd, False, True)
+ except:
+ regstr = 'Exception on instruction:%s' % cmd
+ v = self.decowin.parse(regstr)
+ self.decowin.update(v)
+ self.decowin.refresh()
+
+class DecoWindow:
+ def __init__(self, filename):
+ self.filename = filename
+ self.file = None
+ self.pre = {}
+ self.pre2 = {}
+ def insertLine(self, i, v, prev):
+ if self.file == None:
+ self.file = open(self.filename, 'w')
+ if v == prev:
+ print(v , file=self.file)
+ else:
+ print('@@' + v, file=self.file)
+ self.file.flush()
+ def update(self, valuelist):
+ for (i, v) in valuelist:
+ try:
+ prevalue = self.pre[i]
+ except:
+ prevalue = None
+ self.insertLine(i, v, prevalue)
+ self.pre2[i] = v
+ def parse(self, regstr):
+ vlist = []
+ for line in regstr.split('\n'):
+ if len(line.strip()) == 0:
+ continue
+ vlist.append((line.split()[0], line))
+ return vlist
+ def refresh(self):
+ if self.file == None:
+ self.file = open(self.filename, 'w')
+ self.file.close()
+ self.file = None
+ self.pre = self.pre2
+ self.pre2 = {}
+
+
+def findRegAnnotate(regstr):
+ b = regstr.find('<')
+ if b > 0:
+ return regstr[b:regstr.find('>') + 1]
+ b = regstr.find('[')
+ if b > 0:
+ return regstr[b:regstr.find(']') + 1]
+
+
+class RegDecoWindow(DecoWindow):
+ def __init__(self, filename):
+ DecoWindow.__init__(self, filename)
+ def insertLine(self, i, v, prev):
+ if self.file == None:
+ self.file = open(self.filename, 'w')
+ ann = findRegAnnotate(v)
+ if v == prev:
+ prefix = '\r'
+ else:
+ prefix = '\r@@'
+ if ann != None:
+ output = prefix + i + '\t' + v.split()[1] + '///\t' + ann
+ else:
+ output = prefix + i + '\t' + v.split()[1]
+ print(output, file=self.file)
+ self.file.flush()
+
+'''
+ decorate bt output string
+'''
+class BtDecoWindow(DecoWindow):
+ def __init__(self, filename):
+ DecoWindow.__init__(self, filename)
+ def parse(self, regstr):
+ vlist = []
+ lines = regstr.strip().split('\n')
+ # reverse the index order
+ index = len(lines)
+ for line in lines:
+ if len(line.strip()) == 0:
+ continue
+ if line.split()[0] == 'Exception':
+ pass
+ else:
+ # remove first token
+ line = line.replace(line.split()[0], '').lstrip()
+ line = ('#%-3d' % index ) + line
+ vlist.append((str(index), line))
+ index = index -1
+ return vlist
+
+
+'''
+ store watch variables
+'''
+global LxWatch
+LxWatch = {}
+LxWatch[1] = "p $lx_current().comm"
+LxWatch[2] = "x/32x $rsp"
+LxWatch[3] = "x/8i $rip"
+
+'''
+ Watch data window's decorator
+'''
+class WatchDecoWindow(DecoWindow):
+ def __init__(self, filename):
+ DecoWindow.__init__(self, filename)
+ def parse(self, index, cmd, regstr):
+ vlist = []
+ vlist.append(('[%d]'%index, '[%d] '%index + cmd))
+ cmd0 = cmd.split()[0]
+ lineno = 0
+ for line in regstr.split('\n'):
+ if len(line.strip()) == 0:
+ continue
+ istr = '[%d](%d)' % (index, lineno)
+ if cmd0 == 'p' and line.find('=') > 0:
+ line = line[line.find('=')+1:]
+ vlist.append((istr, line))
+ else:
+ vlist.append((istr, line))
+ lineno = lineno + 1
+ return vlist
+
+class WatchWindow(CmdWindow):
+ def __init__(self, file, cmd, DecoClass):
+ CmdWindow.__init__(self, file, cmd, DecoClass)
+ def refresh(self, events):
+ for index in LxWatch:
+ cmd = LxWatch[index]
+ try:
+ regstr = gdb.execute(cmd, False, True)
+ except:
+ regstr = 'Exception on instruction:%s' % cmd
+ v = self.decowin.parse(index, cmd, regstr)
+ self.decowin.update(v)
+ self.decowin.refresh()
+ def parse(self, index, regstr):
+ vlist = []
+ for line in regstr.split('\n'):
+ if len(line.strip()) == 0:
+ continue
+ vlist.append(('[%d]' % index + line.split()[0], line))
+ return vlist
+
+class LxGuiFUnction(gdb.Command):
+ """Enable GDB data window feature.
+
+lx-dw: to enable the data window feature, including reg/ bt and watch data windows.
+ """
+ def __init__ (self):
+ gdb.Command.__init__(self, "lx-dw", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL, True)
+ def invoke (self, arg, from_tty):
+ if arg == "off":
+ try:
+ gdb.events.stop.disconnect(self.regw.refresh)
+ gdb.events.stop.disconnect(self.btw.refresh)
+ gdb.events.stop.disconnect(self.watchw.refresh)
+ except:
+ pass
+ else:
+ self.regw = CmdWindow('/tmp/reg-dw', 'info reg', RegDecoWindow)
+ self.btw = CmdWindow('/tmp/bt-dw', 'bt', BtDecoWindow)
+ self.watchw = WatchWindow('/tmp/watch-dw', '', WatchDecoWindow)
+ gdb.events.stop.connect(self.regw.refresh)
+ gdb.events.stop.connect(self.btw.refresh)
+ gdb.events.stop.connect(self.watchw.refresh)
+
+LxGuiFUnction()
+
+def findLxWatchSlot():
+ i = 1
+ while True:
+ if not i in LxWatch:
+ return i
+ i = i +1
+
+class LxAddFunction(gdb.Command):
+ """Add gdb command into watch data window.
+
+lx-add <gdb-command>: add gdb command into watch data window; the command would be
+executed on every step or execution break and command results would be updated on
+to watch data window."""
+
+ def __init__ (self):
+ gdb.Command.__init__(self, "lx-add", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL, True)
+ def invoke (self, arg, from_tty):
+ index = findLxWatchSlot()
+ LxWatch[index] = arg
+ print(LxWatch)
+
+LxAddFunction()
+
+class LxDelFunction(gdb.Command):
+ """Remove one expression into watch data window.
+
+lx-del <id>: remove the gdb command from watch data window."""
+
+ def __init__ (self):
+ gdb.Command.__init__(self, "lx-del", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL, True)
+ def invoke (self, arg, from_tty):
+ index = int(arg)
+ del LxWatch[index]
+ print(LxWatch)
+
+LxDelFunction()
diff --git a/scripts/gdb/linux/lx-dw.py b/scripts/gdb/linux/lx-dw.py
new file mode 100644
index 0000000..26a8c2c
--- /dev/null
+++ b/scripts/gdb/linux/lx-dw.py
@@ -0,0 +1,53 @@
+#
+# gdb data window feature for Linux kernel debugging
+#
+#
+# Authors:
+# Houcheng Lin <[email protected]>
+#
+# This work is licensed under the terms of the GNU GPL version 2.
+#
+
+import os, time, sys
+import curses
+
+''' for curses control '''
+global stdscr
+stdscr = curses.initscr()
+prevlen = 0
+
+def update(filename):
+ global prevlen
+ fd = open(filename, 'r')
+ count = 0
+ stdscr.erase()
+ try:
+ for line in fd.readlines():
+ if line.find('@@') >= 0:
+ line = line.replace('@@', '')
+ for oline in line.split('///'):
+ stdscr.addstr(count, 0, oline, curses.A_REVERSE)
+ count = count +1
+ else:
+ for oline in line.split('///'):
+ stdscr.addstr(count, 0, oline)
+ count = count +1
+ stdscr.refresh()
+ while count < prevlen:
+ stdscr.addstr(count, 0, "")
+ stdscr.refresh()
+ count = count + 1
+ except:
+ pass
+ prevlen = count
+ fd.close()
+
+''' main display loop '''
+file = '/tmp/' + sys.argv[1]
+pretime = None
+while True:
+ nowtime = os.path.getmtime(file)
+ if pretime != nowtime:
+ update(file)
+ pretime = nowtime
+ time.sleep(0.1)
diff --git a/scripts/gdb/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py
index 4848928..7ca6f7b 100644
--- a/scripts/gdb/vmlinux-gdb.py
+++ b/scripts/gdb/vmlinux-gdb.py
@@ -28,3 +28,4 @@ else:
import linux.dmesg
import linux.tasks
import linux.cpus
+ import linux.dw
\ No newline at end of file
diff --git a/scripts/lx-dw.sh b/scripts/lx-dw.sh
new file mode 100755
index 0000000..831b36c
--- /dev/null
+++ b/scripts/lx-dw.sh
@@ -0,0 +1,4 @@
+xterm -fa 'Monospace' -fs 12 -e "python gdb/linux/lx-dw.py bt-dw" &
+xterm -fa 'Monospace' -fs 12 -e "python gdb/linux/lx-dw.py reg-dw" &
+xterm -fa 'Monospace' -fs 12 -e "python gdb/linux/lx-dw.py watch-dw" &
+
--
2.1.4
On 2015-07-16 17:58, Houcheng Lin wrote:
> Add data window feature to show current kernel status
> on separate consoles, including: 1) registers, 2) back
> trace and 3) watch data windows.
>
> The data window would help kernel developer to understand
> current hardware/ kernel status, and on single-stepping, the
> modified fields in the data window would be highlighted.
I was trying to get an overview of this feature. Unfortunately, it does
not work for me. I type in the commands you describe in the
documentation, but nothing shows up. I suspect there are some hard-coded
assumption about directory layouts etc. that aren't fulfilled here
(out-of-tree build).
Anyway, let's try to understand what you would like to achieve. This
seems to be some add-on tool for gdb debugging sessions to display
target states continuously in separate terminals. Right? Is this
comparable to gdb's tui mode?
More important, is this specific to the debugging infrastructure we have
in scripts/gdb? Or could I install it (after some modifications, I
guess) in my home directory and use it for any gdb session, not just
with Linux kernels? Is there anything in it that particularly refers to
Linux structures? In that case I would suggest to make it stand-alone
first so that one could easily include it whenever needed, either
explicitly or via ~/.gitinit.
Thanks for sharing in any case!
Jan
2015-07-22 5:04 GMT+08:00 Jan Kiszka <[email protected]>:
> On 2015-07-16 17:58, Houcheng Lin wrote:
>> Add data window feature to show current kernel status
>> on separate consoles, including: 1) registers, 2) back
>> trace and 3) watch data windows.
>>
>> The data window would help kernel developer to understand
>> current hardware/ kernel status, and on single-stepping, the
>> modified fields in the data window would be highlighted.
>
> I was trying to get an overview of this feature. Unfortunately, it does
> not work for me. I type in the commands you describe in the
> documentation, but nothing shows up. I suspect there are some hard-coded
> assumption about directory layouts etc. that aren't fulfilled here
> (out-of-tree build).
Sorry for not working. The steps might be run the "lx-dw" command
first, then step the kernel in gdb; the kernel status would be dump
to files in /tmp. Then you can run lx-dw.sh that creates 3 xterm window
to display registers, back trace and watch variables.
>
> Anyway, let's try to understand what you would like to achieve. This
> seems to be some add-on tool for gdb debugging sessions to display
> target states continuously in separate terminals. Right? Is this
> comparable to gdb's tui mode?
>
Yes, it is similar feature; dumps the content of register and kernel
status into a separate window continuously and highlight the difference.
It's snapshot is here:
https://www.dropbox.com/s/tl67sxm6474jd8i/dw-screenshot.png?dl=0
> More important, is this specific to the debugging infrastructure we have
> in scripts/gdb? Or could I install it (after some modifications, I
> guess) in my home directory and use it for any gdb session, not just
> with Linux kernels? Is there anything in it that particularly refers to
> Linux structures? In that case I would suggest to make it stand-alone
> first so that one could easily include it whenever needed, either
> explicitly or via ~/.gitinit.
This is not under the infrastructure of kernel gdb, however, this feature
is needed and specific for kernel debugging. When trace an optimized kernel
source, we usually need to trace it in assembly and throught the data window
to update the current kernel status. It can be standalone, but I think it may
helps a lot when developing kernel and good to be part of kernel gdb scripts.
best regards,
Houcheng Lin
>
> Thanks for sharing in any case!
>
> Jan
>
--
Best regards,
Houcheng Lin