<commit_before>import itertools
import traceback

import vqt.tree as vq_tree
import vqt.saveable as vq_save
import envi.qt.memory as e_q_memory
import envi.qt.memcanvas as e_q_memcanvas
import vivisect.qt.ctxmenu as v_q_ctxmenu
import vivisect.tools.graphutil as viv_graph
import vivisect.symboliks.common as viv_sym_common
import vivisect.symboliks.effects as viv_sym_effects
import vivisect.symboliks.analysis as viv_sym_analysis
import vivisect.symboliks.expression as viv_sym_expression
import vivisect.symboliks.constraints as viv_sym_constraints

from PyQt4 import QtGui,QtCore

from vqt.main import *
from vqt.basics import *
from vivisect.const import *

class VivSymbolikPathsModel(vq_tree.VQTreeModel):
    columns = ('Path','Effect Count')

class VivSymbolikPathsView(vq_tree.VQTreeView):

    pathSelected = QtCore.pyqtSignal( object, object )

    def __init__(self, vw, parent=None):
        vq_tree.VQTreeView.__init__(self, parent=parent)
        self.setModel( VivSymbolikPathsModel( parent=self ) )

    def loadSymbolikPaths(self, paths):
        model = VivSymbolikPathsModel( parent=self )
        for i, (emu,effects) in enumerate(paths):
            model.append( (str(i), len(effects), emu, effects) )
        self.setModel(model)

    def selectionChanged(self, selected, unselected):

        indexes = selected.indexes()
        if indexes:
            index = indexes[0]
            rowdata = index.internalPointer().rowdata
            emu = rowdata[-2]
            path = rowdata[-1]
            self.pathSelected.emit(emu,path)

class VivSymbolikFuncPane(e_q_memory.EnviNavMixin, vq_save.SaveableWidget, QtGui.QWidget):

    viewidx = itertools.count()

    def __init__(self, vw, parent=None):
        self.vw = vw
        self.fva = None
        self.vwgui = vw.getVivGui()

        self.curemu = None
        self.cureffects = None

        self.symctx = viv_sym_analysis.getSymbolikAnalysisContext(vw)
        self.symexpr = viv_sym_expression.SymbolikExpressionParser(defwidth=vw.psize)

        if self.symctx == None:
            raise Exception('No Symboliks For: %s (yet)' % vw.getMeta('Architecture'))

        self.symctx.consolve = True

        QtGui.QWidget.__init__(self, parent=parent)
        e_q_memory.EnviNavMixin.__init__(self)
        self.setEnviNavName('Symboliks%d' % self.viewidx.next())

        self.exprtext = QtGui.QLineEdit(parent=self)
        self.constraintext = QtGui.QLineEdit(parent=self)
        self.alleffs = QtGui.QCheckBox('all effects', parent=self)
        self.alleffs.stateChanged.connect(self.rendSymbolikPath)

        self.pathview = VivSymbolikPathsView(vw, parent=self)
        self.memcanvas = e_q_memcanvas.VQMemoryCanvas(vw, syms=vw, parent=self)

        self.pathview.pathSelected.connect(self.symPathSelected)
        self.exprtext.returnPressed.connect(self.renderSymbolikPaths)
        self.constraintext.returnPressed.connect(self.renderSymbolikPaths)

        fvalabel = QtGui.QLabel(QtCore.QString("Function VA:"), parent=self)
        inccblabel = QtGui.QLabel(QtCore.QString("Must Include VA:"), parent=self)
        navbox = HBox(fvalabel, self.exprtext, inccblabel, self.constraintext, self.alleffs)

        mainbox = VBox()
        mainbox.addLayout(navbox)
        mainbox.addWidget(self.pathview)
        mainbox.addWidget(self.memcanvas)
        self.setLayout(mainbox)
        self.updateWindowTitle()

    def updateWindowTitle(self):
        ename = self.getEnviNavName()
        expr = str(self.exprtext.text())
        self.setWindowTitle('%s: %s' % (ename,expr))

    def enviNavGoto(self, expr, sizeexpr=None):
        self.exprtext.setText(expr)
        self.renderSymbolikPaths()
        self.updateWindowTitle()

    def vqGetSaveState(self):
        return { 'expr':str(self.exprtext.text()), }

    def vqSetSaveState(self, state):
        self.exprtext.setText( state.get('expr','') )
        self.renderSymbolikPaths()

    def renderSymbolikPaths(self):
        try:

            self.memcanvas.clearCanvas()
            exprtxt = str(self.exprtext.text())
            if not exprtxt:
                return

            exprparts = exprtxt.split(';')
            expr = exprparts[0]

            preeff = []
            precons = []
            for e in exprparts[1:]:
                s = self.symexpr.parseExpression(e)

                if isinstance(s, viv_sym_constraints.Constraint):
                    precons.append(s)
                    continue

                if isinstance(s, viv_sym_effects.SymbolikEffect):
                    preeff.append(s)
                    continue

                raise Exception('Unhandled Symbolik Expression: %s' % e)

            self.symctx.setSymPreEffects(preeff)
            self.symctx.setSymPreConstraints(precons)

            va = self.vw.parseExpression(expr)
            self.fva = self.vw.getFunction(va)
            if self.fva == None:
                raise Exception('Invalid Address: 0x%.8x' % va )

            # check the constraints
            codepaths = None
            codegraph = self.symctx.getSymbolikGraph(self.fva)
            cexpr = str(self.constraintext.text())
            if cexpr:
                cva = self.vw.parseExpression(cexpr)
                ccb = self.vw.getCodeBlock(cva)
                if ccb != None and ccb in self.vw.getFunctionBlocks(self.fva):
                    #FIXME: allow the GUI-setting of loopcnt, instead of hard-coding
                    codepaths = viv_graph.getCodePathsThru(codegraph, ccb[0], loopcnt=3)   
                    paths = self.symctx.getSymbolikPaths(self.fva, paths=codepaths, graph=codegraph, maxpath=100)

            if codepaths == None:
                codepaths = viv_graph.getCodePaths(codegraph, 3)
                paths = self.symctx.walkSymbolikPaths(self.fva, maxpath=100)
                    
            self.pathview.loadSymbolikPaths(paths)

        except Exception, e:
            traceback.print_exc()
            self.memcanvas.addText('ERROR: %s' % e)

    def addVivNames(self, path, symobj, ctx):

        emu,symctx = ctx

        width = emu.__width__ # FIXME factory thing?
