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

gdb/workqueue: add basic workqueue dump support #15723

Merged
merged 1 commit into from
Jan 30, 2025
Merged
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
71 changes: 71 additions & 0 deletions tools/gdb/nuttxgdb/protocols/wqueue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
############################################################################
# tools/gdb/nuttxgdb/protocols/wqueue.py
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you 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.
#
############################################################################

from __future__ import annotations

from typing import List

from .value import Value


class Work(Value):
"""struct work_s"""

class U(Value):
class S(Value):
dq: Value
qtime: Value

s: S
timer: Value # wdog_s

u: U
worker: Value # void (*worker_t)(FAR void *arg);
arg: Value
wq: KWorkQueue


class KWorker(Value):
"""struct kworker_s"""

pid: Value
work: Value
wait: Value


class KWorkQueue(Value):
"""struct kwork_wqueue_s"""

q: Value
sem: Value
exsem: Value
nthreads: int
exit: bool
worker: List[Value]


class HPWorkQueue(KWorkQueue):
"""struct hp_wqueue_s"""


class LPWorkQueue(KWorkQueue):
"""struct lp_wqueue_s"""
25 changes: 25 additions & 0 deletions tools/gdb/nuttxgdb/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,31 @@ def get_thread_tls(pid, key):
return None


def get_task_argvstr(tcb: Tcb) -> List[str]:
args = []
try:
TCB_FLAG_TTYPE_MASK = get_symbol_value("TCB_FLAG_TTYPE_MASK")
TCB_FLAG_TTYPE_PTHREAD = get_symbol_value("TCB_FLAG_TTYPE_PTHREAD")

if tcb.flags & TCB_FLAG_TTYPE_MASK == TCB_FLAG_TTYPE_PTHREAD:
if tcb.type.code != gdb.TYPE_CODE_PTR:
tcb = tcb.address
tcb = tcb.cast(lookup_type("struct pthread_tcb_s").pointer())
return ["", f"{tcb['cmn']['entry']['main']}", f'{tcb["arg"]}']

tls_info_s = lookup_type("struct tls_info_s").pointer()
tls = tcb.stack_alloc_ptr.cast(tls_info_s)
argv = int(tcb.stack_alloc_ptr) + int(tls.tl_size)
argv = gdb.Value(argv).cast(lookup_type("char").pointer().pointer())
while argv.dereference():
args.append(argv.dereference().string())
argv += 1
except gdb.error:
pass

return args


def gather_modules(dir=None) -> List[str]:
dir = os.path.normpath(dir) if dir else os.path.dirname(__file__)
return [
Expand Down
170 changes: 170 additions & 0 deletions tools/gdb/nuttxgdb/wqueue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
############################################################################
# tools/gdb/nuttxgdb/wqueue.py
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you 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.
#
############################################################################

from __future__ import annotations

from typing import List

import gdb

from . import lists, utils
from .protocols import wqueue as p
from .utils import Value


class Work(Value, p.Work):
def __init__(self, work: p.Work):
if work.type.code == gdb.TYPE_CODE_PTR:
work = work.dereference()
super().__init__(work)

@property
def wqueue(self) -> WorkQueue:
return WorkQueue(self.wq)

def __repr__(self) -> str:
return f"work_s@{self.address:#x}: {self.worker.format_string(styling=True)} arg={self.arg}"

def __str__(self) -> str:
return self.__repr__()


class KWorker(Value, p.KWorker):
"""Worker thread information"""

def __init__(self, worker: p.KWorker, wqueue=None):
if worker.type.code == gdb.TYPE_CODE_PTR:
worker = worker.dereference()
super().__init__(worker)
self.wqueue = wqueue

@property
def is_running(self):
return self.work

@property
def work(self) -> Work:
work = self["work"]
return Work(work) if work else None

@property
def name(self):
return utils.get_task_name(utils.get_tcb(self.pid))

def __repr__(self):
return f"kworker_s@{self.address:#x} {self.work or 'idle'}"

def __str__(self):
return self.__repr__()


class WorkQueue(Value, p.KWorkQueue):
"""Work queue information"""

def __init__(self, queue: p.KWorkQueue, name: str = None):
if queue.type.code == gdb.TYPE_CODE_PTR:
queue = queue.dereference()
super().__init__(queue)
self.name = name or "<noname>"

@property
def workers(self) -> List[Work]:
work_s = utils.lookup_type("struct work_s")
return [
Work(worker.cast(work_s.pointer())) for worker in lists.NxDQueue(self.q)
]

@property
def threads(self) -> List[KWorker]:
return [
KWorker(thread, wqueue=self)
for thread in utils.ArrayIterator(self.worker, self.nthreads)
]

@property
def nthreads(self):
return int(self["nthreads"])

@property
def is_running(self):
return any(thread.is_running for thread in self.threads)

@property
def is_exiting(self):
return self.exit

def __repr__(self):
state = "running" if self.is_running else "idle"
return f"{self.name}@{self.address:#x}, {state}, {self.nthreads} threads, {len(self.workers)} work"

def __str__(self):
return self.__repr__()


def get_work_queues() -> List[WorkQueue]:
entry = gdb.parse_and_eval("work_thread")
kwork_wqueue_s = utils.lookup_type("struct kwork_wqueue_s")

tcbs = utils.get_tcbs()
# The function address may be or'ed with 0x01
tcbs = filter(lambda tcb: int(tcb.entry.main) & ~0x01 == entry, tcbs)
queue = []
for tcb in tcbs:
if not (args := utils.get_task_argvstr(tcb)):
continue
# wqueue = (FAR struct kwork_wqueue_s *)
# ((uintptr_t)strtoul(argv[1], NULL, 16));
# kworker = (FAR struct kworker_s *)
# ((uintptr_t)strtoul(argv[2], NULL, 16));
wqueue = gdb.Value(int(args[1], 16)).cast(kwork_wqueue_s.pointer())
wqueue = WorkQueue(wqueue, name=utils.get_task_name(tcb))
if wqueue not in queue:
queue.append(wqueue)

return queue


class WorkQueueDump(gdb.Command):
"""Show work queue information"""

def __init__(self):
if not utils.get_symbol_value("CONFIG_SCHED_WORKQUEUE"):
return

super().__init__("worker", gdb.COMMAND_USER)

def invoke(self, arg, from_tty):
queues = get_work_queues()
for queue in queues:
print(f"{queue}")
if not queue.is_running:
continue

print(" Running:") # Dump the work that is running
running = [thread for thread in queue.threads if thread.is_running]
print("\n".join(f" {thread}" for thread in running))

if not queue.workers:
continue

print(" Queued:")
print("\n".join(f" {work}" for work in queue.workers))
Loading