Skip to content

Commit

Permalink
locking: address review comments.
Browse files Browse the repository at this point in the history
Also add some missing fixes into rwsem helpers.

Signed-off-by: Imran Khan <[email protected]>
  • Loading branch information
imran-kn committed Mar 13, 2024
1 parent a74ba25 commit 372258f
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 58 deletions.
56 changes: 30 additions & 26 deletions drgn_tools/lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,15 @@
"""
import argparse
from collections import defaultdict
from typing import DefaultDict
from typing import List
from typing import Set
from typing import Tuple

import drgn
from drgn import Object
from drgn import Program
from drgn import StackFrame
from drgn.helpers.linux.cpumask import for_each_present_cpu
from drgn.helpers.linux.percpu import per_cpu

Expand All @@ -47,7 +51,7 @@
from drgn_tools.task import nanosecs_to_secs


def scan_osq_node(prog: Program, verbosity=0) -> None:
def scan_osq_node(prog: Program, verbosity: int = 0) -> None:
"""
Show CPUs spinning to grab sleeping lock(s).
Expand All @@ -59,18 +63,14 @@ def scan_osq_node(prog: Program, verbosity=0) -> None:
till the point of spin
"""

osq_spinners: defaultdict = defaultdict(list)
osq_spinners: DefaultDict[int, List[int]] = defaultdict(list)
for cpu in for_each_present_cpu(prog):
osq_node = per_cpu(prog["osq_node"], cpu)
if not osq_node.next.value_():
continue

while osq_node.next.value_():
osq_node = Object(
prog,
"struct optimistic_spin_node",
address=osq_node.next.value_(),
)
osq_node = osq_node.next[0]

if osq_node.address_ in osq_spinners.keys():
continue
Expand All @@ -80,24 +80,25 @@ def scan_osq_node(prog: Program, verbosity=0) -> None:

if not len(osq_spinners):
print("There are no spinners on any osq_lock")
else:
print("There are spinners on one or more osq_lock")
for key in osq_spinners.keys():
print(f"CPU(s): {osq_spinners[key]} are spinning on same osq_lock")
if verbosity >= 1:
cpu_list = osq_spinners[key]
for cpu in cpu_list:
run_time_ns = get_current_run_time(prog, cpu)
run_time_s = nanosecs_to_secs(run_time_ns)
print(
f"CPU: {cpu} has been spinning for {run_time_s} seconds."
)

if (
verbosity == 2
): # for max verbosity dump call stack of spinners
print("\nCall stack at cpu: ", cpu)
bt(prog, cpu=cpu)
return

print("There are spinners on one or more osq_lock")
for key in osq_spinners.keys():
print(f"CPU(s): {osq_spinners[key]} are spinning on same osq_lock")
if verbosity >= 1:
cpu_list = osq_spinners[key]
for cpu in cpu_list:
run_time_ns = get_current_run_time(prog, cpu)
run_time_s = nanosecs_to_secs(run_time_ns)
print(
f"CPU: {cpu} has been spinning for {run_time_s} secs, since it last got on CPU."
)

if (
verbosity == 2
): # for max verbosity dump call stack of spinners
print("\nCall stack at cpu: ", cpu)
bt(prog, cpu=cpu)


def scan_mutex_lock(prog: Program, stack: bool) -> None:
Expand Down Expand Up @@ -178,7 +179,10 @@ def show_sem_lock(prog: Program, frame_list, seen_sems, stack: bool) -> None:


def show_rwsem_lock(
prog: Program, frame_list, seen_rwsems, stack: bool
prog: Program,
frame_list: List[Tuple[Object, StackFrame]],
seen_rwsems: Set[int],
stack: bool,
) -> None:
"""Show rw_semaphore details"""
warned_absent = False
Expand Down
101 changes: 69 additions & 32 deletions drgn_tools/locking.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@
from drgn.helpers.linux.list import list_empty
from drgn.helpers.linux.list import list_for_each_entry
from drgn.helpers.linux.percpu import per_cpu
from drgn.helpers.linux.sched import cpu_curr
from drgn.helpers.linux.sched import task_cpu
from drgn.helpers.linux.sched import task_state_to_char

from drgn_tools.bt import bt
from drgn_tools.table import FixedTable
from drgn_tools.task import get_current_run_time
from drgn_tools.task import task_lastrun2now
from drgn_tools.util import per_cpu_owner
from drgn_tools.util import timestamp_str
Expand Down Expand Up @@ -56,9 +59,7 @@ def get_osq_owner_cpu(osq: Object) -> int:
return tail - 1

while osq_node.prev and osq_node.prev.next == osq_node.address_of_():
osq_node = Object(
prog, "struct optimistic_spin_node", address=osq_node.prev.value_()
)
osq_node = osq_node.prev[0]

return per_cpu_owner("osq_node", osq_node)

Expand All @@ -76,18 +77,13 @@ def tail_osq_node_to_spinners(osq_node: Object) -> Iterable[int]:
:returns: Iterator of spinning CPUs
"""

prog = osq_node.prog_
tail_osq_node = osq_node
while (
tail_osq_node.prev
and tail_osq_node.prev.next == tail_osq_node.address_of_()
):
yield per_cpu_owner("osq_node", tail_osq_node)
tail_osq_node = Object(
prog,
"struct optimistic_spin_node",
address=tail_osq_node.prev.value_(),
)
tail_osq_node = tail_osq_node.prev[0]

yield per_cpu_owner("osq_node", tail_osq_node)

