mirror of
https://github.com/apache/nuttx.git
synced 2025-12-13 23:17:11 +08:00
tools/gdb: fallback to parse elf to get macro
For LTO optimization, we may not be able to parse the macro value. Parse .debug_macro section from elf manually. Signed-off-by: xuxingliang <xuxingliang@xiaomi.com>
This commit is contained in:
@@ -40,7 +40,7 @@
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
PUNCTUATORS = [
|
||||
"\[",
|
||||
@@ -128,35 +128,39 @@ def fetch_macro_info(file):
|
||||
# it's broken on some GDB distribution :(, I haven't
|
||||
# found a solution to it.
|
||||
|
||||
with tempfile.NamedTemporaryFile(delete=False) as f1:
|
||||
cache = os.path.splitext(file)[0] + ".macro"
|
||||
if not os.path.isfile(cache):
|
||||
start = time.time()
|
||||
with open(cache, "w") as f:
|
||||
# # os.system(f"readelf -wm {file} > {output}")
|
||||
process = subprocess.Popen(
|
||||
f"readelf -wm {file}", shell=True, stdout=f, stderr=subprocess.STDOUT
|
||||
)
|
||||
|
||||
# # os.system(f"readelf -wm {file} > {output}")
|
||||
process = subprocess.Popen(
|
||||
f"readelf -wm {file}", shell=True, stdout=f1, stderr=subprocess.STDOUT
|
||||
)
|
||||
process.communicate()
|
||||
errcode = process.returncode
|
||||
|
||||
process.communicate()
|
||||
errcode = process.returncode
|
||||
f.close()
|
||||
|
||||
f1.close()
|
||||
if errcode != 0:
|
||||
return {}
|
||||
print(f"readelf took {time.time() - start:.1f} seconds")
|
||||
|
||||
if errcode != 0:
|
||||
return {}
|
||||
p = re.compile(".*macro[ ]*:[ ]*([\S]+\(.*?\)|[\w]+)[ ]*(.*)")
|
||||
macros = {}
|
||||
|
||||
p = re.compile(".*macro[ ]*:[ ]*([\S]+\(.*?\)|[\w]+)[ ]*(.*)")
|
||||
macros = {}
|
||||
start = time.time()
|
||||
with open(cache, "r") as f2:
|
||||
for line in f2.readlines():
|
||||
if not line.startswith(" DW_MACRO_define") and not line.startswith(
|
||||
" DW_MACRO_undef"
|
||||
):
|
||||
continue
|
||||
|
||||
with open(f1.name, "rb") as f2:
|
||||
for line in f2.readlines():
|
||||
line = line.decode("utf-8")
|
||||
if not line.startswith(" DW_MACRO_define") and not line.startswith(
|
||||
" DW_MACRO_undef"
|
||||
):
|
||||
continue
|
||||
|
||||
if not parse_macro(line, macros, p):
|
||||
print(f"Failed to parse {line}")
|
||||
if not parse_macro(line, macros, p):
|
||||
print(f"Failed to parse {line}")
|
||||
|
||||
print(f"Parse macro took {time.time() - start:.1f} seconds")
|
||||
return macros
|
||||
|
||||
|
||||
|
||||
@@ -25,7 +25,9 @@ import traceback
|
||||
import gdb
|
||||
import utils
|
||||
|
||||
STACK_COLORATION_PATTERN = utils.get_symbol_value("STACK_COLOR")
|
||||
STACK_COLORATION_PATTERN = utils.get_symbol_value(
|
||||
"STACK_COLOR", locspec="up_create_stack"
|
||||
)
|
||||
|
||||
|
||||
class Stack(object):
|
||||
|
||||
@@ -25,8 +25,11 @@ from typing import List
|
||||
|
||||
import gdb
|
||||
|
||||
from macros import fetch_macro_info, try_expand
|
||||
|
||||
g_symbol_cache = {}
|
||||
g_type_cache = {}
|
||||
g_macro_ctx = None
|
||||
|
||||
|
||||
def backtrace(addresses: List[gdb.Value]) -> List[str]:
|
||||
@@ -102,6 +105,35 @@ class ContainerOf(gdb.Function):
|
||||
ContainerOf()
|
||||
|
||||
|
||||
class MacroCtx:
|
||||
"""
|
||||
This is a singleton class which only initializes once to
|
||||
cache a context of macro definition which can be queried later
|
||||
TODO: we only deal with single ELF at the moment for simplicity
|
||||
If you load more object files while debugging, only the first one gets loaded
|
||||
will be used to retrieve macro information
|
||||
"""
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if not hasattr(cls, "instance"):
|
||||
cls.instance = super(MacroCtx, cls).__new__(cls)
|
||||
return cls.instance
|
||||
|
||||
def __init__(self, filename):
|
||||
self._macro_map = {}
|
||||
self._file = filename
|
||||
|
||||
self._macro_map = fetch_macro_info(filename)
|
||||
|
||||
@property
|
||||
def macro_map(self):
|
||||
return self._macro_map
|
||||
|
||||
@property
|
||||
def objfile(self):
|
||||
return self._file
|
||||
|
||||
|
||||
def gdb_eval_or_none(expresssion):
|
||||
"""Evaluate an expression and return None if it fails"""
|
||||
try:
|
||||
@@ -158,6 +190,19 @@ def get_symbol_value(name, locspec="nx_start", cacheable=True):
|
||||
gdb.execute("inferior 2", to_string=True)
|
||||
gdb.execute(f"list {locspec}", to_string=True)
|
||||
value = gdb_eval_or_none(name)
|
||||
if not value:
|
||||
# Try to expand macro by reading elf
|
||||
global g_macro_ctx
|
||||
if not g_macro_ctx:
|
||||
gdb.write("No macro context found, trying to load from ELF\n")
|
||||
if len(gdb.objfiles()) > 0:
|
||||
g_macro_ctx = MacroCtx(gdb.objfiles()[0].filename)
|
||||
else:
|
||||
raise gdb.GdbError("An executable file must be provided")
|
||||
|
||||
expr = try_expand(name, g_macro_ctx.macro_map)
|
||||
value = gdb_eval_or_none(expr)
|
||||
|
||||
if cacheable:
|
||||
g_symbol_cache[(name, locspec)] = value
|
||||
|
||||
|
||||
Reference in New Issue
Block a user