Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4edde9a

Browse files
committed
Add zfs_refcount command.
1 parent db1b967 commit 4edde9a

File tree

13 files changed

+292
-136
lines changed

13 files changed

+292
-136
lines changed

.github/scripts/download-dump-from-s3.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616
# └── vmlinux-5.0.0-36-generic
1717
#
1818

19+
# use the default dir unless alternate dir passed as arg2 to script
1920
DATA_DIR="tests/integration/data"
21+
if [ ! -z "$2" ]; then
22+
DATA_DIR=$2
23+
fi
2024

2125
echo "checking folder structure ..."
2226
if [ ! -d $DATA_DIR ]; then

.github/scripts/install-drgn.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ sudo apt install bison flex libelf-dev libdw-dev libomp5 libomp-dev
1010
git clone https://github.com/osandov/drgn.git
1111

1212
cd drgn
13+
git checkout -b drgn_february 0a6aaaae5d31ed142448f8220e208d65478ec80d
1314
python3 setup.py install
1415
cd -

.github/workflows/main.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ jobs:
6464
- run: python3 -m pip install aws python-config pytest pytest-cov
6565
- run: ./.github/scripts/install-libkdumpfile.sh
6666
- run: ./.github/scripts/install-drgn.sh
67-
- run: ./.github/scripts/download-dump-from-s3.sh dump-201912060006.tar.lzma
67+
- run: ./.github/scripts/download-dump-from-s3.sh dump-201912060006.tar.lzma tests/integration/data
68+
- run: ./.github/scripts/download-dump-from-s3.sh dump-202102031354.tar.lzma tests/integration/data_alternate
6869
- run: pytest -v --cov sdb --cov-report xml tests
6970
- uses: codecov/codecov-action@v1
7071
with:

sdb/commands/zfs/zfs_refcount.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#
2+
# Copyright 2020 Datto, Inc.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
17+
# pylint: disable=missing-docstring
18+
19+
from typing import Iterable
20+
import drgn
21+
import sdb
22+
from sdb.commands.spl.spl_list import SPLList
23+
24+
25+
class Zfs_Refcount(sdb.Locator, sdb.PrettyPrinter):
26+
names = ["zfs_refcount"]
27+
input_type = "zfs_refcount_t *"
28+
output_type = "zfs_refcount_t *"
29+
30+
@staticmethod
31+
def print_ref(obj: drgn.Object):
32+
ptr = int(obj.ref_holder)
33+
c = sdb.create_object("char *", ptr)
34+
s = c.string_().decode("utf-8")
35+
print(f"{hex(ptr)} {s} ")
36+
37+
def pretty_print(self, objs: Iterable[drgn.Object]) -> None:
38+
for zr in objs:
39+
# handle the lack of rc_tracked in non-debug zfs build
40+
tracked = 0
41+
try:
42+
tracked = zr.rc_tracked
43+
except AttributeError:
44+
pass
45+
print(f"zfs_recount_t at {hex(zr)} has {int(zr.rc_count)} "
46+
f"current holds tracked={int(tracked)}")
47+
if tracked:
48+
ref_list = zr.rc_list
49+
list_addr = ref_list.address_of_()
50+
refs = sdb.execute_pipeline(
51+
[list_addr],
52+
[SPLList(), sdb.Cast(["reference_t *"])],
53+
)
54+
55+
for ref in refs:
56+
Zfs_Refcount.print_ref(ref)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
zfs_recount_t at 0xffffa0894e7223c8 has 12 current holds tracked=0
2+
zfs_recount_t at 0xffffa089413ba3c8 has 15 current holds tracked=0
3+
zfs_recount_t at 0xffffa08955c463c8 has 39 current holds tracked=0
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
zfs_recount_t at 0xffff911fc6886778 has 13 current holds tracked=1
2+
0xffffffffc1089360 dsl_sync_task_common
3+
0xffff91223d23a000
4+
0xffff911f9659f000
5+
0xffff91223f870800
6+
0xffff91223f870800
7+
0xffff911f9659c000
8+
0xffff912249c9d800
9+
0xffff912249c9d800
10+
0xffff911f9659c000
11+
0xffff91223caa6800
12+
0xffff91223caa6800
13+
0xffff911f9659c000
14+
0xffff91223caa4800

