Beispiel #1
0
  private DUAAnalysis() {
    // prepare required collection and map
    List<SootMethod> allReachableMethods = ProgramFlowGraph.inst().getReachableAppMethods();
    Map<SootMethod, ReachableUsesDefs> methodsToReachUseDefs =
        dua.util.Util.convertToRUMap(ProgramFlowGraph.inst().getMethodToCFGMap());

    if (!Options.localDUAsOnly())
      computeFieldArrayObjDUAs(allReachableMethods, methodsToReachUseDefs);

    // goal: find inter-proc uses for each param of each method (reachable from entry)

    // ReachableUses already inited param to local real uses, and linked params to local call uses

    // iteratively:
    // for each method, add to each param all real uses of call uses for that param
    ArrayList<SootMethod> worklist = new ArrayList<SootMethod>(allReachableMethods);
    int propCount = 1;
    while (!worklist.isEmpty()) {
      System.out.println("Inter-procedural reachable uses to params iteration #" + propCount++);
      for (SootMethod m : (ArrayList<SootMethod>) worklist.clone()) {
        worklist.remove(m);

        ReachableUsesDefs ru = methodsToReachUseDefs.get(m);
        if (ru.propagateAllUsesDefsToParams(methodsToReachUseDefs)) {
          MethodTag mTag = (MethodTag) m.getTag(MethodTag.TAG_NAME);
          for (SootMethod mCaller : mTag.getCallerMethods()) {
            MethodTag mCallerTag = (MethodTag) mCaller.getTag(MethodTag.TAG_NAME);
            if (mCallerTag.isReachableFromEntry()) worklist.add(mCaller);
          }
        }
      }
    }

    for (SootMethod m : allReachableMethods) {
      ReachableUsesDefs ru = methodsToReachUseDefs.get(m);

      // DEBUG
      ru.dumpReachUsesDefs();

      ru.findLocalUsesDefsForDefs(methodsToReachUseDefs);
    }

    // for each def, create duas for local real uses and real uses of params in call uses
    // also, collect statistics
    // first, determine # interproc defs per use
    HashMap<Use, Integer> interProcDefsPerUse =
        new HashMap<Use, Integer>(); // maps use -> # interproc defs
    for (SootMethod m : allReachableMethods) {
      ReachableUsesDefs ru = methodsToReachUseDefs.get(m);
      HashMap<Def, HashMap<Use, ArrayList<Use>>> dus = ru.getDUs();
      for (Def def : dus.keySet()) {
        Map<Use, ArrayList<Use>> usesMap = dus.get(def);
        for (Use use : usesMap.keySet()) {
          // determine and update # of interproc defs for use
          Integer numInterDefs = interProcDefsPerUse.get(use);
          if (numInterDefs == null) {
            numInterDefs = 0;
            interProcDefsPerUse.put(use, numInterDefs); // just init entry in map
          }
        }
      }
    }

    // create local var DUAs
    int cDuas = 0, pDuas = 0, cIntraOnly = 0, cInterOnly = 0, pIntraOnly = 0, pInterOnly = 0;
    int numKills = 0, minKills = Integer.MAX_VALUE, maxKills = 0;
    int possibleKills = 0, subsDuasWithPossKill = 0;
    ArrayList<Integer> useCallDepthsCount = new ArrayList<Integer>();
    for (SootMethod m : allReachableMethods) { // methods are sorted
      if (verbose) System.out.print("Local-var duas for " + m + ": ");

      // build map stmt->idx
      ReachableUsesDefs ru = methodsToReachUseDefs.get(m);
      HashMap<Def, HashMap<Use, ArrayList<Use>>> dus = ru.getDUs();
      HashMap<Def, HashSet<Def>> dds = ru.getDDs();

      // determine subsumability and update stats for these DUAs / DDAs
      List<Def> sortedDefs = new ArrayList<Def>(dus.keySet());
      Collections.sort(sortedDefs, new DefComparator());
      for (Def def : sortedDefs) {
        HashSet<Def> defKills = dds.get(def);

        if (verbose) System.out.print(def + "={");

        Map<Use, ArrayList<Use>> realToLocalUsesMap = dus.get(def);
        List<Use> sortedUses = new ArrayList<Use>(realToLocalUsesMap.keySet());
        Collections.sort(sortedUses, new UseComparator());
        for (Use use : sortedUses) {
          // determine type of use (c or p; intra or inter)
          PUse pUse = (use instanceof PUse) ? (PUse) use : null;

          // determine D-U node order, taking use's src statement first, and tgt if puse
          CFGNode nDef = def.getN();
          CFGNode nUseSrc = use.getSrcNode();
          CFGNode nUseTgt = (pUse == null) ? null : use.getBranch().getTgt();
          Branch brUse = use.getBranch();
          final boolean useReachesDef =
              Options.reachability()
                  ? ((pUse == null)
                      ? ReachabilityAnalysis.reachesFromTop(nUseSrc, nDef, true)
                      : ReachabilityAnalysis.reachesFromTop(nUseTgt, nDef, true))
                  : true; // assume use reaches def, if no reachability analysis available
          final boolean duInNodeOrder =
              (pUse == null)
                  ? orderGuaranteed(useReachesDef, nDef, nUseSrc)
                  : orderGuaranteed(useReachesDef, nDef, brUse);

          // get # interproc defs for use
          final int numInterProcDefs = interProcDefsPerUse.get(use);

          // create and store DUA(s)
          // d-u event order is guaranteed if d-u node order is guaranteed,
          // and if use doesn't have multiple inter-proc defs
          // (p-uses with > 1 defs will be considered later)
          final boolean duEventOrderGuaranteed = duInNodeOrder && numInterProcDefs <= 1;
          ArrayList<Use> localUses = realToLocalUsesMap.get(use);
          Use[] localUsesArr = new Use[localUses.size()];
          int uId = 0;
          for (Use u : localUses) localUsesArr[uId++] = u;
          DUA dua = new DUA(def, use, localUsesArr, duEventOrderGuaranteed);
          duaSet.addDUA(dua);

          // DEBUG
          if (verbose) System.out.print((duEventOrderGuaranteed ? "" : "(P)") + use + "[");

          // increment c-use/p-use counter
          if (pUse == null) ++cDuas;
          else ++pDuas;

          // determine kills for this DUA, and update kill stats
          int killsForThisDua = 0;
          int possKillsForThisDua = 0;
          if (defKills != null) {
            for (Def kill : defKills) {
              if (kill != def) {
                // get dus for kill's method
                SootMethod mKill =
                    ProgramFlowGraph.inst().getContainingMethod(kill.getN().getStmt());
                HashMap<Def, HashMap<Use, ArrayList<Use>>> killDUs =
                    methodsToReachUseDefs.get(mKill).getDUs();
                Set<Use> usesForKill = null;
                if (killDUs.get(kill) != null) usesForKill = killDUs.get(kill).keySet();
                if (usesForKill != null && usesForKill.contains(use)) { // kills this DUA
                  CFGNode nKill = kill.getN();
                  final boolean killAligned =
                      orderGuaranteed(nDef, nKill, true)
                          && ((pUse == null)
                              ? orderGuaranteed(nKill, nUseSrc, true)
                              : orderGuaranteed(nKill, brUse, true));

                  if (verbose) System.out.print((killAligned ? "" : "(P)") + kill + ",");

                  ++killsForThisDua;
                  // not only kill must be aligned, but use must not reach def
                  if (!useReachesDef && killAligned) dua.addKillInOrder(kill);
                  else {
                    dua.addKillNotInOrder(kill);
                    ++possKillsForThisDua;
                  }
                }
              }
            }
          }
          numKills += killsForThisDua;
          if (killsForThisDua < minKills) minKills = killsForThisDua;
          if (killsForThisDua > maxKills) maxKills = killsForThisDua;
          possibleKills += possKillsForThisDua;
          if (duEventOrderGuaranteed) subsDuasWithPossKill += possKillsForThisDua;

          if (verbose) System.out.print("],");
        }

        if (verbose) System.out.print("}, ");
      }

      // after regular DUAs, store same-BB dus
      duaSet.addSameBBDUs(ru.getSameBBDUs());

      if (verbose) System.out.println();
    }
    duaSet.updateInferrability();

    // inferrability stats
    final int numAllDuas = duaSet.getAllDUAs().size();
    int inferrDuas = 0, condInfDuas = 0, nonInfDuas = 0;
    int inferrDuasIntra = 0, condInfDuasIntra = 0, nonInfDuasIntra = 0;
    for (DUA dua : duaSet.getAllDUAs()) {
      SootMethod m = ProgramFlowGraph.inst().getContainingMethod(dua.getDef().getN().getStmt());
      ReachableUsesDefs ru = methodsToReachUseDefs.get(m);

      if (dua.isInferrableOrCondInf()) {
        if (dua.isDefinitelyInferrable()) {
          ++inferrDuas;
        } else {
          ++condInfDuas;
        }
      } else {
        ++nonInfDuas;
      }
    }

    final int totalDuas = cDuas + pDuas;
    final int totalIntra = cIntraOnly + pIntraOnly;
    final int totalInter = cInterOnly + pInterOnly;

    System.out.println(
        "INFERRABILITY: inf "
            + inferrDuas
            + ", cond "
            + condInfDuas
            + ", non-inf "
            + (numAllDuas - inferrDuas - condInfDuas));
    System.out.println(
        "INF: intra " + inferrDuasIntra + ", inter " + (inferrDuas - inferrDuasIntra));
    System.out.println(
        "COND-INF: intra " + condInfDuasIntra + ", inter " + (condInfDuas - condInfDuasIntra));
    assert nonInfDuasIntra == (totalIntra - inferrDuasIntra - condInfDuasIntra);
    System.out.println(
        "NON-INF: intra " + nonInfDuasIntra + ", inter " + (nonInfDuas - nonInfDuasIntra));
    for (int i = 1; i < useCallDepthsCount.size(); ++i)
      System.out.print(i + ":" + useCallDepthsCount.get(i) + " ");
    System.out.println();
    if (!useCallDepthsCount.isEmpty() && useCallDepthsCount.get(0) != totalDuas - totalInter)
      System.out.println(
          "NOTE -- DUAs at depth 0 are "
              + useCallDepthsCount.get(0)
              + ", but intra duas are "
              + (totalDuas - totalInter));

    //		// TEST
    //		reportPaths(methodsToReachUseDefs);

    System.out.println(
        "DUA totals: "
            + totalDuas
            + "; c-duas "
            + cDuas
            + ", p-duas "
            + pDuas
            + "; intra "
            + totalIntra
            + ", inter "
            + totalInter
            + ", both "
            + (totalDuas - totalIntra - totalInter));
    System.out.println(
        "c-DUAs intra "
            + cIntraOnly
            + ", inter "
            + cInterOnly
            + ", both "
            + (cDuas - cIntraOnly - cInterOnly)
            + "; p-DUAs intra "
            + pIntraOnly
            + ", inter "
            + pInterOnly
            + ", both "
            + (pDuas - pIntraOnly - pInterOnly));
  }
