diff --git a/crash/__init__.py b/crash/__init__.py index aae19e3a567..4f960d2a44e 100644 --- a/crash/__init__.py +++ b/crash/__init__.py @@ -1,2 +1,9 @@ #!/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) + import crash.commands diff --git a/crash/arch/__init__.py b/crash/arch/__init__.py new file mode 100644 index 00000000000..f28b360226e --- /dev/null +++ b/crash/arch/__init__.py @@ -0,0 +1,46 @@ +#!/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_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_info(self, thread): + raise NotImplementedError("setup_thread_info not implemented") + + def setup_thread(self, thread): + if thread.info.active: + self.setup_thread_active(thread) + else: + self.setup_thread_scheduled(thread) + +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..e6671a88f2f --- /dev/null +++ b/crash/arch/x86_64.py @@ -0,0 +1,66 @@ +#!/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') + 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 + 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()) + 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 + + 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/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() 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) 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") 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 e1500b27945..f69e65d9dbf 100644 --- a/crash/kdump/target.py +++ b/crash/kdump/target.py @@ -3,44 +3,14 @@ import gdb from kdumpfile import kdumpfile -from util import list_for_each_entry +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 +from crash.types.task import LinuxTask +import crash.arch -#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 +LINUX_KERNEL_PID = 1 def symbol_func(symname): ms = gdb.lookup_minimal_symbol(symname) @@ -53,51 +23,90 @@ 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.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) + + # 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): 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) + 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 + if active: + cpu = rqscurrs[long(task.address)] + regs = self.kdump.attr.cpu[cpu].reg + + 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() + 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 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 (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): - return 1 + return True def to_pid_to_str(self, ptid): return "pid %d" % ptid[1] 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): 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/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) diff --git a/crash/types/task.py b/crash/types/task.py new file mode 100644 index 00000000000..34c81a0e6b2 --- /dev/null +++ b/crash/types/task.py @@ -0,0 +1,283 @@ +#!/usr/bin/env python +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: + +import gdb + +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 != 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.") 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()