From e110836bd17b8015b3e21bb902e76c9c9f6b6381 Mon Sep 17 00:00:00 2001 From: Teng Zhang Date: Thu, 4 Sep 2025 11:53:58 +0800 Subject: [PATCH 1/5] Log cpu cores and memory size --- .../java/pascal/taie/util/RuntimeInfoLogger.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/pascal/taie/util/RuntimeInfoLogger.java b/src/main/java/pascal/taie/util/RuntimeInfoLogger.java index 829cf8a4d..7130a8de8 100644 --- a/src/main/java/pascal/taie/util/RuntimeInfoLogger.java +++ b/src/main/java/pascal/taie/util/RuntimeInfoLogger.java @@ -64,6 +64,7 @@ public class RuntimeInfoLogger { */ public static void logRuntimeInfo() { logEnvInfo(); + logSystemInfo(); logTaieInfo(); } @@ -77,6 +78,18 @@ private static void logEnvInfo() { } } + /** + * Logs system information such as the number of CPU cores and total memory size. + */ + private static void logSystemInfo() { + var osBean = (com.sun.management.OperatingSystemMXBean) + java.lang.management.ManagementFactory.getOperatingSystemMXBean(); + int cpuCores = osBean.getAvailableProcessors(); + long memoryMB = osBean.getTotalMemorySize() / (1024 * 1024); + logger.info("Available CPU cores: {}", cpuCores); + logger.info("Total memory (MB): {}", memoryMB); + } + /** * Logs Tai-e version and commit information by attempting to read from the build properties file * or fallback methods if the build properties are not available. From 5ac2f36d3116564a24e25836b5c1bf4bbf2cdc0d Mon Sep 17 00:00:00 2001 From: Teng Zhang Date: Thu, 4 Sep 2025 12:04:10 +0800 Subject: [PATCH 2/5] Unify timer logging --- src/main/java/pascal/taie/util/Timer.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/pascal/taie/util/Timer.java b/src/main/java/pascal/taie/util/Timer.java index 1b7471788..eb494b005 100644 --- a/src/main/java/pascal/taie/util/Timer.java +++ b/src/main/java/pascal/taie/util/Timer.java @@ -87,13 +87,12 @@ public String toString() { * @param taskName name of the task */ public static T runAndCount(Supplier task, String taskName, Level level) { - logger.info("{} starts ...", taskName); + logger.info("[{}] starts ...", taskName); Timer timer = new Timer(taskName); timer.start(); T result = task.get(); timer.stop(); - logger.log(level, "{} finishes, elapsed time: {}", taskName, - String.format("%.2fs", timer.inSecond())); + logger.log(level, timer); return result; } From e360a53192156da9f4680fc38a38b144c507ac34 Mon Sep 17 00:00:00 2001 From: Teng Zhang Date: Fri, 5 Sep 2025 12:45:25 +0800 Subject: [PATCH 3/5] Add performance monitor function to Timer --- .../pascal/taie/util/RuntimeInfoLogger.java | 2 +- src/main/java/pascal/taie/util/Timer.java | 84 ++++++++++++++++++- 2 files changed, 81 insertions(+), 5 deletions(-) diff --git a/src/main/java/pascal/taie/util/RuntimeInfoLogger.java b/src/main/java/pascal/taie/util/RuntimeInfoLogger.java index 7130a8de8..525ef6322 100644 --- a/src/main/java/pascal/taie/util/RuntimeInfoLogger.java +++ b/src/main/java/pascal/taie/util/RuntimeInfoLogger.java @@ -87,7 +87,7 @@ private static void logSystemInfo() { int cpuCores = osBean.getAvailableProcessors(); long memoryMB = osBean.getTotalMemorySize() / (1024 * 1024); logger.info("Available CPU cores: {}", cpuCores); - logger.info("Total memory (MB): {}", memoryMB); + logger.info("Total memory: {}MB", memoryMB); } /** diff --git a/src/main/java/pascal/taie/util/Timer.java b/src/main/java/pascal/taie/util/Timer.java index eb494b005..e00fa274b 100644 --- a/src/main/java/pascal/taie/util/Timer.java +++ b/src/main/java/pascal/taie/util/Timer.java @@ -22,15 +22,19 @@ package pascal.taie.util; +import com.sun.management.OperatingSystemMXBean; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; import java.time.Duration; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Supplier; @@ -39,29 +43,71 @@ public class Timer { private static final Logger logger = LogManager.getLogger(Timer.class); + /** + * Monitoring interval in seconds + */ + private static final int INTERVAL = 1; + private final String name; - private long elapsedTime = 0; + private final OperatingSystemMXBean osBean; + private final MemoryMXBean memoryBean; + private final ScheduledExecutorService scheduler; + // Time tracking private long startTime; + private long elapsedTime = 0; + + // CPU and Memory tracking + private double startCpuUsage; + private double peakCpuUsage; + private double endCpuUsage; + private long startMemoryMB; + private long peakMemoryMB; + private long endMemoryMB; private boolean inCounting = false; public Timer(String name) { this.name = name; + this.osBean = (OperatingSystemMXBean) ManagementFactory + .getOperatingSystemMXBean(); + this.memoryBean = ManagementFactory.getMemoryMXBean(); + this.scheduler = Executors.newSingleThreadScheduledExecutor(r -> { + Thread t = new Thread(r, this.getClass().getName() + "[" + name + "]"); + t.setDaemon(true); + return t; + }); } public void start() { if (!inCounting) { inCounting = true; startTime = System.currentTimeMillis(); + startCpuUsage = getCpuUsage(); + startMemoryMB = getMemoryUsedMB(); + // start up the scheduler + scheduler.scheduleAtFixedRate(this::updatePeakValues, + 0, INTERVAL, TimeUnit.SECONDS); } } public void stop() { if (inCounting) { - elapsedTime += System.currentTimeMillis() - startTime; inCounting = false; + elapsedTime += System.currentTimeMillis() - startTime; + endCpuUsage = getCpuUsage(); + endMemoryMB = getMemoryUsedMB(); + // shut down the scheduler + scheduler.shutdown(); + try { + if (!scheduler.awaitTermination(2, TimeUnit.SECONDS)) { + scheduler.shutdownNow(); + } + } catch (InterruptedException e) { + scheduler.shutdownNow(); + Thread.currentThread().interrupt(); + } } } @@ -76,8 +122,38 @@ public void clear() { @Override public String toString() { - return String.format("[%s] elapsed time: %.2fs", - name, inSecond()); + StringBuilder sb = new StringBuilder(); + sb.append(String.format("[%s] elapsed time: %.2fs", name, inSecond())); + sb.append(String.format("; CPU usage: start %.2f%%, peak %.2f%%, end %.2f%%", + startCpuUsage * 100, peakCpuUsage * 100, endCpuUsage * 100)); + sb.append(String.format("; Memory usage: start %dMB, peak %dMB, end %dMB", + startMemoryMB, peakMemoryMB, endMemoryMB)); + return sb.toString(); + } + + private void updatePeakValues() { + double currentCpuUsage = getCpuUsage(); + if (currentCpuUsage > peakCpuUsage) { + peakCpuUsage = currentCpuUsage; + } + long currentMemoryMB = getMemoryUsedMB(); + if (currentMemoryMB > peakMemoryMB) { + peakMemoryMB = currentMemoryMB; + } + } + + private double getCpuUsage() { + double processCpuUsage = osBean.getProcessCpuLoad(); + if (processCpuUsage < 0) { + processCpuUsage = 0.0; + } + return processCpuUsage; + } + + private long getMemoryUsedMB() { + long heapMemoryUsed = memoryBean.getHeapMemoryUsage().getUsed(); + long nonHeapMemoryUsed = memoryBean.getNonHeapMemoryUsage().getUsed(); + return (heapMemoryUsed + nonHeapMemoryUsed) / (1024 * 1024); } /** From 82a6a7b1cf84c578c637fa58e9226ff3949bb26f Mon Sep 17 00:00:00 2001 From: Teng Zhang Date: Fri, 5 Dec 2025 13:22:56 +0800 Subject: [PATCH 4/5] Rename Timer to Monitor --- src/main/java/pascal/taie/Main.java | 6 ++--- .../pascal/taie/analysis/AnalysisManager.java | 4 +-- .../taie/analysis/pta/PointerAnalysis.java | 8 +++--- .../analysis/pta/plugin/AnalysisTimer.java | 12 ++++----- .../pta/plugin/taint/TaintAnalysis.java | 4 +-- .../analysis/pta/toolkit/mahjong/Mahjong.java | 8 +++--- .../analysis/pta/toolkit/zipper/Zipper.java | 8 +++--- .../frontend/cache/CachedWorldBuilder.java | 26 +++++++++---------- .../pascal/taie/frontend/soot/IRBuilder.java | 10 +++---- .../taie/util/{Timer.java => Monitor.java} | 14 +++++----- 10 files changed, 50 insertions(+), 50 deletions(-) rename src/main/java/pascal/taie/util/{Timer.java => Monitor.java} (96%) diff --git a/src/main/java/pascal/taie/Main.java b/src/main/java/pascal/taie/Main.java index d84887fd2..f091ae509 100644 --- a/src/main/java/pascal/taie/Main.java +++ b/src/main/java/pascal/taie/Main.java @@ -35,8 +35,8 @@ import pascal.taie.config.PlanConfig; import pascal.taie.config.Scope; import pascal.taie.frontend.cache.CachedWorldBuilder; +import pascal.taie.util.Monitor; import pascal.taie.util.RuntimeInfoLogger; -import pascal.taie.util.Timer; import pascal.taie.util.collection.Lists; import java.io.InputStream; @@ -49,7 +49,7 @@ public class Main { private static final Logger logger = LogManager.getLogger(Main.class); public static void main(String... args) { - Timer.runAndCount(() -> { + Monitor.runAndCount(() -> { Options options = processArgs(args); LoggerConfigs.setOutput(options.getOutputDir()); RuntimeInfoLogger.logRuntimeInfo(); @@ -123,7 +123,7 @@ public static void buildWorld(String... args) { } private static void buildWorld(Options options, List analyses) { - Timer.runAndCount(() -> { + Monitor.runAndCount(() -> { try { Class builderClass = options.getWorldBuilderClass(); Constructor builderCtor = builderClass.getConstructor(); diff --git a/src/main/java/pascal/taie/analysis/AnalysisManager.java b/src/main/java/pascal/taie/analysis/AnalysisManager.java index e7722cbaf..923a6ae8f 100644 --- a/src/main/java/pascal/taie/analysis/AnalysisManager.java +++ b/src/main/java/pascal/taie/analysis/AnalysisManager.java @@ -36,7 +36,7 @@ import pascal.taie.language.classes.JClass; import pascal.taie.language.classes.JMethod; import pascal.taie.util.AnalysisException; -import pascal.taie.util.Timer; +import pascal.taie.util.Monitor; import pascal.taie.util.graph.SimpleGraph; import java.lang.reflect.Constructor; @@ -100,7 +100,7 @@ public void execute() { methodScope = null; // execute analyses plan.analyses().forEach(config -> { - Analysis analysis = Timer.runAndCount( + Analysis analysis = Monitor.runAndCount( () -> runAnalysis(config), config.getId(), Level.INFO); if (!keepAllResults) { executedAnalyses.add(analysis); diff --git a/src/main/java/pascal/taie/analysis/pta/PointerAnalysis.java b/src/main/java/pascal/taie/analysis/pta/PointerAnalysis.java index 81ea32923..7b293b951 100644 --- a/src/main/java/pascal/taie/analysis/pta/PointerAnalysis.java +++ b/src/main/java/pascal/taie/analysis/pta/PointerAnalysis.java @@ -55,7 +55,7 @@ import pascal.taie.config.AnalysisOptions; import pascal.taie.config.ConfigException; import pascal.taie.util.AnalysisException; -import pascal.taie.util.Timer; +import pascal.taie.util.Monitor; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -85,15 +85,15 @@ public PointerAnalysisResult analyze() { PointerAnalysisResult preResult = runAnalysis(heapModel, ContextSelectorFactory.makeCISelector()); if (advanced.startsWith("scaler")) { - selector = Timer.runAndCount(() -> ContextSelectorFactory + selector = Monitor.runAndCount(() -> ContextSelectorFactory .makeGuidedSelector(Scaler.run(preResult, advanced)), "Scaler", Level.INFO); } else if (advanced.startsWith("zipper")) { - selector = Timer.runAndCount(() -> ContextSelectorFactory + selector = Monitor.runAndCount(() -> ContextSelectorFactory .makeSelectiveSelector(cs, Zipper.run(preResult, advanced)), "Zipper", Level.INFO); } else if (advanced.equals("mahjong")) { - heapModel = Timer.runAndCount(() -> Mahjong.run(preResult, options), + heapModel = Monitor.runAndCount(() -> Mahjong.run(preResult, options), "Mahjong", Level.INFO); } else { throw new IllegalArgumentException( diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/AnalysisTimer.java b/src/main/java/pascal/taie/analysis/pta/plugin/AnalysisTimer.java index 6897b99e3..8020155da 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/AnalysisTimer.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/AnalysisTimer.java @@ -25,7 +25,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import pascal.taie.analysis.Analysis; -import pascal.taie.util.Timer; +import pascal.taie.util.Monitor; /** * Records the elapsed time of pointer analysis. @@ -34,17 +34,17 @@ public class AnalysisTimer implements Plugin { private static final Logger logger = LogManager.getLogger(Analysis.class); - private Timer ptaTimer; + private Monitor ptaMonitor; @Override public void onStart() { - ptaTimer = new Timer("Pointer analysis"); - ptaTimer.start(); + ptaMonitor = new Monitor("Pointer analysis"); + ptaMonitor.start(); } @Override public void onFinish() { - ptaTimer.stop(); - logger.info(ptaTimer); + ptaMonitor.stop(); + logger.info(ptaMonitor); } } diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/taint/TaintAnalysis.java b/src/main/java/pascal/taie/analysis/pta/plugin/taint/TaintAnalysis.java index 157aa57ee..1f3e96e02 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/taint/TaintAnalysis.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/taint/TaintAnalysis.java @@ -41,7 +41,7 @@ import pascal.taie.language.classes.JMethod; import pascal.taie.language.type.TypeSystem; import pascal.taie.util.AnalysisException; -import pascal.taie.util.Timer; +import pascal.taie.util.Monitor; import javax.annotation.Nullable; import java.io.File; @@ -251,7 +251,7 @@ private void reportTaintFlows() { taintFlows.forEach(logger::info); solver.getResult().storeResult(getClass().getName(), taintFlows); TaintManager manager = context.manager(); - Timer.runAndCount(() -> new TFGDumper().dump( + Monitor.runAndCount(() -> new TFGDumper().dump( new TFGBuilder(solver.getResult(), taintFlows, manager).build(), new File(World.get().getOptions().getOutputDir(), TAINT_FLOW_GRAPH_FILE)), "TFGDumper"); diff --git a/src/main/java/pascal/taie/analysis/pta/toolkit/mahjong/Mahjong.java b/src/main/java/pascal/taie/analysis/pta/toolkit/mahjong/Mahjong.java index a683d3e0c..0ee0c4dea 100644 --- a/src/main/java/pascal/taie/analysis/pta/toolkit/mahjong/Mahjong.java +++ b/src/main/java/pascal/taie/analysis/pta/toolkit/mahjong/Mahjong.java @@ -28,7 +28,7 @@ import pascal.taie.analysis.pta.core.heap.Obj; import pascal.taie.config.AnalysisOptions; import pascal.taie.language.type.Type; -import pascal.taie.util.Timer; +import pascal.taie.util.Monitor; import pascal.taie.util.collection.Maps; import pascal.taie.util.collection.UnionFindSet; @@ -55,12 +55,12 @@ public static HeapModel run(PointerAnalysisResult pta, HeapModel buildHeapModel(PointerAnalysisResult pta, AnalysisOptions options) { - FieldPointsToGraph fpg = Timer.runAndCount( + FieldPointsToGraph fpg = Monitor.runAndCount( () -> new FieldPointsToGraph(pta), "Building field points-to graph", Level.INFO); - dfaFactory = Timer.runAndCount(() -> new DFAFactory(fpg), + dfaFactory = Monitor.runAndCount(() -> new DFAFactory(fpg), "Building DFA", Level.INFO); - UnionFindSet uf = Timer.runAndCount( + UnionFindSet uf = Monitor.runAndCount( () -> mergeTypeConsistentObjects(fpg), "Merging type-consistent objects", Level.INFO); // build resulting heap model based on merge map diff --git a/src/main/java/pascal/taie/analysis/pta/toolkit/zipper/Zipper.java b/src/main/java/pascal/taie/analysis/pta/toolkit/zipper/Zipper.java index 2b7b9cdf8..1e74f2c74 100644 --- a/src/main/java/pascal/taie/analysis/pta/toolkit/zipper/Zipper.java +++ b/src/main/java/pascal/taie/analysis/pta/toolkit/zipper/Zipper.java @@ -37,8 +37,8 @@ import pascal.taie.ir.stmt.New; import pascal.taie.language.classes.JMethod; import pascal.taie.language.type.Type; +import pascal.taie.util.Monitor; import pascal.taie.util.MutableInt; -import pascal.taie.util.Timer; import pascal.taie.util.collection.Maps; import pascal.taie.util.collection.Sets; @@ -110,9 +110,9 @@ public Zipper(PointerAnalysisResult ptaBase, boolean isExpress, float pv) { this.pta = new PointerAnalysisResultExImpl(ptaBase, true); this.isExpress = isExpress; this.pv = pv; - this.oag = Timer.runAndCount(() -> new ObjectAllocationGraph(pta), + this.oag = Monitor.runAndCount(() -> new ObjectAllocationGraph(pta), "Building OAG", Level.INFO); - this.pce = Timer.runAndCount(() -> new PotentialContextElement(pta, oag), + this.pce = Monitor.runAndCount(() -> new PotentialContextElement(pta, oag), "Building PCE", Level.INFO); this.ofg = ptaBase.getObjectFlowGraph(); logger.info("{} nodes in OFG", ofg.getNodes().size()); @@ -148,7 +148,7 @@ public Set selectPrecisionCriticalMethods() { // build and analyze precision-flow graphs Set types = pta.getObjectTypes(); - Timer.runAndCount(() -> types.parallelStream().forEach(this::analyze), + Monitor.runAndCount(() -> types.parallelStream().forEach(this::analyze), "Building and analyzing PFG", Level.INFO); logger.info("#types: {}", types.size()); logger.info("#avg. nodes in PFG: {}", totalPFGNodes.get() / types.size()); diff --git a/src/main/java/pascal/taie/frontend/cache/CachedWorldBuilder.java b/src/main/java/pascal/taie/frontend/cache/CachedWorldBuilder.java index e00d17eb9..d9db0f0bd 100644 --- a/src/main/java/pascal/taie/frontend/cache/CachedWorldBuilder.java +++ b/src/main/java/pascal/taie/frontend/cache/CachedWorldBuilder.java @@ -28,7 +28,7 @@ import pascal.taie.WorldBuilder; import pascal.taie.config.AnalysisConfig; import pascal.taie.config.Options; -import pascal.taie.util.Timer; +import pascal.taie.util.Monitor; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -84,8 +84,8 @@ private boolean loadCache(Options options, File worldCacheFile) { return false; } logger.info("Loading the world cache from {}", worldCacheFile); - Timer timer = new Timer("Load the world cache"); - timer.start(); + Monitor monitor = new Monitor("Load the world cache"); + monitor.start(); ObjectInputStream ois = null; try { ois = new ObjectInputStream( @@ -105,25 +105,25 @@ private boolean loadCache(Options options, File worldCacheFile) { logger.error("Failed to close input stream", e); } } - timer.stop(); - logger.info(timer); + monitor.stop(); + logger.info(monitor); } return false; } private void runWorldBuilder(Options options, List analyses) { logger.info("Running the WorldBuilder ..."); - Timer timer = new Timer("Run the WorldBuilder"); - timer.start(); + Monitor monitor = new Monitor("Run the WorldBuilder"); + monitor.start(); delegate.build(options, analyses); - timer.stop(); - logger.info(timer); + monitor.stop(); + logger.info(monitor); } private void saveCache(File worldCacheFile) { logger.info("Saving the world cache to {}", worldCacheFile); - Timer timer = new Timer("Save the world cache"); - timer.start(); + Monitor monitor = new Monitor("Save the world cache"); + monitor.start(); try (ObjectOutputStream oos = new ObjectOutputStream( new BufferedOutputStream(new FileOutputStream(worldCacheFile)))) { oos.writeObject(World.get()); @@ -131,8 +131,8 @@ private void saveCache(File worldCacheFile) { logger.error("Failed to save world cache from {} due to {}", worldCacheFile, e); } finally { - timer.stop(); - logger.info(timer); + monitor.stop(); + logger.info(monitor); } } diff --git a/src/main/java/pascal/taie/frontend/soot/IRBuilder.java b/src/main/java/pascal/taie/frontend/soot/IRBuilder.java index dce62b99c..c6ba04ab0 100644 --- a/src/main/java/pascal/taie/frontend/soot/IRBuilder.java +++ b/src/main/java/pascal/taie/frontend/soot/IRBuilder.java @@ -29,7 +29,7 @@ import pascal.taie.language.classes.ClassHierarchy; import pascal.taie.language.classes.JClass; import pascal.taie.language.classes.JMethod; -import pascal.taie.util.Timer; +import pascal.taie.util.Monitor; import java.util.ArrayList; import java.util.List; @@ -67,8 +67,8 @@ public IR buildIR(JMethod method) { */ @Override public void buildAll(ClassHierarchy hierarchy) { - Timer timer = new Timer("Build IR for all methods"); - timer.start(); + Monitor monitor = new Monitor("Build IR for all methods"); + monitor.start(); int nThreads = Runtime.getRuntime().availableProcessors(); // Group all methods by number of threads List> groups = new ArrayList<>(); @@ -95,7 +95,7 @@ public void buildAll(ClassHierarchy hierarchy) { } catch (InterruptedException e) { throw new RuntimeException(e); } - timer.stop(); - logger.info(timer); + monitor.stop(); + logger.info(monitor); } } diff --git a/src/main/java/pascal/taie/util/Timer.java b/src/main/java/pascal/taie/util/Monitor.java similarity index 96% rename from src/main/java/pascal/taie/util/Timer.java rename to src/main/java/pascal/taie/util/Monitor.java index e00fa274b..b3affcb3d 100644 --- a/src/main/java/pascal/taie/util/Timer.java +++ b/src/main/java/pascal/taie/util/Monitor.java @@ -39,9 +39,9 @@ import java.util.concurrent.TimeoutException; import java.util.function.Supplier; -public class Timer { +public class Monitor { - private static final Logger logger = LogManager.getLogger(Timer.class); + private static final Logger logger = LogManager.getLogger(Monitor.class); /** * Monitoring interval in seconds @@ -68,7 +68,7 @@ public class Timer { private boolean inCounting = false; - public Timer(String name) { + public Monitor(String name) { this.name = name; this.osBean = (OperatingSystemMXBean) ManagementFactory .getOperatingSystemMXBean(); @@ -164,11 +164,11 @@ private long getMemoryUsedMB() { */ public static T runAndCount(Supplier task, String taskName, Level level) { logger.info("[{}] starts ...", taskName); - Timer timer = new Timer(taskName); - timer.start(); + Monitor monitor = new Monitor(taskName); + monitor.start(); T result = task.get(); - timer.stop(); - logger.log(level, timer); + monitor.stop(); + logger.log(level, monitor); return result; } From 8f5762c8b678e22e51c1a2376aa413ff5da292d2 Mon Sep 17 00:00:00 2001 From: Teng Zhang Date: Fri, 5 Dec 2025 13:42:22 +0800 Subject: [PATCH 5/5] Keep Timer for compatibility --- src/main/java/pascal/taie/util/Monitor.java | 117 ++++++++++++++-- src/main/java/pascal/taie/util/Timer.java | 146 ++++++++++++++++++++ 2 files changed, 255 insertions(+), 8 deletions(-) create mode 100644 src/main/java/pascal/taie/util/Timer.java diff --git a/src/main/java/pascal/taie/util/Monitor.java b/src/main/java/pascal/taie/util/Monitor.java index b3affcb3d..6d857a57d 100644 --- a/src/main/java/pascal/taie/util/Monitor.java +++ b/src/main/java/pascal/taie/util/Monitor.java @@ -39,12 +39,48 @@ import java.util.concurrent.TimeoutException; import java.util.function.Supplier; +/** + * A comprehensive monitoring utility for tracking task execution metrics including + * elapsed time, CPU usage, and memory consumption. + * + *

