Example #1
0
    def _verify_list_filter(self, filter: List, argument_name: str) -> None:
        # Check this because filter is user input
        if not isinstance(filter, list):
            raise UserError(f"'{argument_name}' should be a list.")

        if not filter:
            raise UserError(f"'{argument_name}' should be non-empty.")
Example #2
0
    def parents(self) -> None:
        self._verify_entrypoint_selected()
        current_trace_frame = self.trace_tuples[
            self.current_trace_frame_index
        ].trace_frame

        # Don't allow calling from the leaf node in a trace. Instead, call
        # parents() from the placeholder of the caller of the leaf node.
        if self._is_leaf(current_trace_frame):
            raise UserError("Try running from a non-leaf node.")

        with self.db.make_session() as session:
            parent_trace_frames = self._next_trace_frames(
                session, current_trace_frame, backwards=True
            )

        if len(parent_trace_frames) == 0:
            print(
                f"No parents calling [{current_trace_frame.callee} "
                f": {current_trace_frame.callee_port}]."
            )
            return

        parent_trace_frame = self._select_parent_trace_frame(parent_trace_frames)
        if not parent_trace_frame:
            return

        self._update_trace_tuples_new_parent(parent_trace_frame)
        self.trace()
Example #3
0
    def _verify_entrypoint_selected(self) -> None:
        assert self.current_issue_id == -1 or self.current_frame_id == -1

        if self.current_issue_id == -1 and self.current_frame_id == -1:
            raise UserError(
                "Use 'set_issue(ID)' or 'set_frame(ID)' to select an"
                " entrypoint first.")
Example #4
0
    def jump(self, selected_number: int) -> None:
        """Jump to a specific trace frame in a trace.

        Parameters:
            selected_number: int    the trace frame number from trace() output
        """
        self._verify_entrypoint_selected()
        if selected_number < 1 or selected_number > len(self.trace_tuples):
            raise UserError(
                "Trace frame number out of bounds "
                f"(expected 1-{len(self.trace_tuples)} but got {selected_number})."
            )

        self.current_trace_frame_index = selected_number - 1
        self.trace()
Example #5
0
    def _select_parent_trace_frame(
        self, parent_trace_frames: List[TraceFrame]
    ) -> Optional[TraceFrame]:
        for i, parent in enumerate(parent_trace_frames):
            print(f"[{i + 1}] {parent.caller} : {parent.caller_port}")

        try:
            parent_number = click.prompt(
                f"\nParent number (1-{len(parent_trace_frames)})", type=int
            )
            if parent_number < 1 or parent_number > len(parent_trace_frames):
                raise UserError("Out of bounds.")
            print()
            return parent_trace_frames[parent_number - 1]  # pyre-ignore
        except click.Abort:
            print("\nParent not selected.")
            pass
Example #6
0
    def branch(self, selected_number: int) -> None:
        """Selects a branch when there are multiple possible traces to follow.

        The trace output that follows includes the new branch and its children
        frames.

        Parameters:
            selected_number: int    branch number from expand() output
        """
        self._verify_entrypoint_selected()
        self._verify_multiple_branches()

        with self.db.make_session() as session:
            branches = self._get_trace_frame_branches(session)

            if selected_number < 1 or selected_number > len(branches):
                raise UserError(
                    "Branch number out of bounds "
                    f"(expected 1-{len(branches)} but got {selected_number})."
                )

            new_navigation = self._navigate_trace_frames(
                session, branches, selected_number - 1
            )

        new_trace_tuples = self._create_trace_tuples(new_navigation)

        if self._is_before_root():
            new_trace_tuples.reverse()
            self.trace_tuples = (
                new_trace_tuples
                + self.trace_tuples[self.current_trace_frame_index + 1 :]
            )

            # If length of prefix changes, it will change some indices
            trace_frame_index_delta = (
                len(new_navigation) - self.current_trace_frame_index - 1
            )
            self.current_trace_frame_index += trace_frame_index_delta
        else:
            self.trace_tuples = (
                self.trace_tuples[: self.current_trace_frame_index] + new_trace_tuples
            )

        self.trace()
Example #7
0
    def frames(
        self, *, callers: Optional[List[str]] = None, kind: Optional[TraceKind] = None
    ):
        """Display trace frames independent of the current issue.

        Parameters (all optional):
            callers: list[str]                  filter traces by this caller name
            kind: precondition|postcondition    the type of trace frames to show

        Sample usage:
            frames(callers=["module.function"], kind=postcondition)

        String filters support LIKE wildcards (%, _) from SQL:
            % matches anything (like .* in regex)
            _ matches 1 character (like . in regex)
        """
        with self.db.make_session() as session:
            query = session.query(TraceFrame).filter(
                TraceFrame.run_id == self.current_run_id
            )

            if callers is not None:
                self._verify_list_filter(callers, "callers")
                query = self._add_list_filter_to_query(
                    callers, query, TraceFrame.caller
                )

            if kind is not None:
                if kind not in {TraceKind.PRECONDITION, TraceKind.POSTCONDITION}:
                    raise UserError(
                        "Try 'frames(kind=postcondition)'"
                        " or 'frames(kind=precondition)'."
                    )
                query = query.filter(TraceFrame.kind == kind)

            trace_frames = (
                query.group_by(TraceFrame.id)
                .order_by(TraceFrame.caller, TraceFrame.callee)
                .all()
            )

            self._output_trace_frames(self._group_trace_frames(trace_frames))
Example #8
0
 def _verify_multiple_branches(self) -> None:
     current_trace_tuple = self.trace_tuples[self.current_trace_frame_index]
     if current_trace_tuple.branches < 2:
         raise UserError("This trace frame has no alternate branches to take.")