Skip to content

Commit

Permalink
gdb/utils: enhance utils.Value
Browse files Browse the repository at this point in the history
Added __format__ method to support format spec like {:>10} that gdb.Value doesn't support. For such case, gdb.Value is converted to python value firstly and then format natively.
Override all methods/attributes could return gdb.Value to return utils.Value instead.

Signed-off-by: xuxingliang <[email protected]>
  • Loading branch information
XuNeo authored and xiaoxiang781216 committed Nov 24, 2024
1 parent 03a38fa commit a59a28d
Show file tree
Hide file tree
Showing 3 changed files with 287 additions and 10 deletions.
122 changes: 122 additions & 0 deletions tools/gdb/nuttxgdb/protocols/thread.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
############################################################################
# tools/gdb/nuttxgdb/protocols/thread.py
#
# 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 .value import Value


class Group(Value):
"""struct group_s"""

tg_pid: Value
tg_ppid: Value
tg_flags: Value
tg_uid: Value
tg_gid: Value
tg_euid: Value
tg_egid: Value
tg_members: Value
tg_bininfo: Value
tg_children: Value
tg_nchildren: Value
tg_exitcode: Value
tg_nwaiters: Value
tg_waitflags: Value
tg_exitsem: Value
tg_statloc: Value
tg_joinlock: Value
tg_joinqueue: Value
tg_info: Value
tg_sigactionq: Value
tg_sigpendingq: Value
tg_sigdefault: Value
tg_envp: Value
tg_envc: Value
itimer: Value
tg_filelist: Value
tg_mm_map: Value


class Tcb(Value):
"""struct tcb_s"""

flink: Value
blink: Value
group: Group
member: Value
join_queue: Value
join_entry: Value
join_sem: Value
join_val: Value
addrenv_own: Value
addrenv_curr: Value
pid: Value
sched_priority: Value
init_priority: Value
start: Value
entry: Value
task_state: Value
boost_priority: Value
base_priority: Value
holdsem: Value
cpu: Value
affinity: Value
flags: Value
lockcount: Value
irqcount: Value
errcode: Value
timeslice: Value
sporadic: Value
waitdog: Value
adj_stack_size: Value
stack_alloc_ptr: Value
stack_base_ptr: Value
dspace: Value
waitobj: Value
sigprocmask: Value
sigwaitmask: Value
sigpendactionq: Value
sigpostedq: Value
sigunbinfo: Value
mhead: Value
ticks: Value
run_start: Value
run_max: Value
run_time: Value
premp_start: Value
premp_max: Value
premp_caller: Value
premp_max_caller: Value
crit_start: Value
crit_max: Value
crit_caller: Value
crit_max_caller: Value
perf_event_ctx: Value
perf_event_mutex: Value
xcp: Value
sigdeliver: Value
name: Value
stackrecord_pc: Value
stackrecord_sp: Value
stackrecord_pc_deepest: Value
stackrecord_sp_deepest: Value
sp_deepest: Value
caller_deepest: Value
level_deepest: Value
level: Value
65 changes: 65 additions & 0 deletions tools/gdb/nuttxgdb/protocols/value.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
############################################################################
# tools/gdb/nuttxgdb/protocols/value.py
#
# 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 Protocol

import gdb


class Value(Protocol):
address: Value
is_optimized_out: bool
type: gdb.Type
dynamic_type: gdb.Type
is_lazy: bool
bytes: bytes

def cast(self, type: gdb.Type) -> Value: ...
def dereference(self) -> Value: ...
def referenced_value(self) -> Value: ...
def reference_value(self) -> Value: ...
def rvalue_reference_value(self) -> Value: ...
def const_value(self) -> Value: ...
def dynamic_cast(self, type: gdb.Type) -> Value: ...
def reinterpret_cast(self, type: gdb.Type) -> Value: ...

def format_string(
self,
raw: bool = ...,
pretty_arrays: bool = ...,
pretty_structs: bool = ...,
array_indexes: bool = ...,
symbols: bool = ...,
unions: bool = ...,
address: bool = ...,
deref_refs: bool = ...,
actual_objects: bool = ...,
static_members: bool = ...,
max_elements: int = ...,
max_depth: int = ...,
repeat_threshold: int = ...,
format: str = ...,
) -> str: ...

def string(
self, encoding: str = ..., errors: str = ..., length: int = ...
) -> str: ...
110 changes: 100 additions & 10 deletions tools/gdb/nuttxgdb/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
# under the License.
#
############################################################################

from __future__ import annotations

import argparse
Expand All @@ -28,18 +29,101 @@
import re
import shlex
from enum import Enum
from typing import List, Tuple, Union
from typing import List, Optional, Tuple, Union

import gdb

from .macros import fetch_macro_info, try_expand
from .protocols.thread import Tcb