Expand All @@ -112,9 +108,6 @@ def for_osq_owner_and_each_spinner(osq: Object) -> Iterable[int]:
tail_cpu = tail - 1
tail_osq_node = per_cpu(prog["osq_node"], tail_cpu)

if not tail_osq_node.prev.value_():
return -1

for cpu in tail_osq_node_to_spinners(tail_osq_node):
yield cpu

Expand Down Expand Up @@ -293,14 +286,20 @@ def get_rwsem_owner(rwsem: Object) -> Object:
return NULL(prog, "struct task_struct *")

if is_rwsem_writer_owned(rwsem):
if (rwsem.owner.type_.type_name() != "atomic_long_t") and (
rwsem.owner.value_() & _RWSEM_ANONYMOUSLY_OWNED
):
print("rwsem is owned by anonymous writer")
return NULL(prog, "struct task_struct *")
if rwsem.owner.type_.type_name() != "atomic_long_t":
if rwsem.owner.value_() & _RWSEM_ANONYMOUSLY_OWNED:
print("rwsem is owned by anonymous writer")
return NULL(prog, "struct task_struct *")
else:
return rwsem.owner
else:
owner = cast("struct task_struct *", rwsem.owner.counter)
return owner
else:
# If rwsem is owned by one or more readers or other cases
# when owner field is not reliable
print("Could not reliably determine rwsem owner")
return NULL(prog, "struct task_struct *")


def get_rwsem_waiter_type(rwsem_waiter: Object) -> str:
Expand All @@ -312,9 +311,15 @@ def get_rwsem_waiter_type(rwsem_waiter: Object) -> str:
"""

prog = rwsem_waiter.prog_
if rwsem_waiter.type.value_() == prog["RWSEM_WAITING_FOR_WRITE"].value_():
if (
rwsem_waiter.type.value_()
== prog.constant("RWSEM_WAITING_FOR_WRITE").value_()
):
waiter_type = "down_write"
elif rwsem_waiter.type.value_() == prog["RWSEM_WAITING_FOR_READ"].value_():
elif (
rwsem_waiter.type.value_()
== prog.constant("RWSEM_WAITING_FOR_READ").value_()
):
waiter_type = "down_read"
else:
waiter_type = "waiter type unknown"
Expand All @@ -332,15 +337,51 @@ def get_rwsem_waiters_info(rwsem: Object, callstack: int = 0) -> None:

waiter_type = "none"
print("The waiters of rwsem are as follows: ")
tbl = FixedTable(["TASK:>x", "PID:>", "TYPE:16s", "CPU:>", "ST", "WAIT:>"])
for waiter in for_each_rwsem_waiter_entity(rwsem):
waiter_type = get_rwsem_waiter_type(waiter)
task = waiter.task
print(
f" ({task.type_.type_name()})0x{task.value_():x} (pid){task.pid.value_():<8} (waiter_type){waiter_type:<16} (cpu){task_cpu(task):<4} (state){task_state_to_char(task):<4} (wait_time){timestamp_str(task_lastrun2now(task))}"
tbl.row(
task.value_(),
task.pid.value_(),
waiter_type,
task_cpu(task),
task_state_to_char(task),
timestamp_str(task_lastrun2now(task)),
)
if callstack:
print("call stack of waiter:\n ")
bt(task)
tbl.write()


def get_rwsem_spinners_info(rwsem: Object, callstack: int = 0) -> None:
"""
Get a summary of rwsem spinners.
The summary consists of ``struct task_struct *``, pid, CPU, state
and spin time
:param rwsem: ``struct rw_semaphore *``
"""

prog = rwsem.prog_
spinner_list = [
cpu for cpu in for_osq_owner_and_each_spinner(rwsem.osq.address_of_())
]
print(f"rwsem has {len(spinner_list)} spinners, which are as follows: ")
tbl = FixedTable(["TASK:>x", "PID:>", "CPU:>", "CURRENT SPINTIME:>"])
for cpu in spinner_list:
task = cpu_curr(prog, cpu)
tbl.row(
task.value_(),
task.pid.value_(),
task_cpu(task),
timestamp_str(get_current_run_time(prog, cpu)),
)
if callstack:
print("call stack of spinner:\n ")
bt(task)
tbl.write()


def is_rwsem_reader_owned(rwsem: Object) -> bool:
Expand Down Expand Up @@ -431,21 +472,17 @@ def get_rwsem_info(rwsem: Object, callstack: int = 0) -> None:
print("rwsem is free.")
return

prog = rwsem.prog_
if rwsem_has_spinner(rwsem):
get_rwsem_spinners_info(rwsem, callstack)
else:
print("rwsem has no spinners.")

owner_is_writer = is_rwsem_writer_owned(rwsem)
owner_is_reader = is_rwsem_reader_owned(rwsem)

if owner_is_writer:
if (rwsem.owner.type_.type_name() != "atomic_long_t") and (
rwsem.owner.value_() & _RWSEM_ANONYMOUSLY_OWNED
):
print("rwsem is owned by anonymous writer")
else:
owner_task = Object(
prog,
"struct task_struct",
address=cast("unsigned long", rwsem.owner.counter),
).address_of_()
owner_task = get_rwsem_owner(rwsem)
if owner_task:
print(
f"Writer owner ({owner_task.type_.type_name()})0x{owner_task.value_():x}: (pid){owner_task.pid.value_()}"
)
Expand Down

0 comments on commit 372258f

Please sign in to comment.