<commit_msg>SymboliksView get a "loop count" spinbox for "Must Include" options.<commit_after>import itertools
import traceback

import vqt.tree as vq_tree
import vqt.saveable as vq_save
import envi.qt.memory as e_q_memory
import envi.qt.memcanvas as e_q_memcanvas
import vivisect.qt.ctxmenu as v_q_ctxmenu
import vivisect.tools.graphutil as viv_graph
import vivisect.symboliks.common as viv_sym_common
import vivisect.symboliks.effects as viv_sym_effects
import vivisect.symboliks.analysis as viv_sym_analysis
import vivisect.symboliks.expression as viv_sym_expression
import vivisect.symboliks.constraints as viv_sym_constraints

from PyQt4 import QtGui,QtCore

from vqt.main import *
from vqt.basics import *
from vivisect.const import *

class VivSymbolikPathsModel(vq_tree.VQTreeModel):
    columns = ('Path','Effect Count')

class VivSymbolikPathsView(vq_tree.VQTreeView):

    pathSelected = QtCore.pyqtSignal( object, object )

    def __init__(self, vw, parent=None):
        vq_tree.VQTreeView.__init__(self, parent=parent)
        self.setModel( VivSymbolikPathsModel( parent=self ) )

    def loadSymbolikPaths(self, paths):
        model = VivSymbolikPathsModel( parent=self )
        for i, (emu,effects) in enumerate(paths):
            model.append( (str(i), len(effects), emu, effects) )
        self.setModel(model)

    def selectionChanged(self, selected, unselected):

        indexes = selected.indexes()
        if indexes:
            index = indexes[0]
            rowdata = index.internalPointer().rowdata
            emu = rowdata[-2]
            path = rowdata[-1]
            self.pathSelected.emit(emu,path)

