Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
L
librarytrader
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container registry
Model registry
Operate
Environments
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Andreas Ziegler
librarytrader
Commits
fe3dec71
Commit
fe3dec71
authored
Jan 29, 2022
by
Andreas Ziegler
Browse files
Options
Downloads
Patches
Plain Diff
store, scripts: allow extraction of depth-limited transitive callgraphs
parent
391f8b98
No related branches found
No related tags found
No related merge requests found
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
librarytrader/librarystore.py
+15
-4
15 additions, 4 deletions
librarytrader/librarystore.py
scripts/generate_callgraph_depth.py
+174
-0
174 additions, 0 deletions
scripts/generate_callgraph_depth.py
with
189 additions
and
4 deletions
librarytrader/librarystore.py
+
15
−
4
View file @
fe3dec71
...
...
@@ -237,14 +237,23 @@ class LibraryStore(BaseStore):
worked_on
.
add
(
cur_obj
)
return
result
def
get_transitive_calls
(
self
,
library
,
function
,
working_on
=
None
):
def
get_transitive_calls
(
self
,
library
,
function
,
working_on
=
None
,
target_depth
=
None
,
cur_depth
=
0
):
if
target_depth
and
cur_depth
==
target_depth
:
return
set
()
if
working_on
is
None
:
working_on
=
set
()
libname
=
library
.
fullname
if
libname
not
in
self
.
_callee_cache
:
self
.
_callee_cache
[
libname
]
=
{}
if
function
in
self
.
_callee_cache
[
libname
]:
if
target_depth
is
None
and
function
in
self
.
_callee_cache
[
libname
]:
# Don't access the cache if we're collecting depth-limited call
# graphs. Otherwise, successive calls with increasing depths would
# return empty sets for functions at the boundary of the previous
# depth and lead to too small results.
return
self
.
_callee_cache
[
libname
][
function
]
# No cache hit, calculate it
...
...
@@ -260,7 +269,8 @@ class LibraryStore(BaseStore):
callee
,
library
.
fullname
)
if
callee
in
working_on
:
continue
subcalls
=
self
.
get_transitive_calls
(
library
,
callee
,
working_on
)
subcalls
=
self
.
get_transitive_calls
(
library
,
callee
,
working_on
,
target_depth
,
cur_depth
+
1
)
local_cache
.
update
(
subcalls
)
working_on
.
remove
(
function
)
...
...
@@ -293,7 +303,8 @@ class LibraryStore(BaseStore):
callee
,
intermediate_object
,
library
.
fullname
)
if
callee
in
working_on
:
continue
subcalls
=
self
.
get_transitive_calls
(
library
,
callee
,
working_on
)
subcalls
=
self
.
get_transitive_calls
(
library
,
callee
,
working_on
,
target_depth
,
cur_depth
+
1
)
local_cache
.
update
(
subcalls
)
working_on
.
remove
(
function
)
...
...
This diff is collapsed.
Click to expand it.
scripts/generate_callgraph_depth.py
0 → 100644
+
174
−
0
View file @
fe3dec71
#!/usr/bin/env python3
import
os
import
sys
from
librarytrader.librarystore
import
LibraryStore
def
format_node
(
library
,
addr
):
name
=
'
{:x}
'
.
format
(
addr
)
options
=
[]
# Category of nodes: local/static objects/functions
if
addr
in
library
.
init_functions
:
if
addr
in
library
.
local_functions
:
name
=
library
.
local_functions
[
addr
][
0
]
elif
addr
in
library
.
exported_addrs
:
name
=
library
.
exported_addrs
[
addr
][
0
]
options
.
append
(
'
shape=house
'
)
elif
addr
in
library
.
local_functions
:
name
=
library
.
local_functions
[
addr
][
0
]
options
.
append
(
'
shape=box
'
)
elif
addr
in
library
.
exported_addrs
:
name
=
library
.
exported_addrs
[
addr
][
0
]
options
.
append
(
'
shape=diamond
'
)
elif
addr
in
library
.
local_objs
:
name
=
library
.
local_objs
[
addr
][
0
]
options
.
append
(
'
shape=ellipse
'
)
elif
addr
in
library
.
exported_objs
:
name
=
library
.
exported_objs
[
addr
][
0
]
options
.
append
(
'
shape=hexagon
'
)
# Usage:
if
addr
in
library
.
object_users
and
library
.
object_users
[
addr
]:
options
.
append
(
'
color=green
'
)
elif
addr
in
library
.
local_users
and
library
.
local_users
[
addr
]:
options
.
append
(
'
color=green
'
)
elif
addr
in
library
.
export_users
and
library
.
export_users
[
addr
]:
options
.
append
(
'
color=green
'
)
options
.
append
(
'
label=
"
{}
"'
.
format
(
name
))
return
'"
{:x}
"
'
.
format
(
addr
)
+
'
[
'
+
'
,
'
.
join
(
options
)
+
'
]
'
def
format_edge
(
library
,
source
,
target
):
retval
=
'"
{:x}
"
->
"
{:x}
"'
.
format
(
source
,
target
)
source_used
=
False
target_used
=
False
if
source
in
library
.
local_users
and
library
.
local_users
[
source
]:
source_used
=
True
elif
source
in
library
.
export_users
and
library
.
export_users
[
source
]:
source_used
=
True
elif
source
in
library
.
object_users
and
library
.
object_users
[
source
]:
source_used
=
True
if
source_used
:
if
target
in
library
.
local_users
and
library
.
local_users
[
target
]:
target_used
=
True
elif
target
in
library
.
export_users
and
library
.
export_users
[
target
]:
target_used
=
True
elif
target
in
library
.
object_users
and
library
.
object_users
[
target
]:
target_used
=
True
if
target_used
:
retval
+=
'
[color=green]
'
retval
+=
'
\n
'
return
retval
def
maybe_print_node
(
library
,
addr
,
seen
,
outfd
):
if
addr
not
in
seen
:
seen
.
add
(
addr
)
outfd
.
write
(
format_node
(
library
,
addr
)
+
'
\n
'
)
def
print_edges
(
library
,
source
,
targets
,
seen
,
seen_edges
,
outfd
):
maybe_print_node
(
library
,
source
,
seen
,
outfd
)
for
target
in
targets
:
maybe_print_node
(
library
,
target
,
seen
,
outfd
)
if
(
source
,
target
)
not
in
seen_edges
:
seen_edges
.
add
((
source
,
target
))
outfd
.
write
(
format_edge
(
library
,
source
,
target
))
s
=
LibraryStore
()
s
.
load
(
sys
.
argv
[
1
])
lname
=
sys
.
argv
[
2
]
addr
=
int
(
sys
.
argv
[
3
])
depth
=
int
(
sys
.
argv
[
4
])
l
=
s
[
lname
]
outname
=
os
.
path
.
basename
(
l
.
fullname
)
+
'
_
'
+
hex
(
addr
)
+
'
_
'
+
str
(
depth
)
+
'
.dot
'
with
open
(
outname
,
'
w
'
)
as
outfd
:
print
(
'
writing to {}
'
.
format
(
outname
))
seen
=
set
()
seen_edges
=
set
()
seen_import
=
set
()
outfd
.
write
(
'
digraph D {
'
+
'
\n
'
)
# Get all function nodes reachable from given address
nodes
=
set
(
k
for
k
,
v
in
s
.
get_transitive_calls
(
l
,
addr
,
target_depth
=
depth
)
if
v
.
fullname
==
lname
)
# Add the initial node itself as it is not part of the transitive call chain
nodes
.
add
(
addr
)
# Additionally, extract the visited objects from the LibraryStore to allow
# a reconstruction of the dependency flow through these objects.
for
(
src
,
subl
),
targets
in
s
.
_object_cache
.
items
():
if
subl
.
fullname
!=
lname
:
continue
nodes
.
add
(
src
)
nodes
.
update
(
targets
)
i
=
1
prev_nodes
=
set
()
while
nodes
!=
prev_nodes
:
print
(
'
round {}
'
.
format
(
i
))
i
+=
1
prev_nodes
=
nodes
.
copy
()
# Add nodes to output
for
addr
in
nodes
:
maybe_print_node
(
l
,
addr
,
seen
,
outfd
)
# Add edges through local calls
for
source
,
targets
in
l
.
local_calls
.
items
():
if
source
in
nodes
:
for
target
in
targets
:
if
target
not
in
nodes
:
continue
print_edges
(
l
,
source
,
[
target
],
seen
,
seen_edges
,
outfd
)
# Add edges through calls to exported functions
for
source
,
targets
in
l
.
internal_calls
.
items
():
if
source
in
nodes
:
for
target
in
targets
:
if
target
not
in
nodes
:
continue
print_edges
(
l
,
source
,
[
target
],
seen
,
seen_edges
,
outfd
)
# Add edges to imported (== external) functions
for
source
,
targets
in
l
.
external_calls
.
items
():
if
source
not
in
nodes
:
continue
maybe_print_node
(
l
,
source
,
seen
,
outfd
)
for
target
in
targets
:
if
target
not
in
seen_import
:
seen_import
.
add
(
target
)
outfd
.
write
(
'"
{}
"
[shape=doubleoctagon]
\n
'
.
format
(
target
))
outfd
.
write
(
'"
{:x}
"
->
"
{}
"'
.
format
(
source
,
target
))
outfd
.
write
(
'
\n
'
)
# Same for local objects
for
source
,
targets
in
l
.
local_object_refs
.
items
():
if
source
in
nodes
:
print_edges
(
l
,
source
,
targets
,
seen
,
seen_edges
,
outfd
)
nodes
.
update
(
targets
)
# ... exported objects
for
source
,
targets
in
l
.
export_object_refs
.
items
():
if
source
in
nodes
:
print_edges
(
l
,
source
,
targets
,
seen
,
seen_edges
,
outfd
)
nodes
.
update
(
targets
)
# ... references between objects themselves
for
source
,
targets
in
l
.
object_to_objects
.
items
():
if
source
not
in
nodes
:
continue
print_edges
(
l
,
source
,
targets
,
seen
,
seen_edges
,
outfd
)
nodes
.
update
(
targets
)
# ... and outgoing edges from objects to functions
for
source
,
targets
in
l
.
object_to_functions
.
items
():
if
source
not
in
nodes
:
continue
if
source
in
l
.
exports_plt
:
continue
print_edges
(
l
,
source
,
targets
,
seen
,
seen_edges
,
outfd
)
outfd
.
write
(
'
}
'
+
'
\n
'
)
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment