Skip to content

Commit

Permalink
Merge branch 'main' of github.com:sola-st/DynaPyt
Browse files Browse the repository at this point in the history
  • Loading branch information
AryazE committed Jun 1, 2023
2 parents 9f121fe + 6adb521 commit 73c68e1
Showing 1 changed file with 68 additions and 10 deletions.
78 changes: 68 additions & 10 deletions src/dynapyt/analyses/CallGraph.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,84 @@
from typing import Callable, Tuple, Dict
import logging
import libcst as cst
import libcst.matchers as m
from .BaseAnalysis import BaseAnalysis
from ..utils.nodeLocator import get_node_by_location, get_parent_by_type
from ..utils.nodeLocator import get_parent_by_type
import json
from inspect import getmodule

class CallGraph(BaseAnalysis):
def __init__(self):
super(CallGraph, self).__init__()
self.graph = set()
logging.basicConfig(filename="dynapyt.json", format='%(message)s', level=logging.INFO)
self.graph = {}

'''
DynaPyt hook for pre function call
'''
def pre_call(self, dyn_ast: str, iid: int, function: Callable, pos_args: Tuple, kw_args: Dict):
ast, iids = self._get_ast(dyn_ast)
module = getmodule(function)
module = str(module).split(' ')[1] if module is not None else "''"
# calling function
caller = get_parent_by_type(ast, iids.iid_to_location[iid], m.FunctionDef())
callee = get_node_by_location(ast, iids.iid_to_location[iid], m.Call())
# called function
if hasattr(function, "__qualname__"):
'''
module of the callee is added through the module if present
'''
callee = module[1:-1] + '.' + function.__qualname__ if module != "''" else function.__qualname__
else:
'''
this is done to capture functions whose function.__qualname__ is not defined,
but the string gives an idea as to which function is called.
Captures MarkDecorator annotations, lambdas object calls, etc
'''
temp = str(function)
callee = temp

#file name
key = dyn_ast.replace('.py.orig', '').replace('/','.')
# format = "file"

if caller is None:
f = 'root module'
f = key
else:
f = caller.name.value
if callee is None:
t = 'unknown'
# if caller is a part of class, find the class name
caller_parent = get_parent_by_type(ast, iids.iid_to_location[iid], m.ClassDef())
if caller_parent is None:
f = key + '.' + caller.name.value
# format += ".func"
else:
f = key + '.' + caller_parent.name.value + '.' + caller.name.value
# format += ".class.func"

# if caller already added
if f in self.graph.keys():
temp = self.graph[f]
# filter dupilcate callees
if callee not in temp:
temp.append(callee)
self.graph[f] = temp
else:
t = callee.func.value
self.graph.add((f, t))
# self.graph[f] = [format, callee]
self.graph[f] = [callee]

'''
DynaPyt hook for end of execution
'''
def end_execution(self):
print(self.graph)
'''
to avoid serialization failures in converting dict to json
'''
try:
logging.info(json.dumps(self.graph))
except Exception:
logging.info("{")
for idx, key in enumerate(self.graph):
values = ["\"{}\"".format(x) for x in self.graph[key]]
if not idx == (len(self.graph.keys()) - 1):
logging.info("\"{}\" : {}, ".format(key, values))
else:
logging.info("\"{}\" : {}".format(key, values))
logging.info("}")

0 comments on commit 73c68e1

Please sign in to comment.