16
16
17
17
import json
18
18
import sys
19
- from os import environ , makedirs , remove
20
- from os .path import isdir , join , splitdrive
19
+ from os import environ
20
+ from os .path import join , splitdrive
21
+ from pathlib import Path
21
22
22
23
from elftools .elf .descriptions import describe_sh_flags
23
24
from elftools .elf .elffile import ELFFile
24
25
25
26
from platformio .compat import IS_WINDOWS
26
- from platformio .proc import exec_command
27
27
28
28
29
- def _run_tool (cmd , env , tool_args ):
30
- sysenv = environ .copy ()
31
- sysenv ["PATH" ] = str (env ["ENV" ]["PATH" ])
29
+ def _get_source_path (top_DIE ):
30
+ src_file_dir = (
31
+ top_DIE .attributes ["DW_AT_name" ].value .decode ("utf-8" ).replace ("\\ " , "/" )
32
+ if ("DW_AT_name" in top_DIE .attributes )
33
+ else "?"
34
+ )
35
+ comp_dir = (
36
+ top_DIE .attributes ["DW_AT_comp_dir" ].value .decode ("utf-8" ).replace ("\\ " , "/" )
37
+ if ("DW_AT_comp_dir" in top_DIE .attributes )
38
+ else ""
39
+ )
32
40
33
- build_dir = env .subst ("$BUILD_DIR" )
34
- if not isdir (build_dir ):
35
- makedirs (build_dir )
36
- tmp_file = join (build_dir , "size-data-longcmd.txt" )
41
+ return str (Path .resolve (Path (comp_dir ) / Path (src_file_dir )).as_posix ())
37
42
38
- with open (tmp_file , mode = "w" , encoding = "utf8" ) as fp :
39
- fp .write ("\n " .join (tool_args ))
40
43
41
- cmd .append ("@" + tmp_file )
42
- result = exec_command (cmd , env = sysenv )
43
- remove (tmp_file )
44
+ def _collect_dwarf_info (elffile ):
45
+ dwarf_list = []
46
+ dwarfinfo = elffile .get_dwarf_info ()
47
+ src_path = ""
44
48
45
- return result
49
+ for CU in dwarfinfo .iter_CUs ():
50
+ top_DIE = CU .get_top_DIE ()
46
51
52
+ if top_DIE .tag == "DW_TAG_compile_unit" :
53
+ src_path = _get_source_path (top_DIE )
47
54
48
- def _get_symbol_locations (env , elf_path , addrs ):
49
- if not addrs :
50
- return {}
51
- cmd = [env .subst ("$CC" ).replace ("-gcc" , "-addr2line" ), "-e" , elf_path ]
52
- result = _run_tool (cmd , env , addrs )
53
- locations = [line for line in result ["out" ].split ("\n " ) if line ]
54
- assert len (addrs ) == len (locations )
55
+ for DIE in top_DIE .iter_children ():
56
+ die_name_attr = DIE .attributes .get ("DW_AT_name" , None )
57
+ die_name = die_name_attr .value .decode ("utf-8" ) if die_name_attr else ""
55
58
56
- return dict (zip (addrs , [loc .strip () for loc in locations ]))
59
+ if die_name != "" :
60
+ if (DIE .tag == "DW_TAG_subprogram" ) or (
61
+ DIE .tag == "DW_TAG_variable" and "DW_AT_location" in DIE .attributes
62
+ ):
63
+ src_line_attr = DIE .attributes .get ("DW_AT_decl_line" , None )
64
+ src_line = src_line_attr .value if src_line_attr else ""
57
65
66
+ dwarf_list .append (
67
+ {
68
+ "dw_name" : die_name ,
69
+ "src_path" : src_path ,
70
+ "src_line" : src_line ,
71
+ }
72
+ )
58
73
59
- def _get_demangled_names (env , mangled_names ):
60
- if not mangled_names :
61
- return {}
62
- result = _run_tool (
63
- [env .subst ("$CC" ).replace ("-gcc" , "-c++filt" )], env , mangled_names
64
- )
65
- demangled_names = [line for line in result ["out" ].split ("\n " ) if line ]
66
- assert len (mangled_names ) == len (demangled_names )
67
-
68
- return dict (
69
- zip (
70
- mangled_names ,
71
- [dn .strip ().replace ("::__FUNCTION__" , "" ) for dn in demangled_names ],
72
- )
74
+ sorted_dwarf_list = sorted (
75
+ dwarf_list , key = lambda x : len (x ["dw_name" ]), reverse = False
73
76
)
77
+ return sorted_dwarf_list
78
+
79
+
80
+ def _get_dwarf_info (symbol , dwarfs ):
81
+ location = "?"
82
+ line = ""
83
+ demangled_name = ""
84
+
85
+ for d in dwarfs :
86
+ if d ["dw_name" ] in symbol ["name" ]:
87
+ location = d ["src_path" ]
88
+ line = d ["src_line" ]
89
+ demangled_name = d ["dw_name" ]
90
+
91
+ if demangled_name == "" :
92
+ demangled_name = symbol ["name" ]
93
+
94
+ return location , line , demangled_name
74
95
75
96
76
97
def _collect_sections_info (env , elffile ):
@@ -98,7 +119,7 @@ def _collect_sections_info(env, elffile):
98
119
return sections
99
120
100
121
101
- def _collect_symbols_info (env , elffile , elf_path , sections ):
122
+ def _collect_symbols_info (env , elffile , sections ):
102
123
symbols = []
103
124
104
125
symbol_section = elffile .get_section_by_name (".symtab" )
@@ -110,14 +131,13 @@ def _collect_symbols_info(env, elffile, elf_path, sections):
110
131
sysenv ["PATH" ] = str (env ["ENV" ]["PATH" ])
111
132
112
133
symbol_addrs = []
113
- mangled_names = []
114
134
for s in symbol_section .iter_symbols ():
115
135
symbol_info = s .entry ["st_info" ]
116
136
symbol_addr = s ["st_value" ]
117
137
symbol_size = s ["st_size" ]
118
138
symbol_type = symbol_info ["type" ]
119
139
120
- if not env .pioSizeIsValidSymbol (s .name , symbol_type , symbol_addr ):
140
+ if not env .pioSizeIsValidSymbol (s .name , symbol_size , symbol_type , symbol_addr ):
121
141
continue
122
142
123
143
symbol = {
@@ -129,31 +149,26 @@ def _collect_symbols_info(env, elffile, elf_path, sections):
129
149
"section" : env .pioSizeDetermineSection (sections , symbol_addr ),
130
150
}
131
151
132
- if s .name .startswith ("_Z" ):
133
- mangled_names .append (s .name )
134
-
135
152
symbol_addrs .append (hex (symbol_addr ))
136
153
symbols .append (symbol )
137
154
138
- symbol_locations = _get_symbol_locations (env , elf_path , symbol_addrs )
139
- demangled_names = _get_demangled_names (env , mangled_names )
140
- for symbol in symbols :
141
- if symbol ["name" ].startswith ("_Z" ):
142
- symbol ["demangled_name" ] = demangled_names .get (symbol ["name" ])
143
- location = symbol_locations .get (hex (symbol ["addr" ]))
155
+ sorted_symbols = sorted (symbols , key = lambda x : len (x ["name" ]), reverse = True )
156
+ sorted_dwarf = _collect_dwarf_info (elffile )
157
+
158
+ for symbol in sorted_symbols :
159
+
160
+ location , line , demangled_name = _get_dwarf_info (symbol , sorted_dwarf )
161
+ symbol ["demangled_name" ] = demangled_name
162
+
144
163
if not location or "?" in location :
145
164
continue
146
165
if IS_WINDOWS :
147
166
drive , tail = splitdrive (location )
148
167
location = join (drive .upper (), tail )
149
168
symbol ["file" ] = location
150
- symbol ["line" ] = 0
151
- if ":" in location :
152
- file_ , line = location .rsplit (":" , 1 )
153
- if line .isdigit ():
154
- symbol ["file" ] = file_
155
- symbol ["line" ] = int (line )
156
- return symbols
169
+ symbol ["line" ] = line if (line != "" ) else 0
170
+
171
+ return sorted_symbols
157
172
158
173
159
174
def pioSizeDetermineSection (_ , sections , symbol_addr ):
@@ -165,8 +180,13 @@ def pioSizeDetermineSection(_, sections, symbol_addr):
165
180
return "unknown"
166
181
167
182
168
- def pioSizeIsValidSymbol (_ , symbol_name , symbol_type , symbol_address ):
169
- return symbol_name and symbol_address != 0 and symbol_type != "STT_NOTYPE"
183
+ def pioSizeIsValidSymbol (_ , symbol_name , symbol_size , symbol_type , symbol_address ):
184
+ return (
185
+ symbol_name
186
+ and symbol_size != 0
187
+ and symbol_address != 0
188
+ and symbol_type != "STT_NOTYPE"
189
+ )
170
190
171
191
172
192
def pioSizeIsRamSection (_ , section ):
@@ -224,7 +244,7 @@ def DumpSizeData(_, target, source, env): # pylint: disable=unused-argument
224
244
}
225
245
226
246
files = {}
227
- for symbol in _collect_symbols_info (env , elffile , elf_path , sections ):
247
+ for symbol in _collect_symbols_info (env , elffile , sections ):
228
248
file_path = symbol .get ("file" ) or "unknown"
229
249
if not files .get (file_path , {}):
230
250
files [file_path ] = {"symbols" : [], "ram_size" : 0 , "flash_size" : 0 }
0 commit comments