diff --git a/README.md b/README.md index 4b91638a..cbb024db 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # sdb -The Slick/Simple Debugger +The Slick Debugger ![](https://github.com/delphix/sdb/workflows/.github/workflows/main.yml/badge.svg) @@ -21,6 +21,35 @@ $ sudo python3 setup.py install The above should install `sdb` under `/usr/local/bin/`. +### Quickstart + +Running `sudo sdb` attaches sdb to the running kernel by default. +To debug a running program, run `sudo sdb -p `. +For post-mortem debugging (either a kernel crash dump or a userland core dump), use `sudo sdb `. + +``` +$ sudo sdb +sdb> find_task 1 | member comm +(char [16])"systemd" +sdb> find_task 1 | stack +TASK_STRUCT STATE COUNT +========================================== +0xffff89cea441dd00 INTERRUPTIBLE 1 + __schedule+0x2e5 + schedule+0x33 + schedule_hrtimeout_range_clock+0xfd + schedule_hrtimeout_range+0x13 + ep_poll+0x40a + do_epoll_wait+0xb7 + __x64_sys_epoll_wait+0x1e + do_syscall_64+0x57 + entry_SYSCALL_64+0x7c +sdb> addr modules | lxlist "struct module" list | member name ! sort | head -n 3 +(char [56])"aesni_intel" +(char [56])"async_memcpy" +(char [56])"async_pq" +``` + ### Resources User and developer resources for sdb can be found in the [project's wiki](https://github.com/delphix/sdb/wiki). diff --git a/sdb/commands/linux/internal/slub_helpers.py b/sdb/commands/linux/internal/slub_helpers.py index 8903d550..e9cc452b 100644 --- a/sdb/commands/linux/internal/slub_helpers.py +++ b/sdb/commands/linux/internal/slub_helpers.py @@ -16,11 +16,11 @@ # pylint: disable=missing-docstring -from typing import Iterable, Set +from typing import Iterable, Set, Optional import drgn from drgn.helpers.linux.list import list_for_each_entry -from drgn.helpers.linux.mm import for_each_page, page_to_virt +from drgn.helpers.linux.mm import for_each_page, page_to_virt, virt_to_pfn, pfn_to_page import sdb @@ -136,6 +136,18 @@ def cache_red_left_padding(cache: drgn.Object) -> int: return padding +def lookup_cache_by_address(obj: drgn.Object) -> Optional[drgn.Object]: + pfn = virt_to_pfn(sdb.get_prog(), obj) + page = pfn_to_page(pfn) + cache = page.slab_cache + try: + # read the name to force FaultError if any + _ = cache.name.string_().decode('utf-8') + except drgn.FaultError: + return None + return cache + + def cache_get_free_pointer(cache: drgn.Object, p: drgn.Object) -> drgn.Object: """ Get the next pointer in the freelist. Note, that this diff --git a/sdb/commands/linux/whatis.py b/sdb/commands/linux/whatis.py new file mode 100644 index 00000000..fb669380 --- /dev/null +++ b/sdb/commands/linux/whatis.py @@ -0,0 +1,77 @@ +# +# Copyright 2020 Delphix +# Copyright 2021 Datto Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# pylint: disable=missing-docstring + +import argparse +from typing import Iterable + +import drgn +import sdb +from sdb.commands.linux.internal import slub_helpers as slub + + +class WhatIs(sdb.Command): + """ + Print the name of the kmem cache from which the address is allocated. + + DESCRIPTION + The address can be specified as an argument, or + passed through a pipe. + + EXAMPLES + Determine the kmem_cache for a given address: + + sdb> whatis 0xfffffe06596e21b8 + 0xfffffe06596e21b8 is allocated from dmu_buf_impl_t + + Determine the kmem_cache of address passed through a + pipe: + + sdb> dbuf |head 1 |deref |member db_buf |whatis + 0xffff8e804368ac80 is allocated from arc_buf_t + """ + + names = ["whatis"] + + @staticmethod + def print_cache(cache: drgn.Object, addr: str) -> None: + if cache is None: + print(f"{addr} does not map to a kmem_cache") + else: + assert sdb.type_canonical_name(cache.type_) == 'struct kmem_cache *' + cache_nm = cache.name.string_().decode('utf-8') + print(f"{addr} is allocated from {cache_nm}") + + @classmethod + def _init_parser(cls, name: str) -> argparse.ArgumentParser: + parser = super()._init_parser(name) + parser.add_argument("address", nargs="*", metavar="
") + return parser + + def _call(self, objs: Iterable[drgn.Object]) -> None: + for obj in objs: + cache = slub.lookup_cache_by_address(obj) + self.print_cache(cache, hex(int(obj))) + for addr in self.args.address: + try: + obj = sdb.create_object("void *", int(addr, 16)) + except ValueError: + print(f"{addr} is not a valid address") + continue + cache = slub.lookup_cache_by_address(obj) + self.print_cache(cache, addr) diff --git a/tests/integration/data/regression_output/linux/dbuf |head 1 |deref |member db_buf |whatis b/tests/integration/data/regression_output/linux/dbuf |head 1 |deref |member db_buf |whatis new file mode 100644 index 00000000..3fe189cd --- /dev/null +++ b/tests/integration/data/regression_output/linux/dbuf |head 1 |deref |member db_buf |whatis @@ -0,0 +1 @@ +0xffffa08943a90050 is allocated from arc_buf_t diff --git a/tests/integration/data/regression_output/linux/whatis 0xf987kkbbh b/tests/integration/data/regression_output/linux/whatis 0xf987kkbbh new file mode 100644 index 00000000..69470daf --- /dev/null +++ b/tests/integration/data/regression_output/linux/whatis 0xf987kkbbh @@ -0,0 +1 @@ +0xf987kkbbh is not a valid address diff --git a/tests/integration/data/regression_output/linux/whatis 0xffff b/tests/integration/data/regression_output/linux/whatis 0xffff new file mode 100644 index 00000000..dfa6cea3 --- /dev/null +++ b/tests/integration/data/regression_output/linux/whatis 0xffff @@ -0,0 +1 @@ +0xffff does not map to a kmem_cache diff --git a/tests/integration/data/regression_output/linux/whatis 0xffffa0888c766000 0xffffa089407ca870 b/tests/integration/data/regression_output/linux/whatis 0xffffa0888c766000 0xffffa089407ca870 new file mode 100644 index 00000000..ffd82192 --- /dev/null +++ b/tests/integration/data/regression_output/linux/whatis 0xffffa0888c766000 0xffffa089407ca870 @@ -0,0 +1,2 @@ +0xffffa0888c766000 is allocated from arc_buf_hdr_t_full +0xffffa089407ca870 is allocated from dmu_buf_impl_t diff --git a/tests/integration/data/regression_output/linux/whatis 0xffffa089407ca870 b/tests/integration/data/regression_output/linux/whatis 0xffffa089407ca870 new file mode 100644 index 00000000..60a15428 --- /dev/null +++ b/tests/integration/data/regression_output/linux/whatis 0xffffa089407ca870 @@ -0,0 +1 @@ +0xffffa089407ca870 is allocated from dmu_buf_impl_t diff --git a/tests/integration/test_linux_generic.py b/tests/integration/test_linux_generic.py index 5a1f00ba..7504bb13 100644 --- a/tests/integration/test_linux_generic.py +++ b/tests/integration/test_linux_generic.py @@ -94,6 +94,13 @@ "threads | count", 'threads | filter \'obj.comm == "java"\' | threads', "thread", + + # whatis + "dbuf |head 1 |deref |member db_buf |whatis", + "whatis 0xffffa089407ca870", + "whatis 0xffffa0888c766000 0xffffa089407ca870", + "whatis 0xffff", + "whatis 0xf987kkbbh" ] STRIPPED_POS_CMDS = [