tests/integration/gen_regression_output.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@
1717
# pylint: disable=missing-module-docstring
1818
# pylint: disable=missing-function-docstring
1919

20-
from tests.integration.infra import generate_known_regression_output
20+
from tests.integration.infra import (Infra, PRIMARY, ALTERNATE)
2121

2222

2323
def main() -> None:
24-
generate_known_regression_output()
24+
infra_p = Infra(PRIMARY)
25+
infra_p.generate_known_regression_output()
26+
infra_a = Infra(ALTERNATE)
27+
infra_a.generate_known_regression_output()
2528

2629

2730
if __name__ == '__main__':

tests/integration/infra.py

Lines changed: 128 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -24,116 +24,19 @@
2424
from importlib import import_module
2525
from pathlib import Path
2626
from typing import Iterable, List, Optional
27+
from collections import namedtuple
2728

2829
import drgn
2930
import sdb
3031
from sdb.internal.cli import load_debug_info
3132
from sdb.internal.repl import REPL
3233

3334
THIS_DIR = os.path.dirname(os.path.realpath(__file__))
34-
DATA_DIR = f"{THIS_DIR}/data"
35-
DUMP_PATH = f"{DATA_DIR}/dump.201912060006"
36-
MODS_PATH = f"{DATA_DIR}/mods"
37-
VMLX_PATH = f"{DATA_DIR}/vmlinux-5.0.0-36-generic"
38-
TEST_OUTPUT_DIR = f"{DATA_DIR}/regression_output"
39-
40-
41-
def dump_exists() -> bool:
42-
"""
43-
Used as the sole indicator of whether the integration
44-
tests will run.
45-
"""
46-
return os.path.exists(DUMP_PATH) and os.path.exists(
47-
MODS_PATH) and os.path.exists(VMLX_PATH)
48-
49-
50-
def setup_target() -> Optional[drgn.Program]:
51-
"""
52-
Create a drgn.Program instance and setup the SDB
53-
context for all the integration tests. If there
54-
is no crash dump to attach to this is going to
55-
be an empty drgn.Program.
56-
"""
57-
prog = drgn.Program()
58-
if not dump_exists():
59-
return prog
60-
prog.set_core_dump(DUMP_PATH)
61-
load_debug_info(prog, [VMLX_PATH, MODS_PATH])
62-
return prog
63-
64-
65-
TEST_PROGRAM = setup_target()
66-
TEST_REPL = REPL(TEST_PROGRAM, list(sdb.get_registered_commands().keys()))
67-
68-
69-
def repl_invoke(cmd: str) -> int:
70-
"""
71-
Accepts a command/pipeline in string form and evaluates
72-
it returning the exit code of the evaluation emulating
73-
the SDB repl.
74-
"""
75-
assert TEST_PROGRAM
76-
return TEST_REPL.eval_cmd(cmd)
77-
78-
79-
def sdb_invoke(objs: Iterable[drgn.Object], line: str) -> Iterable[drgn.Object]:
80-
"""
81-
Dispatch to sdb.invoke, but also drain the generator it returns, so
82-
the tests can more easily access the returned objects.
83-
84-
This method is preferred over repl_invoke() when the test wants to
85-
do fancier checks by mocking a few objects that are later passed
86-
down to the pipeline. Other scenarios include but are not limited
87-
to testing that specific exceptions are thrown or analyzing internal
88-
state of objects that is not part of the output in stdout.
89-
"""
90-
assert TEST_PROGRAM
91-
return list(sdb.invoke(TEST_PROGRAM, objs, line))
92-
93-
94-
def slurp_output_file(modname: str, cmd: str) -> str:
95-
"""
96-
Given a module name and a command, find the output file
97-
and return all of its contents as a string.
98-
"""
99-
return Path(f"{TEST_OUTPUT_DIR}/{modname}/{cmd}").read_text()
100-
101-
102-
def generate_output_for_commands(cmds: List[str], dirpath: str) -> None:
103-
"""
104-
Takes a list of SDB commands in string form, invokes them in the
105-
context of the current crash dump/sdb.REPL, and stores their output
106-
in the directory specified, each under a different file.
107-
108-
Note: Keep in mind that if the directory specified exists then
109-
it will be removed together with all of its contents.
110-
"""
111-
assert TEST_PROGRAM
112-
if os.path.exists(dirpath):
113-
shutil.rmtree(dirpath)
114-
os.makedirs(dirpath)
115-
for cmd in cmds:
116-
with open(f"{dirpath}/{cmd}", 'w') as f:
117-
with redirect_stdout(f):
118-
repl_invoke(cmd)
119-
120-
121-
def generate_output_for_test_module(modname: str) -> None:
122-
"""
123-
Generates the regression output for all the commands of
124-
a test module given module name. The assumption for this
125-
to work automatically is that for the given modname "mod"
126-
there exist a test module under test.integration named
127-
test_mod_generic which has a list of commands in string
128-
form called CMD_TABLE.
129-
"""
130-
test_mod = import_module(f"tests.integration.test_{modname}_generic")
131-
generate_output_for_commands(
132-
test_mod.CMD_TABLE, # type: ignore[attr-defined]
133-
f"{TEST_OUTPUT_DIR}/{modname}")
134-
print(f"Generated regression test output for {modname}...")
35+
PRIMARY = "primary"
36+
ALTERNATE = "alternate"
13537

