mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Python: call-graph tracing: callable => callee
to use consistent naming
This commit is contained in:
@@ -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).
|
||||
|
||||
|
||||
@@ -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='')
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user