incubator-nuttx/tools/gdb/thread.py

298 lines
9.3 KiB
Python

############################################################################
# tools/gdb/thread.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.
#
############################################################################
import gdb
import utils
UINT16_MAX = 0xFFFF
saved_regs = None
def save_regs():
global saved_regs
tcbinfo = gdb.parse_and_eval("g_tcbinfo")
if saved_regs is not None:
return
arch = gdb.selected_frame().architecture()
saved_regs = []
i = 0
for reg in arch.registers():
if i >= tcbinfo["regs_num"]:
break
saved_regs.append(gdb.parse_and_eval("$%s" % reg.name))
i += 1
def restore_regs():
tcbinfo = gdb.parse_and_eval("g_tcbinfo")
global saved_regs
if saved_regs is None:
return
arch = gdb.selected_frame().architecture()
i = 0
for reg in arch.registers():
if i >= tcbinfo["regs_num"]:
break
gdb.execute("set $%s=%d" % (reg.name, int(saved_regs[i])))
i += 1
saved_regs = None
class Nxsetregs(gdb.Command):
"""
Set registers to the specified values.
Usage: nxsetregs [regs]
Etc: nxsetregs
nxsetregs g_current_regs[0]
nxsetregs tcb->xcp.regs
Nxsetregs g_pidhash[0].tcb->xcp.regs
Default regs is g_current_regs[0],if regs is NULL, it will not set registers.
"""
def __init__(self):
super(Nxsetregs, self).__init__("nxsetregs", gdb.COMMAND_USER)
def invoke(self, args, from_tty):
current_regs = gdb.parse_and_eval("g_current_regs")
tcbinfo = gdb.parse_and_eval("g_tcbinfo")
arg = args.split(" ")
if arg[0] != "":
regs = gdb.parse_and_eval("%s" % arg[0]).cast(
gdb.lookup_type("char").pointer()
)
else:
if utils.is_target_smp():
gdb.execute("set $_index=up_cpu_index()")
index = gdb.parse_and_eval("$_index")
else:
index = 0
if current_regs[index] == 0:
return
regs = current_regs[index].cast(gdb.lookup_type("char").pointer())
if regs == 0:
gdb.write("regs is NULL\n")
return
save_regs()
arch = gdb.selected_frame().architecture()
i = 0
for reg in arch.registers():
if i >= tcbinfo["regs_num"]:
return
if tcbinfo["reg_off"]["p"][i] != UINT16_MAX:
value = gdb.Value(regs + tcbinfo["reg_off"]["p"][i]).cast(
gdb.lookup_type("uintptr_t").pointer()
)[0]
gdb.execute("set $%s = 0x%x" % (reg.name, value))
i += 1
def get_pc_value(tcb):
arch = gdb.selected_frame().architecture()
tcbinfo = gdb.parse_and_eval("g_tcbinfo")
i = 0
for reg in arch.registers():
if reg.name == "pc" or reg.name == "rip" or reg.name == "eip":
break
i += 1
regs = tcb["xcp"]["regs"].cast(gdb.lookup_type("char").pointer())
value = gdb.Value(regs + tcbinfo["reg_off"]["p"][i]).cast(
gdb.lookup_type("uintptr_t").pointer()
)[0]
return int(value)
class Nxinfothreads(gdb.Command):
def __init__(self):
super(Nxinfothreads, self).__init__("info threads", gdb.COMMAND_USER)
def invoke(self, args, from_tty):
npidhash = gdb.parse_and_eval("g_npidhash")
pidhash = gdb.parse_and_eval("g_pidhash")
statenames = gdb.parse_and_eval("g_statenames")
if utils.is_target_smp():
gdb.write(
"%-4s %-4s %-21s %-80s %-30s\n"
% ("Id", "Cpu", "Thread", "Info", "Frame")
)
else:
gdb.write("%-4s %-21s %-80s %-30s\n" % ("Id", "Thread", "Info", "Frame"))
for i in range(0, npidhash):
if pidhash[i] == 0:
continue
if pidhash[i]["task_state"] == gdb.parse_and_eval("TSTATE_TASK_RUNNING"):
id = "*%s" % i
pc = int(gdb.parse_and_eval("$pc"))
else:
id = "%s" % i
pc = get_pc_value(pidhash[i])
thread = "Thread 0x%x" % pidhash[i]
try:
"""Maybe tcb not have name member, or name is not utf-8"""
info = "(Name: %s, State: %s, Priority: %d, Stack: %d)" % (
pidhash[i]["name"].string(),
statenames[pidhash[i]["task_state"]].string(),
pidhash[i]["sched_priority"],
pidhash[i]["adj_stack_size"],
)
except gdb.error and UnicodeDecodeError:
info = "(Name: Not utf-8, State: %s, Priority: %d, Stack: %d)" % (
statenames[pidhash[i]["task_state"]].string(),
pidhash[i]["sched_priority"],
pidhash[i]["adj_stack_size"],
)
line = gdb.find_pc_line(pc)
if line.symtab:
func = gdb.execute("info symbol %d " % pc, to_string=True)
frame = "0x%x %s at %s:%d" % (
pc,
func.split()[0] + "()",
line.symtab,
line.line,
)
else:
frame = "No symbol with pc"
if utils.is_target_smp():
cpu = "%d" % pidhash[i]["cpu"]
gdb.write(
"%-4s %-4s %-21s %-80s %-30s\n" % (id, cpu, thread, info, frame)
)
else:
gdb.write("%-4s %-21s %-80s %-30s\n" % (id, thread, info, frame))
class Nxthread(gdb.Command):
def __init__(self):
super(Nxthread, self).__init__("thread", gdb.COMMAND_USER)
def invoke(self, args, from_tty):
npidhash = gdb.parse_and_eval("g_npidhash")
pidhash = gdb.parse_and_eval("g_pidhash")
arg = args.split(" ")
arglen = len(arg)
if arg[0] == "":
pass
elif arg[0] == "apply":
if arglen <= 1:
gdb.write("Please specify a thread ID list\n")
elif arglen <= 2:
gdb.write("Please specify a command following the thread ID list\n")
elif arg[1] == "all":
for i in range(0, npidhash):
if pidhash[i] == 0:
continue
try:
gdb.write("Thread %d %s\n" % (i, pidhash[i]["name"].string()))
except gdb.error and UnicodeDecodeError:
gdb.write("Thread %d\n" % (i))
gdb.execute("nxsetregs g_pidhash[%d]->xcp.regs" % i)
cmd_arg = ""
for cmd in arg[2:]:
cmd_arg += cmd + " "
gdb.execute("%s\n" % cmd_arg)
restore_regs()
else:
threadlist = []
i = 0
cmd = ""
for i in range(1, arglen):
if arg[i].isnumeric():
threadlist.append(int(arg[i]))
else:
cmd += arg[i] + " "
if len(threadlist) == 0 or cmd == "":
gdb.write("Please specify a thread ID list and command\n")
else:
for i in threadlist:
if i >= npidhash:
break
if pidhash[i] == 0:
continue
try:
gdb.write(
"Thread %d %s\n" % (i, pidhash[i]["name"].string())
)
except gdb.error and UnicodeDecodeError:
gdb.write("Thread %d\n" % (i))
gdb.execute("nxsetregs g_pidhash[%d]->xcp.regs" % i)
gdb.execute("%s\n" % cmd)
restore_regs()
else:
if arg[0].isnumeric() and pidhash[int(arg[0])] != 0:
gdb.execute("nxsetregs g_pidhash[%s]->xcp.regs" % arg[0])
else:
gdb.write("Invalid thread id %s\n" % arg[0])
class Nxcontinue(gdb.Command):
def __init__(self):
super(Nxcontinue, self).__init__("nxcontinue", gdb.COMMAND_USER)
def invoke(self, args, from_tty):
restore_regs()
gdb.execute("continue")
# We can't use a user command to rename continue it will recursion
gdb.execute("define c\n nxcontinue \n end\n")
gdb.write("\nif use thread command, please don't use 'continue', use 'c' instead !!!\n")
Nxsetregs()
Nxinfothreads()
Nxthread()
Nxcontinue()