class VivSymbolikFuncPane(e_q_memory.EnviNavMixin, vq_save.SaveableWidget, QtGui.QWidget):

    viewidx = itertools.count()

    def __init__(self, vw, parent=None):
        self.vw = vw
        self.fva = None
        self.vwgui = vw.getVivGui()

        self.curemu = None
        self.cureffects = None

        self.symctx = viv_sym_analysis.getSymbolikAnalysisContext(vw)
        self.symexpr = viv_sym_expression.SymbolikExpressionParser(defwidth=vw.psize)

        if self.symctx == None:
            raise Exception('No Symboliks For: %s (yet)' % vw.getMeta('Architecture'))

        self.symctx.consolve = True

        QtGui.QWidget.__init__(self, parent=parent)
        e_q_memory.EnviNavMixin.__init__(self)
        self.setEnviNavName('Symboliks%d' % self.viewidx.next())

        self.exprtext = QtGui.QLineEdit(parent=self)
        self.constraintext = QtGui.QLineEdit(parent=self)
        self.alleffs = QtGui.QCheckBox('all effects', parent=self)
        self.alleffs.stateChanged.connect(self.rendSymbolikPath)

        self.loop_count  = QtGui.QSpinBox(parent=self)
        self.loop_count.setPrefix('Max Loops:')
        
        self.pathview = VivSymbolikPathsView(vw, parent=self)
        self.memcanvas = e_q_memcanvas.VQMemoryCanvas(vw, syms=vw, parent=self)

        self.pathview.pathSelected.connect(self.symPathSelected)
        self.exprtext.returnPressed.connect(self.renderSymbolikPaths)
        self.constraintext.returnPressed.connect(self.renderSymbolikPaths)

        fvalabel = QtGui.QLabel(QtCore.QString("Function VA:"), parent=self)
        inccblabel = QtGui.QLabel(QtCore.QString("Must Include VA:"), parent=self)
        navbox = HBox(fvalabel, self.exprtext, inccblabel, self.constraintext, self.loop_count, self.alleffs)

        mainbox = VBox()
        mainbox.addLayout(navbox)
        mainbox.addWidget(self.pathview)
        mainbox.addWidget(self.memcanvas)
        self.setLayout(mainbox)
        self.updateWindowTitle()

    def updateWindowTitle(self):
        ename = self.getEnviNavName()
        expr = str(self.exprtext.text())
        self.setWindowTitle('%s: %s' % (ename,expr))

    def enviNavGoto(self, expr, sizeexpr=None):
        self.exprtext.setText(expr)
        self.renderSymbolikPaths()
        self.updateWindowTitle()

    def vqGetSaveState(self):
        return { 'expr':str(self.exprtext.text()), }

    def vqSetSaveState(self, state):
        self.exprtext.setText( state.get('expr','') )
        self.renderSymbolikPaths()

    def renderSymbolikPaths(self):
        try:

            self.memcanvas.clearCanvas()
            exprtxt = str(self.exprtext.text())
            if not exprtxt:
                return

            exprparts = exprtxt.split(';')
            expr = exprparts[0]

            preeff = []
            precons = []
            for e in exprparts[1:]:
                s = self.symexpr.parseExpression(e)

                if isinstance(s, viv_sym_constraints.Constraint):
                    precons.append(s)
                    continue

                if isinstance(s, viv_sym_effects.SymbolikEffect):
                    preeff.append(s)
                    continue

                raise Exception('Unhandled Symbolik Expression: %s' % e)

            self.symctx.setSymPreEffects(preeff)
            self.symctx.setSymPreConstraints(precons)

            va = self.vw.parseExpression(expr)
            self.fva = self.vw.getFunction(va)
            if self.fva == None:
                raise Exception('Invalid Address: 0x%.8x' % va )

            # check the constraints
            # FIXME: add ability to page through more than just the first 100 paths.  requires 
            #   storing the codegraph and codepaths
            codepaths = None
            codegraph = self.symctx.getSymbolikGraph(self.fva)
            cexpr = str(self.constraintext.text())
            if cexpr:
                cva = self.vw.parseExpression(cexpr)
                ccb = self.vw.getCodeBlock(cva)

                if ccb != None and ccb in self.vw.getFunctionBlocks(self.fva):
                    #FIXME: allow the GUI-setting of loopcnt, instead of hard-coding
                    loopcnt = self.loop_count.value()
                    codepaths = viv_graph.getCodePathsThru(codegraph, ccb[0], loopcnt=loopcnt)   
                    paths = self.symctx.getSymbolikPaths(self.fva, paths=codepaths, graph=codegraph, maxpath=100)

            if codepaths == None:
                paths = self.symctx.walkSymbolikPaths(self.fva, maxpath=100)
                    
            self.pathview.loadSymbolikPaths(paths)

        except Exception, e:
            traceback.print_exc()
            self.memcanvas.addText('ERROR: %s' % e)

    def addVivNames(self, path, symobj, ctx):

        emu,symctx = ctx

        width = emu.__width__ # FIXME factory thing?

{'fraction_non_alphanumeric': 0.09781576448243115, 'fraction_numerical': 0.002769863880974992, 'mean_word_length': 2.4764786795048144, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6579583', 'n_tokens_mistral': 4008, 'n_tokens_neox': 3858, 'n_words': 720}