g_symbol_cache = {}
g_type_cache = {}
g_macro_ctx = None
g_backtrace_cache = {}


class Value(gdb.Value):
def __init__(self, obj: Union[gdb.Value, Value]):
super().__init__(obj)

def __isabstractmethod__(self):
# Added to avoid getting error using __getattr__
return False

def __getattr__(self, key):
if hasattr(super(), key):
value = super().__getattribute__(key)
else:
value = super().__getitem__(key)

return Value(value) if not isinstance(value, Value) else value

def __getitem__(self, key):
value = super().__getitem__(key)
return Value(value) if not isinstance(value, Value) else value

def __format__(self, format_spec: str) -> str:
try:
return super().__format__(format_spec)
except TypeError:
# Convert GDB value to python value, and then format it
type_code_map = {
gdb.TYPE_CODE_INT: int,
gdb.TYPE_CODE_PTR: int,
gdb.TYPE_CODE_ENUM: int,
gdb.TYPE_CODE_FUNC: hex,
gdb.TYPE_CODE_BOOL: bool,
gdb.TYPE_CODE_FLT: float,
gdb.TYPE_CODE_STRING: str,
gdb.TYPE_CODE_CHAR: lambda x: chr(int(x)),
}

t = self.type
while t.code == gdb.TYPE_CODE_TYPEDEF:
t = t.target()

type_code = t.code
try:
converter = type_code_map[type_code]
return f"{converter(self):{format_spec}}"
except KeyError:
raise TypeError(
f"Unsupported type: {self.type}, {self.type.code} {self}"
)

@property
def address(self) -> Value:
value = super().address
return value and Value(value)

def cast(self, type: str | gdb.Type, ptr: bool = False) -> Optional["Value"]:
try:
gdb_type = lookup_type(type) if isinstance(type, str) else type
if ptr:
gdb_type = gdb_type.pointer()
return Value(super().cast(gdb_type))
except gdb.error:
return None

def dereference(self) -> Value:
return Value(super().dereference())

def reference_value(self) -> Value:
return Value(super().reference_value())

def referenced_value(self) -> Value:
return Value(super().referenced_value())

def rvalue_reference_value(self) -> Value:
return Value(super().rvalue_reference_value())

def const_value(self) -> Value:
return Value(super().const_value())

def dynamic_cast(self, type: gdb.Type) -> Value:
return Value(super().dynamic_cast(type))


class Backtrace:
"""
Convert addresses to backtrace
Expand Down Expand Up @@ -261,10 +345,16 @@ def objfile(self):
return self._file


def parse_and_eval(expression: str, global_context: bool = False):
"""Equivalent to gdb.parse_and_eval, but returns a Value object"""
gdb_value = gdb.parse_and_eval(expression)
return Value(gdb_value)


def gdb_eval_or_none(expresssion):
"""Evaluate an expression and return None if it fails"""
try:
return gdb.parse_and_eval(expresssion)
return parse_and_eval(expresssion)
except gdb.error:
return None

Expand Down Expand Up @@ -405,7 +495,7 @@ def parse_arg(arg: str) -> Union[gdb.Value, int]:
return int(arg, 16)

try:
return gdb.parse_and_eval(f"{arg}")
return parse_and_eval(f"{arg}")
except gdb.error:
return None

Expand Down Expand Up @@ -544,7 +634,7 @@ def swap64(val):

def is_target_arch(arch, exact=False):
"""
For non extact match, this function will
For non exact match, this function will
return True if the target architecture contains
keywords of an ARCH family. For example, x86 is
contained in i386:x86_64.
Expand Down Expand Up @@ -632,19 +722,19 @@ def get_pc(tcb=None):
return get_register_byname("pc", tcb)


def get_tcbs():
def get_tcbs() -> List[Tcb]:
# In case we have created/deleted tasks at runtime, the tcbs will change
# so keep it as fresh as possible
pidhash = gdb.parse_and_eval("g_pidhash")
npidhash = gdb.parse_and_eval("g_npidhash")
pidhash = parse_and_eval("g_pidhash")
npidhash = parse_and_eval("g_npidhash")

return [pidhash[i] for i in range(0, npidhash) if pidhash[i]]


def get_tcb(pid):
def get_tcb(pid) -> Tcb:
"""get tcb from pid"""
g_pidhash = gdb.parse_and_eval("g_pidhash")
g_npidhash = gdb.parse_and_eval("g_npidhash")
g_pidhash = parse_and_eval("g_pidhash")
g_npidhash = parse_and_eval("g_npidhash")
tcb = g_pidhash[pid & (g_npidhash - 1)]
if not tcb or pid != tcb["pid"]:
return None
Expand Down

0 comments on commit a59a28d

Please sign in to comment.