diff --git a/ant/build-core.xml b/ant/build-core.xml index 47d9c682e5..64dd2f7e3c 100644 --- a/ant/build-core.xml +++ b/ant/build-core.xml @@ -647,6 +647,8 @@ + + + + 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 + 0 + + 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 diff --git a/core/src/main/java/resource/setting/sysprop-envvar.json b/core/src/main/java/resource/setting/sysprop-envvar.json index 25ba8c6a70..fe31d2867f 100644 --- a/core/src/main/java/resource/setting/sysprop-envvar.json +++ b/core/src/main/java/resource/setting/sysprop-envvar.json @@ -378,6 +378,8 @@ "sysprop": "lucee.dump.threads", "envvar": "LUCEE_DUMP_THREADS", "desc": "Used for debugging, when enabled, it will dump out running threads to the console via the background controller thread" + }, + { "sysprop": "lucee.scope.local.capacity", "envvar": "LUCEE_SCOPE_LOCAL_CAPACITY", "desc": "Sets the initial capacity (size) for the local scope hashmap" @@ -386,5 +388,15 @@ "sysprop": "lucee.scope.arguments.capacity", "envvar": "LUCEE_SCOPE_ARGUMENTS_CAPACITY", "desc": "Sets the initial capacity (size) for the arguments scope hashmap" + }, + { + "sysprop": "lucee.cache.variableKeys", + "envvar": "LUCEE_CACHE_VARIABLEKEYS", + "desc": "Sets the max number of variable names (keys) to cache" + }, + { + "sysprop": "lucee.threads.maxDefault", + "envvar": "LUCEE_THREADS_MAXDEFAULT", + "desc": "Sets the default max number of parallel threads, default 20" } ] \ No newline at end of file diff --git a/loader/build.xml b/loader/build.xml index a2eec916d3..4ac0aafc4f 100644 --- a/loader/build.xml +++ b/loader/build.xml @@ -2,7 +2,7 @@ - + diff --git a/loader/pom.xml b/loader/pom.xml index 1fa6f416ff..0edcc434f8 100644 --- a/loader/pom.xml +++ b/loader/pom.xml @@ -3,7 +3,7 @@ org.lucee lucee - 7.0.0.85-SNAPSHOT + 7.0.0.86-SNAPSHOT jar Lucee Loader Build diff --git a/loader/src/main/java/lucee/loader/engine/CFMLEngineFactory.java b/loader/src/main/java/lucee/loader/engine/CFMLEngineFactory.java index 06f0a96de1..91f3c0bd33 100755 --- a/loader/src/main/java/lucee/loader/engine/CFMLEngineFactory.java +++ b/loader/src/main/java/lucee/loader/engine/CFMLEngineFactory.java @@ -290,8 +290,6 @@ public void shutdownFelix() throws BundleException { BundleCollection bc = singelton.getBundleCollection(); if (bc == null || bc.felix == null) return; - - // BundleLoader.removeBundles(bc, false); BundleUtil.stop(felix, false); } @@ -744,12 +742,12 @@ public Felix getFelix(final File cacheRootDir, Map config) throw // Enables or disables bundle cache locking, which is used to prevent concurrent access to the // bundle cache. - extend(config, "felix.cache.locking", "false", false); + extend(config, "felix.cache.locking", null, false); extend(config, "org.osgi.framework.executionenvironment", null, false); extend(config, "org.osgi.framework.storage", null, false); - extend(config, "org.osgi.framework.storage.clean", "none", false); + extend(config, "org.osgi.framework.storage.clean", Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT, false); extend(config, Constants.FRAMEWORK_BUNDLE_PARENT, Constants.FRAMEWORK_BUNDLE_PARENT_FRAMEWORK, false); - // felix.cache.bufsize + boolean isNew = false; // felix.cache.rootdir if (Util.isEmpty((String) config.get("felix.cache.rootdir"))) { @@ -764,7 +762,7 @@ public Felix getFelix(final File cacheRootDir, Map config) throw extend(config, Constants.FRAMEWORK_SYSTEMPACKAGES, null, true); extend(config, Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, null, true); extend(config, "felix.cache.filelimit", null, false); - extend(config, "felix.cache.bufsize", "65536", false); // 64kb default is 8kb + extend(config, "felix.cache.bufsize", null, false); extend(config, "felix.bootdelegation.implicit", null, false); extend(config, "felix.systembundle.activators", null, false); extend(config, "org.osgi.framework.startlevel.beginning", null, false); @@ -789,12 +787,14 @@ public Felix getFelix(final File cacheRootDir, Map config) throw } } - /* - * final StringBuilder sb = new StringBuilder("Loading felix with config:"); final - * Iterator> it = config.entrySet().iterator(); Entry e; while - * (it.hasNext()) { e = it.next(); - * sb.append("\n- ").append(e.getKey()).append(':').append(e.getValue()); } System.err.println(sb); - */ + final StringBuilder sb = new StringBuilder("Loading felix with config:"); + final Iterator> it = config.entrySet().iterator(); + Entry e; + while (it.hasNext()) { + e = it.next(); + sb.append("\n- ").append(e.getKey()).append(':').append(e.getValue()); + } + // log(Logger.LOG_INFO, sb.toString()); felix = new Felix(config); try { @@ -804,7 +804,9 @@ public Felix getFelix(final File cacheRootDir, Map config) throw // this could be cause by an invalid felix cache, so we simply delete it and try again if (!isNew && "Error creating bundle cache.".equals(be.getMessage())) { Util.deleteContent(cacheRootDir, null); + } + } return felix; diff --git a/loader/src/main/java/lucee/loader/util/Util.java b/loader/src/main/java/lucee/loader/util/Util.java index 701ca08982..18cba37a94 100755 --- a/loader/src/main/java/lucee/loader/util/Util.java +++ b/loader/src/main/java/lucee/loader/util/Util.java @@ -58,6 +58,7 @@ public class Util { static { UTF8 = Charset.forName("UTF-8"); } + private static final int QUALIFIER_APPENDIX_SNAPSHOT = 1; private static final int QUALIFIER_APPENDIX_BETA = 2; private static final int QUALIFIER_APPENDIX_RC = 3; @@ -392,7 +393,6 @@ public static void write(File file, String string, Charset charset, boolean appe if (charset == null) { charset = UTF8; } - Writer writer = null; try { writer = getWriter(file, charset, append); @@ -623,27 +623,6 @@ private static Throwable unwrap(Throwable t) { return t; } - public static String getSystemPropOrEnvVar(String name, String defaultValue) { - // env - String value = System.getenv(name); - if (!isEmpty(value)) return value; - - // prop - value = System.getProperty(name); - if (!isEmpty(value)) return value; - - // env 2 - name = convertSystemPropToEnvVar(name); - value = System.getenv(name); - if (!isEmpty(value)) return value; - - return defaultValue; - } - - private static String convertSystemPropToEnvVar(String name) { - return name.replace('.', '_').toUpperCase(); - } - public static void sleep(int time) { try { Thread.sleep(time); diff --git a/test/functions/BitOr.cfc b/test/functions/BitOr.cfc index ba7d28eb8c..f0979ecd2a 100644 --- a/test/functions/BitOr.cfc +++ b/test/functions/BitOr.cfc @@ -44,7 +44,7 @@ component extends="org.lucee.cfml.test.LuceeTestCase"{ application action="update" preciseMath=true; expect( BitOr(2147483647, 1) ).toBe(2147483647); application action="update" preciseMath=false; - expect( BitOr(2147483647, 1) ).toBe(2147483648); + expect( BitOr(2147483647, 1) ).toBe(2147483647); }); it("should return the non-zero value when one number is zero", function() { diff --git a/test/functions/BitSHLN.cfc b/test/functions/BitSHLN.cfc index 0c2c507fb5..1242c47325 100644 --- a/test/functions/BitSHLN.cfc +++ b/test/functions/BitSHLN.cfc @@ -35,7 +35,7 @@ component extends="org.lucee.cfml.test.LuceeTestCase"{ application action="update" preciseMath=true; assertEquals("4294967294", toString(BitSHLN(2147483647, 1))); // 2147483647 << 1 = 0 (overflow) application action="update" preciseMath=false; - assertEquals("4294967296", toString(BitSHLN(2147483647, 1))); // 2147483647 << 1 = 0 (overflow) + assertEquals("4294967294", toString(BitSHLN(2147483647, 1))); // 2147483647 << 1 = 0 (overflow) }); it(title="Checking BitSHLN() function with negative shift", body = function(currentSpec) { diff --git a/test/functions/BitSHRN.cfc b/test/functions/BitSHRN.cfc index d637dc384e..e9568fbe63 100644 --- a/test/functions/BitSHRN.cfc +++ b/test/functions/BitSHRN.cfc @@ -35,7 +35,7 @@ component extends="org.lucee.cfml.test.LuceeTestCase"{ application action="update" preciseMath=true; assertEquals("2147483647", BitSHRN(4294967295, 1)); // Large number shifted right application action="update" preciseMath=false; - assertEquals("2147483648", BitSHRN(4294967295, 1)); // Large number shifted right + assertEquals("2147483647", BitSHRN(4294967295, 1)); // Large number shifted right }); it(title="Checking BitSHRN() function with negative numbers", body = function(currentSpec) { diff --git a/test/tickets/LDEV5173.cfc b/test/tickets/LDEV5173.cfc new file mode 100644 index 0000000000..d53bcc6332 --- /dev/null +++ b/test/tickets/LDEV5173.cfc @@ -0,0 +1,24 @@ +component extends = "org.lucee.cfml.test.LuceeTestCase" skip="true" { + function run( testResults, testBox ){ + describe( "Test for LDEV-5173", function() { + beforeEach( function(){ + variables.startingTZ=getTimeZone(); + setTimeZone("IST"); + }); + afterEach( function(){ + setTimeZone(variables.startingTZ?:"UTC"); + }); + it( title="round trip DateTimeFormat/ParseDateTime with format 'epoch'", body=function( currentSpec ) { + var date = DateTimeFormat( datetime="2024/12/11 16:19:54", mask="epoch" ); + expect(toString(parseDateTime(date=date, format="epoch"))).toBe("{ts '2024-12-11 16:19:54'}"); + expect( parseDateTime( date=date, format="epoch" ) ).toBe( date ); + }); + + it( title="round trip DateTimeFormat/ParseDateTime with format 'epochms'", body=function( currentSpec ) { + var date = DateTimeFormat( datetime="2024/12/11 16:19:54", mask="epochms" ); + expect(toString(parseDateTime(date=date, format="epochms"))).toBe("{ts '2024-12-11 16:19:54'}"); + expect( parseDateTime( date=date, format="epochms" ) ).toBe( date ); + }); + } ); + } +} diff --git a/test/tickets/LDEV5198.cfc b/test/tickets/LDEV5198.cfc new file mode 100644 index 0000000000..dd44b3183b --- /dev/null +++ b/test/tickets/LDEV5198.cfc @@ -0,0 +1,32 @@ +component extends = "org.lucee.cfml.test.LuceeTestCase" { + + function beforeAll(){ + variables.preciseMath = getApplicationSettings().preciseMath; + }; + + function afterAll(){ + application action="update" preciseMath=variables.preciseMath; + }; + + function run( testResults, testBox ){ + describe( "LDEV-5198 regression", function(){ + + it( "bit preciseMath=false", function(){ + application action="update" preciseMath=false; + var t= 197; + var num = 9103313; + expect ( bitSHLN( t, 24) ).toBe( 3305111552 ); + expect ( bitOr( num, 3305111552) ).toBe( 3314214865 ); // returns 3314214912 + }); + + it( "bit preciseMath=true", function(){ + application action="update" preciseMath=true; + var t= 197; + var num = 9103313; + expect ( bitSHLN( t, 24) ).toBe( 3305111552 ); + expect ( bitOr( num, 3305111552) ).toBe( 3314214865 ); // returns 3314214912 + }); + } ); + } + +}