From 451a8e6947c5882185a5831b2974cd9c1f1c5a37 Mon Sep 17 00:00:00 2001 From: jpwang Date: Mon, 21 Apr 2025 13:23:00 +0800 Subject: [PATCH 1/3] Implement a client for counting the number of variable pairs that are may-aliases --- .../analysis/pta/client/MayAliasPair.java | 149 ++++++++++++++++++ src/main/resources/tai-e-analyses.yml | 5 + .../analysis/pta/client/MayAliasPairTest.java | 82 ++++++++++ 3 files changed, 236 insertions(+) create mode 100644 src/main/java/pascal/taie/analysis/pta/client/MayAliasPair.java create mode 100644 src/test/java/pascal/taie/analysis/pta/client/MayAliasPairTest.java diff --git a/src/main/java/pascal/taie/analysis/pta/client/MayAliasPair.java b/src/main/java/pascal/taie/analysis/pta/client/MayAliasPair.java new file mode 100644 index 000000000..250e028e0 --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/client/MayAliasPair.java @@ -0,0 +1,149 @@ +/* + * Tai-e: A Static Analysis Framework for Java + * + * Copyright (C) 2022 Tian Tan + * Copyright (C) 2022 Yue Li + * + * This file is part of Tai-e. + * + * Tai-e is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Tai-e is distributed in the hope that it will be useful,but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + * Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Tai-e. If not, see . + */ + +package pascal.taie.analysis.pta.client; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import pascal.taie.World; +import pascal.taie.analysis.ProgramAnalysis; +import pascal.taie.analysis.pta.PointerAnalysis; +import pascal.taie.analysis.pta.PointerAnalysisResult; +import pascal.taie.analysis.pta.core.heap.Obj; +import pascal.taie.config.AnalysisConfig; +import pascal.taie.ir.exp.Var; +import pascal.taie.util.Indexer; +import pascal.taie.util.collection.IndexerBitSet; +import pascal.taie.util.collection.Maps; +import pascal.taie.util.collection.Sets; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public class MayAliasPair extends ProgramAnalysis { + + private static final Logger logger = LogManager.getLogger(MayAliasPair.class); + + public static final String ID = "may-alias-pair"; + + private PointerAnalysisResult ptaResult; + + public MayAliasPair(AnalysisConfig config) { + super(config); + } + + @Override + public MayAliasPairResult analyze() { + ptaResult = World.get().getResult(PointerAnalysis.ID); + Set vars = Sets.newSet(ptaResult.getVars()); + Set appVars = vars.stream() + .filter(MayAliasPair::isApp) + .collect(Collectors.toUnmodifiableSet()); + + long nAliasPairs = computeMayAliasPairs(vars); + long nVars = vars.size(); + + long nAppAliasPairs = computeMayAliasPairs(appVars); + long nAppVars = appVars.size(); + + // Log statistics + logger.info("#{}: found {} in {} variable pairs", + getDescription(), nAliasPairs, (nVars - 1) * nVars / 2); + logger.info("#{}: found {} in {} variable pairs (app)", + getDescription(), nAppAliasPairs, (nAppVars - 1) * nAppVars / 2); + + return new MayAliasPairResult(nAliasPairs, nAppAliasPairs); + } + + private long computeMayAliasPairs(Set vars) { + VarIndexer indexer = new VarIndexer(vars); + Map> obj2Vars = Maps.newMap(); + vars.forEach(v -> { + for (Obj obj : ptaResult.getPointsToSet(v)) { + obj2Vars.computeIfAbsent(obj, k -> indexer.makeIndexerBitSet()) + .add(v); + } + }); + + // mayAlias(u, v) if + // exists o s.t. o in pts(u) and o in pts(v) + long nAliasPairs = vars.parallelStream() + .mapToLong(v -> { + Set aliasVars = indexer.makeIndexerBitSet(); + for (Obj o : ptaResult.getPointsToSet(v)) { + aliasVars.addAll(obj2Vars.getOrDefault(o, Collections.emptySet())); + } + // v may alias to itself, but we do not count in this case + aliasVars.remove(v); + return aliasVars.size(); + }).sum(); + // mayAlias(u, v) iff mayAlias(v, u), so a pair is counted twice + return nAliasPairs / 2; + } + + private static boolean isApp(Var v) { + return v.getMethod().isApplication(); + } + + String getDescription() { + return ID; + } + + public record MayAliasPairResult(long numberOfAliasPairs, long numberOfAppAliasPairs) { + } + + // A global indexer for Vars + private static class VarIndexer implements Indexer { + + private static final boolean IS_SPARSE = true; + + private final Var[] vars; + + private final Map varIndexMap = Maps.newMap(); + + VarIndexer(Collection vars) { + this.vars = vars.stream() + .distinct() + .toArray(Var[]::new); + for (int index = 0; index < this.vars.length; index++) { + varIndexMap.put(this.vars[index], index); + } + } + + public IndexerBitSet makeIndexerBitSet() { + return new IndexerBitSet<>(this, IS_SPARSE); + } + + @Override + public int getIndex(Var o) { + return varIndexMap.get(o); + } + + @Override + public Var getObject(int index) { + return vars[index]; + } + } +} diff --git a/src/main/resources/tai-e-analyses.yml b/src/main/resources/tai-e-analyses.yml index 09a4bb4ba..72e244ece 100644 --- a/src/main/resources/tai-e-analyses.yml +++ b/src/main/resources/tai-e-analyses.yml @@ -54,6 +54,11 @@ id: poly-call requires: [ pta ] +- description: identify variable pairs that may alias + analysisClass: pascal.taie.analysis.pta.client.MayAliasPair + id: may-alias-pair + requires: [ pta ] + - description: find modified objects of methods and statements analysisClass: pascal.taie.analysis.sideeffect.SideEffectAnalysis id: side-effect diff --git a/src/test/java/pascal/taie/analysis/pta/client/MayAliasPairTest.java b/src/test/java/pascal/taie/analysis/pta/client/MayAliasPairTest.java new file mode 100644 index 000000000..7575a9bf0 --- /dev/null +++ b/src/test/java/pascal/taie/analysis/pta/client/MayAliasPairTest.java @@ -0,0 +1,82 @@ +/* + * Tai-e: A Static Analysis Framework for Java + * + * Copyright (C) 2022 Tian Tan + * Copyright (C) 2022 Yue Li + * + * This file is part of Tai-e. + * + * Tai-e is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Tai-e is distributed in the hope that it will be useful,but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + * Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Tai-e. If not, see . + */ + + +package pascal.taie.analysis.pta.client; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import pascal.taie.Main; +import pascal.taie.World; +import pascal.taie.analysis.pta.PointerAnalysis; +import pascal.taie.analysis.pta.PointerAnalysisResult; +import pascal.taie.ir.exp.Var; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class MayAliasPairTest { + static final String DIR = "basic"; + + @ParameterizedTest + @ValueSource(strings = { + "New", + "Assign", + "StoreLoad", + "Call", + "InstanceField", + "CallParamRet", + "StaticCall", + "MergeParam", + }) + void test(String mainClass) { + String ptaTestRoot = "src/test/resources/pta"; + String classPath = ptaTestRoot + "/" + DIR; + List args = List.of( + // for loading class PTAAssert + "-cp", ptaTestRoot, + // for loading main class + "-cp", classPath, "-m", mainClass, "-a", "may-alias-pair"); + Main.main(args.toArray(new String[0])); + MayAliasPair.MayAliasPairResult resultByAnalysis = World.get().getResult(MayAliasPair.ID); + long appResultByDefinition = computeByDefinition(); + assertEquals(appResultByDefinition, resultByAnalysis.numberOfAppAliasPairs()); + } + + private static long computeByDefinition() { + PointerAnalysisResult ptaResult = World.get().getResult(PointerAnalysis.ID); + Var[] appVars = ptaResult.getVars().stream() + .filter(v -> v.getMethod().isApplication()) + .toArray(Var[]::new); + long aliasPairs = 0; + for (int i = 0; i < appVars.length; i++) { + for (int j = i + 1; j < appVars.length; j++) { + Var v1 = appVars[i], v2 = appVars[j]; + if (ptaResult.mayAlias(v1, v2)) { + aliasPairs++; + } + } + } + return aliasPairs; + } +} From 3e8fbd5d7d301e08850129894cc59eb773021611 Mon Sep 17 00:00:00 2001 From: jpwang Date: Mon, 21 Apr 2025 16:50:19 +0800 Subject: [PATCH 2/3] Code style --- .../taie/analysis/pta/client/MayAliasPair.java | 12 ++++-------- .../taie/analysis/pta/client/MayAliasPairTest.java | 6 ++++-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/main/java/pascal/taie/analysis/pta/client/MayAliasPair.java b/src/main/java/pascal/taie/analysis/pta/client/MayAliasPair.java index 250e028e0..ee8945e95 100644 --- a/src/main/java/pascal/taie/analysis/pta/client/MayAliasPair.java +++ b/src/main/java/pascal/taie/analysis/pta/client/MayAliasPair.java @@ -70,9 +70,9 @@ public MayAliasPairResult analyze() { // Log statistics logger.info("#{}: found {} in {} variable pairs", - getDescription(), nAliasPairs, (nVars - 1) * nVars / 2); + ID, nAliasPairs, (nVars - 1) * nVars / 2); logger.info("#{}: found {} in {} variable pairs (app)", - getDescription(), nAppAliasPairs, (nAppVars - 1) * nAppVars / 2); + ID, nAppAliasPairs, (nAppVars - 1) * nAppVars / 2); return new MayAliasPairResult(nAliasPairs, nAppAliasPairs); } @@ -88,7 +88,7 @@ private long computeMayAliasPairs(Set vars) { }); // mayAlias(u, v) if - // exists o s.t. o in pts(u) and o in pts(v) + // exists o, s.t. in(o, pts(u)) and in(o, pts(v)) long nAliasPairs = vars.parallelStream() .mapToLong(v -> { Set aliasVars = indexer.makeIndexerBitSet(); @@ -107,11 +107,7 @@ private static boolean isApp(Var v) { return v.getMethod().isApplication(); } - String getDescription() { - return ID; - } - - public record MayAliasPairResult(long numberOfAliasPairs, long numberOfAppAliasPairs) { + public record MayAliasPairResult(long aliasPairs, long appAliasPairs) { } // A global indexer for Vars diff --git a/src/test/java/pascal/taie/analysis/pta/client/MayAliasPairTest.java b/src/test/java/pascal/taie/analysis/pta/client/MayAliasPairTest.java index 7575a9bf0..ade3876e5 100644 --- a/src/test/java/pascal/taie/analysis/pta/client/MayAliasPairTest.java +++ b/src/test/java/pascal/taie/analysis/pta/client/MayAliasPairTest.java @@ -56,11 +56,13 @@ void test(String mainClass) { // for loading class PTAAssert "-cp", ptaTestRoot, // for loading main class - "-cp", classPath, "-m", mainClass, "-a", "may-alias-pair"); + "-cp", classPath, "-m", mainClass, + "-a", "may-alias-pair" + ); Main.main(args.toArray(new String[0])); MayAliasPair.MayAliasPairResult resultByAnalysis = World.get().getResult(MayAliasPair.ID); long appResultByDefinition = computeByDefinition(); - assertEquals(appResultByDefinition, resultByAnalysis.numberOfAppAliasPairs()); + assertEquals(appResultByDefinition, resultByAnalysis.appAliasPairs()); } private static long computeByDefinition() { From 4fb151e8351eb214531542b612639f56446230a4 Mon Sep 17 00:00:00 2001 From: jpwang Date: Tue, 22 Apr 2025 10:05:45 +0800 Subject: [PATCH 3/3] Replace string literals with static fields --- .../java/pascal/taie/analysis/pta/client/MayAliasPairTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/pascal/taie/analysis/pta/client/MayAliasPairTest.java b/src/test/java/pascal/taie/analysis/pta/client/MayAliasPairTest.java index ade3876e5..23ce51c93 100644 --- a/src/test/java/pascal/taie/analysis/pta/client/MayAliasPairTest.java +++ b/src/test/java/pascal/taie/analysis/pta/client/MayAliasPairTest.java @@ -57,7 +57,7 @@ void test(String mainClass) { "-cp", ptaTestRoot, // for loading main class "-cp", classPath, "-m", mainClass, - "-a", "may-alias-pair" + "-a", MayAliasPair.ID ); Main.main(args.toArray(new String[0])); MayAliasPair.MayAliasPairResult resultByAnalysis = World.get().getResult(MayAliasPair.ID);