All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opalj.ai.ConsoleTracer.scala Maven / Gradle / Ivy

The newest version!
/* BSD 2-Clause License:
 * Copyright (c) 2009 - 2017
 * Software Technology Group
 * Department of Computer Science
 * Technische Universität Darmstadt
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package org.opalj
package ai

import org.opalj.collection.immutable.{Chain ⇒ List}
import org.opalj.br.Code
import org.opalj.br.instructions.Instruction
import org.opalj.ai.domain.TheCode

/**
 * A tracer that prints out a trace's results on the console.
 *
 * @author Michael Eichberg
 */
trait ConsoleTracer extends AITracer { tracer ⇒

    import Console._

    val printOIDs: Boolean = false

    def oidString(value: Object): String = s"[#${System.identityHashCode(value).toHexString}]"

    def toStringWithOID(value: Object): String = s"$value ${oidString(value)}"

    private def localsToString(domain: Domain)(locals: domain.Locals): String = {
        if (locals eq null)
            "\tlocals: not available (null);\n"
        else
            locals.zipWithIndex.map { vi ⇒
                val (v, i) = vi
                i+":"+(
                    if (v == null)
                        correctIndent("-", false)
                    else
                        correctIndent(v, printOIDs)
                )
            }.mkString("\tlocals:\n\t\t", "\n\t\t", "\n")
    }

    def initialLocals(
        domain: Domain
    )(
        locals: domain.Locals
    ): Unit = {
        println(localsToString(domain)(locals))
    }

