From b53b0b01519707d43d098e0c8676cbbb465ad490 Mon Sep 17 00:00:00 2001 From: michaeloffner Date: Fri, 13 Dec 2024 20:34:25 +0100 Subject: [PATCH] add BIF "LogAllThreads" --- .../lucee/runtime/engine/CFMLEngineImpl.java | 3 +- .../java/lucee/runtime/engine/Controler.java | 8 +- .../functions/system/LogAllThreads.java | 65 ++++++++++++++++ .../bytecode/expression/var/VariableImpl.java | 6 +- core/src/main/java/resource/fld/core-base.fld | 75 +++++++++++++++++++ loader/build.xml | 2 +- loader/pom.xml | 2 +- 7 files changed, 149 insertions(+), 12 deletions(-) create mode 100644 core/src/main/java/lucee/runtime/functions/system/LogAllThreads.java diff --git a/core/src/main/java/lucee/runtime/engine/CFMLEngineImpl.java b/core/src/main/java/lucee/runtime/engine/CFMLEngineImpl.java index aa569037bd..7d223d1955 100644 --- a/core/src/main/java/lucee/runtime/engine/CFMLEngineImpl.java +++ b/core/src/main/java/lucee/runtime/engine/CFMLEngineImpl.java @@ -234,7 +234,8 @@ private CFMLEngineImpl(CFMLEngineFactory factory, BundleCollection bc) { break; } // Call the dumpThreadPositions method - Controler.dumpThreadPositions(dumpPath); + Resource target = ResourcesImpl.getFileResourceProvider().getResource(dumpPath); + Controler.dumpThreadPositions(target); // Pause for the specified interval SystemUtil.sleep(interval); diff --git a/core/src/main/java/lucee/runtime/engine/Controler.java b/core/src/main/java/lucee/runtime/engine/Controler.java index 8c87a4df81..3ee8accb53 100755 --- a/core/src/main/java/lucee/runtime/engine/Controler.java +++ b/core/src/main/java/lucee/runtime/engine/Controler.java @@ -35,7 +35,6 @@ import lucee.commons.io.log.Log; import lucee.commons.io.log.LogUtil; import lucee.commons.io.res.Resource; -import lucee.commons.io.res.ResourcesImpl; import lucee.commons.io.res.filter.ExtensionResourceFilter; import lucee.commons.io.res.filter.ResourceFilter; import lucee.commons.io.res.util.ResourceUtil; @@ -198,8 +197,7 @@ else if (time > TIMEOUT) { } } - public static void dumpThreadPositions(String path) throws IOException { - Resource target = ResourcesImpl.getFileResourceProvider().getResource(path); + public static void dumpThreadPositions(Resource target) throws IOException { StackTraceElement[] stes; String line; @@ -239,10 +237,6 @@ private static void dumpThreads() { } - public static void main(String[] args) throws IOException { - dumpThreadPositions("/Users/mic/Tmp3/tmp/data.jsonl"); - } - private void control(CFMLFactoryImpl[] factories, boolean firstRun, Log log) { long now = System.currentTimeMillis(); boolean do10Seconds = last10SecondsInterval + 10000 < now; diff --git a/core/src/main/java/lucee/runtime/functions/system/LogAllThreads.java b/core/src/main/java/lucee/runtime/functions/system/LogAllThreads.java new file mode 100644 index 0000000000..a27dcad796 --- /dev/null +++ b/core/src/main/java/lucee/runtime/functions/system/LogAllThreads.java @@ -0,0 +1,65 @@ + +package lucee.runtime.functions.system; + +import java.io.IOException; + +import lucee.commons.io.SystemUtil; +import lucee.commons.io.res.Resource; +import lucee.commons.io.res.util.ResourceUtil; +import lucee.runtime.PageContext; +import lucee.runtime.engine.Controler; +import lucee.runtime.exp.FunctionException; +import lucee.runtime.exp.PageException; +import lucee.runtime.ext.function.Function; +import lucee.runtime.op.Caster; + +public final class LogAllThreads implements Function { + + private static final long serialVersionUID = -1922482127354478506L; + + public static String call(PageContext pc, String path) throws PageException { + return call(pc, path, 10, 10000); + } + + public static String call(PageContext pc, String path, Number interval) throws PageException { + return call(pc, path, interval, 10000); + } + + public static String call(PageContext pc, String path, Number interval, Number duration) throws PageException { + Resource res = ResourceUtil.toResourceNotExisting(pc, path); + if (!res.getParentResource().isDirectory()) + throw new FunctionException(pc, "LogAllThreads", 1, "path", "the directory [" + res.getParent() + "] for your log file [" + path + "] does not exist."); + + int tmp = Caster.toIntValue(interval); + if (tmp < 1) tmp = 10; + final long interv = tmp; + + long ltmp = Caster.toLongValue(duration); + if (ltmp < 1) ltmp = 10000; + final long dur = ltmp; + + long start = System.currentTimeMillis(); + // Create a new thread to run the task + Thread thread = new Thread(() -> { + while (true) { + try { + if ((start + dur) < System.currentTimeMillis()) { + break; + } + // Call the dumpThreadPositions method + Controler.dumpThreadPositions(res); + + // Pause for the specified interval + SystemUtil.sleep(interv); + } + catch (IOException e) { + SystemUtil.sleep(1000); + } + } + }); + + // Start the thread + thread.start(); + return null; + } +} \ No newline at end of file diff --git a/core/src/main/java/lucee/transformer/bytecode/expression/var/VariableImpl.java b/core/src/main/java/lucee/transformer/bytecode/expression/var/VariableImpl.java index aab24849a1..0312a4b6f6 100644 --- a/core/src/main/java/lucee/transformer/bytecode/expression/var/VariableImpl.java +++ b/core/src/main/java/lucee/transformer/bytecode/expression/var/VariableImpl.java @@ -568,8 +568,10 @@ static Type _writeOutFirstBIF(BytecodeContext bc, BIF bif, int mode, boolean las } ArrayList list = bif.getFlf().getArg(); lucee.transformer.dynamic.meta.Method method = getMethod(clazzz, list, rtnType, bc, line); - if (method == null) throw new TransformerException(bc, "not matching method founf for function [" + bif.getName() + "]", line); - + if (method == null) { + throw new TransformerException(bc, "not matching method found for function [" + bif.getName() + "] in class [" + clazzz.getDeclaringClass().getName() + + "] with return type [" + rtnType.getClassName() + "], when developing clear dynclasses folder", line); + } Iterator it = list.iterator(); argTypes = method.getArgumentTypes(); diff --git a/core/src/main/java/resource/fld/core-base.fld b/core/src/main/java/resource/fld/core-base.fld index 8b26c2ed87..e9454a321f 100755 --- a/core/src/main/java/resource/fld/core-base.fld +++ b/core/src/main/java/resource/fld/core-base.fld @@ -9533,6 +9533,81 @@ The following things are considered to be empty: number + + + LogAllThreads + lucee.runtime.functions.system.LogAllThreads + system,log,debugging,threads,performance,analysis + + Creates detailed thread stack trace logs in JSONL format for performance analysis and debugging. + This function captures stack traces from all running threads at specified intervals for a given duration. + It executes asynchronously, returning immediately after starting the logging process, making it ideal + for analyzing specific code segments by initiating logging just before the target code execution. + + The output format is JSONL (JSON Lines), where each line represents a separate JSON object containing: + - Timestamp offset in milliseconds from 1/1/1970 00:00:00 UTC (Unix 0) + - Complete stack trace of each thread's current location + + This data can be used for: + - Performance bottleneck identification + - Thread behavior analysis + - Deadlock detection + - Resource usage patterns + + + + path + string + true + + Full file path where the log will be written. The file should have a '.jsonl' extension + for proper identification as a JSON Lines format file. The function will create the file + if it doesn't exist, or append to it if it does. + + Example: "/var/log/lucee/thread_analysis.jsonl" + + + + + interval + number + false + 10 + + The time interval (in milliseconds) between stack trace captures. Lower values provide + more detailed analysis but generate larger log files and may impact performance. + + Recommended ranges: + - 1-10ms: Very detailed analysis, higher overhead + - 10-100ms: Balanced detail and performance + - 100ms+: Lower detail, minimal performance impact + + + + + duration + number + false + 10000 + + Total duration (in milliseconds) for which the function will collect thread data. + After this period, logging automatically stops. + + Common durations: + - 1000-5000ms: Quick snapshots + - 10000ms: Standard analysis period + - 30000ms+: Extended analysis for complex operations + + + + + void + + Returns immediately without waiting for logging completion. The logging process + continues in the background for the specified duration. + + + diff --git a/loader/build.xml b/loader/build.xml index b0d5803094..fcfed06dad 100644 --- a/loader/build.xml +++ b/loader/build.xml @@ -2,7 +2,7 @@ - + diff --git a/loader/pom.xml b/loader/pom.xml index c71405480c..565152b220 100644 --- a/loader/pom.xml +++ b/loader/pom.xml @@ -3,7 +3,7 @@ org.lucee lucee - 7.0.0.80-SNAPSHOT + 7.0.0.81-SNAPSHOT jar Lucee Loader Build