This class provides both instance-based and static methods for monitoring tasks. + * It tracks the following metrics: + *

    + *
  • Elapsed Time: Total execution time in seconds
  • + *
  • CPU Usage: Process CPU load at start, peak, and end of execution
  • + *
  • Memory Usage: Heap and non-heap memory consumption at start, peak, and end
  • + *
+ * + *

The monitor uses a scheduled executor to periodically sample CPU and memory usage + * (every {@value #INTERVAL} second) to capture peak values during task execution. + * + *

Example usage (instance-based): + *

{@code
+ * Monitor monitor = new Monitor("MyTask");
+ * monitor.start();
+ * // ... perform task ...
+ * monitor.stop();
+ * System.out.println(monitor); // Prints all metrics
+ * }
+ * + *

Example usage (static convenience method): + *

{@code
+ * Monitor.runAndCount(() -> {
+ *     // ... perform task ...
+ * }, "MyTask");
+ * }
+ * + *

This class replaces the deprecated {@link Timer} class and provides enhanced + * monitoring capabilities beyond simple time tracking. + * + * @see Timer + */ public class Monitor { private static final Logger logger = LogManager.getLogger(Monitor.class); /** - * Monitoring interval in seconds + * Monitoring interval in seconds for periodic CPU and memory sampling. */ private static final int INTERVAL = 1; @@ -68,6 +104,11 @@ public class Monitor { private boolean inCounting = false; + /** + * Creates a new Monitor with the given name. + * + * @param name the name of this monitor, used in output messages + */ public Monitor(String name) { this.name = name; this.osBean = (OperatingSystemMXBean) ManagementFactory @@ -80,6 +121,13 @@ public Monitor(String name) { }); } + /** + * Starts the monitoring process. + * + *

Records the start time and initial CPU/memory metrics, and begins + * periodic sampling of CPU and memory usage to track peak values. + * If the monitor is already running, this call has no effect. + */ public void start() { if (!inCounting) { inCounting = true; @@ -92,6 +140,13 @@ public void start() { } } + /** + * Stops the monitoring process. + * + *

Records the final elapsed time and end CPU/memory metrics, then + * shuts down the periodic sampling scheduler. If the monitor is not + * currently running, this call has no effect. + */ public void stop() { if (inCounting) { inCounting = false; @@ -111,15 +166,34 @@ public void stop() { } } + /** + * Returns the total elapsed time in seconds. + * + * @return the elapsed time in seconds as a floating-point number + */ public float inSecond() { return elapsedTime / 1000F; } + /** + * Clears all monitoring data and resets the counting state. + * + *

This resets the elapsed time to zero and marks the monitor as not counting. + */ public void clear() { elapsedTime = 0; inCounting = false; } + /** + * Returns a string representation of the monitoring results. + * + *

The output includes the monitor name, elapsed time, CPU usage statistics + * (start, peak, and end percentages), and memory usage statistics (start, peak, + * and end in megabytes). + * + * @return a formatted string containing all monitoring metrics + */ @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -157,10 +231,16 @@ private long getMemoryUsedMB() { } /** - * Runs a task, log the elapsed time, and return the result. + * Runs a task, monitors its execution, logs the results, and returns the task's result. + * + *

This is a convenience method that creates a Monitor, starts it, executes the task, + * stops monitoring, and logs the results at the specified logging level. * - * @param task task to be executed - * @param taskName name of the task + * @param the type of result returned by the task + * @param task the task to be executed + * @param taskName the name of the task for logging purposes + * @param level the logging level for the monitoring results + * @return the result of the task execution */ public static T runAndCount(Supplier task, String taskName, Level level) { logger.info("[{}] starts ...", taskName); @@ -173,15 +253,28 @@ public static T runAndCount(Supplier task, String taskName, Level level) } /** - * Runs a task and log the elapsed time. + * Runs a task, monitors its execution, and logs the results at INFO level. * - * @param task task to be executed - * @param taskName taskName of the task + *

This is a convenience method that creates a Monitor, starts it, executes the task, + * stops monitoring, and logs the results at INFO level. + * + * @param task the task to be executed + * @param taskName the name of the task for logging purposes */ public static void runAndCount(Runnable task, String taskName) { runAndCount(task, taskName, Level.INFO); } + /** + * Runs a task, monitors its execution, and logs the results at the specified level. + * + *

This is a convenience method that creates a Monitor, starts it, executes the task, + * stops monitoring, and logs the results at the specified logging level. + * + * @param task the task to be executed + * @param taskName the name of the task for logging purposes + * @param level the logging level for the monitoring results + */ public static void runAndCount(Runnable task, String taskName, Level level) { runAndCount(() -> { task.run(); @@ -190,7 +283,14 @@ public static void runAndCount(Runnable task, String taskName, Level level) { } /** - * Runs a task with given time budget. + * Runs a task with a given time budget. + * + *

Executes the task in a separate thread with a timeout. If the task does not + * complete within the specified time limit, the execution is terminated and the + * program exits with status code 1. + * + * @param task the task to be executed + * @param seconds the time budget in seconds */ public static void runWithTimeout(Runnable task, long seconds) { Duration timeout = Duration.ofSeconds(seconds); @@ -207,4 +307,5 @@ public static void runWithTimeout(Runnable task, long seconds) { executor.shutdown(); } } + } diff --git a/src/main/java/pascal/taie/util/Timer.java b/src/main/java/pascal/taie/util/Timer.java new file mode 100644 index 000000000..13ca8b9fd --- /dev/null +++ b/src/main/java/pascal/taie/util/Timer.java @@ -0,0 +1,146 @@ +/* + * 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.util; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.time.Duration; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Supplier; + +/** + * A utility class for timing tasks and logging elapsed time. + * + *

Note: This class is deprecated and will be removed in a future version. + * Please use {@link Monitor} instead, which provides enhanced monitoring capabilities + * including CPU and memory usage tracking in addition to elapsed time. + * + * @deprecated Use {@link Monitor} instead for improved monitoring with CPU and memory tracking. + * @see Monitor + */ +@Deprecated(forRemoval = true) +public class Timer { + + private static final Logger logger = LogManager.getLogger(Timer.class); + + private final String name; + + private long elapsedTime = 0; + + private long startTime; + + private boolean inCounting = false; + + public Timer(String name) { + this.name = name; + } + + public void start() { + if (!inCounting) { + inCounting = true; + startTime = System.currentTimeMillis(); + } + } + + public void stop() { + if (inCounting) { + elapsedTime += System.currentTimeMillis() - startTime; + inCounting = false; + } + } + + public float inSecond() { + return elapsedTime / 1000F; + } + + public void clear() { + elapsedTime = 0; + inCounting = false; + } + + @Override + public String toString() { + return String.format("[%s] elapsed time: %.2fs", + name, inSecond()); + } + + /** + * Runs a task, log the elapsed time, and return the result. + * + * @param task task to be executed + * @param taskName name of the task + */ + public static T runAndCount(Supplier task, String taskName, Level level) { + logger.info("{} starts ...", taskName); + Timer timer = new Timer(taskName); + timer.start(); + T result = task.get(); + timer.stop(); + logger.log(level, "{} finishes, elapsed time: {}", taskName, + String.format("%.2fs", timer.inSecond())); + return result; + } + + /** + * Runs a task and log the elapsed time. + * + * @param task task to be executed + * @param taskName taskName of the task + */ + public static void runAndCount(Runnable task, String taskName) { + runAndCount(task, taskName, Level.INFO); + } + + public static void runAndCount(Runnable task, String taskName, Level level) { + runAndCount(() -> { + task.run(); + return null; + }, taskName, level); + } + + /** + * Runs a task with given time budget. + */ + public static void runWithTimeout(Runnable task, long seconds) { + Duration timeout = Duration.ofSeconds(seconds); + ExecutorService executor = Executors.newSingleThreadExecutor(); + Future handler = executor.submit(task); + try { + handler.get(timeout.getSeconds(), TimeUnit.SECONDS); + } catch (TimeoutException e) { + e.printStackTrace(); + System.exit(1); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } finally { + executor.shutdown(); + } + } +}