    private def correctIndent(value: Object, printOIDs: Boolean): String = {
        if (value eq null)
            return "";

        def toString(value: Object) =
            value.toString.replaceAll("\n\t", "\n\t\t\t").replaceAll("\n\\)", "\n\t\t)")

        def toStringWithOID(value: Object) = toString(value)+" "+oidString(value)

        if (printOIDs) {
            value match {
                case rv: IsReferenceValue[_] if rv.allValues.size > 1 ⇒
                    val values = rv.allValues
                    val t =
                        if (rv.isInstanceOf[domain.l1.ReferenceValues#ReferenceValue])
                            s";t=${rv.asInstanceOf[domain.l1.ReferenceValues#ReferenceValue].t}"
                        else
                            ""
                    values.map(toStringWithOID(_)).mkString("OneOf["+values.size+"](", ",", ")") +
                        rv.upperTypeBound.map(_.toJava).mkString(";lutb=", " with ", ";") +
                        s"isPrecise=${rv.isPrecise};isNull=${rv.isNull}$t "+
                        oidString(rv)
                case _ ⇒
                    toStringWithOID(value)
            }
        } else {
            toString(value)
        }
    }

    private def line(domain: Domain, pc: PC): String = {
        domain match {
            case d: TheCode ⇒ d.code.lineNumber(pc).map("[line="+_+"]").getOrElse("")
            case _          ⇒ ""
        }
    }

    override def instructionEvalution(
        domain: Domain
    )(
        pc:          PC,
        instruction: Instruction,
        operands:    domain.Operands,
        locals:      domain.Locals
    ): Unit = {

        val os =
            if (operands eq null)
                "\toperands: not available (null);\n"
            else {
                val os = operands.map { o ⇒
                    correctIndent(o, printOIDs)
                }
                if (os.isEmpty)
                    "\toperands ;\n"
                else
                    os.mkString("\toperands:\n\t\t", "\n\t\t", "\n\t;\n")
            }

        val ls = localsToString(domain)(locals)

        val ps = {
            val ps = domain.properties(pc)
            if ((ps eq null) || ps == None)
                ""
            else {
                s"\tproperties: ${ps.get}\n"
            }
        }

        println(
            pc + line(domain, pc)+":"+instruction.toString(pc)+
                " [\n"+os + ls + ps+"\t]"
        )
    }

    override def continuingInterpretation(
        code:   Code,
        domain: Domain
    )(
        initialWorkList:                  List[PC],
        alreadyEvaluated:                 List[PC],
        operandsArray:                    domain.OperandsArray,
        localsArray:                      domain.LocalsArray,
        memoryLayoutBeforeSubroutineCall: List[(PC, domain.OperandsArray, domain.LocalsArray)]
    ): Unit = {

        println(BLACK_B + WHITE+"Starting Code Analysis"+RESET)
        println("Number of registers:      "+code.maxLocals)
        println("Size of operand stack:    "+code.maxStack)
        println("PCs where paths join:     "+code.cfJoins.mkString(", "))
        //println("Program counters:         "+code.programCounters.mkString(", "))
    }

    override def rescheduled(
        domain: Domain
    )(
        sourcePC:                 PC,
        targetPC:                 PC,
        isExceptionalControlFlow: Boolean,
        worklist:                 List[PC]
    ): Unit = {
        println(
            CYAN_B + RED+"rescheduled the evaluation of instruction: "+
                targetPC + line(domain, targetPC) + RESET+
                "; new worklist: "+worklist.mkString(", ")
        )
    }

    override def flow(
        domain: Domain
    )(
        currentPC:                PC,
        targetPC:                 PC,
        isExceptionalControlFlow: Boolean
    ): Unit = { /* ignored */ }

    override def deadLocalVariable(domain: Domain)(pc: PC, lvIndex: Int): Unit = {
        println(
            pc.toString + line(domain, pc).toString+":"+
                Console.BLACK_B + Console.WHITE + s"local variable $lvIndex is dead"
        )
    }

    override def noFlow(domain: Domain)(currentPC: PC, targetPC: PC): Unit = {
        println(Console.RED_B + Console.YELLOW+
            "did not schedule the interpretation of instruction "+
            targetPC + line(domain, targetPC)+
            "; the abstract state didn't change"+Console.RESET)
    }

    override def join(
        domain: Domain
    )(
        pc:            PC,
        thisOperands:  domain.Operands,
        thisLocals:    domain.Locals,
        otherOperands: domain.Operands,
        otherLocals:   domain.Locals,
        result:        Update[(domain.Operands, domain.Locals)]
    ): Unit = {

        print(Console.BLUE + pc + line(domain, pc)+": JOIN: ")
        result match {
            case NoUpdate ⇒ println("no changes")
            case u @ SomeUpdate((updatedOperands, updatedLocals)) ⇒
                println(u.updateType)
                println(
                    thisOperands.
                        zip(otherOperands).
                        zip(updatedOperands).
                        map { v ⇒
                            val ((thisOp, thatOp), updatedOp) = v
                            val s = if (thisOp eq updatedOp)
                                "✓ "+Console.GREEN + updatedOp.toString
                            else {
                                "given "+correctIndent(thisOp, printOIDs)+"\n\t\t join "+
                                    correctIndent(thatOp, printOIDs)+"\n\t\t   => "+
                                    correctIndent(updatedOp, printOIDs)
                            }
                            s + Console.RESET
                        }.
                        mkString("\tOperands:\n\t\t", "\n\t\t", "")
                )
                println(
                    thisLocals.
                        zip(otherLocals).
                        zip(updatedLocals.iterator).map(v ⇒ (v._1._1, v._1._2, v._2)).
                        zipWithIndex.map(v ⇒ (v._2, v._1._1, v._1._2, v._1._3)).
                        filterNot(v ⇒ (v._2 eq null) && (v._3 eq null)).
                        map(v ⇒
                            v._1 + {
                                if (v._2 == v._3)
                                    if (v._2 eq v._3)
                                        Console.GREEN+": ✓ "
                                    else
                                        Console.YELLOW+":(✓) "+
                                            oidString(v._2)+
                                            " join "+
                                            oidString(v._3)+
                                            " => "
                                else {
                                    ":\n\t\t   given "+correctIndent(v._2, printOIDs)+
                                        "\n\t\t    join "+correctIndent(v._3, printOIDs)+
                                        "\n\t\t      => "
                                }
                            } +
                                correctIndent(v._4, printOIDs)).
                        mkString("\tLocals:\n\t\t", "\n\t\t"+Console.BLUE, Console.BLUE)
                )
        }
        println(Console.RESET)
    }

    override def establishedConstraint(
        domain: Domain
    )(
        pc:          PC,
        effectivePC: PC,
        operands:    domain.Operands,
        locals:      domain.Locals,
        newOperands: domain.Operands,
        newLocals:   domain.Locals
    ): Unit = {
        println(
            pc + line(domain, pc)+":"+
                YELLOW_B + BLUE+"Establishing Constraint w.r.t. "+
                effectivePC + line(domain, effectivePC)+":"
        )
        val changedOperands = operands.zip(newOperands).filter(ops ⇒ ops._1 ne ops._2)
        if (changedOperands.nonEmpty) {
            println(YELLOW_B + BLUE+"\tUpdated Operands:")
            changedOperands.foreach(ops ⇒ print(YELLOW_B + BLUE+"\t\t"+ops._1+" => "+ops._2+"\n"))
        }
        val changedLocals =
            locals.zip(newLocals).zipWithIndex.map(localsWithIdx ⇒
                (localsWithIdx._1._1, localsWithIdx._1._2, localsWithIdx._2)).
                filter(ops ⇒ ops._1 ne ops._2)
        if (changedLocals.hasNext) {
            println(YELLOW_B + BLUE+"\tUpdated Locals:")
            changedLocals.foreach(locals ⇒
                print(YELLOW_B + BLUE+"\t\t"+locals._3+":"+
                    toStringWithOID(locals._1)+" => "+
                    toStringWithOID(locals._2)+"\n"))
        }
        println(YELLOW_B + BLUE+"\tDone"+RESET)

    }

    override def abruptMethodExecution(
        domain: Domain
    )(
        pc:        Int,
        exception: domain.ExceptionValue
    ): Unit = {
        println(
            BOLD + RED + pc + line(domain, pc)+
                ":RETURN FROM METHOD DUE TO UNHANDLED EXCEPTION: "+exception +
                RESET
        )
    }

    override def jumpToSubroutine(
        domain: Domain
    )(
        pc: PC, target: PC, nestingLevel: Int
    ): Unit = {
        import Console._
        println(
            pc + line(domain, pc)+":"+YELLOW_B + BOLD+
                "JUMP TO SUBROUTINE(Nesting level: "+nestingLevel+"): "+target +
                RESET
        )
    }

    override def returnFromSubroutine(
        domain: Domain
    )(
        pc:                     PC,
        returnAddress:          PC,
        subroutineInstructions: List[PC]
    ): Unit = {
        println(
            YELLOW_B + BOLD + pc + line(domain, pc)+
                ":RETURN FROM SUBROUTINE: target="+returnAddress+
                " : RESETTING : "+subroutineInstructions.mkString(", ") +
                RESET
        )
    }

    override def abruptSubroutineTermination(
        domain: Domain
    )(
        sourcePC: PC, targetPC: PC, jumpToSubroutineId: Int,
        terminatedSubroutinesCount: Int,
        oldWorklist:                List[PC],
        newWorklist:                List[PC]
    ): Unit = {
        println(
            RED_B + WHITE + sourcePC + line(domain, sourcePC)+
                ":ABRUPT RETURN FROM SUBROUTINE: target="+targetPC+
                " : number of terminated subroutines="+terminatedSubroutinesCount + RESET+
                "\n"+RED_B + WHITE+"\t\told worklist: "+oldWorklist.mkString(",") + RESET+
                "\n"+RED_B + WHITE+"\t\tnew worklist: "+newWorklist.mkString(",") + RESET
        )
    }

    /**
     * Called when a ret instruction is encountered.
     */
    override def ret(
        domain: Domain
    )(
        pc:            PC,
        returnAddress: PC,
        oldWorklist:   List[PC],
        newWorklist:   List[PC]
    ): Unit = {
        println(
            GREEN_B + BOLD + pc + line(domain, pc)+
                ":RET : target="+returnAddress+
                " : OLD_WORKLIST : "+oldWorklist.mkString(", ")+
                " : NEW_WORKLIST : "+newWorklist.mkString(", ") +
                RESET
        )
    }

    override def result(result: AIResult): Unit = { /*ignored*/ }

    override def domainMessage(
        domain: Domain,
        source: Class[_], typeID: String,
        pc: Option[PC], message: ⇒ String
    ): Unit = {
        val loc = pc.map(pc ⇒ s"$pc:").getOrElse("")
        println(
            s"$loc[Domain:${source.getSimpleName().split('$')(0)} - $typeID] $message"
        )
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy