Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
Mozi
rt-thread
提交
4b8d446b
R
rt-thread
项目概览
Mozi
/
rt-thread
与 Fork 源项目一致
Fork自
RT-Thread / rt-thread
通知
0
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
rt-thread
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
4b8d446b
编写于
6月 21, 2020
作者:
csdn_JZ_
提交者:
Gitee
6月 21, 2020
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
update tools/WCS.py.
添加 WCS.py
上级
37808b13
变更
1
显示空白变更内容
内联
并排
Showing
1 changed file
with
440 addition
and
0 deletion
+440
-0
tools/WCS.py
tools/WCS.py
+440
-0
未找到文件。
tools/WCS.py
0 → 100644
浏览文件 @
4b8d446b
import
re
import
pprint
import
os
from
subprocess
import
check_output
from
optparse
import
OptionParser
# Constants
rtl_ext_end
=
".dfinish"
rtl_ext
=
None
# e.g. '.c.270r.dfinish'. The number '270' will change with gcc version and is auto-detected by the
# function find_rtl_ext
dir
=
r
'.'
# Working directory
su_ext
=
'.su'
obj_ext
=
'.o'
manual_ext
=
'.msu'
read_elf_path
=
"arm-none-eabi-readelf.exe"
# You may need to enter the full path here
stdout_encoding
=
"utf-8"
# System dependant
class
Printable
:
def
__repr__
(
self
):
return
"<"
+
type
(
self
).
__name__
+
"> "
+
pprint
.
pformat
(
vars
(
self
),
indent
=
4
,
width
=
1
)
class
Symbol
(
Printable
):
pass
def
read_symbols
(
file
):
from
subprocess
import
check_output
def
to_symbol
(
read_elf_line
):
v
=
read_elf_line
.
split
()
s2
=
Symbol
()
s2
.
value
=
int
(
v
[
1
],
16
)
s2
.
size
=
int
(
v
[
2
])
s2
.
type
=
v
[
3
]
s2
.
binding
=
v
[
4
]
if
len
(
v
)
>=
8
:
s2
.
name
=
v
[
7
]
else
:
s2
.
name
=
""
return
s2
output
=
check_output
([
read_elf_path
,
"-s"
,
"-W"
,
file
]).
decode
(
stdout_encoding
)
lines
=
output
.
splitlines
()[
3
:]
return
[
to_symbol
(
line
)
for
line
in
lines
]
def
read_obj
(
tu
,
call_graph
):
"""
Reads the file tu.o and gets the binding (global or local) for each function
:param tu: name of the translation unit (e.g. for main.c, this would be 'main')
:param call_graph: a object used to store information about each function, results go here
"""
symbols
=
read_symbols
(
tu
[
0
:
tu
.
rindex
(
"."
)]
+
obj_ext
)
for
s
in
symbols
:
if
s
.
type
==
'FUNC'
:
if
s
.
binding
==
'GLOBAL'
:
# Check for multiple declarations
if
s
.
name
in
call_graph
[
'globals'
]
or
s
.
name
in
call_graph
[
'locals'
]:
raise
Exception
(
'Multiple declarations of {}'
.
format
(
s
.
name
))
call_graph
[
'globals'
][
s
.
name
]
=
{
'tu'
:
tu
,
'name'
:
s
.
name
,
'binding'
:
s
.
binding
}
elif
s
.
binding
==
'LOCAL'
:
# Check for multiple declarations
if
s
.
name
in
call_graph
[
'locals'
]
and
tu
in
call_graph
[
'locals'
][
s
.
name
]:
raise
Exception
(
'Multiple declarations of {}'
.
format
(
s
.
name
))
if
s
.
name
not
in
call_graph
[
'locals'
]:
call_graph
[
'locals'
][
s
.
name
]
=
{}
call_graph
[
'locals'
][
s
.
name
][
tu
]
=
{
'tu'
:
tu
,
'name'
:
s
.
name
,
'binding'
:
s
.
binding
}
elif
s
.
binding
==
'WEAK'
:
if
s
.
name
in
call_graph
[
'weak'
]:
raise
Exception
(
'Multiple declarations of {}'
.
format
(
s
.
name
))
call_graph
[
'weak'
][
s
.
name
]
=
{
'tu'
:
tu
,
'name'
:
s
.
name
,
'binding'
:
s
.
binding
}
else
:
raise
Exception
(
'Error Unknown Binding "{}" for symbol: {}'
.
format
(
s
.
binding
,
s
.
name
))
def
find_fxn
(
tu
,
fxn
,
call_graph
):
"""
Looks up the dictionary associated with the function.
:param tu: The translation unit in which to look for locals functions
:param fxn: The function name
:param call_graph: a object used to store information about each function
:return: the dictionary for the given function or None
"""
if
fxn
in
call_graph
[
'globals'
]:
return
call_graph
[
'globals'
][
fxn
]
else
:
try
:
return
call_graph
[
'locals'
][
fxn
][
tu
]
except
KeyError
:
return
None
def
find_demangled_fxn
(
tu
,
fxn
,
call_graph
):
"""
Looks up the dictionary associated with the function.
:param tu: The translation unit in which to look for locals functions
:param fxn: The function name
:param call_graph: a object used to store information about each function
:return: the dictionary for the given function or None
"""
for
f
in
call_graph
[
'globals'
].
values
():
if
'demangledName'
in
f
:
if
f
[
'demangledName'
]
==
fxn
:
return
f
for
f
in
call_graph
[
'locals'
].
values
():
if
tu
in
f
:
if
'demangledName'
in
f
[
tu
]:
if
f
[
tu
][
'demangledName'
]
==
fxn
:
return
f
[
tu
]
return
None
def
read_rtl
(
tu
,
call_graph
):
"""
Read an RTL file and finds callees for each function and if there are calls via function pointer.
:param tu: the translation unit
:param call_graph: a object used to store information about each function, results go here
"""
# Construct A Call Graph
function
=
re
.
compile
(
r
'^;; Function (.*) \((\S+), funcdef_no=\d+(, [a-z_]+=\d+)*\)( \([a-z ]+\))?$'
)
static_call
=
re
.
compile
(
r
'^.*\(call.*"(.*)".*$'
)
other_call
=
re
.
compile
(
r
'^.*call .*$'
)
for
line_
in
open
(
tu
+
rtl_ext
).
readlines
():
m
=
function
.
match
(
line_
)
if
m
:
fxn_name
=
m
.
group
(
2
)
fxn_dict2
=
find_fxn
(
tu
,
fxn_name
,
call_graph
)
if
not
fxn_dict2
:
pprint
.
pprint
(
call_graph
)
raise
Exception
(
"Error locating function {} in {}"
.
format
(
fxn_name
,
tu
))
fxn_dict2
[
'demangledName'
]
=
m
.
group
(
1
)
fxn_dict2
[
'calls'
]
=
set
()
fxn_dict2
[
'has_ptr_call'
]
=
False
continue
m
=
static_call
.
match
(
line_
)
if
m
:
fxn_dict2
[
'calls'
].
add
(
m
.
group
(
1
))
# print("Call: {0} -> {1}".format(current_fxn, m.group(1)))
continue
m
=
other_call
.
match
(
line_
)
if
m
:
fxn_dict2
[
'has_ptr_call'
]
=
True
continue
def
read_su
(
tu
,
call_graph
):
"""
Reads the 'local_stack' for each function. Local stack ignores stack used by callees.
:param tu: the translation unit
:param call_graph: a object used to store information about each function, results go here
:return:
"""
su_line
=
re
.
compile
(
r
'^([^ :]+):([\d]+):([\d]+):(.+)\t(\d+)\t(\S+)$'
)
i
=
1
for
line
in
open
(
tu
[
0
:
tu
.
rindex
(
"."
)]
+
su_ext
).
readlines
():
m
=
su_line
.
match
(
line
)
if
m
:
fxn
=
m
.
group
(
4
)
fxn_dict2
=
find_demangled_fxn
(
tu
,
fxn
,
call_graph
)
fxn_dict2
[
'local_stack'
]
=
int
(
m
.
group
(
5
))
else
:
print
(
"error parsing line {} in file {}"
.
format
(
i
,
tu
))
i
+=
1
def
read_manual
(
file
,
call_graph
):
"""
reads the manual stack useage files.
:param file: the file name
:param call_graph: a object used to store information about each function, results go here
"""
for
line
in
open
(
file
).
readlines
():
fxn
,
stack_sz
=
line
.
split
()
if
fxn
in
call_graph
:
raise
Exception
(
"Redeclared Function {}"
.
format
(
fxn
))
call_graph
[
'globals'
][
fxn
]
=
{
'wcs'
:
int
(
stack_sz
),
'calls'
:
set
(),
'has_ptr_call'
:
False
,
'local_stack'
:
int
(
stack_sz
),
'is_manual'
:
True
,
'name'
:
fxn
,
'tu'
:
'#MANUAL'
,
'binding'
:
'GLOBAL'
}
def
validate_all_data
(
call_graph
):
"""
Check that every entry in the call graph has the following fields:
.calls, .has_ptr_call, .local_stack, .scope, .src_line
"""
def
validate_dict
(
d
):
if
not
(
'calls'
in
d
and
'has_ptr_call'
in
d
and
'local_stack'
in
d
and
'name'
in
d
and
'tu'
in
d
):
print
(
"Error data is missing in fxn dictionary {}"
.
format
(
d
))
# Loop through every global and local function
# and resolve each call, save results in r_calls
for
fxn_dict2
in
call_graph
[
'globals'
].
values
():
validate_dict
(
fxn_dict2
)
for
l_dict
in
call_graph
[
'locals'
].
values
():
for
fxn_dict2
in
l_dict
.
values
():
validate_dict
(
fxn_dict2
)
def
resolve_all_calls
(
call_graph
):
def
resolve_calls
(
fxn_dict2
):
fxn_dict2
[
'r_calls'
]
=
[]
fxn_dict2
[
'unresolved_calls'
]
=
set
()
for
call
in
fxn_dict2
[
'calls'
]:
call_dict
=
find_fxn
(
fxn_dict2
[
'tu'
],
call
,
call_graph
)
if
call_dict
:
fxn_dict2
[
'r_calls'
].
append
(
call_dict
)
else
:
fxn_dict2
[
'unresolved_calls'
].
add
(
call
)
# Loop through every global and local function
# and resolve each call, save results in r_calls
for
fxn_dict
in
call_graph
[
'globals'
].
values
():
resolve_calls
(
fxn_dict
)
for
l_dict
in
call_graph
[
'locals'
].
values
():
for
fxn_dict
in
l_dict
.
values
():
resolve_calls
(
fxn_dict
)
def
calc_all_wcs
(
call_graph
):
def
calc_wcs
(
fxn_dict2
,
call_graph1
,
parents
):
"""
Calculates the worst case stack for a fxn that is declared (or called from) in a given file.
:param parents: This function gets called recursively through the call graph. If a function has recursion the
tuple file, fxn will be in the parents stack and everything between the top of the stack and the matching entry
has recursion.
:return:
"""
# If the wcs is already known, then nothing to do
if
'wcs'
in
fxn_dict2
:
return
# Check for pointer calls
if
fxn_dict2
[
'has_ptr_call'
]:
fxn_dict2
[
'wcs'
]
=
'unbounded'
return
# Check for recursion
if
fxn_dict2
in
parents
:
fxn_dict2
[
'wcs'
]
=
'unbounded'
return
# Calculate WCS
call_max
=
0
for
call_dict
in
fxn_dict2
[
'r_calls'
]:
# Calculate the WCS for the called function
parents
.
append
(
fxn_dict2
)
calc_wcs
(
call_dict
,
call_graph1
,
parents
)
parents
.
pop
()
# If the called function is unbounded, so is this function
if
call_dict
[
'wcs'
]
==
'unbounded'
:
fxn_dict2
[
'wcs'
]
=
'unbounded'
return
# Keep track of the call with the largest stack use
call_max
=
max
(
call_max
,
call_dict
[
'wcs'
])
# Propagate Unresolved Calls
for
unresolved_call
in
call_dict
[
'unresolved_calls'
]:
fxn_dict2
[
'unresolved_calls'
].
add
(
unresolved_call
)
fxn_dict2
[
'wcs'
]
=
call_max
+
fxn_dict2
[
'local_stack'
]
# Loop through every global and local function
# and resolve each call, save results in r_calls
for
fxn_dict
in
call_graph
[
'globals'
].
values
():
calc_wcs
(
fxn_dict
,
call_graph
,
[])
for
l_dict
in
call_graph
[
'locals'
].
values
():
for
fxn_dict
in
l_dict
.
values
():
calc_wcs
(
fxn_dict
,
call_graph
,
[])
def
print_all_fxns
(
call_graph
):
def
print_fxn
(
row_format
,
fxn_dict2
):
unresolved
=
fxn_dict2
[
'unresolved_calls'
]
stack
=
str
(
fxn_dict2
[
'wcs'
])
if
unresolved
:
unresolved_str
=
'({})'
.
format
(
' ,'
.
join
(
unresolved
))
if
stack
!=
'unbounded'
:
stack
=
"unbounded:"
+
stack
else
:
unresolved_str
=
''
print
(
row_format
.
format
(
fxn_dict2
[
'tu'
],
fxn_dict2
[
'demangledName'
],
stack
,
unresolved_str
))
def
get_order
(
val
):
if
val
==
'unbounded'
:
return
1
else
:
return
-
val
# Loop through every global and local function
# and resolve each call, save results in r_calls
d_list
=
[]
for
fxn_dict
in
call_graph
[
'globals'
].
values
():
d_list
.
append
(
fxn_dict
)
for
l_dict
in
call_graph
[
'locals'
].
values
():
for
fxn_dict
in
l_dict
.
values
():
d_list
.
append
(
fxn_dict
)
d_list
.
sort
(
key
=
lambda
item
:
get_order
(
item
[
'wcs'
]))
# Calculate table width
tu_width
=
max
(
max
([
len
(
d
[
'tu'
])
for
d
in
d_list
]),
16
)
name_width
=
max
(
max
([
len
(
d
[
'name'
])
for
d
in
d_list
]),
13
)
row_format
=
"{:<"
+
str
(
tu_width
+
2
)
+
"} {:<"
+
str
(
name_width
+
2
)
+
"} {:>14} {:<17}"
# Print out the table
print
(
""
)
print
(
row_format
.
format
(
'Translation Unit'
,
'Function Name'
,
'Stack'
,
'Unresolved Dependencies'
))
for
d
in
d_list
:
print_fxn
(
row_format
,
d
)
def
find_rtl_ext
():
# Find the rtl_extension
global
rtl_ext
for
root
,
directories
,
filenames
in
os
.
walk
(
'.'
):
for
f
in
filenames
:
if
(
f
.
endswith
(
rtl_ext_end
)):
rtl_ext
=
f
[
f
[:
-
len
(
rtl_ext_end
)].
rindex
(
"."
):]
print
(
"rtl_ext = "
+
rtl_ext
)
return
print
(
"Could not find any files ending with '.dfinish'. Check that the script is being run from the correct "
"directory. Check that the code was compiled with the correct flags"
)
exit
(
-
1
)
def
find_files
():
tu
=
[]
manual
=
[]
all_files
=
[]
for
root
,
directories
,
filenames
in
os
.
walk
(
dir
):
for
filename
in
filenames
:
all_files
.
append
(
os
.
path
.
join
(
root
,
filename
))
files
=
[
f
for
f
in
all_files
if
os
.
path
.
isfile
(
f
)
and
f
.
endswith
(
rtl_ext
)]
for
f
in
files
:
base
=
f
[
0
:
-
len
(
rtl_ext
)]
short_base
=
base
[
0
:
base
.
rindex
(
"."
)]
if
short_base
+
su_ext
in
all_files
and
short_base
+
obj_ext
in
all_files
:
tu
.
append
(
base
)
print
(
'Reading: {}{}, {}{}, {}{}'
.
format
(
base
,
rtl_ext
,
short_base
,
su_ext
,
short_base
,
obj_ext
))
files
=
[
f
for
f
in
all_files
if
os
.
path
.
isfile
(
f
)
and
f
.
endswith
(
manual_ext
)]
for
f
in
files
:
manual
.
append
(
f
)
print
(
'Reading: {}'
.
format
(
f
))
# Print some diagnostic messages
if
not
tu
:
print
(
"Could not find any translation units to analyse"
)
exit
(
-
1
)
return
tu
,
manual
def
main
():
# Find the appropriate RTL extension
find_rtl_ext
()
# Find all input files
call_graph
=
{
'locals'
:
{},
'globals'
:
{},
'weak'
:
{}}
tu_list
,
manual_list
=
find_files
()
# Read the input files
for
tu
in
tu_list
:
read_obj
(
tu
,
call_graph
)
# This must be first
for
fxn
in
call_graph
[
'weak'
].
values
():
if
fxn
[
'name'
]
not
in
call_graph
[
'globals'
].
keys
():
call_graph
[
'globals'
][
fxn
[
'name'
]]
=
fxn
for
tu
in
tu_list
:
read_rtl
(
tu
,
call_graph
)
for
tu
in
tu_list
:
read_su
(
tu
,
call_graph
)
# Read manual files
for
m
in
manual_list
:
read_manual
(
m
,
call_graph
)
# Validate Data
validate_all_data
(
call_graph
)
# Resolve All Function Calls
resolve_all_calls
(
call_graph
)
# Calculate Worst Case Stack For Each Function
calc_all_wcs
(
call_graph
)
# Print A Nice Message With Each Function and the WCS
print_all_fxns
(
call_graph
)
def
ThreadStackStaticAnalysis
(
env
):
print
(
'Start thread stack static analysis...'
)
import
rtconfig
read_elf_path
=
rtconfig
.
EXEC_PATH
+
r
'\readelf.exe'
main
()
print
(
'
\n
Thread stack static analysis done!'
)
return
\ No newline at end of file
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录