Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

helper and corelens module to show unsubmitted pending works. #140

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions drgn_tools/workqueue.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@
from typing import Union

from drgn import cast
from drgn import container_of
from drgn import FaultError
from drgn import IntegerLike
from drgn import NULL
from drgn import Object
from drgn import Program
from drgn import sizeof
from drgn.helpers.common.format import escape_ascii_string
from drgn.helpers.linux.bitops import for_each_set_bit
from drgn.helpers.linux.cpumask import for_each_online_cpu
from drgn.helpers.linux.cpumask import for_each_possible_cpu
from drgn.helpers.linux.idr import idr_find
from drgn.helpers.linux.idr import idr_for_each
from drgn.helpers.linux.list import hlist_for_each_entry
Expand Down Expand Up @@ -52,6 +58,7 @@
"is_task_a_worker",
"find_worker_executing_work",
"workqueue_get_pwq",
"show_pending_but_unsubmitted_delayed_works",
)


Expand Down Expand Up @@ -614,6 +621,74 @@ def find_worker_executing_work(work: Object) -> Object:
return NULL(prog, "struct worker *")


def show_pending_but_unsubmitted_delayed_works(
prog: Program, only_offline_cpus: bool = True
) -> None:
"""
Show delayed_work(s) whose timers have not yet expired.
delayed_work(s) get their `WORK_STRUCT_PENDING_BIT` set, but get
submitted only at expiration of corresponding timer.
This helper dumps all delayed_work(s) that have not yet made it to
any worker_pool, due to their timers not firing for one reason or
another.

:param only_offline_cpus: if True only delayed_works on offlined CPUs are shown.
"""
online_cpus = list(for_each_online_cpu(prog))
for cpu in for_each_possible_cpu(prog):
cpu_state = "online" if cpu in online_cpus else "offline"
if only_offline_cpus and cpu in online_cpus:
continue
print(f"CPU: {cpu} state: {cpu_state}")
try:
for timer_base in per_cpu(prog["timer_bases"], cpu):
for idx in for_each_set_bit(
timer_base.pending_map, sizeof(timer_base.pending_map) * 8
):
for timer in hlist_for_each_entry(
"struct timer_list",
timer_base.vectors[idx].address_of_(),
"entry",
):
if (
prog["delayed_work_timer_fn"].address_of_()
== timer.function
):
dwork = container_of(
timer,
"struct delayed_work",
"timer",
)
tte = (
timer.expires.value_()
- prog["jiffies"].value_()
)
work = dwork.work.address_
try:
func = prog.symbol(
dwork.work.func.value_()
).name
except LookupError:
func = (
f"UNKNOWN: 0x{dwork.work.func.value_():x}"
)
print(
f"timer: {timer.value_():x} tte(jiffies): {tte} work: {work:x} func: {func}"
)

except FaultError:
continue


class UnsubmittedPendingWorkModule(CorelensModule):
"""Show pending but unsubmitted works"""

name = "unsubmitted_pending_works"

def run(self, prog: Program, args: argparse.Namespace) -> None:
show_pending_but_unsubmitted_delayed_works(prog)


class WorkqueueModule(CorelensModule):
"""Show details about all workqueues"""

Expand Down
6 changes: 6 additions & 0 deletions tests/test_workqueue.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,9 @@ def test_for_each_pending_work_of_pwq(prog: drgn.Program) -> None:

def test_show_all_workqueues(prog: drgn.Program) -> None:
wq.show_all_workqueues(prog)


def test_show_pending_but_unsubmitted_delayed_works(
prog: drgn.Program,
) -> None:
wq.show_pending_but_unsubmitted_delayed_works(prog)
Loading