def _basic_trace_frames(self): return [ TraceFrame( id=1, kind=TraceKind.PRECONDITION, caller="call1", caller_port="root", callee="call2", callee_port="param0", callee_location=SourceLocation(1, 1), filename="file.py", run_id=1, ), TraceFrame( id=2, kind=TraceKind.PRECONDITION, caller="call2", caller_port="param0", callee="leaf", callee_port="sink", callee_location=SourceLocation(1, 2), filename="file.py", run_id=1, ), ]
def testTraceCursorLocation(self): run = Run(id=1, date=datetime.now(), status=RunStatus.FINISHED) issue = self._generic_issue() issue_instance = self._generic_issue_instance() trace_frames = [ TraceFrame( id=1, kind=TraceKind.POSTCONDITION, caller="call1", caller_port="root", callee="leaf", callee_port="source", callee_location=SourceLocation(1, 1), filename="file.py", run_id=1, ), TraceFrame( id=2, kind=TraceKind.PRECONDITION, caller="call1", caller_port="root", callee="leaf", callee_port="sink", callee_location=SourceLocation(1, 2), filename="file.py", run_id=1, ), ] assocs = [ IssueInstanceTraceFrameAssoc(trace_frame_id=1, issue_instance_id=1), IssueInstanceTraceFrameAssoc(trace_frame_id=2, issue_instance_id=1), TraceFrameLeafAssoc(trace_frame_id=1, leaf_id=1), TraceFrameLeafAssoc(trace_frame_id=2, leaf_id=1), ] with self.db.make_session() as session: session.add(run) session.add(issue) session.add(issue_instance) self._add_to_session(session, trace_frames) self._add_to_session(session, assocs) session.commit() self.interactive.setup() self.interactive.set_issue(1) self.assertEqual(self.interactive.current_trace_frame_index, 1) self.interactive.next_cursor_location() self.assertEqual(self.interactive.current_trace_frame_index, 2) self.interactive.next_cursor_location() self.assertEqual(self.interactive.current_trace_frame_index, 2) self.interactive.prev_cursor_location() self.assertEqual(self.interactive.current_trace_frame_index, 1) self.interactive.prev_cursor_location() self.assertEqual(self.interactive.current_trace_frame_index, 0) self.interactive.prev_cursor_location() self.assertEqual(self.interactive.current_trace_frame_index, 0)
def testTrace(self): run = Run(id=1, date=datetime.now(), status=RunStatus.FINISHED) issue = self._generic_issue() issue_instance = self._generic_issue_instance() trace_frames = [ TraceFrame( id=1, kind=TraceKind.POSTCONDITION, caller="call1", caller_port="root", callee="leaf", callee_port="source", callee_location=SourceLocation(1, 1, 1), filename="file.py", run_id=1, ), TraceFrame( id=2, kind=TraceKind.PRECONDITION, caller="call1", caller_port="root", callee="leaf", callee_port="sink", callee_location=SourceLocation(1, 1, 2), filename="file.py", run_id=1, ), ] assocs = [ IssueInstanceTraceFrameAssoc(trace_frame_id=1, issue_instance_id=1), IssueInstanceTraceFrameAssoc(trace_frame_id=2, issue_instance_id=1), TraceFrameLeafAssoc(trace_frame_id=1, leaf_id=1), TraceFrameLeafAssoc(trace_frame_id=2, leaf_id=1), ] with self.db.make_session() as session: session.add(run) session.add(issue) session.add(issue_instance) self._add_to_session(session, trace_frames) self._add_to_session(session, assocs) session.commit() self.interactive.setup() self.interactive.trace() stderr = self.stderr.getvalue().strip() self.assertIn("Use 'set_issue(ID)' to select an issue first.", stderr) self.interactive.set_issue(1) self.interactive.trace() output = self.stdout.getvalue().strip() self.assertIn(" leaf source file.py:1|1|1", output) self.assertIn(" --> call1 root file.py:1|2|3", output) self.assertIn(" leaf sink file.py:1|1|2", output)
def testVerifyMultipleBranches(self): # Leads to no-op on _generate_trace self.interactive.trace_tuples_id = 1 self.interactive.current_issue_id = 1 self.interactive.current_trace_frame_index = 0 self.interactive.trace_tuples = [ TraceTuple(trace_frame=TraceFrame(id=1), branches=1), TraceTuple(trace_frame=TraceFrame(id=2), branches=2), ] self.assertFalse(self.interactive._verify_multiple_branches()) self.interactive.current_trace_frame_index = 1 self.assertTrue(self.interactive._verify_multiple_branches())
def testCurrentBranchIndex(self): trace_frames = [TraceFrame(id=1), TraceFrame(id=2), TraceFrame(id=3)] self.interactive.current_trace_frame_index = 0 self.interactive.trace_tuples = [TraceTuple(trace_frame=TraceFrame(id=1))] self.assertEqual(0, self.interactive._current_branch_index(trace_frames)) self.interactive.trace_tuples[0].trace_frame.id = 2 self.assertEqual(1, self.interactive._current_branch_index(trace_frames)) self.interactive.trace_tuples[0].trace_frame.id = 3 self.assertEqual(2, self.interactive._current_branch_index(trace_frames)) self.interactive.trace_tuples[0].trace_frame.id = 4 self.assertEqual(-1, self.interactive._current_branch_index(trace_frames))
def testTraceMissingFrames(self): run = Run(id=1, date=datetime.now(), status=RunStatus.FINISHED) issue = self._generic_issue() issue_instance = self._generic_issue_instance() trace_frames = [ TraceFrame( id=1, kind=TraceKind.POSTCONDITION, caller="call1", caller_port="root", callee="leaf", callee_port="source", callee_location=SourceLocation(1, 1, 1), filename="file.py", run_id=1, ), TraceFrame( id=2, kind=TraceKind.PRECONDITION, caller="call1", caller_port="root", callee="call2", callee_port="param0", callee_location=SourceLocation(1, 1, 1), filename="file.py", run_id=1, ), ] assocs = [ IssueInstanceTraceFrameAssoc(trace_frame_id=1, issue_instance_id=1), IssueInstanceTraceFrameAssoc(trace_frame_id=2, issue_instance_id=1), TraceFrameLeafAssoc(trace_frame_id=1, leaf_id=1), TraceFrameLeafAssoc(trace_frame_id=2, leaf_id=1), ] with self.db.make_session() as session: session.add(run) session.add(issue) session.add(issue_instance) self._add_to_session(session, trace_frames) self._add_to_session(session, assocs) session.commit() self.interactive.setup() self.interactive.set_issue(1) self.interactive.trace() stdout = self.stdout.getvalue().strip() self.assertIn("Missing trace frame: call2:param0", stdout)
def _navigate_trace_frames(self, session: Session, initial_trace_frames: List[TraceFrame], index: int = 0) -> List[Tuple[TraceFrame, int]]: if not initial_trace_frames: return [] trace_frames = [(initial_trace_frames[index], len(initial_trace_frames))] while not self._is_leaf(trace_frames[-1]): trace_frame, branches = trace_frames[-1] next_nodes = self._next_trace_frames(session, trace_frame) if len(next_nodes) == 0: # Denote a missing frame by setting caller to None trace_frames.append(( TraceFrame( # pyre-ignore: T41318465 callee=trace_frame.callee, callee_port=trace_frame.callee_port, caller=None, ), 0, )) return trace_frames trace_frames.append((next_nodes[0], len(next_nodes))) return trace_frames
def _update_trace_tuples_new_parent(self, parent_trace_frame: TraceFrame) -> None: # Construct the placeholder trace tuple and the actual one. new_head = [ TraceTuple( trace_frame=TraceFrame( caller="unused", callee=parent_trace_frame.caller, callee_port=parent_trace_frame.caller_port, filename=parent_trace_frame.filename, callee_location=parent_trace_frame.callee_location, kind=parent_trace_frame.kind, ) ), TraceTuple(trace_frame=parent_trace_frame), ] if parent_trace_frame.kind == TraceKind.POSTCONDITION: # If current state is: C in [A,B,C,D,E] # Then new state is: [A,B] + new_tail new_tail = new_head[::-1] self.trace_tuples = ( self.trace_tuples[: self.current_trace_frame_index] + new_tail ) self.current_trace_frame_index = len(self.trace_tuples) - 1 return # If current state is: C in [A,B,C,D,E] # Then new state is: new_head + [D,E] self.trace_tuples = ( new_head + self.trace_tuples[self.current_trace_frame_index + 1 :] ) self.current_trace_frame_index = 0
def _generate_trace(self): if self.trace_tuples_id == self.current_issue_id: return # already generated with self.db.make_session() as session: issue_instance, issue = self._get_current_issue(session) postcondition_navigation = self._navigate_trace_frames( session, self._initial_trace_frames(session, issue_instance.id, TraceKind.POSTCONDITION), ) precondition_navigation = self._navigate_trace_frames( session, self._initial_trace_frames(session, issue_instance.id, TraceKind.PRECONDITION), ) self.trace_tuples = ( self._create_trace_tuples(reversed(postcondition_navigation)) + [ TraceTuple(trace_frame=TraceFrame( callee=issue.callable, callee_port="root", filename=issue_instance.filename, callee_location=issue_instance.location, )) ] + self._create_trace_tuples(precondition_navigation)) self.trace_tuples_id = self.current_issue_id self.root_trace_frame_index = len(postcondition_navigation) self.current_trace_frame_index = self.root_trace_frame_index
def testCreateTraceTuples(self): # reverse order postcondition_traces = [ ( TraceFrame( callee="call3", callee_port="result", filename="file3.py", callee_location=SourceLocation(1, 1, 3), caller="main", caller_port="root", ), 1, ), ( TraceFrame( callee="call2", callee_port="result", caller="dummy caller", filename="file2.py", callee_location=SourceLocation(1, 1, 2), ), 2, ), ( TraceFrame( callee="leaf", callee_port="source", caller="dummy caller", filename="file1.py", callee_location=SourceLocation(1, 1, 1), ), 3, ), ] trace_tuples = self.interactive._create_trace_tuples(postcondition_traces) self.assertEqual(len(trace_tuples), 3) self.assertEqual( trace_tuples, [ TraceTuple(postcondition_traces[0][0], 1), TraceTuple(postcondition_traces[1][0], 2), TraceTuple(postcondition_traces[2][0], 3), ], )
def testListSourceCode(self): mock_data = """if this_is_true: print("This was true") else: print("This was false") """ self.interactive.setup() self.interactive.current_issue_id = 1 self.interactive.trace_tuples_id = 1 self.interactive.current_trace_frame_index = 0 self.interactive.trace_tuples = [ TraceTuple( trace_frame=TraceFrame( filename="file.py", callee_location=SourceLocation(2, 1, 1) ) ) ] with patch("builtins.open", mock_open(read_data=mock_data)) as mock_file: self._clear_stdout() self.interactive.list_source_code(2) mock_file.assert_called_once_with(f"{os.getcwd()}/file.py", "r") output = self.stdout.getvalue() self.assertEqual( output.split("\n"), [ "file.py:2|1|1", " 1 if this_is_true:", ' --> 2 print("This was true")', " 3 else:", ' 4 print("This was false")', "", ], ) mock_file.reset_mock() self._clear_stdout() self.interactive.list_source_code(1) mock_file.assert_called_once_with(f"{os.getcwd()}/file.py", "r") output = self.stdout.getvalue() self.assertEqual( output.split("\n"), [ "file.py:2|1|1", " 1 if this_is_true:", ' --> 2 print("This was true")', " 3 else:", "", ], )
def testListSourceCodeFileNotFound(self): self.interactive.setup() self.interactive.current_issue_id = 1 self.interactive.trace_tuples_id = 1 self.interactive.current_trace_frame_index = 0 self.interactive.trace_tuples = [ TraceTuple( trace_frame=TraceFrame( filename="file.py", callee_location=SourceLocation(2, 1, 1) ) ) ] with patch("builtins.open", mock_open(read_data="not read")) as mock_file: mock_file.side_effect = FileNotFoundError() self.interactive.list_source_code() self.assertIn("Couldn't open", self.stderr.getvalue()) self.assertNotIn("file.py", self.stdout.getvalue())
def _generate_raw_precondition( self, run, filename, caller, caller_port, callee, callee_port, callee_location, titos, sinks, type_interval, features, ): lb, ub, preserves_type_context = self._get_interval(type_interval) trace_frame = TraceFrame.Record( id=DBID(), kind=TraceKind.PRECONDITION, caller=caller, caller_port=caller_port, callee=callee, callee_port=callee_port, callee_location=SourceLocation( callee_location["line"], callee_location["start"], callee_location["end"], ), filename=filename, titos=titos, run_id=run.id, preserves_type_context=preserves_type_context, type_interval_lower=lb, type_interval_upper=ub, migrated_id=None, ) for (sink, depth) in sinks: sink_record = self._get_shared_text(SharedTextKind.SINK, sink) self.graph.add_trace_frame_leaf_assoc(trace_frame, sink_record, depth) self.graph.add_trace_frame(trace_frame) self._generate_trace_annotations(trace_frame.id, features) return trace_frame
def _generate_raw_postcondition( self, run, filename, caller, caller_port, callee, callee_port, callee_location, sources, type_interval, ): lb, ub, preserves_type_context = self._get_interval(type_interval) trace_frame = TraceFrame.Record( id=DBID(), kind=TraceKind.POSTCONDITION, caller=caller, callee=callee, callee_location=SourceLocation( callee_location["line"], callee_location["start"], callee_location["end"], ), filename=filename, run_id=run.id, caller_port=caller_port, callee_port=callee_port, preserves_type_context=preserves_type_context, type_interval_lower=lb, type_interval_upper=ub, migrated_id=None, titos=[], ) for (source, depth) in sources: source_record = self._get_shared_text(SharedTextKind.SOURCE, source) self.graph.add_trace_frame_leaf_assoc(trace_frame, source_record, depth) self.graph.add_trace_frame(trace_frame) return trace_frame
def testTraceNoSinks(self): run = Run(id=1, date=datetime.now(), status=RunStatus.FINISHED) issue = self._generic_issue() issue_instance = self._generic_issue_instance() trace_frame = TraceFrame( id=1, kind=TraceKind.POSTCONDITION, caller="call1", caller_port="root", callee="leaf", callee_port="source", callee_location=SourceLocation(1, 1), filename="file.py", run_id=1, ) source = SharedText(id=1, contents="source1", kind=SharedTextKind.SOURCE) assocs = [ IssueInstanceTraceFrameAssoc(trace_frame_id=1, issue_instance_id=1), TraceFrameLeafAssoc(trace_frame_id=1, leaf_id=1), ] with self.db.make_session() as session: session.add(run) session.add(issue) session.add(issue_instance) session.add(trace_frame) session.add(source) self._add_to_session(session, assocs) session.commit() self.interactive.setup() self.interactive.sources = {"source1"} self.interactive.set_issue(1) self._clear_stdout() self.interactive.trace() self.assertEqual( self.stdout.getvalue().split("\n"), [ " [branches] [callable] [port] [location]", " leaf source file.py:1|1|1", " --> call1 root file.py:1|2|3", "", ], )
def _generate_trace_from_frame(self): with self.db.make_session() as session: trace_frame = ( session.query(TraceFrame) .filter(TraceFrame.id == self.current_frame_id) .scalar() ) navigation = self._navigate_trace_frames(session, [trace_frame]) # We need to "fake" another node for the selected trace frame. # Suppose we select a trace frame (A->B) and the generated navigation # is (A->B), (B->C), (C->D) with D as leaf. # When we display traces, we only use the callee, so this trace would # look like B->C->D. If we also want to see A->, then we need to add a # placeholder. # Set caller to "unused", since _create_trace_tuples checks presence # of a caller to determine insertion of "Missing frame". first_trace_frame = navigation[0][0] navigation.insert( 0, ( TraceFrame( caller="unused", callee=first_trace_frame.caller, callee_port=first_trace_frame.caller_port, filename=first_trace_frame.filename, callee_location=first_trace_frame.callee_location, kind=first_trace_frame.kind, ), 1, ), ) self.current_trace_frame_index = 0 if trace_frame.kind == TraceKind.POSTCONDITION: self.current_trace_frame_index = len(navigation) - 1 navigation = reversed(navigation) self.trace_tuples = self._create_trace_tuples(navigation)
def testOutputTraceTuples(self): trace_tuples = [ TraceTuple( trace_frame=TraceFrame( callee="leaf", callee_port="source", filename="file1.py", callee_location=SourceLocation(1, 1, 1), ) ), TraceTuple( trace_frame=TraceFrame( callee="call2", callee_port="result", filename="file2.py", callee_location=SourceLocation(1, 1, 2), ) ), TraceTuple( trace_frame=TraceFrame( callee="call3", callee_port="result", filename="file3.py", callee_location=SourceLocation(1, 1, 3), ) ), TraceTuple( trace_frame=TraceFrame( callee="main", callee_port="root", filename="file4.py", callee_location=SourceLocation(1, 1, 4), ) ), TraceTuple( trace_frame=TraceFrame( callee="call4", callee_port="param0", filename="file4.py", callee_location=SourceLocation(1, 1, 4), ) ), TraceTuple( trace_frame=TraceFrame( callee="call5", callee_port="param1", filename="file5.py", callee_location=SourceLocation(1, 1, 5), ) ), TraceTuple( trace_frame=TraceFrame( callee="leaf", callee_port="sink", filename="file6.py", callee_location=SourceLocation(1, 1, 6), ) ), ] self.interactive.current_trace_frame_index = 1 self.interactive._output_trace_tuples(trace_tuples) output = self.stdout.getvalue() self.assertEqual( output.split("\n"), [ " [branches] [callable] [port] [location]", " leaf source file1.py:1|1|1", " --> call2 result file2.py:1|1|2", " call3 result file3.py:1|1|3", " main root file4.py:1|1|4", " call4 param0 file4.py:1|1|4", " call5 param1 file5.py:1|1|5", " leaf sink file6.py:1|1|6", "", ], ) self._clear_stdout() self.interactive.current_trace_frame_index = 4 self.interactive._output_trace_tuples(trace_tuples) output = self.stdout.getvalue() self.assertEqual( output.split("\n"), [ " [branches] [callable] [port] [location]", " leaf source file1.py:1|1|1", " call2 result file2.py:1|1|2", " call3 result file3.py:1|1|3", " main root file4.py:1|1|4", " --> call4 param0 file4.py:1|1|4", " call5 param1 file5.py:1|1|5", " leaf sink file6.py:1|1|6", "", ], )
def _set_up_branched_trace(self): run = Run(id=1, date=datetime.now(), status=RunStatus.FINISHED) issue = self._generic_issue() issue_instance = self._generic_issue_instance() messages = [ SharedText(id=1, contents="source1", kind=SharedTextKind.SOURCE), SharedText(id=2, contents="sink1", kind=SharedTextKind.SINK), ] trace_frames = [] assocs = [ IssueInstanceSharedTextAssoc(issue_instance_id=1, shared_text_id=1), IssueInstanceSharedTextAssoc(issue_instance_id=1, shared_text_id=2), ] for i in range(6): trace_frames.append( TraceFrame( id=i + 1, caller="call1", caller_port="root", filename="file.py", callee_location=SourceLocation(i, i, i), run_id=1, ) ) if i < 2: # 2 postconditions trace_frames[i].kind = TraceKind.POSTCONDITION trace_frames[i].callee = "leaf" trace_frames[i].callee_port = "source" assocs.append( TraceFrameLeafAssoc(trace_frame_id=i + 1, leaf_id=1, trace_length=0) ) assocs.append( IssueInstanceTraceFrameAssoc( trace_frame_id=i + 1, issue_instance_id=1 ) ) elif i < 4: trace_frames[i].kind = TraceKind.PRECONDITION trace_frames[i].callee = "call2" trace_frames[i].callee_port = "param2" assocs.append( TraceFrameLeafAssoc(trace_frame_id=i + 1, leaf_id=2, trace_length=1) ) assocs.append( IssueInstanceTraceFrameAssoc( trace_frame_id=i + 1, issue_instance_id=1 ) ) else: trace_frames[i].kind = TraceKind.PRECONDITION trace_frames[i].caller = "call2" trace_frames[i].caller_port = "param2" trace_frames[i].callee = "leaf" trace_frames[i].callee_port = "sink" assocs.append( TraceFrameLeafAssoc(trace_frame_id=i + 1, leaf_id=2, trace_length=0) ) with self.db.make_session() as session: session.add(run) session.add(issue) session.add(issue_instance) self._add_to_session(session, messages) self._add_to_session(session, trace_frames) self._add_to_session(session, assocs) session.commit()
def testBranchPrefixLengthChanges(self): run = Run(id=1, date=datetime.now(), status=RunStatus.FINISHED) issue = self._generic_issue() issue_instance = self._generic_issue_instance() messages = [ SharedText(id=1, contents="source1", kind=SharedTextKind.SOURCE), SharedText(id=2, contents="sink1", kind=SharedTextKind.SINK), ] trace_frames = [ TraceFrame( id=1, kind=TraceKind.POSTCONDITION, caller="call1", caller_port="root", callee="leaf", callee_port="source", callee_location=SourceLocation(1, 1), filename="file.py", run_id=1, ), TraceFrame( id=2, kind=TraceKind.POSTCONDITION, caller="call1", caller_port="root", callee="prev_call", callee_port="result", callee_location=SourceLocation(1, 1), filename="file.py", run_id=1, ), TraceFrame( id=3, kind=TraceKind.POSTCONDITION, caller="prev_call", caller_port="result", callee="leaf", callee_port="source", callee_location=SourceLocation(1, 1), filename="file.py", run_id=1, ), TraceFrame( id=4, kind=TraceKind.PRECONDITION, caller="call1", caller_port="root", callee="leaf", callee_port="sink", callee_location=SourceLocation(1, 2), filename="file.py", run_id=1, ), ] assocs = [ IssueInstanceSharedTextAssoc(issue_instance_id=1, shared_text_id=1), IssueInstanceSharedTextAssoc(issue_instance_id=1, shared_text_id=2), IssueInstanceTraceFrameAssoc(issue_instance_id=1, trace_frame_id=1), IssueInstanceTraceFrameAssoc(issue_instance_id=1, trace_frame_id=2), IssueInstanceTraceFrameAssoc(issue_instance_id=1, trace_frame_id=4), TraceFrameLeafAssoc(trace_frame_id=1, leaf_id=1, trace_length=0), TraceFrameLeafAssoc(trace_frame_id=2, leaf_id=1, trace_length=1), TraceFrameLeafAssoc(trace_frame_id=3, leaf_id=1, trace_length=0), TraceFrameLeafAssoc(trace_frame_id=4, leaf_id=2, trace_length=0), ] with self.db.make_session() as session: session.add(run) session.add(issue) session.add(issue_instance) self._add_to_session(session, messages) self._add_to_session(session, trace_frames) self._add_to_session(session, assocs) session.commit() self.interactive.setup() self.interactive.set_issue(1) self._clear_stdout() self.interactive.prev_cursor_location() self.assertEqual( self.stdout.getvalue().split("\n"), [ " [branches] [callable] [port] [location]", " --> + 2 leaf source file.py:1|1|1", " call1 root file.py:1|2|3", " leaf sink file.py:1|2|2", "", ], ) self._clear_stdout() self.interactive.branch(1) self.assertEqual( self.stdout.getvalue().split("\n"), [ " [branches] [callable] [port] [location]", " leaf source file.py:1|1|1", " --> + 2 prev_call result file.py:1|1|1", " call1 root file.py:1|2|3", " leaf sink file.py:1|2|2", "", ], ) self._clear_stdout() self.interactive.expand() output = self.stdout.getvalue().strip() self.assertIn("[*] prev_call : result", output) self.assertIn(" [1 hops: source1]", output)