From 4bc2c1e31ab4a3032ce84d7d76a1720f484819e8 Mon Sep 17 00:00:00 2001 From: Paul Zuchowski Date: Mon, 28 Jun 2021 10:39:03 -0400 Subject: [PATCH] Create a znode command for sdb. Also create znode2inode and inode2znode commands. Signed-off-by: Paul Zuchowski Closes #180 --- sdb/commands/zfs/znode.py | 162 ++++++++++++++++++ .../zfs/echo 0xffffa08884646ec0 | znode | 2 + ...o 0xffffa08884646ec0 | znode | znode2inode | 2 + ...6470b8 | cast struct inode * | inode2znode | 2 + .../zfs/znode |head 10 |znode | 11 ++ tests/integration/test_zfs_generic.py | 6 + 6 files changed, 185 insertions(+) create mode 100644 sdb/commands/zfs/znode.py create mode 100644 tests/integration/data/regression_output/zfs/echo 0xffffa08884646ec0 | znode create mode 100644 tests/integration/data/regression_output/zfs/echo 0xffffa08884646ec0 | znode | znode2inode create mode 100644 tests/integration/data/regression_output/zfs/echo 0xffffa088846470b8 | cast struct inode * | inode2znode create mode 100644 tests/integration/data/regression_output/zfs/znode |head 10 |znode diff --git a/sdb/commands/zfs/znode.py b/sdb/commands/zfs/znode.py new file mode 100644 index 00000000..d3076229 --- /dev/null +++ b/sdb/commands/zfs/znode.py @@ -0,0 +1,162 @@ +# +# Copyright 2019 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 + +from typing import Iterable +import os +import drgn +from drgn.helpers.linux.fs import inode_path +import sdb +from sdb.target import type_canonicalize, get_prog +from sdb.command import Command + + +class Inode2znode(Command): + """ + Convert an inode to a znode and display it. + + sdb> echo 0xffff9ca45647f588 | cast struct inode * | itoz + ADDR OBJ UNLINKED BLKSZ SIZE ... + 0xffff9ca45647f398 2 0 29184 28772 ... + """ + + names = ["inode2znode", "i2z", "itoz"] + + def _call(self, objs: Iterable[drgn.Object]) -> None: + for obj in objs: + obj_type = type_canonicalize(obj.type_) + if obj_type.kind != drgn.TypeKind.POINTER: + raise sdb.CommandError( + self.name, + f"'{obj.type_.type_name()}' is not a valid pointer type") + obj = drgn.Object(get_prog(), + type=obj.type_.type, + address=obj.value_()) + znode = drgn.container_of(obj.address_of_(), 'struct znode', + 'z_inode') + znodes = sdb.execute_pipeline([znode], [Znode()]) + Znode.pretty_print(znode, znodes) + + +class Znode2inode(Command): + """ + Convert a znode to an inode and display it. + + sdb> echo 0xffff9ca45647f398 | znode | ztoi + ADDR INO MODE LINKS BLOCKS SB + 0xffff9ca45647f588 2 33184 1 1 0xffff9ca464ae8800 + + sdb> znode | znode2inode + ADDR INO MODE LINKS BLOCKS SB + 0xffff9ca456478638 2 16877 2 1 0xffff9ca486819800 + 0xffff9ca456478a80 34 16877 2 1 0xffff9ca464ae8800 + 0xffff9ca456478ec8 -1 16895 1 0 0xffff9ca464ae8800 + 0xffff9ca45647b108 3 33184 1 1 0xffff9ca464ae8800 + 0xffff9ca45647b998 -1 16895 1 0 0xffff9ca486819800 + 0xffff9ca45647c228 3 16877 2 1 0xffff9ca486819800 + 0xffff9ca45647dbd8 34 16877 4 1 0xffff9ca486819800 + 0xffff9ca45647e8b0 34 16877 2 1 0xffff9ca464aeb000 + 0xffff9ca45647f588 2 33184 1 1 0xffff9ca464ae8800 + 0xffff9ca45647f9d0 -1 16895 1 0 0xffff9ca464aeb000 + """ + + names = ["znode2inode", "z2i", "ztoi"] + + @staticmethod + def inode_print(znode: drgn.Object) -> None: + inode = znode.z_inode + i = drgn.cast('int', znode.z_id) + print("{:18} {:>8} {:>8} {:>8} {:>8} {:18}".format( + hex(inode.address_), int(i), int(inode.i_mode), int(inode.i_nlink), + int(inode.i_blocks), hex(int(inode.i_sb)))) + + def _call(self, objs: Iterable[drgn.Object]) -> None: + print("{:18} {:>8} {:>8} {:>8} {:>8} {:18}".format( + "ADDR", "INO", "MODE", "LINKS", "BLOCKS", "SB")) + for obj in objs: + obj_type = type_canonicalize(obj.type_) + if obj_type.kind != drgn.TypeKind.POINTER: + raise sdb.CommandError( + self.name, + f"'{obj.type_.type_name()}' is not a valid pointer type") + obj = drgn.Object(get_prog(), + type=obj.type_.type, + address=obj.value_()) + self.inode_print(obj) + + +class Znode(sdb.Locator, sdb.PrettyPrinter): + """ + Iterate and pretty-print znodes + + DESCRIPTION + + Iterate znodes or convert an address to a znode + and display it. + + sdb> znode + ADDR OBJ UNLINKED BLKSZ SIZE ... + 0xffff9ca456478448 2 0 512 2 ... + 0xffff9ca456478890 34 0 512 6 ... + 0xffff9ca456478cd8 -1 0 0 0 ... + 0xffff9ca45647af18 3 0 29696 29257 ... + 0xffff9ca45647b7a8 -1 0 0 0 ... + 0xffff9ca45647c038 3 0 512 2 ... + 0xffff9ca45647d9e8 34 0 512 4 ... + 0xffff9ca45647e6c0 34 0 512 4 ... + 0xffff9ca45647f398 2 0 29184 28772 ... + 0xffff9ca45647f7e0 -1 0 0 0 ... + + sdb> echo 0xffff9ca45647f398 | znode + ADDR OBJ UNLINKED BLKSZ SIZE ... + 0xffff9ca45647f398 2 0 29184 28772 ... + """ + + names = ["znode"] + input_type = "znode_t *" + output_type = "znode_t *" + + def pretty_print(self, objs: Iterable[drgn.Object]) -> None: + print("{:18} {:>8} {:8} {:>8} {:>18} {:>18} {:>18} {:<18}".format( + "ADDR", "OBJ", "UNLINKED", "BLKSZ", "SIZE", "INODE", "ZFSVFS", + "FILENAME")) + for znode in objs: + i = drgn.cast('int', znode.z_id) + inode = znode.z_inode + ipath = inode_path(inode) + fn = "" + if ipath is not None: + fn = os.fsdecode(ipath) + print( + "{:18} {:>8d} {:>8d} {:>8d} {:>18} {:>18} {:>18} {:<18}".format( + hex(znode), int(i), int(znode.z_unlinked), + int(znode.z_blksz), int(znode.z_size), + hex(znode.z_inode.address_of_()), + hex(znode.z_inode.i_sb.s_fs_info), str(fn))) + + def no_input(self) -> drgn.Object: + znode_cache = drgn.cast("spl_kmem_cache_t *", + sdb.get_object("znode_cache")) + znode_kmem = znode_cache.skc_linux_cache + znodes = sdb.execute_pipeline( + [znode_kmem], + [sdb.Walk(), sdb.Cast(["znode_t *"])], + ) + for znode in znodes: + if znode.z_id != 0: + yield znode diff --git a/tests/integration/data/regression_output/zfs/echo 0xffffa08884646ec0 | znode b/tests/integration/data/regression_output/zfs/echo 0xffffa08884646ec0 | znode new file mode 100644 index 00000000..b87812f8 --- /dev/null +++ b/tests/integration/data/regression_output/zfs/echo 0xffffa08884646ec0 | znode @@ -0,0 +1,2 @@ +ADDR OBJ UNLINKED BLKSZ SIZE INODE ZFSVFS FILENAME +0xffffa08884646ec0 1032 0 1024 581 0xffffa088846470b8 0xffffa08953ea8000 etc/profile diff --git a/tests/integration/data/regression_output/zfs/echo 0xffffa08884646ec0 | znode | znode2inode b/tests/integration/data/regression_output/zfs/echo 0xffffa08884646ec0 | znode | znode2inode new file mode 100644 index 00000000..a40085f2 --- /dev/null +++ b/tests/integration/data/regression_output/zfs/echo 0xffffa08884646ec0 | znode | znode2inode @@ -0,0 +1,2 @@ +ADDR INO MODE LINKS BLOCKS SB +0xffffa088846470b8 1032 33188 1 2 0xffffa08955983800 diff --git a/tests/integration/data/regression_output/zfs/echo 0xffffa088846470b8 | cast struct inode * | inode2znode b/tests/integration/data/regression_output/zfs/echo 0xffffa088846470b8 | cast struct inode * | inode2znode new file mode 100644 index 00000000..b87812f8 --- /dev/null +++ b/tests/integration/data/regression_output/zfs/echo 0xffffa088846470b8 | cast struct inode * | inode2znode @@ -0,0 +1,2 @@ +ADDR OBJ UNLINKED BLKSZ SIZE INODE ZFSVFS FILENAME +0xffffa08884646ec0 1032 0 1024 581 0xffffa088846470b8 0xffffa08953ea8000 etc/profile diff --git a/tests/integration/data/regression_output/zfs/znode |head 10 |znode b/tests/integration/data/regression_output/zfs/znode |head 10 |znode new file mode 100644 index 00000000..0d28c090 --- /dev/null +++ b/tests/integration/data/regression_output/zfs/znode |head 10 |znode @@ -0,0 +1,11 @@ +ADDR OBJ UNLINKED BLKSZ SIZE INODE ZFSVFS FILENAME +0xffffa08884644000 123 0 512 7 0xffffa088846441f8 0xffffa08953ea8000 etc/profile.d +0xffffa08884644440 68902 0 512 298 0xffffa08884644638 0xffffa08953ea8000 usr/lib/systemd/user/gpg-agent-browser.socket +0xffffa08884644880 68905 0 512 223 0xffffa08884644a78 0xffffa08953ea8000 usr/lib/systemd/user/gpg-agent.service +0xffffa08884644cc0 68898 0 512 231 0xffffa08884644eb8 0xffffa08953ea8000 usr/lib/systemd/user/dirmngr.service +0xffffa08884645100 1900 0 4096 3635 0xffffa088846452f8 0xffffa08953ea8000 etc/security/group.conf +0xffffa08884645540 68904 0 512 308 0xffffa08884645738 0xffffa08953ea8000 usr/lib/systemd/user/gpg-agent-ssh.socket +0xffffa08884645980 2037 0 512 96 0xffffa08884645b78 0xffffa08953ea8000 etc/profile.d/01-locale-fix.sh +0xffffa08884645dc0 67553 0 512 27 0xffffa08884645fb8 0xffffa08953ea8000 usr/lib/systemd/user/sockets.target.wants/gpg-agent-browser.socket +0xffffa08884646200 68899 0 512 204 0xffffa088846463f8 0xffffa08953ea8000 usr/lib/systemd/user/dirmngr.socket +0xffffa08884646640 67554 0 512 25 0xffffa08884646838 0xffffa08953ea8000 usr/lib/systemd/user/sockets.target.wants/gpg-agent-extra.socket diff --git a/tests/integration/test_zfs_generic.py b/tests/integration/test_zfs_generic.py index 812c2ae7..812e4810 100644 --- a/tests/integration/test_zfs_generic.py +++ b/tests/integration/test_zfs_generic.py @@ -61,6 +61,12 @@ "spa data | vdev | metaslab | filter 'obj.ms_loaded == 1' | head 1 | member ms_sm.sm_phys.smp_histogram | zhist", "spa data | vdev | metaslab | filter 'obj.ms_loaded == 1' | head 1 | member ms_sm.sm_phys.smp_histogram | zhist 9", "spa data | vdev | metaslab | filter 'obj.ms_loaded == 1' | head 1 | member ms_allocatable.rt_histogram | zhist", + + # znode + "znode |head 10 |znode", + "echo 0xffffa08884646ec0 | znode", + "echo 0xffffa08884646ec0 | znode | znode2inode", + "echo 0xffffa088846470b8 | cast struct inode * | inode2znode", ] # yapf: disable