Skip to content

Commit

Permalink
Add dataflowAPI/slicing.h
Browse files Browse the repository at this point in the history
  • Loading branch information
hainest committed Dec 27, 2023
1 parent 742bb4a commit b5d533d
Show file tree
Hide file tree
Showing 2 changed files with 277 additions and 18 deletions.
21 changes: 3 additions & 18 deletions dataflowAPI/h/slicing.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,7 @@ class Slicer {
typedef std::pair<InstructionAPI::Instruction, Address> InsnInstance;
typedef std::vector<InsnInstance> InsnVec;

// An instruction cache to avoid redundant instruction decoding.
// A user can optionaly provide a cache shared by multiple slicers.
// The cache is keyed with basic block starting address.
// Instruction cache. The key is the basic block starting address.
typedef dyn_hash_map<Address, InsnVec> InsnCache;

DATAFLOW_EXPORT Slicer(AssignmentPtr a,
Expand Down Expand Up @@ -191,7 +189,6 @@ class Slicer {
func(f), block(NULL), stackDepth(depth) {}
};

// This should be sufficient...
typedef std::deque<ContextElement> Context;


Expand All @@ -217,15 +214,7 @@ class Slicer {
Location() : func(NULL), block(NULL), fwd(true) {}
};

// Describes an abstract region, a minimal context
// (block and function), and the assignment that
// relates to that region (uses or defines it,
// depending on slice direction)
//
// A slice is composed of Elements; SliceFrames
// keep a list of the currently active elements
// that are at the `leading edge' of the
// under-construction slice
// A slice is composed of abstract regions called Elements
struct DATAFLOW_EXPORT Element {
Element(ParseAPI::Block * b,
ParseAPI::Function * f,
Expand Down Expand Up @@ -318,13 +307,9 @@ class Slicer {
DATAFLOW_EXPORT virtual ~Predicates() {}

// Callback function when adding a new node to the slice.
// Return true if we want to continue slicing
DATAFLOW_EXPORT virtual bool addNodeCallback(AssignmentPtr,
std::set<ParseAPI::Edge*> &) { return true;}
// Callback function after we have added new a node and corresponding new edges to the slice.
// This function allows users to inspect the current slice graph and determine which abslocs
// need further slicing and which abslocs are no longer interesting, by modifying the current
// SliceFrame.
// Callback function after adding a new node and corresponding new edges to the slice.
DATAFLOW_EXPORT virtual bool modifyCurrentFrame(SliceFrame &, GraphPtr, Slicer*) {return true;}
DATAFLOW_EXPORT virtual bool ignoreEdge(ParseAPI::Edge*) { return false;}
DATAFLOW_EXPORT Predicates() : clearCache(false), controlFlowDep(false) {}
Expand Down
274 changes: 274 additions & 0 deletions docs/dataflowAPI/public/slicing.h.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
.. _`sec:slicing.h`:

slicing.h
#########

.. cpp:namespace:: Dyninst

The slicing algorithm starts with a user provided Assignment
and generates a graph as the slicing results. A slice is represented as a
:cpp:class:`Dyninst::Graph` where the nodes are individual assignments that
affect the starting assignment (backward slicing) or are affected by the starting
assignment (forward slicing). The edges in the graph are directed and represent either
dataflow dependencies or controlflow dependencies.

Callback functions are provided and allow control over when to stop
slicing. In particular, :cpp:class:`Slicer::Predicates` contains a
collection of callback functions that can control the specific
behaviors of the slicer. Users can extend this class to provide customized
stopping criteria for the slicer.

.. cpp:type:: boost::shared_ptr<Assignment> AssignmentPtr
.. cpp:type:: boost::shared_ptr<Graph> GraphPtr
.. cpp:type:: boost::shared_ptr<InstructionAPI::Instruction> InstructionPtr

.. cpp:class:: Slicer

**An interface for performing forward and backward slicing**

.. cpp:type:: std::pair<InstructionAPI::Instruction, Address> InsnInstance

An instruction and its associated address.

.. cpp:type:: std::vector<InsnInstance> InsnVec

A collection of :cpp:type:`InsnInstance`\ s encountered during slicing.

.. cpp:type:: std::deque<ContextElement> Context

A collection of :cpp:class:`ContextElement`\ s that creates the code region for slicing.

.. cpp:type:: dyn_hash_map<Address, InsnVec> InsnCache

An instruction cache to avoid redundant instruction decoding.
A user can optionally provide a cache shared by multiple slicers.
The cache key is the starting address of the basic block.

.. cpp:member:: std::set<ParseAPI::Edge *> visitedEdges

A set of edges that have been visited during slicing that can be used
for external users to figure out which part of code has been analyzed.

.. cpp:function:: Slicer(AssignmentPtr a, ParseAPI::Block *block, ParseAPI::Function *func, \
bool cache = true, bool stackAnalysis = true)

Creates a Slicer to perform forward or backward slicing starting at the assignment ``a`` in the basic
block ``block`` contained in ``func``. When ``cache`` is ``true``, conversions from instructions to
assignments are cached for future use. When ``stackAnalysis`` is ``true``, stack analysis is used
to distinguish stack variables.

.. cpp:function:: Slicer(AssignmentPtr a, ParseAPI::Block *block, ParseAPI::Function *func, AssignmentConverter *ac)

Creates a Slicer to perform forward or backward slicing starting at the assignment ``a`` in the basic
block ``block`` contained in ``func`` using the converter ``ac``.

.. cpp:function:: Slicer(AssignmentPtr a, ParseAPI::Block *block, ParseAPI::Function *func, AssignmentConverter *ac, InsnCache *c)

Creates a Slicer to perform forward or backward slicing starting at the assignment ``a`` in the basic
block ``block`` contained in ``func`` using the converter ``ac``. Instructions discovered during slicing
are stored in ``c``.

.. cpp:function:: static bool isWidenNode(Node::Ptr n)

Checks if ``n`` is a :cpp:class:`SliceNode` that has been assigned to.

.. cpp:function:: GraphPtr forwardSlice(Predicates &predicates)
.. cpp:function:: GraphPtr backwardSlice(Predicates &predicates)

Perform forward or backward slicing and use ``predicates`` to control
the stopping criteria and return the slicing results as a graph

.. cpp:function:: void getInsnsBackward(Location &loc)

Retrieve the location of the previous instruction encountered during slicing.

.. cpp:struct:: Slicer::ContextElement

**Description of an area of code under scrutiny for slicing**

.. cpp:member:: ParseAPI::Function *func

We can implicitly find the callsite given a block, since calls end blocks. It's easier to look up
the successor this way than with an address.

.. cpp:member:: ParseAPI::Block *block

If non-NULL this must be an internal context element, since we have an active call site.

.. cpp:member:: int stackDepth

To enter or leave a function we must be able to map corresponding abstract regions.
In particular, we need to know the depth of the stack in the caller.

.. cpp:function:: ContextElement(ParseAPI::Function *f)

Creates a context associated with ``f``.

.. cpp:function:: ContextElement(ParseAPI::Function *f, long depth)

Creates a context associated with ``f`` with a caller stack depth of ``depth``.

.. cpp:struct:: Slicer::Location

**A description of the current location in the slicing search**

.. cpp:function:: Location(ParseAPI::Function *f, ParseAPI::Block *b)

Creates a location inside of the block ``b`` contained in the function ``f``.

.. cpp:member:: ParseAPI::Function *func

Function that contains :cpp:member:`block`.

.. cpp:member:: ParseAPI::Block *block

The current block.

.. cpp:member:: InsnVec::iterator current
.. cpp:member:: InsnVec::iterator end

Current and last instructions in :cpp:member:`block`.

.. cpp:member:: bool fwd

Slicing direction. ``true`` indicates forward slicing.

.. cpp:member:: InsnVec::reverse_iterator rcurrent
.. cpp:member:: InsnVec::reverse_iterator rend

Same as :cpp:member:`current` and :cpp:member:`end` but in the reverse direction.

.. cpp:function:: Address addr() const

Returns the current address of the instruction being examined.

.. cpp:struct:: Slicer::Element

**The features of a slice**

.. cpp:function:: Element(ParseAPI::Block *b, ParseAPI::Function *f, AbsRegion const &r, Assignment::Ptr p)

Creates an element describing the abstract region ``r`` with a minimal context (the block ``b`` contained in the
function ``f``). The assignment ``p`` relates to that region (uses or defines it, depending on slice direction).

.. cpp:struct:: SliceFrame

**State for recursive slicing**

It is a context/location pair and a list of AbsRegions that are being searched for. SliceFrames keep a list of
the currently active elements that are at the 'leading edge' of the under-construction slice.

.. cpp:function:: SliceFrame()

Creates an empty, but valid, SliceFrame.

.. cpp:function:: SliceFrame(Location const &l, Context const &c)

Creates a SliceFrame starting at ``l`` in the context ``c``.

.. cpp:function:: SliceFrame(bool v)

Creates an empty SliceFrame with validity ``v``.

.. cpp:member:: std::map<AbsRegion, std::vector<Element>> active

Active slice nodes -- describe regions that are currently under scrutiny

.. cpp:function:: Address addr() const

Returns the address of the current location.

.. cpp:class:: Slicer::Predicates

**Stopping criteria of slicing**

Users can extend this class to control slicing in various situations such as
performing inter-procedural slicing, searching for controlflow dependencies,
stopping slicing after discovering certain assignments. A set of callback
functions are provided to allow dynamic control over the behavior of the
:cpp:class:`Slicer`.

.. cpp:function:: Predicates()

Constructs a default predicate that only searches for intraprocedural dataflow dependencies.

.. cpp:type:: std::pair<ParseAPI::Function *, int> StackDepth_t

Stack depth of a function.

.. cpp:type:: std::stack<StackDepth_t> CallStack_t

A collection of :cpp:type:`StackDepth_t` representing a complete call stack.

.. cpp:function:: bool searchForControlFlowDep()

Checks if this predicate searches for controlflow dependencies.

.. cpp:function:: void setSearchForControlFlowDep(bool cfd)

Enables or disables searching for controlflow dependencies.

.. cpp:function:: virtual bool widenAtPoint(AssignmentPtr)

The default behavior is to not widen.

.. cpp:function:: virtual bool endAtPoint(AssignmentPtr)

Returns ``true`` if searching for this assignment should stop.

In backward slicing, this function is invoked for every matched assignment.

The default behavior is to stop.

.. cpp:function:: virtual bool followCall(ParseAPI::Function * callee, CallStack_t & cs, AbsRegion argument)

Checks if the slicer will follow the *direct* call to ``callee``.

The callstack leading to the current callsite is stored in ``cs``. The slice in the callee is carried out
with respect to the variable in ``argument``.

.. Note:: Dyninst does not currently try to resolve indirect calls, so the slicer will NOT call this when examining an indirect callsite.

.. cpp:function:: virtual std::vector<ParseAPI::Function *> followCallBackward(ParseAPI::Block * caller, CallStack_t & cs, AbsRegion argument)

Returns the callers to follow during slicing starting at the block ``caller``. The callstack leading to the current location
is provided in ``cs``. The slice with the caller function is carried out with respect to the variable ``argument``.

This predicate is invoked when the slicer reaches the entry of a
function in the case of backward slicing or reaches a return instruction
in the case of forward slicing.

By default, there are no callers to follow.

.. cpp:function:: virtual bool addPredecessor(AbsRegion reg)

Checks if searching for dependencies for the region ``reg`` should continue.

.. Note:: This callback is invoked for every matching assignment during backward slicing.

The default is to continue searching.

.. cpp:function:: virtual bool addNodeCallback(AssignmentPtr assign, std::set<ParseAPI::Edge*> &visited)

Checks if slicing should continue when a node is added to the slice. The newly-added assignment, ``assign``,
and the set of visited controlflow edges, ``visited``, are provided.

.. Note:: This callback is only invoked during backward slicing.

The default is to continue.

.. cpp:function:: virtual bool modifyCurrentFrame(SliceFrame &f, GraphPtr g, Slicer *s)

Allows inspection of the current slice graph, ``p``, being inspected by the slicer ``s`` to determine
which :ref:`Abstract Locations <sec:dataflow-abstractions>` need further slicing by modifying the current
SliceFrame, ``f``, after adding a new node and corresponding new edges to the slice.

The default is to continue slicing.

.. cpp:function:: virtual bool ignoreEdge(ParseAPI::Edge *e)

Checks if the edge ``e`` should be ignored during slicing.

.. Note:: This callback is only invoked during backward slicing.

The default is to not ignore the edge.

0 comments on commit b5d533d

Please sign in to comment.