From b2b1bc51656cfbedae0474a7fc3689d06cff9e87 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Wed, 10 Feb 2016 14:41:14 -0500 Subject: [PATCH 01/14] crash.kdump.target: fix indentation There were some bare tabs lurking in there. Now it's just spaces as expected. Signed-off-by: Jeff Mahoney --- crash/kdump/target.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/crash/kdump/target.py b/crash/kdump/target.py index e1500b27945..530505bda48 100644 --- a/crash/kdump/target.py +++ b/crash/kdump/target.py @@ -53,14 +53,14 @@ class Target(gdb.Target): def __init__(self, fil): if isinstance(fil, str): fil = file(fil) - self.fil = fil - print "kdump (%s)" % fil - self.kdump = kdumpfile(fil) - self.kdump.symbol_func = symbol_func - self.kdump.vtop_init() - super(Target, self).__init__() - gdb.execute('set print thread-events 0') - self.setup_tasks() + self.fil = fil + print "kdump (%s)" % fil + self.kdump = kdumpfile(fil) + self.kdump.symbol_func = symbol_func + self.kdump.vtop_init() + super(Target, self).__init__() + gdb.execute('set print thread-events 0') + self.setup_tasks() def setup_tasks(self): init_task = gdb.lookup_global_symbol('init_task') @@ -77,16 +77,16 @@ def setup_tasks(self): def to_xfer_partial(self, obj, annex, readbuf, writebuf, offset, ln): ret = -1 if obj == self.TARGET_OBJECT_MEMORY: - try: - r = self.kdump.read (self.kdump.KDUMP_KVADDR, offset, ln) - readbuf[:] = r - ret = ln - except EOFException, e: - raise gdb.TargetXferEof(str(e)) - except NoDataException, e: - raise gdb.TargetXferUnavailable(str(e)) - else: - raise IOError("Unknown obj type") + try: + r = self.kdump.read (self.kdump.KDUMP_KVADDR, offset, ln) + readbuf[:] = r + ret = ln + except EOFException, e: + raise gdb.TargetXferEof(str(e)) + except NoDataException, e: + raise gdb.TargetXferUnavailable(str(e)) + else: + raise IOError("Unknown obj type") return ret def to_thread_alive(self, ptid): From 210da4c48419bfe8dffc3d6a5f94e14ae6a894d2 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Wed, 10 Feb 2016 14:44:13 -0500 Subject: [PATCH 02/14] crash.types: introduce new submodule for common types This moves the list handling to crash.types.list and accompanying infrastructure to crash.types.util so that it can be used elsewhere. Signed-off-by: Jeff Mahoney --- crash/kdump/target.py | 2 +- crash/kdump/util.py | 56 ----------------------------------------- crash/types/__init__.py | 2 ++ crash/types/list.py | 23 +++++++++++++++++ crash/types/util.py | 26 +++++++++++++++++++ 5 files changed, 52 insertions(+), 57 deletions(-) delete mode 100644 crash/kdump/util.py create mode 100644 crash/types/__init__.py create mode 100644 crash/types/list.py create mode 100644 crash/types/util.py diff --git a/crash/kdump/target.py b/crash/kdump/target.py index 530505bda48..8e2d02a35e6 100644 --- a/crash/kdump/target.py +++ b/crash/kdump/target.py @@ -3,8 +3,8 @@ import gdb from kdumpfile import kdumpfile -from util import list_for_each_entry from kdumpfile.exceptions import * +from crash.types.list import list_for_each_entry #arch = "i386:x86-64" # diff --git a/crash/kdump/util.py b/crash/kdump/util.py deleted file mode 100644 index 476aa149929..00000000000 --- a/crash/kdump/util.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python -# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: - -import gdb - -charp = gdb.lookup_type('char').pointer() - -def resolve_type(val): - if isinstance(val, str): - gdbtype = gdb.lookup_gdbtype(val) - elif isinstance(val, gdb.Value): - gdbtype = val.gdbtype - else: - gdbtype = val - return gdbtype - -def offsetof(val, member): - gdbtype = resolve_type(val) - if not isinstance(val, gdb.Type): - raise TypeError("offsetof requires gdb.Type or a string/value that can be used to lookup a gdb.Type") - - return gdbtype[member].bitpos >> 3 - -def container_of(val, gdbtype, member): - gdbtype = resolve_type(gdbtype) - offset = offsetof(gdbtype, member) - return (val.cast(charp) - offset).cast(gdbtype.pointer()).dereference() - -list_head_type = gdb.lookup_type("struct list_head") -def list_for_each(list_head): - if list_head.type == list_head_type.pointer(): - list_head = list_head.dereference() - elif list_head.type != list_head_type: - raise gdb.GdbError("Must be struct list_head not %s" % list_head.type) - - node = list_head['next'].dereference() - while node.address != list_head.address: - yield node.address - node = node['next'].dereference() - -def list_for_each_entry(list_head, gdbtype, member): - for node in list_for_each(list_head): - if node.type != list_head_type.pointer(): - raise TypeError("Type %s found. Expected struct list_head *." % node.type) - yield container_of(node, gdbtype, member) - -def per_cpu(symbol, cpu): - if isinstance(symbol, str): - symbol = gdb.lookup_global_symbol(symbol).value() - elif isinstance(symbol, gdb.Symbol): - symbol = symbol.value() - else: - raise TypeError("Must be string or gdb.Symbol") - - percpu_addr = symbol.address.cast(charp) + per_cpu_offset[cpu] - return percpu_addr.cast(symbol.type.pointer()).dereference() diff --git a/crash/types/__init__.py b/crash/types/__init__.py new file mode 100644 index 00000000000..aae19e3a567 --- /dev/null +++ b/crash/types/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/crash/types/list.py b/crash/types/list.py new file mode 100644 index 00000000000..e658dc2db8a --- /dev/null +++ b/crash/types/list.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: + +import gdb +from util import container_of + +list_head_type = gdb.lookup_type("struct list_head") +def list_for_each(list_head): + if list_head.type == list_head_type.pointer(): + list_head = list_head.dereference() + elif list_head.type != list_head_type: + raise gdb.GdbError("Must be struct list_head not %s" % list_head.type) + + node = list_head['next'].dereference() + while node.address != list_head.address: + yield node.address + node = node['next'].dereference() + +def list_for_each_entry(list_head, gdbtype, member): + for node in list_for_each(list_head): + if node.type != list_head_type.pointer(): + raise TypeError("Type %s found. Expected struct list_head *." % node.type) + yield container_of(node, gdbtype, member) diff --git a/crash/types/util.py b/crash/types/util.py new file mode 100644 index 00000000000..a8746d086b9 --- /dev/null +++ b/crash/types/util.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: + +import gdb + +def resolve_type(val): + if isinstance(val, str): + gdbtype = gdb.lookup_gdbtype(val) + elif isinstance(val, gdb.Value): + gdbtype = val.gdbtype + else: + gdbtype = val + return gdbtype + +def offsetof(val, member): + gdbtype = resolve_type(val) + if not isinstance(val, gdb.Type): + raise TypeError("offsetof requires gdb.Type or a string/value that can be used to lookup a gdb.Type") + + return gdbtype[member].bitpos >> 3 + +charp = gdb.lookup_type('char').pointer() +def container_of(val, gdbtype, member): + gdbtype = resolve_type(gdbtype) + offset = offsetof(gdbtype, member) + return (val.cast(charp) - offset).cast(gdbtype.pointer()).dereference() From 0bc8c0554538865adaa08e5b761ad1ffa391f7af Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Wed, 10 Feb 2016 16:40:07 -0500 Subject: [PATCH 03/14] crash.arch: introduce and use architecture-specific thread handling This commit defines a CrashArchitecture base class for architecture-specific code. We use it for implementing initial x86_64 support for thread handling. All that's required to add a new architecture is to drop in a $arch.py file in the crash/arch directory. Signed-off-by: Jeff Mahoney --- crash/arch/__init__.py | 34 ++++++++++++++++++++++++++++++ crash/arch/x86_64.py | 45 +++++++++++++++++++++++++++++++++++++++ crash/kdump/target.py | 48 ++++++++++-------------------------------- 3 files changed, 90 insertions(+), 37 deletions(-) create mode 100644 crash/arch/__init__.py create mode 100644 crash/arch/x86_64.py diff --git a/crash/arch/__init__.py b/crash/arch/__init__.py new file mode 100644 index 00000000000..cf611b6aac1 --- /dev/null +++ b/crash/arch/__init__.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: + +import os +import glob +import importlib + +class CrashArchitecture: + ident = "base-class" + aliases = None + def __init__(self): + pass + + def setup_thread(self, thread): + raise NotImplementedError("setup_thread not implemented") + +architectures = {} +def register(arch): + architectures[arch.ident] = arch + for ident in arch.aliases: + architectures[ident] = arch + +def get_architecture(archname): + if archname in architectures: + return architectures[archname] + + return None + +modules = glob.glob(os.path.dirname(__file__)+"/[A-Za-z]*.py") +__all__ = [ os.path.basename(f)[:-3] for f in modules] + +mods = __all__ +for mod in mods: + x = importlib.import_module("crash.arch.%s" % mod) diff --git a/crash/arch/x86_64.py b/crash/arch/x86_64.py new file mode 100644 index 00000000000..03a17247567 --- /dev/null +++ b/crash/arch/x86_64.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: + +import gdb +from crash.arch import CrashArchitecture, register + +class x86_64Architecture(CrashArchitecture): + ident = "i386:x86-64" + aliases = [ "x86_64" ] + + def __init__(self): + # PC for blocked threads + self.rip = gdb.lookup_minimal_symbol("thread_return").value() + self.ulong_type = gdb.lookup_type('unsigned long') + + def setup_thread(self, task): + ulong_type = self.ulong_type + + rsp = task['thread']['sp'].cast(ulong_type.pointer()) + rbp = rsp.dereference().cast(ulong_type.pointer()) + rbx = (rbp - 1).dereference() + r12 = (rbp - 2).dereference() + r13 = (rbp - 3).dereference() + r14 = (rbp - 4).dereference() + r15 = (rbp - 5).dereference() + + # The two pushes that don't have CFI info + # rsp += 2 + + # ex = in_exception_stack(rsp) + # if ex: + # print "EXCEPTION STACK: pid %d" % task['pid'] + + thread.registers['rsp'].value = rsp + thread.registers['rbp'].value = rbp + thread.registers['rip'].value = self.rip + thread.registers['rbx'].value = rbx + thread.registers['r12'].value = r12 + thread.registers['r13'].value = r13 + thread.registers['r14'].value = r14 + thread.registers['r15'].value = r15 + thread.registers['cs'].value = 2*8 + thread.registers['ss'].value = 3*8 + +register(x86_64Architecture) diff --git a/crash/kdump/target.py b/crash/kdump/target.py index 8e2d02a35e6..ad829678d46 100644 --- a/crash/kdump/target.py +++ b/crash/kdump/target.py @@ -5,42 +5,7 @@ from kdumpfile import kdumpfile from kdumpfile.exceptions import * from crash.types.list import list_for_each_entry - -#arch = "i386:x86-64" -# -#setup = { -# 'i386:x86-64' : setup_thread_amd64, -#} - -ulong_type = gdb.lookup_type('unsigned long') -rip = gdb.lookup_minimal_symbol("thread_return").value() - -def setup_thread_amd64(thread, task): - rsp = task['thread']['sp'].cast(ulong_type.pointer()) - rbp = rsp.dereference().cast(ulong_type.pointer()) - rbx = (rbp - 1).dereference() - r12 = (rbp - 2).dereference() - r13 = (rbp - 3).dereference() - r14 = (rbp - 4).dereference() - r15 = (rbp - 5).dereference() - - # The two pushes that don't have CFI info -# rsp += 2 - -# ex = in_exception_stack(rsp) -# if ex: -# print "EXCEPTION STACK: pid %d" % task['pid'] - - thread.registers['rsp'].value = rsp - thread.registers['rbp'].value = rbp - thread.registers['rip'].value = rip - thread.registers['rbx'].value = rbx - thread.registers['r12'].value = r12 - thread.registers['r13'].value = r13 - thread.registers['r14'].value = r14 - thread.registers['r15'].value = r15 - thread.registers['cs'].value = 2*8 - thread.registers['ss'].value = 3*8 +import crash.arch def symbol_func(symname): ms = gdb.lookup_minimal_symbol(symname) @@ -56,12 +21,21 @@ def __init__(self, fil): self.fil = fil print "kdump (%s)" % fil self.kdump = kdumpfile(fil) + self.setup_arch() self.kdump.symbol_func = symbol_func self.kdump.vtop_init() super(Target, self).__init__() gdb.execute('set print thread-events 0') self.setup_tasks() + def setup_arch(self): + archname = self.kdump.attr("arch")['name'] + archclass = crash.arch.get_architecture(archname) + if not archclass: + raise NotImplementedError("Architecture %s is not supported yet." % archname) + + self.arch = archclass() + def setup_tasks(self): init_task = gdb.lookup_global_symbol('init_task') task_list = init_task.value()['tasks'] @@ -97,7 +71,7 @@ def to_pid_to_str(self, ptid): def to_fetch_registers(self, register): thread = gdb.selected_thread() - setup_thread_amd64(thread, thread.info) + self.arch.setup_thread(thread) return True def to_prepare_to_store(self, thread): From 4725277eece320e6036c3acd8ac3afe928e74aac Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Wed, 10 Feb 2016 16:41:53 -0500 Subject: [PATCH 04/14] crash.types.percpu: Add percpu handling Signed-off-by: Jeff Mahoney --- crash/types/percpu.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 crash/types/percpu.py diff --git a/crash/types/percpu.py b/crash/types/percpu.py new file mode 100644 index 00000000000..fad36817f9a --- /dev/null +++ b/crash/types/percpu.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: + +import gdb + +ulong = gdb.lookup_type('unsigned long') +charp = gdb.lookup_type('char').pointer() + +per_cpu_offset_sym = gdb.lookup_global_symbol('__per_cpu_offset') +per_cpu_offset = long(per_cpu_offset_sym.value().address) +offset_type = per_cpu_offset_sym.value()[0].type +nr_cpus = per_cpu_offset_sym.type.sizeof / offset_type.sizeof + +def is_percpu_symbol(sym): + return sym.section is not None and 'percpu' in sym.section + +def get_percpu_var_nocheck(sym, cpu=None): + symtype = sym.type + if cpu is None: + vals = {} + for cpu in range(0, nr_cpus): + vals[cpu] = get_percpu_var_nocheck(sym, cpu) + return vals + + addr = per_cpu_offset_sym.value()[cpu] + addr += sym.value().address.cast(charp) + return addr.cast(symtype.pointer()).dereference() + +def get_percpu_var(sym, cpu=None): + if not is_percpu_symbol(sym): + raise TypeError("symbol not in percpu section") + + return get_percpu_var_nocheck(sym, cpu) From 46bbcf59871bc11ad7b3b05c865b0d09c2b4a7f3 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Thu, 11 Feb 2016 21:14:40 -0500 Subject: [PATCH 05/14] crash.kdump.target: to_thread_alive returns bool Signed-off-by: Jeff Mahoney --- crash/kdump/target.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash/kdump/target.py b/crash/kdump/target.py index ad829678d46..1e4fb36fe78 100644 --- a/crash/kdump/target.py +++ b/crash/kdump/target.py @@ -64,7 +64,7 @@ def to_xfer_partial(self, obj, annex, readbuf, writebuf, offset, ln): return ret def to_thread_alive(self, ptid): - return 1 + return True def to_pid_to_str(self, ptid): return "pid %d" % ptid[1] From ed139193ae58f35cb64750e6cb4e16615e0d4c3c Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Thu, 11 Feb 2016 22:16:25 -0500 Subject: [PATCH 06/14] crash: add support for active threads The kernel data structures in the crash dump tell us which threads were active when the dump was taken but not the contents of those threads' registers. The dump format does contain that information so we need to access it from the vmcore attributes instead. Signed-off-by: Jeff Mahoney --- crash/arch/__init__.py | 11 ++++++++++- crash/arch/x86_64.py | 10 +++++++++- crash/kdump/target.py | 21 ++++++++++++++++++++- crash/types/task.py | 26 ++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 crash/types/task.py diff --git a/crash/arch/__init__.py b/crash/arch/__init__.py index cf611b6aac1..1242a7c71f6 100644 --- a/crash/arch/__init__.py +++ b/crash/arch/__init__.py @@ -11,8 +11,17 @@ class CrashArchitecture: def __init__(self): pass + def setup_thread_active(self, thread): + raise NotImplementedError("setup_thread_active not implemented") + + def setup_thread_scheduled(self, thread): + raise NotImplementedError("setup_thread_scheduled not implemented") + def setup_thread(self, thread): - raise NotImplementedError("setup_thread not implemented") + if thread.info.active: + self.setup_thread_active(thread) + else: + self.setup_thread_scheduled(thread) architectures = {} def register(arch): diff --git a/crash/arch/x86_64.py b/crash/arch/x86_64.py index 03a17247567..4ead1c2f30a 100644 --- a/crash/arch/x86_64.py +++ b/crash/arch/x86_64.py @@ -13,8 +13,16 @@ def __init__(self): self.rip = gdb.lookup_minimal_symbol("thread_return").value() self.ulong_type = gdb.lookup_type('unsigned long') - def setup_thread(self, task): + def setup_thread_active(self, thread): + task = thread.info + for reg in task.regs: + if reg in ["gs_base", "orig_ax", "rflags", "fs_base"]: + continue + thread.registers[reg].value = task.regs[reg] + + def setup_thread_scheduled(self, thread): ulong_type = self.ulong_type + task = thread.info.task_struct rsp = task['thread']['sp'].cast(ulong_type.pointer()) rbp = rsp.dereference().cast(ulong_type.pointer()) diff --git a/crash/kdump/target.py b/crash/kdump/target.py index 1e4fb36fe78..df86be33842 100644 --- a/crash/kdump/target.py +++ b/crash/kdump/target.py @@ -5,8 +5,12 @@ from kdumpfile import kdumpfile from kdumpfile.exceptions import * from crash.types.list import list_for_each_entry +from crash.types.percpu import get_percpu_var +from crash.types.task import LinuxTask import crash.arch +LINUX_KERNEL_PID = 1 + def symbol_func(symname): ms = gdb.lookup_minimal_symbol(symname) if not ms: @@ -39,13 +43,28 @@ def setup_arch(self): def setup_tasks(self): init_task = gdb.lookup_global_symbol('init_task') task_list = init_task.value()['tasks'] + runqueues = gdb.lookup_global_symbol('runqueues') + + rqs = get_percpu_var(runqueues) + rqscurrs = { long(x["curr"]) : k for (k, x) in rqs.items() } self.pid_to_task_struct = {} for task in list_for_each_entry(task_list, init_task.type, 'tasks'): - thread = gdb.selected_inferior().new_thread((1, task['pid'], 0), task) + cpu = None + regs = None + active = long(task.address) in rqscurrs + if active: + cpu = rqscurrs[long(task.address)] + regs = self.kdump.attr("cpu.%d.reg" % cpu) + + ltask = LinuxTask(task, active, cpu, regs) + ptid = (LINUX_KERNEL_PID, task['pid'], 0) + thread = gdb.selected_inferior().new_thread(ptid, ltask) thread.name = task['comm'].string() + ltask.attach_thread(thread) + gdb.selected_inferior().executing = False def to_xfer_partial(self, obj, annex, readbuf, writebuf, offset, ln): diff --git a/crash/types/task.py b/crash/types/task.py new file mode 100644 index 00000000000..37b4e94327a --- /dev/null +++ b/crash/types/task.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: + +import gdb + +task_struct_type = gdb.lookup_type('struct task_struct') + +class LinuxTask: + def __init__(self, task_struct, active = False, + cpu = None, regs = None): + if cpu is not None and not isinstance(cpu, int): + raise TypeError("cpu must be integer or None") + + if not isinstance(task_struct, gdb.Value) or \ + not task_struct.type != task_struct_type: + raise TypeError("task_struct must be gdb.Value describing struct task_struct") + + self.task_struct = task_struct + self.active = active + self.cpu = cpu + self.regs = regs + + def attach_thread(self, thread): + if not isinstance(thread, gdb.InferiorThread): + raise TypeError("Expected gdb.InferiorThread") + self.thread = thread From 8696a498d768b352a6734473259cb42491965497 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Thu, 11 Feb 2016 22:46:54 -0500 Subject: [PATCH 07/14] crash.Session: add simple driver for crash interface Initially, it just accepts a filename to set up a target. Signed-off-by: Jeff Mahoney --- crash/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crash/__init__.py b/crash/__init__.py index aae19e3a567..2da63e2d8bb 100644 --- a/crash/__init__.py +++ b/crash/__init__.py @@ -1,2 +1,8 @@ #!/usr/bin/env python # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: + +import kdump.target + +class Session: + def __init__(self, filename): + self.target = kdump.target.Target(filename) From 2ad5a16dc506547f01e0ad4c25cc7a87a59dbef8 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Thu, 11 Feb 2016 22:48:12 -0500 Subject: [PATCH 08/14] crash.commands: add initial command interface Add a simple way to add commands. Initially, commands have a 'py' prefix. Signed-off-by: Jeff Mahoney --- crash/__init__.py | 1 + crash/commands/__init__.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 crash/commands/__init__.py diff --git a/crash/__init__.py b/crash/__init__.py index 2da63e2d8bb..0ce3c67448e 100644 --- a/crash/__init__.py +++ b/crash/__init__.py @@ -2,6 +2,7 @@ # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: import kdump.target +import crash.commands class Session: def __init__(self, filename): diff --git a/crash/commands/__init__.py b/crash/commands/__init__.py new file mode 100644 index 00000000000..b627d66440c --- /dev/null +++ b/crash/commands/__init__.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: + +import gdb + +import os +import glob +import importlib + +class CrashCommand(gdb.Command): + def __init__(self, name, parser): + gdb.Command.__init__(self, "py" + name, gdb.COMMAND_USER) + parser.format_help = lambda: self.__doc__ + self.parser = parser + + def invoke(self, argstr, from_tty): + argv = gdb.string_to_argv(argstr) + try: + args = self.parser.parse_args(argv) + self.execute(args) + except SystemExit: + return + except KeyboardInterrupt: + return + +modules = glob.glob(os.path.dirname(__file__)+"/[A-Za-z]*.py") +__all__ = [ os.path.basename(f)[:-3] for f in modules] + +mods = __all__ +for mod in mods: + x = importlib.import_module("crash.commands.%s" % mod) From bc09bc81135a24030e0b84bb4290638803c2c547 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Thu, 11 Feb 2016 22:49:26 -0500 Subject: [PATCH 09/14] crash.commands.dmesg: add a dmesg command to show the kernel log It interprets both new structured log format and old flat format logs. Signed-off-by: Jeff Mahoney --- crash/commands/dmesg.py | 291 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 crash/commands/dmesg.py diff --git a/crash/commands/dmesg.py b/crash/commands/dmesg.py new file mode 100644 index 00000000000..5dc7588f27a --- /dev/null +++ b/crash/commands/dmesg.py @@ -0,0 +1,291 @@ +#!/usr/bin/env python +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: + +import gdb +from crash.commands import CrashCommand +import argparse +import re + +char = gdb.lookup_type('char') +charp = char.pointer() + +class LogTypeException(Exception): + pass + +class LogInvalidOption(Exception): + pass + +def get_value(name): + return gdb.lookup_symbol(name, None)[0].value() + +class LogCommand(CrashCommand): + """ +NAME + log - dump system message buffer + +SYNOPSIS + log [-tdm] + +DESCRIPTION + This command dumps the kernel log_buf contents in chronological order. The + command supports the older log_buf formats, which may or may not contain a + timestamp inserted prior to each message, as well as the newer variable-length + record format, where the timestamp is contained in each log entry's header. + + -t Display the message text without the timestamp. + -d Display the dictionary of key/value pair properties that are optionally + appended to a message by the kernel's dev_printk() function; only + applicable to the variable-length record format. + -m Display the message log level in brackets preceding each message. For + the variable-length record format, the level will be displayed in + hexadecimal, and depending upon the kernel version, also contains the + facility or flags bits. + + +EXAMPLES + Dump the kernel message buffer: + + crash> log + Linux version 2.2.5-15smp (root@mclinux1) (gcc version egcs-2.91.66 19990 + 314/Linux (egcs-1.1.2 release)) #1 SMP Thu Aug 26 11:04:37 EDT 1999 + Intel MultiProcessor Specification v1.4 + Virtual Wire compatibility mode. + OEM ID: DELL Product ID: WS 410 APIC at: 0xFEE00000 + Processor #0 Pentium(tm) Pro APIC version 17 + Processor #1 Pentium(tm) Pro APIC version 17 + I/O APIC #2 Version 17 at 0xFEC00000. + Processors: 2 + mapped APIC to ffffe000 (fee00000) + mapped IOAPIC to ffffd000 (fec00000) + Detected 447696347 Hz processor. + Console: colour VGA+ 80x25 + Calibrating delay loop... 445.64 BogoMIPS + ... + 8K byte-wide RAM 5:3 Rx:Tx split, autoselect/Autonegotiate interface. + MII transceiver found at address 24, status 782d. + Enabling bus-master transmits and whole-frame receives. + Installing knfsd (copyright (C) 1996 okir@monad.swb.de). + nfsd_init: initialized fhcache, entries=256 + ... + + Do the same thing, but also show the log level preceding each message: + + crash> log -m + <4>Linux version 2.2.5-15smp (root@mclinux1) (gcc version egcs-2.91.66 19990 + 314/Linux (egcs-1.1.2 release)) #1 SMP Thu Aug 26 11:04:37 EDT 1999 + <4>Intel MultiProcessor Specification v1.4 + <4> Virtual Wire compatibility mode. + <4>OEM ID: DELL Product ID: WS 410 APIC at: 0xFEE00000 + <4>Processor #0 Pentium(tm) Pro APIC version 17 + <4>Processor #1 Pentium(tm) Pro APIC version 17 + <4>I/O APIC #2 Version 17 at 0xFEC00000. + <4>Processors: 2 + <4>mapped APIC to ffffe000 (fee00000) + <4>mapped IOAPIC to ffffd000 (fec00000) + <4>Detected 447696347 Hz processor. + <4>Console: colour VGA+ 80x25 + <4>Calibrating delay loop... 445.64 BogoMIPS + ... + <6> 8K byte-wide RAM 5:3 Rx:Tx split, autoselect/Autonegotiate interface. + <6> MII transceiver found at address 24, status 782d. + <6> Enabling bus-master transmits and whole-frame receives. + <6>Installing knfsd (copyright (C) 1996 okir@monad.swb.de). + <7>nfsd_init: initialized fhcache, entries=256 + ... + + On a system with the variable-length record format, and whose log_buf has been + filled and wrapped around, display the log with timestamp data: + + crash> log + [ 0.467730] pci 0000:ff:02.0: [8086:2c10] type 00 class 0x060000 + [ 0.467749] pci 0000:ff:02.1: [8086:2c11] type 00 class 0x060000 + [ 0.467769] pci 0000:ff:02.4: [8086:2c14] type 00 class 0x060000 + [ 0.467788] pci 0000:ff:02.5: [8086:2c15] type 00 class 0x060000 + [ 0.467809] pci 0000:ff:03.0: [8086:2c18] type 00 class 0x060000 + [ 0.467828] pci 0000:ff:03.1: [8086:2c19] type 00 class 0x060000 + ... + + Display the same message text as above, without the timestamp data: + + crash> log -t + pci 0000:ff:02.0: [8086:2c10] type 00 class 0x060000 + pci 0000:ff:02.1: [8086:2c11] type 00 class 0x060000 + pci 0000:ff:02.4: [8086:2c14] type 00 class 0x060000 + pci 0000:ff:02.5: [8086:2c15] type 00 class 0x060000 + pci 0000:ff:03.0: [8086:2c18] type 00 class 0x060000 + pci 0000:ff:03.1: [8086:2c19] type 00 class 0x060000 + ... + + Display the same message text as above, with appended dictionary data: + + crash> log -td + pci 0000:ff:02.0: [8086:2c10] type 00 class 0x060000 + SUBSYSTEM=pci + DEVICE=+pci:0000:ff:02.0 + pci 0000:ff:02.1: [8086:2c11] type 00 class 0x060000 + SUBSYSTEM=pci + DEVICE=+pci:0000:ff:02.1 + pci 0000:ff:02.4: [8086:2c14] type 00 class 0x060000 + SUBSYSTEM=pci + DEVICE=+pci:0000:ff:02.4 + pci 0000:ff:02.5: [8086:2c15] type 00 class 0x060000 + SUBSYSTEM=pci + DEVICE=+pci:0000:ff:02.5 + pci 0000:ff:03.0: [8086:2c18] type 00 class 0x060000 + SUBSYSTEM=pci + DEVICE=+pci:0000:ff:03.0 + pci 0000:ff:03.1: [8086:2c19] type 00 class 0x060000 + SUBSYSTEM=pci + DEVICE=+pci:0000:ff:03.1 + ... + + """ + def __init__(self, name): + + parser = argparse.ArgumentParser(prog=name) + + parser.add_argument('-t', action='store_true', default=False) + parser.add_argument('-d', action='store_true', default=False) + parser.add_argument('-m', action='store_true', default=False) + + parser.format_usage = lambda : "log [-tdm]\n" + CrashCommand.__init__(self, name, parser) + + self.printk_log_type = None + try: + printk_log = gdb.lookup_type('struct printk_log') + if printk_log: + self.printk_log_type = printk_log.pointer() + except gdb.error, e: + pass + + def filter_unstructured_log(self, log, args): + lines = log.split('\n') + if not args.m: + newlog = [] + for line in lines: + if not args.m: + line = re.sub("^<[0-9]+>", "", line) + if args.t: + line = re.sub("^\[[0-9\. ]+\] ", "", line) + newlog.append(line) + lines = newlog + + return "\n".join(lines) + + def log_from_idx(self, logbuf, idx, dict_needed=False): + logbuf = logbuf.address.dereference() + msg = (logbuf + idx).cast(self.printk_log_type) + + try: + textval = msg.cast(charp) + self.printk_log_type.target().sizeof + text = textval.string(length=msg['text_len']) + except UnicodeDecodeError, e: + print e + + msglen = int(msg['len']) + + # A zero-length message means we wrap back to the beginning + if msglen == 0: + nextidx = 0 + else: + nextidx = idx + msglen + + textlen = int(msg['text_len']) + + msgdict = { + 'text' : text[0:textlen], + 'timestamp' : long(msg['ts_nsec']), + 'level' : int(msg['level']), + 'next' : nextidx, + 'dict' : [], + } + + if dict_needed: + dict_len = int(msg['dict_len']) + d = msg.cast(charp) + self.printk_log_type.target().sizeof + textlen + s = "" + + for i in range(0,dict_len): + if d[i]: + s += chr(d[i]) + else: + msgdict['dict'].append(s) + s = "" + + if s != "": + msgdict['dict'].append(s) + return msgdict + + def get_log_msgs(self, dict_needed=False): + first_idx = get_value('log_first_idx') + next_idx = get_value("log_next_idx") + clear_seq = get_value('clear_seq') + first_seq = get_value('log_first_seq') + next_seq = get_value('log_next_seq') + logbuf = get_value("log_buf") + + if first_idx == None or next_idx == None: + raise LogTypeException("not structured log") + + if clear_seq < first_seq: + clear_seq = first_seq + + + seq = clear_seq + idx = first_idx + + while seq < next_seq: + msg = self.log_from_idx(logbuf, idx, dict_needed) + seq += 1 + idx = msg['next'] + yield(msg) + + def handle_structured_log(self, args): + for msg in self.get_log_msgs(args.d): + timestamp = "" + if not args.t: + timestamp = "[%5lu.%06lu] " % \ + (msg['timestamp'] / 1000000000, + (msg['timestamp'] % 1000000000) / 1000) + level = "" + if args.m: + level = "<%d>" % msg['level'] + + for line in msg['text'].split('\n'): + print "%s%s%s" % (level, timestamp, line) + + for d in msg['dict']: + print "%15s%s" % ("", d.encode('string_escape')) + + def handle_logbuf(self, args): + + log_buf_len = get_value("log_buf_len") + log_buf = get_value("log_buf") + + if log_buf_len and log_buf: + if (args.d): + raise LogInvalidOption("Unstructured logs don't offer key/value pair support") + print self.filter_unstructured_log(log_buf.string(), args) + + def execute(self, args): + try: + self.handle_structured_log(args) + return + except LogTypeException, lte: + pass + + try: + self.handle_logbuf(args) + return + except LogTypeException, lte: + pass + except LogInvalidOption, lio: + raise gdb.GdbError(str(lio)) + + print "Can't find valid log" + + print args + +LogCommand("log") +LogCommand("dmesg") From 43360e03060664983324d0d919e2bd7c284fecd6 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Thu, 25 Feb 2016 08:45:12 -0500 Subject: [PATCH 10/14] crash: delay importing kdump until we're instantiated This allows us to load the module whenever, but wait until we have the executable environment set up. Signed-off-by: Jeff Mahoney --- crash/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash/__init__.py b/crash/__init__.py index 0ce3c67448e..4f960d2a44e 100644 --- a/crash/__init__.py +++ b/crash/__init__.py @@ -2,8 +2,8 @@ # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: import kdump.target -import crash.commands class Session: def __init__(self, filename): self.target = kdump.target.Target(filename) + import crash.commands From cb9f190c03598f6849a18584154ad8deda7a5b20 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Thu, 25 Feb 2016 08:46:33 -0500 Subject: [PATCH 11/14] tasks: implement `ps' and infrastructure behind it This patch will ultimately be split up but I'm committing for testing. --- crash/arch/__init__.py | 3 + crash/arch/x86_64.py | 13 + crash/commands/ps.py | 538 +++++++++++++++++++++++++++++++++++++++++ crash/kdump/target.py | 9 + crash/types/task.py | 261 +++++++++++++++++++- 5 files changed, 822 insertions(+), 2 deletions(-) create mode 100755 crash/commands/ps.py diff --git a/crash/arch/__init__.py b/crash/arch/__init__.py index 1242a7c71f6..f28b360226e 100644 --- a/crash/arch/__init__.py +++ b/crash/arch/__init__.py @@ -17,6 +17,9 @@ def setup_thread_active(self, thread): def setup_thread_scheduled(self, thread): raise NotImplementedError("setup_thread_scheduled not implemented") + def setup_thread_info(self, thread): + raise NotImplementedError("setup_thread_info not implemented") + def setup_thread(self, thread): if thread.info.active: self.setup_thread_active(thread) diff --git a/crash/arch/x86_64.py b/crash/arch/x86_64.py index 4ead1c2f30a..e6671a88f2f 100644 --- a/crash/arch/x86_64.py +++ b/crash/arch/x86_64.py @@ -12,6 +12,13 @@ def __init__(self): # PC for blocked threads self.rip = gdb.lookup_minimal_symbol("thread_return").value() self.ulong_type = gdb.lookup_type('unsigned long') + thread_info_type = gdb.lookup_type('struct thread_info') + self.thread_info_p_type = thread_info_type.pointer() + + def setup_thread_info(self, thread): + task = thread.info.task_struct + thread_info = task['stack'].cast(self.thread_info_p_type) + thread.info.set_thread_info(thread_info) def setup_thread_active(self, thread): task = thread.info @@ -50,4 +57,10 @@ def setup_thread_scheduled(self, thread): thread.registers['cs'].value = 2*8 thread.registers['ss'].value = 3*8 + thread.info.stack_pointer = rsp + thread.info.valid_stack = True + + def get_stack_pointer(self, thread): + return long(thread.registers['rsp'].value) + register(x86_64Architecture) diff --git a/crash/commands/ps.py b/crash/commands/ps.py new file mode 100755 index 00000000000..0e366de9a27 --- /dev/null +++ b/crash/commands/ps.py @@ -0,0 +1,538 @@ +#!/usr/bin/env python +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: + +import gdb +import argparse +from crash.commands import CrashCommand +from crash.types.task import LinuxTask + +class PSCommand(CrashCommand): + """ +NAME + ps - display process status information + +SYNOPSIS + ps [-k|-u|-G][-s][-p|-c|-t|-l|-a|-g|-r] [pid | taskp | command] ... + +DESCRIPTION + This command displays process status for selected, or all, processes + in the system. If no arguments are entered, the process data is + is displayed for all processes. Specific processes may be selected + by using the following identifier formats: + + pid a process PID. + taskp a hexadecimal task_struct pointer. + command a command name. If a command name is made up of letters that + are all numerical values, precede the name string with a "\". + If the command string is enclosed within "'" characters, then + the encompassed string must be a POSIX extended regular expression + that will be used to match task names. + + The process list may be further restricted by the following options: + + -k restrict the output to only kernel threads. + -u restrict the output to only user tasks. + -G display only the thread group leader in a thread group. + + The process identifier types may be mixed. For each task, the following + items are displayed: + + 1. the process PID. + 2. the parent process PID. + 3. the CPU number that the task ran on last. + 4. the task_struct address or the kernel stack pointer of the process. + (see -s option below) + 5. the task state (RU, IN, UN, ZO, ST, TR, DE, SW). + 6. the percentage of physical memory being used by this task. + 7. the virtual address size of this task in kilobytes. + 8. the resident set size of this task in kilobytes. + 9. the command name. + + The default output shows the task_struct address of each process under a + column titled "TASK". This can be changed to show the kernel stack + pointer under a column titled "KSTACKP". + + -s replace the TASK column with the KSTACKP column. + + On SMP machines, the active task on each CPU will be highlighted by an + angle bracket (">") preceding its information. + + Alternatively, information regarding parent-child relationships, + per-task time usage data, argument/environment data, thread groups, + or resource limits may be displayed: + + -p display the parental hierarchy of selected, or all, tasks. + -c display the children of selected, or all, tasks. + -t display the task run time, start time, and cumulative user + and system times. + -l display the task last_run or timestamp value, whichever applies, + of selected, or all, tasks; the list is sorted with the most + recently-run task (largest last_run/timestamp) shown first, + followed by the task's current state. + -a display the command line arguments and environment strings of + selected, or all, user-mode tasks. + -g display tasks by thread group, of selected, or all, tasks. + -r display resource limits (rlimits) of selected, or all, tasks. + +EXAMPLES + Show the process status of all current tasks: + + crash> ps + PID PPID CPU TASK ST %MEM VSZ RSS COMM + > 0 0 3 c024c000 RU 0.0 0 0 [swapper] + > 0 0 0 c0dce000 RU 0.0 0 0 [swapper] + 0 0 1 c0fa8000 RU 0.0 0 0 [swapper] + > 0 0 2 c009a000 RU 0.0 0 0 [swapper] + 1 0 1 c0098000 IN 0.0 1096 476 init + 2 1 1 c0090000 IN 0.0 0 0 [kflushd] + 3 1 1 c000e000 IN 0.0 0 0 [kpiod] + 4 1 3 c000c000 IN 0.0 0 0 [kswapd] + 5 1 1 c0008000 IN 0.0 0 0 [mdrecoveryd] + 253 1 2 fbc4c000 IN 0.0 1088 376 portmap + 268 1 2 fbc82000 IN 0.1 1232 504 ypbind + 274 268 2 fa984000 IN 0.1 1260 556 ypbind + 321 1 1 fabf6000 IN 0.1 1264 608 syslogd + 332 1 1 fa9be000 RU 0.1 1364 736 klogd + 346 1 2 fae88000 IN 0.0 1112 472 atd + 360 1 2 faeb2000 IN 0.1 1284 592 crond + 378 1 2 fafd6000 IN 0.1 1236 560 inetd + 392 1 0 fb710000 IN 0.1 2264 1468 named + 406 1 3 fb768000 IN 0.1 1284 560 lpd + 423 1 1 fb8ac000 IN 0.1 1128 528 rpc.statd + 434 1 2 fb75a000 IN 0.0 1072 376 rpc.rquotad + 445 1 2 fb4a4000 IN 0.0 1132 456 rpc.mountd + 460 1 1 fa938000 IN 0.0 0 0 [nfsd] + 461 1 1 faa86000 IN 0.0 0 0 [nfsd] + 462 1 0 fac48000 IN 0.0 0 0 [nfsd] + 463 1 0 fb4ca000 IN 0.0 0 0 [nfsd] + 464 1 0 fb4c8000 IN 0.0 0 0 [nfsd] + 465 1 2 fba6e000 IN 0.0 0 0 [nfsd] + 466 1 1 fba6c000 IN 0.0 0 0 [nfsd] + 467 1 2 fac04000 IN 0.0 0 0 [nfsd] + 468 461 2 fa93a000 IN 0.0 0 0 [lockd] + 469 468 2 fa93e000 IN 0.0 0 0 [rpciod] + 486 1 0 fab54000 IN 0.1 1596 880 amd + 523 1 2 fa84e000 IN 0.1 1884 1128 sendmail + 538 1 0 fa82c000 IN 0.0 1112 416 gpm + 552 1 3 fa70a000 IN 0.1 2384 1220 httpd + 556 552 3 fa776000 IN 0.1 2572 1352 httpd + 557 552 2 faba4000 IN 0.1 2572 1352 httpd + 558 552 1 fa802000 IN 0.1 2572 1352 httpd + 559 552 3 fa6ee000 IN 0.1 2572 1352 httpd + 560 552 3 fa700000 IN 0.1 2572 1352 httpd + 561 552 0 fa6f0000 IN 0.1 2572 1352 httpd + 562 552 3 fa6ea000 IN 0.1 2572 1352 httpd + 563 552 0 fa67c000 IN 0.1 2572 1352 httpd + 564 552 3 fa674000 IN 0.1 2572 1352 httpd + 565 552 3 fa66a000 IN 0.1 2572 1352 httpd + 582 1 2 fa402000 IN 0.2 2968 1916 xfs + 633 1 2 fa1ec000 IN 0.2 5512 2248 innd + 636 1 3 fa088000 IN 0.1 2536 804 actived + 676 1 0 fa840000 IN 0.0 1060 384 mingetty + 677 1 1 fa590000 IN 0.0 1060 384 mingetty + 678 1 2 fa3b8000 IN 0.0 1060 384 mingetty + 679 1 0 fa5b8000 IN 0.0 1060 384 mingetty + 680 1 1 fa3a4000 IN 0.0 1060 384 mingetty + 681 1 2 fa30a000 IN 0.0 1060 384 mingetty + 683 1 3 fa5d8000 IN 0.0 1052 280 update + 686 378 1 fa3aa000 IN 0.1 2320 1136 in.rlogind + 687 686 2 f9e52000 IN 0.1 2136 1000 login + 688 687 0 f9dec000 IN 0.1 1732 976 bash + > 700 688 1 f9d62000 RU 0.0 1048 256 gen12 + + Display the parental hierarchy of the "crash" process on a live system: + + crash> ps -p 4249 + PID: 0 TASK: c0252000 CPU: 0 COMMAND: "swapper" + PID: 1 TASK: c009a000 CPU: 1 COMMAND: "init" + PID: 632 TASK: c73b6000 CPU: 1 COMMAND: "prefdm" + PID: 637 TASK: c5a4a000 CPU: 1 COMMAND: "prefdm" + PID: 649 TASK: c179a000 CPU: 0 COMMAND: "kwm" + PID: 683 TASK: c1164000 CPU: 0 COMMAND: "kfm" + PID: 1186 TASK: c165a000 CPU: 0 COMMAND: "xterm" + PID: 1188 TASK: c705e000 CPU: 1 COMMAND: "bash" + PID: 4249 TASK: c6b9a000 CPU: 0 COMMAND: "crash" + + Display all children of the "kwm" window manager: + + crash> ps -c kwm + PID: 649 TASK: c179a000 CPU: 0 COMMAND: "kwm" + PID: 682 TASK: c2d58000 CPU: 1 COMMAND: "kwmsound" + PID: 683 TASK: c1164000 CPU: 1 COMMAND: "kfm" + PID: 685 TASK: c053c000 CPU: 0 COMMAND: "krootwm" + PID: 686 TASK: c13fa000 CPU: 0 COMMAND: "kpanel" + PID: 687 TASK: c13f0000 CPU: 1 COMMAND: "kbgndwm" + + Display all threads in a firefox session: + + crash> ps firefox + PID PPID CPU TASK ST %MEM VSZ RSS COMM + 21273 21256 6 ffff81003ec15080 IN 46.3 1138276 484364 firefox + 21276 21256 6 ffff81003f49e7e0 IN 46.3 1138276 484364 firefox + 21280 21256 0 ffff81003ec1d7e0 IN 46.3 1138276 484364 firefox + 21286 21256 6 ffff81000b0d1820 IN 46.3 1138276 484364 firefox + 21287 21256 2 ffff81000b0d10c0 IN 46.3 1138276 484364 firefox + 26975 21256 5 ffff81003b5c1820 IN 46.3 1138276 484364 firefox + 26976 21256 5 ffff810023232820 IN 46.3 1138276 484364 firefox + 26977 21256 4 ffff810021a11820 IN 46.3 1138276 484364 firefox + 26978 21256 5 ffff810003159040 IN 46.3 1138276 484364 firefox + 26979 21256 5 ffff81003a058820 IN 46.3 1138276 484364 firefox + + Display only the thread group leader in the firefox session: + + crash> ps -G firefox + PID PPID CPU TASK ST %MEM VSZ RSS COMM + 21273 21256 0 ffff81003ec15080 IN 46.3 1138276 484364 firefox + + Show the time usage data for pid 10318: + + crash> ps -t 10318 + PID: 10318 TASK: f7b85550 CPU: 5 COMMAND: "bash" + RUN TIME: 1 days, 01:35:32 + START TIME: 5209 + UTIME: 95 + STIME: 57 + + Show the process status of PID 1, task f9dec000, and all nfsd tasks: + + crash> ps 1 f9dec000 nfsd + PID PPID CPU TASK ST %MEM VSZ RSS COMM + 1 0 1 c0098000 IN 0.0 1096 476 init + 688 687 0 f9dec000 IN 0.1 1732 976 bash + 460 1 1 fa938000 IN 0.0 0 0 [nfsd] + 461 1 1 faa86000 IN 0.0 0 0 [nfsd] + 462 1 0 fac48000 IN 0.0 0 0 [nfsd] + 463 1 0 fb4ca000 IN 0.0 0 0 [nfsd] + 464 1 0 fb4c8000 IN 0.0 0 0 [nfsd] + 465 1 2 fba6e000 IN 0.0 0 0 [nfsd] + 466 1 1 fba6c000 IN 0.0 0 0 [nfsd] + 467 1 2 fac04000 IN 0.0 0 0 [nfsd] + + Show all kernel threads: + + crash> ps -k + PID PPID CPU TASK ST %MEM VSZ RSS COMM + 0 0 1 c0fac000 RU 0.0 0 0 [swapper] + 0 0 0 c0252000 RU 0.0 0 0 [swapper] + 2 1 1 c0fa0000 IN 0.0 0 0 [kflushd] + 3 1 1 c03de000 IN 0.0 0 0 [kpiod] + 4 1 1 c03dc000 IN 0.0 0 0 [kswapd] + 5 1 0 c0092000 IN 0.0 0 0 [mdrecoveryd] + 336 1 0 c4a9a000 IN 0.0 0 0 [rpciod] + 337 1 0 c4830000 IN 0.0 0 0 [lockd] + 487 1 1 c4ba6000 IN 0.0 0 0 [nfsd] + 488 1 0 c18c6000 IN 0.0 0 0 [nfsd] + 489 1 0 c0cac000 IN 0.0 0 0 [nfsd] + 490 1 0 c056a000 IN 0.0 0 0 [nfsd] + 491 1 0 c0860000 IN 0.0 0 0 [nfsd] + 492 1 1 c0254000 IN 0.0 0 0 [nfsd] + 493 1 0 c0a86000 IN 0.0 0 0 [nfsd] + 494 1 0 c0968000 IN 0.0 0 0 [nfsd] + + Show all tasks sorted by their task_struct's last_run or timestamp value, + whichever applies: + + crash> ps -l + [280195] [RU] PID: 2 TASK: c1468000 CPU: 0 COMMAND: "keventd" + [280195] [IN] PID: 1986 TASK: c5af4000 CPU: 0 COMMAND: "sshd" + [280195] [IN] PID: 2039 TASK: c58e6000 CPU: 0 COMMAND: "sshd" + [280195] [RU] PID: 2044 TASK: c5554000 CPU: 0 COMMAND: "bash" + [280195] [RU] PID: 2289 TASK: c70c0000 CPU: 0 COMMAND: "s" + [280190] [IN] PID: 1621 TASK: c54f8000 CPU: 0 COMMAND: "cupsd" + [280184] [IN] PID: 5 TASK: c154c000 CPU: 0 COMMAND: "kswapd" + [280184] [IN] PID: 6 TASK: c7ff6000 CPU: 0 COMMAND: "kscand" + [280170] [IN] PID: 0 TASK: c038e000 CPU: 0 COMMAND: "swapper" + [280166] [IN] PID: 2106 TASK: c0c0c000 CPU: 0 COMMAND: "sshd" + [280166] [IN] PID: 2162 TASK: c03a4000 CPU: 0 COMMAND: "vmstat" + [280160] [IN] PID: 1 TASK: c154a000 CPU: 0 COMMAND: "init" + [280131] [IN] PID: 3 TASK: c11ce000 CPU: 0 COMMAND: "kapmd" + [280117] [IN] PID: 1568 TASK: c5a8c000 CPU: 0 COMMAND: "smartd" + [280103] [IN] PID: 1694 TASK: c4c66000 CPU: 0 COMMAND: "ntpd" + [280060] [IN] PID: 8 TASK: c7ff2000 CPU: 0 COMMAND: "kupdated" + [279767] [IN] PID: 1720 TASK: c4608000 CPU: 0 COMMAND: "sendmail" + [279060] [IN] PID: 13 TASK: c69f4000 CPU: 0 COMMAND: "kjournald" + [278657] [IN] PID: 1523 TASK: c5ad4000 CPU: 0 COMMAND: "ypbind" + [277712] [IN] PID: 2163 TASK: c06e0000 CPU: 0 COMMAND: "sshd" + [277711] [IN] PID: 2244 TASK: c4cdc000 CPU: 0 COMMAND: "ssh" + [277261] [IN] PID: 1391 TASK: c5d8e000 CPU: 0 COMMAND: "syslogd" + [276837] [IN] PID: 1990 TASK: c58d8000 CPU: 0 COMMAND: "bash" + [276802] [IN] PID: 1853 TASK: c3828000 CPU: 0 COMMAND: "atd" + [276496] [IN] PID: 1749 TASK: c4480000 CPU: 0 COMMAND: "cannaserver" + [274931] [IN] PID: 1760 TASK: c43ac000 CPU: 0 COMMAND: "crond" + [246773] [IN] PID: 1844 TASK: c38d8000 CPU: 0 COMMAND: "xfs" + [125620] [IN] PID: 2170 TASK: c48dc000 CPU: 0 COMMAND: "bash" + [119059] [IN] PID: 1033 TASK: c64be000 CPU: 0 COMMAND: "kjournald" + [110916] [IN] PID: 1663 TASK: c528a000 CPU: 0 COMMAND: "sshd" + [ 86122] [IN] PID: 2112 TASK: c0da6000 CPU: 0 COMMAND: "bash" + [ 13637] [IN] PID: 1891 TASK: c67ae000 CPU: 0 COMMAND: "sshd" + [ 13636] [IN] PID: 1894 TASK: c38ec000 CPU: 0 COMMAND: "bash" + [ 7662] [IN] PID: 1885 TASK: c6478000 CPU: 0 COMMAND: "mingetty" + [ 7662] [IN] PID: 1886 TASK: c62da000 CPU: 0 COMMAND: "mingetty" + [ 7662] [IN] PID: 1887 TASK: c5f8c000 CPU: 0 COMMAND: "mingetty" + [ 7662] [IN] PID: 1888 TASK: c5f88000 CPU: 0 COMMAND: "mingetty" + [ 7662] [IN] PID: 1889 TASK: c5f86000 CPU: 0 COMMAND: "mingetty" + [ 7662] [IN] PID: 1890 TASK: c6424000 CPU: 0 COMMAND: "mingetty" + [ 7661] [IN] PID: 4 TASK: c154e000 CPU: 0 COMMAND: "ksoftirqd/0" + [ 7595] [IN] PID: 1872 TASK: c2e7e000 CPU: 0 COMMAND: "inventory.pl" + [ 6617] [IN] PID: 1771 TASK: c435a000 CPU: 0 COMMAND: "jserver" + [ 6307] [IN] PID: 1739 TASK: c48f8000 CPU: 0 COMMAND: "gpm" + [ 6285] [IN] PID: 1729 TASK: c4552000 CPU: 0 COMMAND: "sendmail" + [ 6009] [IN] PID: 1395 TASK: c6344000 CPU: 0 COMMAND: "klogd" + [ 5820] [IN] PID: 1677 TASK: c4d74000 CPU: 0 COMMAND: "xinetd" + [ 5719] [IN] PID: 1422 TASK: c5d04000 CPU: 0 COMMAND: "portmap" + [ 4633] [IN] PID: 1509 TASK: c5ed4000 CPU: 0 COMMAND: "apmd" + [ 4529] [IN] PID: 1520 TASK: c5d98000 CPU: 0 COMMAND: "ypbind" + [ 4515] [IN] PID: 1522 TASK: c5d32000 CPU: 0 COMMAND: "ypbind" + [ 4373] [IN] PID: 1441 TASK: c5d48000 CPU: 0 COMMAND: "rpc.statd" + [ 4210] [IN] PID: 1352 TASK: c5b30000 CPU: 0 COMMAND: "dhclient" + [ 1184] [IN] PID: 71 TASK: c65b6000 CPU: 0 COMMAND: "khubd" + [ 434] [IN] PID: 9 TASK: c11de000 CPU: 0 COMMAND: "mdrecoveryd" + [ 48] [IN] PID: 7 TASK: c7ff4000 CPU: 0 COMMAND: "bdflush" + + Show the kernel stack pointer of each user task: + + crash> ps -us + PID PPID CPU KSTACKP ST %MEM VSZ RSS COMM + 1 0 0 c009bedc IN 0.0 1096 52 init + 239 1 0 c15e7ed8 IN 0.2 1332 224 pump + 280 1 1 c7cbdedc IN 0.2 1092 208 portmap + 295 1 0 c7481edc IN 0.0 1232 0 ypbind + 301 295 0 c7c7bf28 IN 0.1 1260 124 ypbind + 376 1 1 c5053f28 IN 0.0 1316 40 automount + 381 1 0 c34ddf28 IN 0.2 1316 224 automount + 391 1 1 c2777f28 IN 0.2 1316 224 automount + ... + + Display the argument and environment data for the automount task: + + crash> ps -a automount + PID: 3948 TASK: f722ee30 CPU: 0 COMMAND: "automount" + ARG: /usr/sbin/automount --timeout=60 /net program /etc/auto.net + ENV: SELINUX_INIT=YES + CONSOLE=/dev/console + TERM=linux + INIT_VERSION=sysvinit-2.85 + PATH=/sbin:/usr/sbin:/bin:/usr/bin + LC_MESSAGES=en_US + RUNLEVEL=3 + runlevel=3 + PWD=/ + LANG=ja_JP.UTF-8 + PREVLEVEL=N + previous=N + HOME=/ + SHLVL=2 + _=/usr/sbin/automount + + Display the tasks in the thread group containing task c20ab0b0: + + crash> ps -g c20ab0b0 + PID: 6425 TASK: f72f50b0 CPU: 0 COMMAND: "firefox-bin" + PID: 6516 TASK: f71bf1b0 CPU: 0 COMMAND: "firefox-bin" + PID: 6518 TASK: d394b930 CPU: 0 COMMAND: "firefox-bin" + PID: 6520 TASK: c20aa030 CPU: 0 COMMAND: "firefox-bin" + PID: 6523 TASK: c20ab0b0 CPU: 0 COMMAND: "firefox-bin" + PID: 6614 TASK: f1f181b0 CPU: 0 COMMAND: "firefox-bin" + + Display the tasks in the thread group for each instance of the + program named "multi-thread": + + crash> ps -g multi-thread + PID: 2522 TASK: 1003f0dc7f0 CPU: 1 COMMAND: "multi-thread" + PID: 2523 TASK: 10037b13030 CPU: 1 COMMAND: "multi-thread" + PID: 2524 TASK: 1003e064030 CPU: 1 COMMAND: "multi-thread" + PID: 2525 TASK: 1003e13a7f0 CPU: 1 COMMAND: "multi-thread" + + PID: 2526 TASK: 1002f82b7f0 CPU: 1 COMMAND: "multi-thread" + PID: 2527 TASK: 1003e1737f0 CPU: 1 COMMAND: "multi-thread" + PID: 2528 TASK: 10035b4b7f0 CPU: 1 COMMAND: "multi-thread" + PID: 2529 TASK: 1003f0c37f0 CPU: 1 COMMAND: "multi-thread" + PID: 2530 TASK: 10035597030 CPU: 1 COMMAND: "multi-thread" + PID: 2531 TASK: 100184be7f0 CPU: 1 COMMAND: "multi-thread" + + Display the resource limits of "bash" task 13896: + + crash> ps -r 13896 + PID: 13896 TASK: cf402000 CPU: 0 COMMAND: "bash" + RLIMIT CURRENT MAXIMUM + CPU (unlimited) (unlimited) + FSIZE (unlimited) (unlimited) + DATA (unlimited) (unlimited) + STACK 10485760 (unlimited) + CORE (unlimited) (unlimited) + RSS (unlimited) (unlimited) + NPROC 4091 4091 + NOFILE 1024 1024 + MEMLOCK 4096 4096 + AS (unlimited) (unlimited) + LOCKS (unlimited) (unlimited) + + Search for task names matching a POSIX regular expression: + + crash> ps 'migration*' + PID PPID CPU TASK ST %MEM VSZ RSS COMM + 8 2 0 ffff8802128a2e20 IN 0.0 0 0 [migration/0] + 10 2 1 ffff880212969710 IN 0.0 0 0 [migration/1] + 15 2 2 ffff880212989710 IN 0.0 0 0 [migration/2] + 20 2 3 ffff8802129a9710 IN 0.0 0 0 [migration/3] + """ + def __init__(self): + parser = argparse.ArgumentParser(prog="ps") + + group = parser.add_mutually_exclusive_group() + group.add_argument('-k', action='store_true', default=False) + group.add_argument('-u', action='store_true', default=False) + group.add_argument('-G', action='store_true', default=False) + + parser.add_argument('-s', action='store_true', default=False) + + group = parser.add_mutually_exclusive_group() + group.add_argument('-p', action='store_true', default=False) + group.add_argument('-c', action='store_true', default=False) + group.add_argument('-t', action='store_true', default=False) + group.add_argument('-l', action='store_true', default=False) + group.add_argument('-a', action='store_true', default=False) + group.add_argument('-g', action='store_true', default=False) + group.add_argument('-r', action='store_true', default=False) + + parser.add_argument('args', nargs=argparse.REMAINDER) + + parser.format_usage = lambda : \ + "ps [-k|-u|-G][-s][-p|-c|-t|-l|-a|-g|-r] [pid | taskp | command] ...\n" + + CrashCommand.__init__(self, "ps", parser) + + self.task_states = { + LinuxTask.TASK_RUNNING : "RU", + LinuxTask.TASK_INTERRUPTIBLE : "IN", + LinuxTask.TASK_UNINTERRUPTIBLE : "UN", + LinuxTask.TASK_ZOMBIE : "ZO", + LinuxTask.TASK_STOPPED : "ST", + LinuxTask.TASK_SWAPPING : "SW", + LinuxTask.TASK_EXCLUSIVE : "EX", + LinuxTask.TASK_DEAD : "DE", + } + + if LinuxTask.TASK_TRACING_STOPPED: + self.task_states[LinuxTask.TASK_TRACING_STOPPED] = "TR" + + self.header_template = " PID PPID CPU {1:^{0}} ST %MEM " \ + "VSZ RSS COMM" + +# PID PPID CPU TASK ST %MEM VSZ RSS COMM +# 1 0 3 ffff88033aa780c8 RU 0.0 0 0 [systemd] +#> 17080 16749 6 ffff8801db5ae040 RU 0.0 8168 1032 less +# PID PPID CPU TASK ST %MEM VSZ RSS COMM +#> 0 0 0 ffffffff81c13460 RU 0.0 0 0 [swapper/0] +# 17077 16749 0 ffff8800b956b848 RU 0.0 0 0 [less] + self.line_template = "{0} {1:>5} {2:>5} {3:>3} {4:{5}x} {6:3} {7:.1f}" + self.line_template += " {8:7d} {9:6d} {10:.{11}}{12}{13:.{14}}" + + def task_state_string(self, task): + state = task.task_state() + buf = None + exclusive = False + + if task.TASK_EXCLUSIVE: + exclusive = (state & task.TASK_EXCLUSIVE) == task.TASK_EXCLUSIVE + state &= ~task.TASK_EXCLUSIVE + + try: + buf = self.task_states[state & ~task.TASK_DEAD] + except KeyError, e: + buf = "??" + + if state & task.TASK_DEAD and self.maybe_dead(): + buf = self.task_states[task.TASK_DEAD] + + if buf is not None and exclusive: + buf += "EX" + + return buf + + def task_header(self, task): + task_struct = task.task_struct + template = "PID: {0:-5d} TASK: {1:x} CPU: {2:>2d} COMMAND: \"{3}\"" + cpu = int(task.get_thread_info()['cpu']) + if task.active: + cpu = task.cpu + return template.format(int(task_struct['pid']), + long(task_struct.address), cpu, + task_struct['comm'].string()) + + def print_last_run(self, task): + radix = 10 + if radix == 10: + radix_string = "d" + else: + radix_string = "x" + template = "[{0:{1}}] [{2}] {3}" + print template.format(task.last_run(), radix_string, + self.task_state_string(task), + self.task_header(task)) + + def print_one(self, argv, task): + specified = argv.args == None + task_struct = task.task_struct + + pointer = task_struct.address + if argv.s: + pointer = task.get_stack_pointer() + + if argv.l: + self.print_last_run(task) + return + + try: + parent_pid = task_struct['parent']['pid'] + except KeyError, ke: + # This can happen on live systems where pids have gone + # away + print "Couldn't locate task at address %x" % \ + task_struct.parent.address + return + + if task.active: + active = ">" + else: + active = " " + print self.line_template.format(active, task_struct['pid'], parent_pid, + task.get_thread_info()['cpu'], + long(pointer), 16, + self.task_state_string(task), + 0, #task.pct_physmem, + task.total_vm * 4096 / 1024, + task.rss * 4096 / 1024, + "[", int(task.is_kernel_task()), + task_struct['comm'].string(), + "]", int(task.is_kernel_task())) + + def execute(self, argv): + sort_by_pid = lambda x: x.info.task_struct['pid'] + sort_by_last_run = lambda x: -x.info.last_run() + + sort_by = sort_by_pid + if argv.l: + sort_by = sort_by_last_run + else: + if argv.s: + col4name = "KSTACK" + else: + col4name = "TASK" + print self.header_template.format(16, col4name) + + if not argv.args: + for thread in sorted(gdb.selected_inferior().threads(), key=sort_by): + task = thread.info + if task: + if argv.k and not task.is_kernel_task(): + continue + if argv.u and task.is_kernel_task(): + continue + + # Only show thread group leaders +# if argv.G and task.pid != int(task.task_struct['tgid']): + + task.update_mem_usage() + self.print_one(argv, task) +PSCommand() diff --git a/crash/kdump/target.py b/crash/kdump/target.py index df86be33842..f1a0efe05ca 100644 --- a/crash/kdump/target.py +++ b/crash/kdump/target.py @@ -38,6 +38,13 @@ def setup_arch(self): if not archclass: raise NotImplementedError("Architecture %s is not supported yet." % archname) + # Doesn't matter what symbol as long as it's everywhere + # Use vsnprintf since 'printk' can be dropped with CONFIG_PRINTK=n + sym = gdb.lookup_symbol('vsnprintf', None)[0] + if sym.symtab.objfile.architecture.name() != archclass.ident: + raise TypeError("Dump file is for `%s' but provided kernel is for `%s'" \ + % (archname, archclass.ident)) + self.arch = archclass() def setup_tasks(self): @@ -63,7 +70,9 @@ def setup_tasks(self): thread = gdb.selected_inferior().new_thread(ptid, ltask) thread.name = task['comm'].string() + self.arch.setup_thread_info(thread) ltask.attach_thread(thread) + ltask.set_get_stack_pointer(self.arch.get_stack_pointer) gdb.selected_inferior().executing = False diff --git a/crash/types/task.py b/crash/types/task.py index 37b4e94327a..34c81a0e6b2 100644 --- a/crash/types/task.py +++ b/crash/types/task.py @@ -3,24 +3,281 @@ import gdb -task_struct_type = gdb.lookup_type('struct task_struct') +def get_value(symname): + sym = gdb.lookup_symbol(symname, block=None, domain=gdb.SYMBOL_VAR_DOMAIN) + return sym[0].value() + +PF_EXITING = 0x4L class LinuxTask: + task_struct_type = None + mm_struct_fields = None + task_state_has_exit_state = None + + TASK_RUNNING = None + TASK_INTERRUPTIBLE = None + TASK_UNINTERRIBLE = None + TASK_ZOMBIE = None + TASK_STOPPED = None + TASK_SWAPPING = None + TASK_EXCLUSIVE = None + TASK_DEAD = None + + TASK_SWAPPING = None + TASK_TRACING_STOPPED = None + TASK_WAKEKILL = None + TASK_WAKING = None + + get_rss = None + get_stack_pointer = None + initialized = False + def __init__(self, task_struct, active = False, cpu = None, regs = None): + + # We only want to do this once on instantiation + if self.__class__.initialized == False: + self.init_task_types() + if cpu is not None and not isinstance(cpu, int): raise TypeError("cpu must be integer or None") if not isinstance(task_struct, gdb.Value) or \ - not task_struct.type != task_struct_type: + not task_struct.type != self.task_struct_type: raise TypeError("task_struct must be gdb.Value describing struct task_struct") self.task_struct = task_struct self.active = active self.cpu = cpu self.regs = regs + self.thread_info = None + self.stack_pointer = None + self.thread = None + + # mem data + self.mem_valid = False + self.rss = 0 + self.total_vm = 0 + self.pgd_addr = 0 + + def set_default_task_states(self): + self.__class__.TASK_RUNNING = 0 + self.__class__.TASK_INTERRUPTIBLE = 1 + self.__class__.TASK_UNINTERRUPTIBLE = 2 + self.__class__.TASK_ZOMBIE = 4 + self.__class__.TASK_STOPPED = 8 + self.__class__.TASK_SWAPPING = 16 + self.__class__.TASK_EXCLUSIVE = 32 + + def init_task_types(self): + self.__class__.task_struct_type = gdb.lookup_type('struct task_struct') + charp = gdb.lookup_type('char').pointer() + task_state = get_value('task_state_array') + + fields = self.task_struct_type.fields() + self.__class__.task_state_has_exit_state = 'exit_state' in fields + + if not task_state: + self.set_default_task_states() + else: + count = task_state.type.sizeof / charp.sizeof + self.__class__.TASK_DEAD = 0 + self.__class__.TASK_TRACING_STOPPED = 0 + + bit = 0 + for i in range(count): + state = task_state[i].string() + state_strings = { + '(running)' : 'TASK_RUNNING', + '(sleeping)' : 'TASK_INTERRUPTIBLE', + '(disk sleep)' : 'TASK_UNINTERRUPTIBLE', + '(stopped)' : 'TASK_STOPPED', + '(zombie)' : 'TASK_ZOMBIE', + #'(dead)' : 'TASK_DEAD', + '(swapping)' : 'TASK_SWAPPING', + #'(tracing stop)' : 'TASK_TRACING_STOPPED', + '(wakekill)' : 'TASK_WAKEKILL', + '(waking)' : 'TASK_WAKING', + } + for key in state_strings: + if key in state: + setattr(self.__class__, state_strings[key], bit) + if '(dead)' in state: + self.__class__.TASK_DEAD |= bit + if '(tracing stop)' in state: + self.__class__.TASK_TRACING_STOPPED |= bit + if bit == 0: + bit = 1 + else: + bit <<= 1 + + if self.TASK_RUNNING is None or \ + self.TASK_INTERRUPTIBLE is None or \ + self.TASK_UNINTERRUPTIBLE is None or \ + self.TASK_ZOMBIE is None or \ + self.TASK_STOPPED is None: + print self.TASK_RUNNING + print self.TASK_INTERRUPTIBLE + print self.TASK_UNINTERRUPTIBLE + print self.TASK_ZOMBIE + print self.TASK_STOPPED + raise RuntimeError("Missing required task states.") + + self.__class__.mm_struct_fields = gdb.lookup_type('struct mm_struct').keys() + self.__class__.get_rss = self.which_get_rss() + self.__class__.last_run = self.which_last_run() + self.__class__.init_mm = get_value('init_mm') + self.__class__.initialized = True def attach_thread(self, thread): if not isinstance(thread, gdb.InferiorThread): raise TypeError("Expected gdb.InferiorThread") self.thread = thread + + def set_thread_info(self, thread_info): + self.thread_info = thread_info + + def get_thread_info(self): + return self.thread_info + + def task_state(self): + state = long(self.task_struct['state']) + if self.task_state_has_exit_state: + state |= long(self.task_struct['exit_state']) + return state + + def maybe_dead(self): + state = self.task_state() + + known = self.TASK_INTERRUPTIBLE + known |= self.TASK_UNINTERRUPTIBLE + known |= self.TASK_ZOMBIE + known |= self.TASK_STOPPED + + if self.TASK_SWAPPING: + known |= self.TASK_SWAPPING + return (state & known) == 0 + + def task_flags(self): + return long(self.task_struct['flags']) + + def is_exiting(self): + return self.task_flags() & PF_EXITING + + def is_zombie(self): + return self.task_state() & self.TASK_ZOMBIE + + # This will be used eventually for live debugging + def needs_update(self): + return False + + def update_mem_usage(self): + if self.mem_valid and self.needs_update(): + return + + if self.is_zombie() or self.is_exiting(): + return + + mm = self.task_struct['mm'] + if not mm: + self.mem_valid = True + return + + self.rss = self.get_rss() + self.total_vm = long(mm['total_vm']) + self.pgd = long(mm['pgd']) + self.mem_valid = True + + def is_kernel_task(self): + if self.task_struct['pid'] == 0: + return True + + if self.is_zombie() or self.is_exiting(): + return False + + mm = self.task_struct['mm'] + if mm == 0: + return True + elif self.init_mm and mm == self.init_mm.address: + return True + + return False + + # This should be a function bounded to the crash.arch object used + # for this session. That's how we can get the appropriate arch + # without needing to get it explicitly. + def set_get_stack_pointer(self, fn): + self.__class__.get_stack_pointer_fn = fn + + def get_stack_pointer(self): + # This unbinds the function from the task object so we don't + # pass self to the function. + fn = self.get_stack_pointer_fn + return fn(self.thread) + + def get_rss_field(self): + return long(self.task_struct['mm']['rss'].value()) + + def get__rss_field(self): + return long(self.task_struct['mm']['_rss'].value()) + + def get_rss_stat_field(self): + stat = self.task_struct['mm']['rss_stat']['count'] + stat0 = self.task_struct['mm']['rss_stat']['count'][0] + rss = 0 + for i in range(stat.type.sizeof / stat[0].type.sizeof): + rss += long(stat[i]['counter']) + return rss + + def get_anon_file_rss_fields(self): + mm = self.task_struct['mm'] + rss = 0 + for name in ['_anon_rss', '_file_rss']: + if name in mm_struct_fields: + if mm[name].type == self.atomic_long_type: + rss += long(mm[name]['counter']) + else: + rss += long(mm[name]) + return rss + + # The Pythonic way to do this is by generating the LinuxTask class + # dynamically. We may do that eventually, but for now we can just + # select the proper function and assign it to the class. + def which_get_rss(self): + if 'rss' in self.mm_struct_fields: + return self.__class__.get_rss_field + elif '_rss' in self.mm_struct_fields: + return self.__class__.get__rss_field + elif 'rss_stat' in self.mm_struct_fields: + self.__class__.MM_FILEPAGES = get_value('MM_FILEPAGES') + self.__class__.MM_ANONPAGES = get_value('MM_ANONPAGES') + return self.__class__.get_rss_stat_field + elif '_anon_rss' in self.mm_struct_fields or \ + '_file_rss' in self.mm_struct_fields: + self.__class__.atomic_long_type = gdb.lookup_type('atomic_long_t') + return self.__class__.get_anon_file_rss_fields + else: + raise RuntimeError("No method to retrieve RSS from task found.") + + def last_run__last_run(self): + return long(self.task_struct['last_run']) + + def last_run__last_run(self): + return long(self.task_struct['timestamp']) + + def last_run__last_arrival(self): + return long(self.task_struct['sched_info']['last_arrival']) + + def which_last_run(self): + fields = self.task_struct_type.keys() + if 'sched_info' in fields and \ + 'last_arrival' in self.task_struct_type['sched_info'].type.keys(): + return self.__class__.last_run__last_arrival + + if 'last_run' in fields: + return self.__class__.last_run__last_run + + if 'timestamp' in fields: + return self.__class__.last_run__timestamp + + raise RuntimeError("No method to retrieve last run from task found.") From 944e1cd3e8e63e31ca3fc47c2f82ae2a66c4df60 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Mon, 27 Jun 2016 22:14:03 -0400 Subject: [PATCH 12/14] kdump.target: update to latest kdumpfile module Changes in the upstream kdumpfile module mean that KDUMP_KVADDR is now a module constant and attributes are published directly instead of via the .attr() routine. Signed-off-by: Jeff Mahoney --- crash/kdump/target.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crash/kdump/target.py b/crash/kdump/target.py index f1a0efe05ca..8a476bb34d2 100644 --- a/crash/kdump/target.py +++ b/crash/kdump/target.py @@ -3,6 +3,7 @@ import gdb from kdumpfile import kdumpfile +from kdumpfile import KDUMP_KVADDR from kdumpfile.exceptions import * from crash.types.list import list_for_each_entry from crash.types.percpu import get_percpu_var @@ -33,7 +34,7 @@ def __init__(self, fil): self.setup_tasks() def setup_arch(self): - archname = self.kdump.attr("arch")['name'] + archname = self.kdump.attr.arch.name archclass = crash.arch.get_architecture(archname) if not archclass: raise NotImplementedError("Architecture %s is not supported yet." % archname) @@ -63,7 +64,7 @@ def setup_tasks(self): active = long(task.address) in rqscurrs if active: cpu = rqscurrs[long(task.address)] - regs = self.kdump.attr("cpu.%d.reg" % cpu) + regs = self.kdump.attr.cpu[cpu].reg ltask = LinuxTask(task, active, cpu, regs) ptid = (LINUX_KERNEL_PID, task['pid'], 0) @@ -80,7 +81,7 @@ def to_xfer_partial(self, obj, annex, readbuf, writebuf, offset, ln): ret = -1 if obj == self.TARGET_OBJECT_MEMORY: try: - r = self.kdump.read (self.kdump.KDUMP_KVADDR, offset, ln) + r = self.kdump.read (KDUMP_KVADDR, offset, ln) readbuf[:] = r ret = ln except EOFException, e: From c95185a0d02e4647db4c75a3f2b300704d00e386 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Tue, 28 Jun 2016 10:13:36 -0400 Subject: [PATCH 13/14] crash.cache: add new cache submodule and a skel for vm We're getting to the point now where some caches would help, so this sets up the basic infrastructure. Signed-off-by: Jeff Mahoney --- crash/cache/__init__.py | 25 +++++++++++++++++++++++++ crash/cache/vm.py | 14 ++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 crash/cache/__init__.py create mode 100644 crash/cache/vm.py diff --git a/crash/cache/__init__.py b/crash/cache/__init__.py new file mode 100644 index 00000000000..3a56c5c675f --- /dev/null +++ b/crash/cache/__init__.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: + +import gdb + +import os +import glob +import importlib + +class CrashCache(object): + def __init__(self): + pass + + def refresh(self): + pass + + def needs_updating(self): + return False + +modules = glob.glob(os.path.dirname(__file__)+"/[A-Za-z]*.py") +__all__ = [ os.path.basename(f)[:-3] for f in modules] + +mods = __all__ +for mod in mods: + x = importlib.import_module("crash.cache.%s" % mod) diff --git a/crash/cache/vm.py b/crash/cache/vm.py new file mode 100644 index 00000000000..06d199cc749 --- /dev/null +++ b/crash/cache/vm.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: + +import gdb + +from crash.cache import CrashCache +class CrashCacheVM(CrashCache): + def __init__(self): + super(CrashCacheVM, self).__init__() + + def refresh(self): + pass + +cache = CrashCacheVM() From ac3b78d1ad036ea4aa6c5f3a8c78810537cdb995 Mon Sep 17 00:00:00 2001 From: Ales Novak Date: Wed, 29 Jun 2016 13:14:35 +0200 Subject: [PATCH 14/14] Include threads into task list. --- crash/kdump/target.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crash/kdump/target.py b/crash/kdump/target.py index 8a476bb34d2..f69e65d9dbf 100644 --- a/crash/kdump/target.py +++ b/crash/kdump/target.py @@ -58,7 +58,13 @@ def setup_tasks(self): self.pid_to_task_struct = {} - for task in list_for_each_entry(task_list, init_task.type, 'tasks'): + tasks = [] + for taskg in list_for_each_entry(task_list, init_task.type, 'tasks'): + tasks.append(taskg) + for task in list_for_each_entry(taskg['thread_group'], init_task.type, 'thread_group'): + tasks.append(task) + + for task in tasks: cpu = None regs = None active = long(task.address) in rqscurrs