-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
128 additions
and
0 deletions.
There are no files selected for viewing
17 changes: 17 additions & 0 deletions
17
tutorial/tutorial-analyses/src/tutorial_analyses/BranchCoverageAnalysis.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
from typing import Optional | ||
from dynapyt.analyses.BaseAnalysis import BaseAnalysis | ||
|
||
|
||
class BranchCoverageAnalysis(BaseAnalysis): | ||
def __init__(self): | ||
super().__init__() | ||
self.branches = {} | ||
|
||
def enter_control_flow(self, dyn_ast: str, iid: int, cond_value: bool): | ||
if (dyn_ast, iid, cond_value) not in self.branches: | ||
self.branches[(dyn_ast, iid, cond_value)] = 0 | ||
self.branches[(dyn_ast, iid, cond_value)] += 1 | ||
|
||
def end_execution(self): | ||
for k, v in self.branches.items(): | ||
print(f"Branch {k[1]} was taken with value {k[2]} {v} times") |
68 changes: 68 additions & 0 deletions
68
tutorial/tutorial-analyses/src/tutorial_analyses/CallGraphAnalysis.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
from typing import Callable, Tuple, Dict | ||
import logging | ||
import libcst.matchers as m | ||
from dynapyt.analyses.BaseAnalysis import BaseAnalysis | ||
from dynapyt.utils.nodeLocator import get_parent_by_type | ||
import os | ||
from inspect import getmodule | ||
|
||
|
||
class CallGraphAnalysis(BaseAnalysis): | ||
def __init__(self): | ||
super(CallGraphAnalysis, self).__init__() | ||
logging.basicConfig(filename="dynapyt.json", format="%(message)s", level=logging.INFO) | ||
self.graph = {} | ||
|
||
def pre_call(self, dyn_ast: str, iid: int, function: Callable, pos_args: Tuple, kw_args: Dict): | ||
""" | ||
DynaPyt hook for pre function call | ||
""" | ||
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()) | ||
# 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(os.sep, ".") | ||
# format = "file" | ||
|
||
if caller is None: | ||
f = key | ||
else: | ||
# 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: | ||
# self.graph[f] = [format, callee] | ||
self.graph[f] = [callee] | ||
|
||
def end_execution(self): | ||
""" | ||
DynaPyt hook for end of execution | ||
""" | ||
# to avoid serialization failures in converting dict to json | ||
print(self.graph) |
9 changes: 9 additions & 0 deletions
9
tutorial/tutorial-analyses/src/tutorial_analyses/OppositeBranchAnalysis.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from typing import Optional | ||
from dynapyt.analyses.BaseAnalysis import BaseAnalysis | ||
|
||
|
||
class OppositeBranchAnalysis(BaseAnalysis): | ||
def enter_control_flow( | ||
self, dyn_ast: str, iid: int, cond_value: bool | ||
) -> Optional[bool]: | ||
return not cond_value |
34 changes: 34 additions & 0 deletions
34
tutorial/tutorial-analyses/src/tutorial_analyses/SlowStringConcatAnalysis.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
from typing import Iterable | ||
from dynapyt.analyses.BaseAnalysis import BaseAnalysis | ||
|
||
|
||
class SlowStringConcatAnalysis(BaseAnalysis): | ||
def __init__(self): | ||
super().__init__() | ||
self.in_loop = [] | ||
self.concat_count = [] | ||
self.threshold = 5 | ||
|
||
def enter_for(self, dyn_ast: str, iid: int, next_value, iterable: Iterable): | ||
if ( | ||
self.in_loop | ||
and self.in_loop[-1][0] == dyn_ast | ||
and self.in_loop[-1][1] == iid | ||
): | ||
pass | ||
else: | ||
self.in_loop.append((dyn_ast, iid)) | ||
self.concat_count.append(0) | ||
|
||
def exit_for(self, dyn_ast: str, iid: int): | ||
curr = self.in_loop.pop() | ||
assert curr[0] == dyn_ast | ||
assert curr[1] == iid | ||
self.concat_count.pop() | ||
|
||
def add_assign(self, dyn_ast: str, iid: int, lhs, rhs): | ||
if self.in_loop: | ||
if isinstance(rhs, str): | ||
self.concat_count[-1] += 1 | ||
if self.concat_count[-1] >= self.threshold: | ||
print(f"Possible slow string concatenation in {dyn_ast} at {iid}") |