13638

39+
@staticmethod
13740
def get_all_generic_test_modules() -> List[str]:
13841
"""
13942
Look at this current directory and capture all modules
@@ -148,10 +51,127 @@ def get_all_generic_test_modules() -> List[str]:
14851
return modnames
14952

15053

151-
def generate_known_regression_output() -> None:
152-
"""
153-
Auto-generate the baseline regression output for all
154-
the detected test modules in this directory.
155-
"""
156-
for modname in get_all_generic_test_modules():
157-
generate_output_for_test_module(modname)
54+
class Infra:
55+
"""
56+
Encapsulate crash dump management for automated tests.
57+
"""
58+
Crashdump_Record = namedtuple(
59+
'Crashdump_Record', 'data_dir, dump_path, \
60+
mods_path, vmlx_path, regression_directory')
61+
cdr_primary = Crashdump_Record(f"{THIS_DIR}/data",
62+
f"{THIS_DIR}/data/dump.201912060006",
63+
f"{THIS_DIR}/data/mods",
64+
f"{THIS_DIR}/data/vmlinux-5.0.0-36-generic",
65+
f"{THIS_DIR}/data/regression_output")
66+
cdr_alternate = Crashdump_Record(
67+
f"{THIS_DIR}/data_alternate",
68+
f"{THIS_DIR}/data_alternate/dump.202102031354",
69+
f"{THIS_DIR}/data_alternate/mods",
70+
f"{THIS_DIR}/data_alternate/vmlinux-5.8.0-41-generic",
71+
f"{THIS_DIR}/data_alternate/regression_output")
72+
73+
def __init__(self, which_dump: str):
74+
assert which_dump in (PRIMARY, ALTERNATE)
75+
if which_dump == PRIMARY:
76+
self.cdr = self.cdr_primary
77+
else:
78+
self.cdr = self.cdr_alternate
79+
80+
def dump_exists(self) -> bool:
81+
"""
82+
Used as the sole indicator of whether the integration
83+
tests will run.
84+
"""
85+
return os.path.exists(self.cdr.dump_path) and os.path.exists(
86+
self.cdr.mods_path) and os.path.exists(self.cdr.vmlx_path)
87+
88+
def setup_target(self) -> Optional[drgn.Program]:
89+
"""
90+
Create a drgn.Program instance and setup the SDB
91+
context for all the integration tests. If there
92+
is no crash dump to attach to this is going to
93+
be an empty drgn.Program.
94+
"""
95+
prog = drgn.Program()
96+
if not self.dump_exists():
97+
return prog
98+
prog.set_core_dump(self.cdr.dump_path)
99+
load_debug_info(prog, [self.cdr.vmlx_path, self.cdr.mods_path])
100+
return prog
101+
102+
def repl_invoke(self, cmd: str) -> int:
103+
"""
104+
Accepts a command/pipeline in string form and evaluates
105+
it returning the exit code of the evaluation emulating
106+
the SDB repl.
107+
"""
108+
prog = self.setup_target()
109+
assert prog
110+
return REPL(prog,
111+
list(sdb.get_registered_commands().keys())).eval_cmd(cmd)
112+
113+
def sdb_invoke(self, objs: Iterable[drgn.Object],
114+
line: str) -> Iterable[drgn.Object]:
115+
"""
116+
Dispatch to sdb.invoke, but also drain the generator it returns, so
117+
the tests can more easily access the returned objects.
118+
119+
This method is preferred over repl_invoke() when the test wants to
120+
do fancier checks by mocking a few objects that are later passed
121+
down to the pipeline. Other scenarios include but are not limited
122+
to testing that specific exceptions are thrown or analyzing internal
123+
state of objects that is not part of the output in stdout.
124+
"""
125+
prog = self.setup_target()
126+
assert prog
127+
return list(sdb.invoke(prog, objs, line))
128+
129+
def slurp_output_file(self, modname: str, cmd: str) -> str:
130+
"""
131+
Given a module name and a command, find the output file
132+
and return all of its contents as a string.
133+
"""
134+
return Path(
135+
f"{self.cdr.regression_directory}/{modname}/{cmd}").read_text()
136+
137+
def generate_output_for_commands(self, cmds: List[str],
138+
dirpath: str) -> None:
139+
"""
140+
Takes a list of SDB commands in string form, invokes them in the
141+
context of the current crash dump/sdb.REPL, and stores their output
142+
in the directory specified, each under a different file.
143+
144+
Note: Keep in mind that if the directory specified exists then
145+
it will be removed together with all of its contents.
146+
"""
147+
assert self.setup_target()
148+
if os.path.exists(dirpath):
149+
shutil.rmtree(dirpath)
150+
os.makedirs(dirpath)
151+
for cmd in cmds:
152+
with open(f"{dirpath}/{cmd}", 'w') as f:
153+
with redirect_stdout(f):
154+
self.repl_invoke(cmd)
155+
156+
def generate_output_for_test_module(self, modname: str) -> None:
157+
"""
158+
Generates the regression output for all the commands of
159+
a test module given module name. The assumption for this
160+
to work automatically is that for the given modname "mod"
161+
there exist a test module under test.integration named
162+
test_mod_generic which has a list of commands in string
163+
form called CMD_TABLE.
164+
"""
165+
test_mod = import_module(f"tests.integration.test_{modname}_generic")
166+
self.generate_output_for_commands(
167+
test_mod.CMD_TABLE, # type: ignore[attr-defined]
168+
f"{self.cdr.regression_directory}/{modname}")
169+
print(f"Generated regression test output for {modname}...")
170+
171+
def generate_known_regression_output(self) -> None:
172+
"""
173+
Auto-generate the baseline regression output for all
174+
the detected test modules in this directory.
175+
"""
176+
for modname in get_all_generic_test_modules():
177+
self.generate_output_for_test_module(modname)

0 commit comments

Comments
 (0)