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 <houcheng@xxxxxxxxx> --- 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 <houcheng@xxxxxxxxx> +# +# 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 <houcheng@xxxxxxxxx> +# +# 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 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html