Python: call-graph tracing: callable => callee

to use consistent naming
This commit is contained in:
Rasmus Wriedt Larsen
2020-07-14 14:12:02 +02:00
parent 3127bb27d0
commit 1d9c3b3bcd
6 changed files with 35 additions and 36 deletions

View File

@@ -2,7 +2,7 @@
also known as _call graph tracing_.
Execute a python program and for each call being made, record the call and callable. This allows us to compare call graph resolution from static analysis with actual data -- that is, can we statically determine the target of each actual call correctly.
Execute a python program and for each call being made, record the call and callee. This allows us to compare call graph resolution from static analysis with actual data -- that is, can we statically determine the target of each actual call correctly.
This is still in the early stages, and currently only supports a very minimal working example (to show that this approach might work).

View File

@@ -2,7 +2,7 @@
"""Call Graph tracing.
Execute a python program and for each call being made, record the call and callable. This
Execute a python program and for each call being made, record the call and callee. This
allows us to compare call graph resolution from static analysis with actual data -- that
is, can we statically determine the target of each actual call correctly.
@@ -37,7 +37,7 @@ import xml.etree.ElementTree as ET
@dataclasses.dataclass(frozen=True)
class Call():
"""A call to a callable
"""A call
"""
filename: str
linenum: int
@@ -59,12 +59,11 @@ class Call():
@dataclasses.dataclass(frozen=True)
class Callable():
"""A callable (Function/Lambda) should (hopefully) be uniquely identified by its name and
location (filename+line number)
class Callee():
"""A callee (Function/Lambda/???)
TODO: Callable is maybe not a good name, since classes with __call__ will return true
for the python code `callable(cls)` -- will have to consider how __call__ is handled
should (hopefully) be uniquely identified by its name and location (filename+line
number)
"""
funcname: str
filename: str
@@ -97,10 +96,10 @@ class CallGraphTracer(bdb.Bdb):
def user_call(self, frame, argument_list):
call = Call.from_frame(frame.f_back, self)
callable = Callable.from_frame(frame, self)
callee = Callee.from_frame(frame, self)
# _print(f'{call} -> {callable}')
self.recorded_calls.add((call, callable))
# _print(f'{call} -> {callee}')
self.recorded_calls.add((call, callee))
################################################################################
@@ -127,10 +126,10 @@ class CSVExporter(Exporter):
def export(recorded_calls, outfile_path):
with open(outfile_path, 'w', newline='') as csv_file:
writer = None
for (call, callable) in recorded_calls:
for (call, callee) in recorded_calls:
data = {
**Exporter.dataclass_to_dict(call),
**Exporter.dataclass_to_dict(callable)
**Exporter.dataclass_to_dict(callee)
}
if writer is None:
@@ -152,10 +151,10 @@ class XMLExporter(Exporter):
root = ET.Element('root')
for (call, callable) in recorded_calls:
for (call, callee) in recorded_calls:
data = {
**Exporter.dataclass_to_dict(call),
**Exporter.dataclass_to_dict(callable)
**Exporter.dataclass_to_dict(callee)
}
rc = ET.SubElement(root, 'recorded_call')
@@ -216,8 +215,8 @@ if __name__ == "__main__":
elif opts.xml:
XMLExporter.export(cgt.recorded_calls, opts.xml)
else:
for (call, callable) in cgt.recorded_calls:
print(f'{call} -> {callable}')
for (call, callee) in cgt.recorded_calls:
print(f'{call} -> {callee}')
print('--- captured stdout ---')
print(captured_stdout.getvalue(), end='')

View File

@@ -1,6 +1,6 @@
<root>
<recorded_call call_filename="/home/rasmus/code/ql/python/tools/recorded-call-graph-metrics/example/simple.py" call_linenum="10" call_inst_index="36" callable_funcname="bar" callable_filename="/home/rasmus/code/ql/python/tools/recorded-call-graph-metrics/example/simple.py" callable_linenum="4" />
<recorded_call call_filename="/home/rasmus/code/ql/python/tools/recorded-call-graph-metrics/example/simple.py" call_linenum="7" call_inst_index="18" callable_funcname="foo" callable_filename="/home/rasmus/code/ql/python/tools/recorded-call-graph-metrics/example/simple.py" callable_linenum="1" />
<recorded_call call_filename="/home/rasmus/code/ql/python/tools/recorded-call-graph-metrics/example/simple.py" call_linenum="10" call_inst_index="30" callable_funcname="foo" callable_filename="/home/rasmus/code/ql/python/tools/recorded-call-graph-metrics/example/simple.py" callable_linenum="1" />
<recorded_call call_filename="/home/rasmus/code/ql/python/tools/recorded-call-graph-metrics/example/simple.py" call_linenum="8" call_inst_index="24" callable_funcname="bar" callable_filename="/home/rasmus/code/ql/python/tools/recorded-call-graph-metrics/example/simple.py" callable_linenum="4" />
<recorded_call call_filename="/home/rasmus/code/ql/python/tools/recorded-call-graph-metrics/example/simple.py" call_linenum="7" call_inst_index="18" callee_funcname="foo" callee_filename="/home/rasmus/code/ql/python/tools/recorded-call-graph-metrics/example/simple.py" callee_linenum="1" />
<recorded_call call_filename="/home/rasmus/code/ql/python/tools/recorded-call-graph-metrics/example/simple.py" call_linenum="8" call_inst_index="24" callee_funcname="bar" callee_filename="/home/rasmus/code/ql/python/tools/recorded-call-graph-metrics/example/simple.py" callee_linenum="4" />
<recorded_call call_filename="/home/rasmus/code/ql/python/tools/recorded-call-graph-metrics/example/simple.py" call_linenum="10" call_inst_index="30" callee_funcname="foo" callee_filename="/home/rasmus/code/ql/python/tools/recorded-call-graph-metrics/example/simple.py" callee_linenum="1" />
<recorded_call call_filename="/home/rasmus/code/ql/python/tools/recorded-call-graph-metrics/example/simple.py" call_linenum="10" call_inst_index="36" callee_funcname="bar" callee_filename="/home/rasmus/code/ql/python/tools/recorded-call-graph-metrics/example/simple.py" callee_linenum="4" />
</root>

View File

@@ -1,9 +1,9 @@
import RecordedCalls
from ValidRecordedCall rc, Call call, Function callable, CallableValue callableValue
from ValidRecordedCall rc, Call call, Function callee, CallableValue calleeValue
where
call = rc.getCall() and
callable = rc.getCallable() and
callableValue.getScope() = callable and
callableValue.getACall() = call.getAFlowNode()
select call, "-->", callable
callee = rc.getCallee() and
calleeValue.getScope() = callee and
calleeValue.getACall() = call.getAFlowNode()
select call, "-->", callee

View File

@@ -16,23 +16,23 @@ class RecordedCall extends XMLElement {
result.getLocation().hasLocationInfo(this.call_filename(), this.call_linenum(), _, _, _)
}
string callable_filename() { result = this.getAttributeValue("callable_filename") }
string callee_filename() { result = this.getAttributeValue("callee_filename") }
int callable_linenum() { result = this.getAttributeValue("callable_linenum").toInt() }
int callee_linenum() { result = this.getAttributeValue("callee_linenum").toInt() }
string callable_funcname() { result = this.getAttributeValue("callable_funcname") }
string callee_funcname() { result = this.getAttributeValue("callee_funcname") }
Function getCallable() {
result.getLocation().hasLocationInfo(this.callable_filename(), this.callable_linenum(), _, _, _)
Function getCallee() {
result.getLocation().hasLocationInfo(this.callee_filename(), this.callee_linenum(), _, _, _)
}
}
/**
* Class of recorded calls where we can uniquely identify both the `call` and the `callable`.
* Class of recorded calls where we can uniquely identify both the `call` and the `callee`.
*/
class ValidRecordedCall extends RecordedCall {
ValidRecordedCall() {
strictcount(this.getCall()) = 1 and
strictcount(this.getCallable()) = 1
strictcount(this.getCallee()) = 1
}
}

View File

@@ -2,6 +2,6 @@ import RecordedCalls
from RecordedCall rc
where not rc instanceof ValidRecordedCall
select "Could not uniquely identify this recorded call (either call or callable was not uniquely identified)",
rc.call_filename(), rc.call_linenum(), rc.call_inst_index(), "-->", rc.callable_filename(),
rc.callable_linenum(), rc.callable_funcname()
select "Could not uniquely identify this recorded call (either call or callee was not uniquely identified)",
rc.call_filename(), rc.call_linenum(), rc.call_inst_index(), "-->", rc.callee_filename(),
rc.callee_linenum(), rc.callee_funcname()