Beispiel #2
0
  /**
   * Rough and simple overapproximation: cross product of all defs against all uses of a
   * field/arrayElem. This is a static approximation: only one data-dependence per field/arrayElem
   * def x use. (At runtime, there might be multiple dependences of fields of different objects or
   * different array elements.)
   */
  private void computeFieldArrayObjDUAs(
      List<SootMethod> allReachableMethods,
      Map<SootMethod, ReachableUsesDefs> methodsToReachUseDefs) {
    assert methodsToReachUseDefs.keySet().size() == allReachableMethods.size();
    assert methodsToReachUseDefs.keySet().containsAll(allReachableMethods);

    // collect all uses from cfgs
    List<Use> allFieldUses = new ArrayList<Use>();
    List<Use> allArrElemUses = new ArrayList<Use>();
    List<Use> allObjUses = new ArrayList<Use>();
    for (SootMethod m : allReachableMethods) {
      ReachableUsesDefs ru = methodsToReachUseDefs.get(m);
      allFieldUses.addAll(ru.getFieldUses());
      allArrElemUses.addAll(ru.getArrayElemUses());
      allObjUses.addAll(ru.getLibObjUses());
    }

    // sort all these uses
    List<Use> sortedFldUses = new ArrayList<Use>(allFieldUses);
    Collections.sort(sortedFldUses, new UseComparator());
    List<Use> sortedArrElemUses = new ArrayList<Use>(allArrElemUses);
    Collections.sort(sortedArrElemUses, new UseComparator());
    List<Use> sortedObjUses = new ArrayList<Use>(allObjUses);
    Collections.sort(sortedObjUses, new UseComparator());

    // match uses to defs; resolve fields for comparison (field refs are not necessarily "equal"!)
    int numFieldDUAs = 0;
    int numArrayElemDUAs = 0;
    int numObjectDUAs = 0;
    Map<SootField, List<DUA>> duasPerField = new HashMap<SootField, List<DUA>>();
    Map<Type, List<DUA>> duasPerArrElem = new HashMap<Type, List<DUA>>();
    Map<Pair<RefType, Boolean>, List<DUA>> duasPerObjType =
        new HashMap<Pair<RefType, Boolean>, List<DUA>>();
    for (SootMethod m : allReachableMethods) {
      ReachableUsesDefs ru = methodsToReachUseDefs.get(m);
      // 1. Match fields, but only if neither def or use is in catch block
      List<Def> sortedFldDefs = new ArrayList<Def>(ru.getFieldDefs());
      Collections.sort(sortedFldDefs, new DefComparator());
      for (Def fldDef : sortedFldDefs) {
        if (fldDef.isInCatchBlock()) continue;

        SootField fldD = ((FieldRef) fldDef.getValue()).getField();
        // exclude DUAs for some special fields: class (ref to Class of object), and this$0 (link to
        // container obj of nested class obj)
        if (fldD.getName().equals("class$0"))
          continue; // causes trouble when instrumenting <clinit>, where class$0 gets defined (i.e.,
                    // Class loaded on demand)
        if (fldD.getName().equals("this$0"))
          continue; // causes trouble when instrumenting code that links nested class to outer
                    // object

        for (Use fldUse : sortedFldUses) {
          if (fldUse.isInCatchBlock()) continue;

          SootField fldU = ((FieldRef) fldUse.getValue()).getField();
          if (fldD == fldU) {
            // create and store field DUA
            DUA fldDUA = new DUA(fldDef, fldUse, new Use[0], false);
            duaSet.addDUA(fldDUA);

            // associate DUA to field
            List<DUA> duasForFld = duasPerField.get(fldD);
            if (duasForFld == null) {
              duasForFld = new ArrayList<DUA>();
              duasPerField.put(fldD, duasForFld);
            }
            duasForFld.add(fldDUA);

            // update count of field DUAs
            ++numFieldDUAs;
          }
        }
      }
      // 2. Match array elements, simply by element type
      List<Def> sortedArrDefs = new ArrayList<Def>(ru.getArrayElemDefs());
      Collections.sort(sortedArrDefs, new DefComparator());
      for (Def arrElemDef : sortedArrDefs) {
        if (arrElemDef.isInCatchBlock()) continue;

        Variable varDef = arrElemDef.getVar();
        Type defArrElemType = ((ArrayRef) arrElemDef.getValue()).getType();
        for (Use arrElemUse : sortedArrElemUses) {
          if (arrElemUse.isInCatchBlock()) continue;

          //					Type useArrElemType = ((ArrayRef)arrElemUse.getValue()).getType();
          if (arrElemUse.getVar().mayEqual(varDef)) { // useArrElemType.equals(defArrElemType)) {
            // create and store array-elem DUA
            DUA arrElemDUA = new DUA(arrElemDef, arrElemUse, new Use[0], false);
            duaSet.addDUA(arrElemDUA);

            // associate DUA to array elem type
            List<DUA> duasForAE = duasPerArrElem.get(defArrElemType);
            if (duasForAE == null) {
              duasForAE = new ArrayList<DUA>();
              duasPerArrElem.put(defArrElemType, duasForAE);
            }
            duasForAE.add(arrElemDUA);

            // update count of array-elem DUAs
            ++numArrayElemDUAs;
          }
        }
      }

      // 3. Match object use/defs, simply by element type
      if (Options.includeObjDUAs()) {
        List<Def> sortedObjDefs = new ArrayList<Def>(ru.getLibObjDefs());
        Collections.sort(sortedObjDefs, new DefComparator());
        for (Def objDef : sortedObjDefs) {
          if (objDef.isInCatchBlock()) continue;

          // boolean in pair indicates whether it's instance obj (true) or class (false)
          List<Pair<RefType, Boolean>> defObjTypes = getObjTypes(objDef.getValue());
          for (Use objUse : sortedObjUses) {
            if (objUse.isInCatchBlock()) continue;

            List<Pair<RefType, Boolean>> useObjTypes = getObjTypes(objUse.getValue());
            for (Pair<RefType, Boolean> useType : useObjTypes) {
              if (defObjTypes.contains(useType)) {
                // create and store obj DUA
                DUA objDUA = new DUA(objDef, objUse, new Use[0], false);
                duaSet.addDUA(objDUA);

                // associate one DUA to each matching object type
                List<Pair<RefType, Boolean>> matchingObjTypes =
                    getMatchingTypes(defObjTypes, useObjTypes);
                for (Pair<RefType, Boolean> objType : matchingObjTypes) {
                  List<DUA> duasForObjType = duasPerObjType.get(objType);
                  if (duasForObjType == null) {
                    duasForObjType = new ArrayList<DUA>();
                    duasPerObjType.put(objType, duasForObjType);
                  }
                  duasForObjType.add(objDUA);

                  // update count of array-elem DUAs
                  ++numObjectDUAs;
                }
              }
            }
          }
        }
      }
    }

    // add possible kills to field and array-elem DUAs
    //		for (SootField fld : duasPerField.keySet()) {
    //			List<DUA> duasForField = duasPerField.get(fld);
    //			addAllOthersAsKills(duasForField);
    //		}
    //		for (Type t : duasPerArrElem.keySet()) {
    //			List<DUA> duasForAE = duasPerArrElem.get(t);
    //			addAllOthersAsKills(duasForAE);
    //		}
    // TODO: kills for obj DUAs

    System.out.println("Total field DUAs: " + numFieldDUAs);
    System.out.println("Total array elem DUAs: " + numArrayElemDUAs);
    System.out.println("Total object DUAs: " + numObjectDUAs);
  }