Categories: Java

Investigating a Windows ZGC Test Failure

[JDK-8334475] UnsafeIntrinsicsTest.java#ZGenerationalDebug assert(!assert_on_failure) failed: Has low-order bits set – Java Bug System (openjdk.org) was brought to my attention a few months ago. I ran the test on commit b3bf31a0a08d on my x64 machine to see what happens:

cd /c/repos/scratchpad/scripts/java/tests

./run-jtreg-tests.sh /d/java/forks/openjdk/jdk /d/java/forks/openjdk/jdk/build/windows-x86_64-server-slowdebug/jdk  /c/java/binaries/jtreg-7.4+1/lib/jtreg.jar

That script shows the exact command line used to invoke the test:

/d/java/forks/openjdk/jdk/build/windows-x86_64-server-slowdebug/jdk/bin/java -Xmx512m -jar /c/java/binaries/jtreg-7.4+1/lib/jtreg.jar -agentvm -ignore:quiet -automatic -xml -vmoption:-Xmx512m -timeoutFactor:4 -concurrency:1 -testjdk:/d/java/forks/openjdk/jdk/build/windows-x86_64-server-slowdebug/jdk -verbose:fail,error,summary  test/hotspot/jtreg/compiler/gcbarriers/UnsafeIntrinsicsTest.java

The test passed on x64 but I wanted to see how to step into the test code itself so I tried this setup in Visual Studio 2022. Unfortunately, it was not straightforward to break into the correct process when launching the test in Visual Studio 2022.

Command: D:\java\forks\openjdk\jdk\build\windows-x86_64-server-slowdebug\jdk\bin\java.exe

Arguments: -Xmx512m -jar C:/java/binaries/jtreg-7.4+1/lib/jtreg.jar -agentvm -ignore:quiet -automatic -xml -vmoption:-Xmx512m -timeoutFactor:4 -concurrency:1 -testjdk:D:/java/forks/openjdk/jdk/build/windows-x86_64-server-slowdebug/jdk -verbose:fail,error,summary  test/hotspot/jtreg/compiler/gcbarriers/UnsafeIntrinsicsTest.java

Working Dir: D:/java/forks/openjdk/jdk

Test Execution Sequence

I decided to try to understand the test execution sequence and started by looking at the output generated when running the jtreg test. Notice that the CreateCoredumpOnCrash product flag is disabled. The first thing I did was to enable it so that there is a core dump to examine.

D:\java\forks\openjdk\jdk\build\windows-x86_64-server-slowdebug\jdk\bin\java 
-Dtest.vm.opts=-Xmx512m 
-Dtest.tool.vm.opts=-J-Xmx512m 
-Dtest.compiler.opts= 
-Dtest.java.opts= 
-Dtest.jdk=D:\java\forks\openjdk\jdk\build\windows-x86_64-server-slowdebug\jdk 
-Dcompile.jdk=D:\java\forks\openjdk\jdk\build\windows-x86_64-server-slowdebug\jdk 
-Dtest.timeout.factor=4.0 
-Dtest.root=D:\java\forks\openjdk\jdk\test\hotspot\jtreg 
-Dtest.name=compiler/gcbarriers/UnsafeIntrinsicsTest.java#ZGenerationalDebug 
-Dtest.file=D:\java\forks\openjdk\jdk\test\hotspot\jtreg\compiler\gcbarriers\UnsafeIntrinsicsTest.java 
-Dtest.src=D:\java\forks\openjdk\jdk\test\hotspot\jtreg\compiler\gcbarriers 
-Dtest.src.path=D:\java\forks\openjdk\jdk\test\hotspot\jtreg\compiler\gcbarriers;D:\java\forks\openjdk\jdk\test\lib 
-Dtest.classes=D:\java\forks\openjdk\jdk\JTwork\classes\compiler\gcbarriers\UnsafeIntrinsicsTest_ZGenerationalDebug.d 
-Dtest.class.path=D:\java\forks\openjdk\jdk\JTwork\classes\compiler\gcbarriers\UnsafeIntrinsicsTest_ZGenerationalDebug.d;D:\java\forks\openjdk\jdk\JTwork\classes\test\lib 
-Dtest.class.path.prefix=D:\java\forks\openjdk\jdk\JTwork\classes\compiler\gcbarriers\UnsafeIntrinsicsTest_ZGenerationalDebug.d;D:\java\forks\openjdk\jdk\test\hotspot\jtreg\compiler\gcbarriers;D:\java\forks\openjdk\jdk\JTwork\classes\test\lib 
-Dtest.modules=java.base/jdk.internal.misc:+open --add-modules java.base --add-exports java.base/jdk.internal.misc=ALL-UNNAMED --add-opens java.base/jdk.internal.misc=ALL-UNNAMED
-Xmx512m
-XX:+UseZGC
-XX:+ZGenerational
-XX:+UnlockDiagnosticVMOptions
-XX:+ZVerifyOops
-XX:ZCollectionInterval=1
-XX:-CreateCoredumpOnCrash
-XX:CompileCommand=dontinline,*::mergeImpl* com.sun.javatest.regtest.agent.MainWrapper D:\java\forks\openjdk\jdk\JTwork\compiler\gcbarriers\UnsafeIntrinsicsTest_ZGenerationalDebug.d\main.0.jta

The core dump wasn’t particularly helpful. I worked on simplifying the test so that only the failing scenarios remained. The log in the JBS issue was interpreted code only so I added the -Xint argument and could still reproduce the failure on Windows AArch64.

I came back to the idea of capturing the full command line for the final java.exe process that runs the test. Is there a way to log process start on Windows to capture all command lines? Copilot cited PowerShell and Command Line Logging | LogRhythm, which suggested enabling the use of Event ID 4688: a new process has been created.

However, the event viewer didn’t have command line arguments when I did this, which is what I needed. I just looked up Event ID 4688 and found 4688(S) A new process has been created. – Windows 10 | Microsoft Learn, which explains that you must enable “Administrative Templates\System\Audit Process Creation\Include command line in process creation events” group policy to include command line in process creation events. Hmm, I definitely didn’t do that when I was investigating this issue. I just tried this and it does the trick!

I went back to deciphering the jtreg test execution output: which class was this com.sun.javatest.regtest.agent.MainWrapper? The only openjdk/jdk match for MainWrapper.java appeared to be jdk/test/hotspot/jtreg/vmTestbase/nsk/share/MainWrapper.java at b3bf31a0a08da679ec2fd21613243fb17b1135a9 · openjdk/jdk (github.com). However, it’s from a different package! Copilot informed me that the MainWrapper class was actually jtreg/src/share/classes/com/sun/javatest/regtest/agent/MainWrapper.java at master · openjdk/jtreg (github.com)

Callstack Investigation

I stepped back to the callstack in the JBS issue and decide to trace it. Here are the links to the sources:

---------------  T H R E A D  ---------------

Current thread (0x0000020efe6a7fb0):  JavaThread "main"             [_thread_in_vm, id=19132, stack(0x0000003c68f00000,0x0000003c69000000) (1024K)]

Stack: [0x0000003c68f00000,0x0000003c69000000]
Native frames: <unavailable>
Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  java.lang.Object.clone()Ljava/lang/Object;+0 java.base
j  java.util.Arrays.copyOfRange([BII)[B+11 java.base
j  java.lang.String.<init>(Ljava/lang/AbstractStringBuilder;Ljava/lang/Void;)V+32 java.base
j  java.lang.StringBuilder.toString()Ljava/lang/String;+16 java.base
j  sun.nio.cs.StandardCharsets.toLower(Ljava/lang/String;)Ljava/lang/String;+121 java.base
j  sun.nio.cs.StandardCharsets.lookup(Ljava/lang/String;)Ljava/nio/charset/Charset;+44 java.base
j  sun.nio.cs.StandardCharsets.charsetForName(Ljava/lang/String;)Ljava/nio/charset/Charset;+6 java.base
j  java.nio.charset.Charset.lookup2(Ljava/lang/String;)Ljava/nio/charset/Charset;+39 java.base
j  java.nio.charset.Charset.lookup(Ljava/lang/String;)Ljava/nio/charset/Charset;+40 java.base
j  java.nio.charset.Charset.isSupported(Ljava/lang/String;)Z+1 java.base
j  java.lang.System.initPhase1()V+37 java.base
v  ~StubRoutines::call_stub 0x0000020e81410180
Lock stack of current Java thread (top to bottom):
LockStack[0]: sun.nio.cs.StandardCharsets 
{0x0000040000018d28} - klass: 'sun/nio/cs/StandardCharsets'
 - ---- fields (total size 5 words):
 - private 'classMap' 'Ljava/util/Map;' @16  null (0x0000000000000000)
 - private 'aliasMap' 'Ljava/util/Map;' @24  null (0x0000000000000000)
 - private 'cache' 'Ljava/util/Map;' @32  null (0x0000000000000000)

There is a generated class, sun.nio.cs.StandardCharsets.charsetForName, in the stack above! I was puzzled about why jdk/src/java.base/share/classes/java/nio/charset/StandardCharsets.java at b3bf31a0a08da679ec2fd21613243fb17b1135a9 · openjdk/jdk (github.com) did not contain that method. This failure reminds me of another failure I was investigating a while back:

$JAVA_HOME/bin/java -jar /c/java/binaries/jtreg-7.4+1/lib/jtreg.jar -agentvm -timeoutFactor:4 -concurrency:4 -verbose:fail,error,summary -testjdk:/d/java/forks/openjdk/jdk/build/windows-x86_64-server-release/jdk -nativepath:/d/java/forks/openjdk/jdk/build/windows-x86_64-server-release/support/test/jdk/jtreg/native/lib test/jdk/java/foreign/TestLinker.java

In the midst of all this wrangling, I decide to write a simple test to spit out the value of the sun.jnu.encoding property. That test runs fine so next step is to use the same flags as the failing jtreg test. However, as I added the ZGC flags to the test command line, I realized that I hadn’t even tried those flags with the java -version. What an oversight! The bug reproduces without running any specific program!

$JAVA_HOME/bin/java -XX:+UseZGC -XX:+ZGenerational -XX:+ZVerifyOops -version

Debugging Minimal Reproducible Failure

Since the assert happens when cloning an array, I decided to find the native implementation of the clone method. Searched for “clone_” and the only relevant hit (from a quick glance) appeared to be in the LinkResolver::check_method_accessability method.

I had been working on the latest commits and decided to try this test on jdk21u. It failed there as well! I tried it on jdk17 but it terminated with “Unrecognized VM option ‘ZGenerational'”. Blaming jdk/src/hotspot/share/gc/shared/gc_globals.hpp at b3bf31a0a08da679ec2fd21613243fb17b1135a9 · openjdk/jdk (github.com) pointed to 8307058: Implementation of Generational ZGC · openjdk/jdk@d20034b (github.com). Looks like jdk21 was the first release with this flag as per [JDK-8307058] Implementation of Generational ZGC – Java Bug System (openjdk.org).

Back to the LinkResolver: it looked like cloning is being dispatched through the Java object but what I had been looking for is a native call. Nothing about cloning at Arrays (Java SE 11 & JDK 11 ) (oracle.com) other than the fact that the method is inherited from the Object class. Nothing helpful in jdk/src/java.base/share/classes/java/util/Arrays.java or jdk/src/java.base/share/classes/java/lang/reflect/Array.java either. jdk/src/java.base/share/native/libjava/Array.c looked promising though. Methods like Java_java_lang_reflect_Array_getByte use the java.lang.reflect.Array JNIEXPORTs declared in jdk/src/hotspot/share/include/jvm.h. Browsing through the corresponding implementations in jdk/src/hotspot/share/prims/jvm.cpp led me to the clone implementation: JVM_Clone! This is the setup I used in the Visual Studio 2022 debugger:

Command: C:/java/forks/openjdk/jdk/build/windows-aarch64-server-slowdebug/jdk/bin/java.exe

Arguments: -XX:+UseZGC -XX:+ZGenerational -XX:+ZVerifyOops -version

Working Dir: C:/java/forks/openjdk/jdk

The failure appeared to be happening in this statement: HeapAccess<>::clone(obj(), new_obj_oop, size). I got this callstack by stepping into the calls in the slowdebug build because the is_valid function is inlined in the fastdebug build, preventing me from setting a breakpoint in it.

jvm.dll!is_valid(zaddress addr, bool assert_on_failure) Line 298	C++
jvm.dll!assert_is_valid(zaddress addr) Line 321	C++
jvm.dll!to_zaddress(unsigned __int64 value) Line 339	C++
jvm.dll!to_zaddress(oopDesc * o) Line 345	C++
jvm.dll!ZBarrierSet::AccessBarrier<270432,ZBarrierSet>::clone_in_heap(oopDesc * src, oopDesc * dst, unsigned __int64 size) Line 433	C++
jvm.dll!AccessInternal::PostRuntimeDispatch<ZBarrierSet::AccessBarrier<270400,ZBarrierSet>,9,270400>::access_barrier(oopDesc * src, oopDesc * dst, unsigned __int64 size) Line 200	C++
jvm.dll!AccessInternal::RuntimeDispatch<270400,oopDesc *,9>::clone_init(oopDesc * src, oopDesc * dst, unsigned __int64 size) Line 349	C++
jvm.dll!AccessInternal::RuntimeDispatch<270400,oopDesc *,9>::clone(oopDesc * src, oopDesc * dst, unsigned __int64 size) Line 533	C++
jvm.dll!AccessInternal::PreRuntimeDispatch::clone<270400>(oopDesc * src, oopDesc * dst, unsigned __int64 size) Line 890	C++
jvm.dll!AccessInternal::clone<262144>(oopDesc * src, oopDesc * dst, unsigned __int64 size) Line 1181	C++
jvm.dll!Access<262144>::clone(oopDesc * src, oopDesc * dst, unsigned __int64 size) Line 212	C++
jvm.dll!JVM_Clone(JNIEnv_ * env, _jobject * handle) Line 698	C++
00000298b2bbf056()	Unknown
00000298a2ee5590()	Unknown
000000c053dfe908()	Unknown

Here’s the stack from stepping into the fastdebug assembly (note that line numbers might be off since this is not slowdebug):

jvm.dll!report_vm_error(const char * file, int line, const char * error_msg, const char * detail_fmt, ...) Line 181	C++
[Inline Frame] jvm.dll!oop::check_oop() Line 89	C++
[Inline Frame] jvm.dll!oop::on_usage() Line 91	C++
[Inline Frame] jvm.dll!oop::obj() Line 103	C++
[Inline Frame] jvm.dll!oop::operator=(const oop &) Line 114	C++
jvm.dll!Copy::pd_conjoint_oops_atomic(const oop * from, oop * to, unsigned __int64 count) Line 120	C++
[Inline Frame] jvm.dll!Copy::pd_conjoint_jlongs_atomic(const __int64 *) Line 106	C++
[Inline Frame] jvm.dll!Copy::conjoint_jlongs_atomic(const __int64 *) Line 142	C++
jvm.dll!AccessInternal::arraycopy_conjoint_atomic<__int64>(__int64 * src, __int64 * dst, unsigned __int64 length) Line 104	C++
jvm.dll!RawAccessBarrier<270432>::clone(oop src, oop dst, unsigned __int64 size) Line 331	C++
[Inline Frame] jvm.dll!BarrierSet::AccessBarrier<270432,ZBarrierSet>::clone_in_heap(oop) Line 313	C++
jvm.dll!ZBarrierSet::AccessBarrier<270432,ZBarrierSet>::clone_in_heap(oop src, oop dst, unsigned __int64 size) Line 451	C++
jvm.dll!AccessInternal::PostRuntimeDispatch<ZBarrierSet::AccessBarrier<270400,ZBarrierSet>,9,270400>::access_barrier(oop src, oop dst, unsigned __int64 size) Line 200	C++
jvm.dll!AccessInternal::RuntimeDispatch<270400,oop,9>::clone_init(oop src, oop dst, unsigned __int64 size) Line 349	C++
[Inline Frame] jvm.dll!AccessInternal::RuntimeDispatch<270400,oop,9>::clone(oop) Line 532	C++
[Inline Frame] jvm.dll!AccessInternal::PreRuntimeDispatch::clone(oop) Line 889	C++
[Inline Frame] jvm.dll!AccessInternal::clone(oop) Line 1180	C++
jvm.dll!Access<262144>::clone(oop src, oop dst, unsigned __int64 size) Line 211	C++
jvm.dll!JVM_Clone(JNIEnv_ * env, _jobject * handle) Line 698	C++
000001a156d3a45c()	Unknown

ZBarrierSet::AccessBarrier<270432,ZBarrierSet>::clone_in_heap is interesting because it verifies that the source is a valid z-address! Let’s take a look at the memory region and see what is there…

I looked at this and searched for UTF-8 byte 0xBD meaning – Search (bing.com) but that didn’t turn up anything meaningful. I noticed that count is 3 on my Aarch64 device so we are copying 3 oops. Was this expected given that the count is 4 on Windows x64? More importantly though, the big question I have now is why doesn’t slowdebug step into the oop checking code when pressing F11 on line 120 in the screenshot? I expected the behavior of oop::on_usage to be different, not that it wouldn’t be called at all! When browsing the sources in VSCode on my x64 desktop, I clicked on the oop type (of the src argument) and it took me to a typedef of class oopDesc*. That’s when I spotted the CHECK_UNHANDLED_OOPS ifndef. The fastdebug build must have this defined! The only non-cpp/hpp file that contains CHECK_UNHANDLED_OOPS is jdk/make/hotspot/lib/JvmFlags.gmk. Sure enough, it is only defined for fastdebug. This means that I should be able to enable it for slowdebug and release and verify whether the behavior is present there. We can therefore configure a slowdebug build with the --with-extra-cflags=-DCHECK_UNHANDLED_OOPS option.

date; time bash configure --with-jtreg=/cygdrive/c/java/binaries/jtreg-7.4+1 --with-gtest=/cygdrive/c/repos/googletest --with-boot-jdk=/cygdrive/c/java/binaries/jdk/x64/jdk-22.0.1+8 --openjdk-target=aarch64-unknown-cygwin --with-debug-level=slowdebug --with-extra-cflags=-DCHECK_UNHANDLED_OOPS

time /cygdrive/c/repos/scratchpad/scripts/java/cygwin/build-jdk.sh windows aarch64 0 slowdebug

Looking at the actual pointer, I noticed that its value is the data “cp1252..”. After further investigation, I conclude that the bug is that we’re calling oops::operator= instead of just copying the values! I test a fix that simply copies the values directly and it works! The test passes even with the -DCHECK_UNHANDLED_OOPS option!

On to the next question: is there anything else using the pd_conjoint_oops_atomic function (and will it be negatively affected by my change)? While searching for “pd_conjoint_oops_atomic”, I notice that some platforms have an assert that oops == long or smth like that. There are 2 users of pd_conjoint_oops_atomic:

  1. Copy::pd_conjoint_oops_atomic > AccessInternal::arraycopy_conjoint_oops > RawAccessBarrierArrayCopy::arraycopy. The last method’s definition is based on templates so I continue with a regex search for “Raw::.*arraycopy”. Many of the results are related to arraycopy_in_heap. Looking at all the barrierSet results makes me realize I could just search JBS for “arraycopy_in_heap”.
  2. pd_arrayof_conjoint_oops in jdk/src/hotspot/os_cpu/windows_aarch64/copy_windows_aarch64.hpp.

One idea is to run Java programs on a build linked with /PROFILE (Performance Tools Profiler) i.e. configured via --with-extra-ldflags=-profile to enable collection of code coverage data then confirming that those functions are executed. That seems cumbersome though so I try to create some array copying code to see if I can get to those functions (using breakpoints but none are hit). After taking a break: I wonder if I can just search from the bottom instead? Looking in jvm.cpp for copy reveals the JVM_ArrayCopy function. Here is my Java program:

public class CopyArray {
    public static void main(String[] args) {
        int length = 0xdeadc0d;
        int srcPos = 0;

        if (args.length > 0) {
            try {
                int userLength = Integer.parseInt(args[0]);
                length = userLength;
            }
            catch (Throwable e) {
                System.err.println("Ignoring invalid user arguments.");
            }
        }

        byte[] src = new byte[length];

        for (int i = 0; i < src.length; i++) {
            src[i] = (byte)(i % 256);
        }
        byte[] dest = new byte[length];
        System.arraycopy(src, srcPos, dest, 0, length);
    }
}

I debugged the JVM running this program on my x64 machine. This hits the target function, JVM_ArrayCopy but there are so many callers. I have to set a condition on the breakpoint (hence the magic value of the length above) before I can step in to see where my call goes. Here are the source paths (note the different commit)

jvm.dll!Copy::pd_conjoint_bytes_atomic(const void * from, void * to, unsigned __int64 count) Line 119	C++
jvm.dll!Copy::conjoint_memory_atomic(const void * from, void * to, unsigned __int64 size) Line 53	C++
jvm.dll!AccessInternal::arraycopy_conjoint_atomic<void>(void * src, void * dst, unsigned __int64 length) Line 164	C++
jvm.dll!RawAccessBarrierArrayCopy::arraycopy<136585312,void>(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, void * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, void * dst_raw, unsigned __int64 length) Line 298	C++
jvm.dll!RawAccessBarrier<136585312>::arraycopy<void>(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, void * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, void * dst_raw, unsigned __int64 length) Line 308	C++
jvm.dll!AccessInternal::PreRuntimeDispatch::arraycopy<136587328,void>(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, void * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, void * dst_raw, unsigned __int64 length) Line 834	C++
jvm.dll!AccessInternal::PreRuntimeDispatch::arraycopy<136585280,void>(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, void * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, void * dst_raw, unsigned __int64 length) Line 867	C++
jvm.dll!AccessInternal::arraycopy_reduce_types<136585280,void>(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, void * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, void * dst_raw, unsigned __int64 length) Line 1008	C++
jvm.dll!AccessInternal::arraycopy<136577024,void>(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, const void * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, void * dst_raw, unsigned __int64 length) Line 1172	C++
jvm.dll!Access<136577024>::arraycopy<void>(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, const void * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, void * dst_raw, unsigned __int64 length) Line 147	C++
jvm.dll!ArrayAccess<134217728>::arraycopy<void>(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, unsigned __int64 length) Line 301	C++
jvm.dll!TypeArrayKlass::copy_array(arrayOop s, int src_pos, arrayOop d, int dst_pos, int length, JavaThread * __the_thread__) Line 170	C++
jvm.dll!JVM_ArrayCopy(JNIEnv_ * env, _jclass * ignored, _jobject * src, int src_pos, _jobject * dst, int dst_pos, int length) Line 307	C++
00000244ea690702()	Unknown

Copy::conjoint_memory_atomic is interesting because it has a comment indicating that copying bytes is not aligned and so there is no need to be atomic. The if statements in that method indicate that I can change the size of elements in the array to call different paths. Looks like I need to create an array of objects.

/**
export JAVA_HOME=~/java/binaries/jdk/x64/jdk-21.0.2+13
$JAVA_HOME/bin/javac CopyArray.java
$JAVA_HOME/bin/java CopyArray
 */

public class CopyArray {
    public static void main(String[] args) {
        int length = 0xdead;
        int srcPos = 0;

        if (args.length > 0) {
            try {
                int userLength = Integer.parseInt(args[0]);
                length = userLength;
            }
            catch (Throwable e) {
                System.err.println("Ignoring invalid user arguments.");
            }
        }

        Object[] src = new Object[length];

        for (int i = 0; i < src.length; i++) {
            src[i] = new Object();
        }
        Object[] dest = new Object[length];
        System.arraycopy(src, srcPos, dest, 0, length);
    }
}

Now we are closer to the array_oops code I was trying to hit:

jvm.dll!Copy::pd_conjoint_jints_atomic(const int * from, int * to, unsigned __int64 count) Line 52	C++
jvm.dll!Copy::conjoint_oops_atomic(const narrowOop * from, narrowOop * to, unsigned __int64 count) Line 155	C++
jvm.dll!AccessInternal::arraycopy_conjoint_oops(narrowOop * src, narrowOop * dst, unsigned __int64 length) Line 54	C++
jvm.dll!RawAccessBarrierArrayCopy::arraycopy<50331750,HeapWordImpl *>(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, HeapWordImpl * * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, HeapWordImpl * * dst_raw, unsigned __int64 length) Line 234	C++
jvm.dll!RawAccessBarrier<52715622>::arraycopy<enum narrowOop>(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, narrowOop * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, narrowOop * dst_raw, unsigned __int64 length) Line 308	C++
jvm.dll!RawAccessBarrier<52715622>::oop_arraycopy<enum narrowOop>(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, narrowOop * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, narrowOop * dst_raw, unsigned __int64 length) Line 130	C++
jvm.dll!ModRefBarrierSet::AccessBarrier<35938406,CardTableBarrierSet>::oop_arraycopy_in_heap<enum narrowOop>(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, narrowOop * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, narrowOop * dst_raw, unsigned __int64 length) Line 109	C++
jvm.dll!AccessInternal::PostRuntimeDispatch<CardTableBarrierSet::AccessBarrier<35938406,CardTableBarrierSet>,8,35938406>::oop_access_barrier<HeapWordImpl *>(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, HeapWordImpl * * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, HeapWordImpl * * dst_raw, unsigned __int64 length) Line 142	C++
jvm.dll!AccessInternal::RuntimeDispatch<35938374,HeapWordImpl *,8>::arraycopy(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, HeapWordImpl * * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, HeapWordImpl * * dst_raw, unsigned __int64 length) Line 517	C++
jvm.dll!AccessInternal::PreRuntimeDispatch::arraycopy<35938374,HeapWordImpl *>(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, HeapWordImpl * * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, HeapWordImpl * * dst_raw, unsigned __int64 length) Line 871	C++
jvm.dll!AccessInternal::arraycopy_reduce_types<35938372>(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, HeapWordImpl * * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, HeapWordImpl * * dst_raw, unsigned __int64 length) Line 1018	C++
jvm.dll!AccessInternal::arraycopy<35913732,HeapWordImpl *>(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, HeapWordImpl * const * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, HeapWordImpl * * dst_raw, unsigned __int64 length) Line 1172	C++
jvm.dll!Access<35913728>::oop_arraycopy<HeapWordImpl *>(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, HeapWordImpl * const * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, HeapWordImpl * * dst_raw, unsigned __int64 length) Line 136	C++
jvm.dll!ArrayAccess<33554432>::oop_arraycopy(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, unsigned __int64 length) Line 327	C++
jvm.dll!ObjArrayKlass::do_copy(arrayOop s, unsigned __int64 src_offset, arrayOop d, unsigned __int64 dst_offset, int length, JavaThread * __the_thread__) Line 197	C++
jvm.dll!ObjArrayKlass::copy_array(arrayOop s, int src_pos, arrayOop d, int dst_pos, int length, JavaThread * __the_thread__) Line 282	C++
jvm.dll!JVM_ArrayCopy(JNIEnv_ * env, _jclass * ignored, _jobject * src, int src_pos, _jobject * dst, int dst_pos, int length) Line 307	C++
00000202d3f00502()	Unknown
00000202cc269950()	Unknown

In this call stack, the arraycopy_conjoint_oops(narrowOop* src, narrowOop* dst, size_t length) implementation that is called has narrow oops because of the branch in ObjArrayKlass::copy_array. Launch the application using these arguments instead:

C:\java\forks\openjdk\jdk\build\windows-x86_64-server-slowdebug\jdk\bin\java.exe -XX:-UseCompressedOops CopyArray

Now the code block of interest is hit! Hmm, I’m realizing that I should have been explicit about the collector to use. This was debugging the G1 collector (chosen ergonomically).

Notice that ZGC has different code for copying the array on Aarch64 (my fix-jlongs branch based on openjdk/jdk at ee839b7f0ebe471d3877cddd2c87019ccb8ee5ae).

jvm.dll!ZBarrierSet::AccessBarrier<52715590,ZBarrierSet>::oop_arraycopy_in_heap_no_check_cast(zpointer * dst, zpointer * src, unsigned __int64 length) Line 371	C++
jvm.dll!ZBarrierSet::AccessBarrier<35938374,ZBarrierSet>::oop_arraycopy_in_heap(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, zpointer * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, zpointer * dst_raw, unsigned __int64 length) Line 403	C++
jvm.dll!ZBarrierSet::AccessBarrier<35938374,ZBarrierSet>::oop_arraycopy_in_heap(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, oop * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, oop * dst_raw, unsigned __int64 length) Line 128	C++
jvm.dll!AccessInternal::PostRuntimeDispatch<ZBarrierSet::AccessBarrier<35938374,ZBarrierSet>,8,35938374>::oop_access_barrier<HeapWordImpl *>(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, HeapWordImpl * * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, HeapWordImpl * * dst_raw, unsigned __int64 length) Line 142	C++
jvm.dll!AccessInternal::RuntimeDispatch<35938374,HeapWordImpl *,8>::arraycopy(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, HeapWordImpl * * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, HeapWordImpl * * dst_raw, unsigned __int64 length) Line 517	C++
jvm.dll!AccessInternal::PreRuntimeDispatch::arraycopy<35938374,HeapWordImpl *>(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, HeapWordImpl * * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, HeapWordImpl * * dst_raw, unsigned __int64 length) Line 871	C++
jvm.dll!AccessInternal::arraycopy_reduce_types<35938372>(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, HeapWordImpl * * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, HeapWordImpl * * dst_raw, unsigned __int64 length) Line 1018	C++
jvm.dll!AccessInternal::arraycopy<35913732,HeapWordImpl *>(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, HeapWordImpl * const * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, HeapWordImpl * * dst_raw, unsigned __int64 length) Line 1172	C++
jvm.dll!Access<35913728>::oop_arraycopy<HeapWordImpl *>(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, HeapWordImpl * const * src_raw, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, HeapWordImpl * * dst_raw, unsigned __int64 length) Line 136	C++
jvm.dll!ArrayAccess<33554432>::oop_arraycopy(arrayOop src_obj, unsigned __int64 src_offset_in_bytes, arrayOop dst_obj, unsigned __int64 dst_offset_in_bytes, unsigned __int64 length) Line 327	C++
jvm.dll!ObjArrayKlass::do_copy(arrayOop s, unsigned __int64 src_offset, arrayOop d, unsigned __int64 dst_offset, int length, JavaThread * __the_thread__) Line 198	C++
jvm.dll!ObjArrayKlass::copy_array(arrayOop s, int src_pos, arrayOop d, int dst_pos, int length, JavaThread * __the_thread__) Line 290	C++
jvm.dll!JVM_ArrayCopy(JNIEnv_ * env, _jclass * ignored, _jobject * src, int src_pos, _jobject * dst, int dst_pos, int length) Line 308	C++
000002602e8c06a8()	Unknown

I need to turn off compressed oops with the serial collector as well but it looks like there is no check_oop_function for the serial collector. That said, this exploration of array copying code was insightful, showing how the data type sizes determine which path is taken for copying primitives and objects. There didn’t appear to be any red flags about removing the oop::operator= usage so I opened 8334475: UnsafeIntrinsicsTest.java#ZGenerationalDebug assert(!assert_on_failure) failed: Has low-order bits set by swesonga · Pull Request #20390 · openjdk/jdk (github.com) to fix the assertion failure. The most interesting part of this investigation was that the bad address was a data value (cp1252) staring right at me and I missed it. This was quite educational for me though.


Categories: Equine

Used Truck and Trailer Purchase Notes

One of the downsides of horse ownership is the cost. The cost of the horse is just the starting point. Transporting the horse is a non-trivial cost. We needed to buy a trailer and many of the trailers we looked at are heavy enough that we needed to buy a truck as well. This raised the question of what the minimum required towing capacity would be. It’s strange that these trucks are classified using tons. What Does Half-Ton, Three-Quarter-Ton, One-Ton Mean When Talking About Pickup Trucks? | Cars.com gives me the impression that tonnage is a historical artifact of payload measurement. A ton seems to be a reference to a Short ton – Wikipedia. It is also interesting that Toyota and Nissan don’t really have offerings above the half-ton classification. The approximate weight of the trailer (and the horse) that I want to transport (or alternatively, our camper) requires at least a 3/4-ton truck.

I can only afford used trucks though, so which models should I avoid? bad years for cars – Google Search did not have as many helpful results as I had hoped for. Used Cars to Avoid in 2024 | U.S. News (usnews.com) only covered 2019 and newer models. At least Worst Vehicles | CarComplaints.com had a list of vehicles to avoid. Used cars to avoid, ranked by Consumer Reports – Autoblog: Car News, Reviews and Buying Guides also had such a list and also recommended a pre-purchase inspection. I read the list of tips they linked to: How to buy a used car — 9 tips for the best deal – Autoblog: Car News, Reviews and Buying Guides. I was left wondering how you would know if there was a lean on the car? The title should show this (as per Guide to Car Liens: Lien Titles, Lienholders & More – CARFAX). It also suggests a Carfax Vehicle History Report – sounds like a good idea, not sure how costly though. Based on these resources, I concluded that we need to have the title and a bill of sale. Some due diligence related to the title: all persons on the title must sign/release the interest on the back of the title. The seller needs to print their name and sign beneath their printed name. At the end of the day, it turns out that we don’t really need a bill of sale.

One of the trucks we considered is the 2012 F 150 Towing Capacity Full Guide (with Charts) (truckauxiliary.com). The concern here was that even though it had a towing package, it was still a half-ton truck. It was also likely to be outside our budget, so we didn’t really wait for that seller to give us a price. We also looked at a 2006 RAM 2500. Our mechanic took a look at it and exclaimed that everything that could possibly leak on that vehicle was leaking (transmission, power steering, etc). That was an easy pass given that it was already at the top of our price range.

Fortunately, the next truck we looked at worked out. Cylinder 6 was misfiring on this truck (we could hear the tick) and this was confirmed by the digital codes from the vehicle. We decided to buy a new spark plug and coil for that cylinder to see if we could fix it before driving off with the truck but neither AutoZone nor Oreilly Autoparts had the right coil in stock. We walked out with just the spark plug and our mechanic replaced it. In the process of pulling out the old spark plug, the spark plug wire came apart, and we had to buy an entire Duralast Silicone Spark Plug Wire Set. Thankfully, that was all that was needed to address the cylinder misfiring. We were glad to have our mechanic available to fix that problem before we drove off with our “new” 2002 truck (through a private sale). We had also confirmed with our insurance company that the new vehicle was covered as we drove it away and that we had up to 5 days to add it to our insurance plan.

Ironically, the towing hitch was significantly damaged on this truck (one of our most important requirements). However, the mechanic pointed out that it can be readily replaced (just don’t do any welding on the existing setup since its integrity cannot be guaranteed). The only question I have remaining is how to compute the tongue weight (came up when we were looking up new hitches online). What is Tongue Weight and What Does it Mean for Safe Towing? explains various ways to determine the tongue weight. They also recommend their weigh-safe hitch, which has a built-in scale (I like how convenient this hitch makes it). This is the video they linked discussing this option.

Behind the scenes of the Ike Gauntlet: How to measuring Tongue Weight for Safe Towing

I decide to buy the Adjustable Trailer Hitch, Adjustable Ball Mount, Truck Ball Hitch, Aluminum Trailer Tow Hitch w/Built in Scale for Anti Sway, 12,500 lbs GTW – Weigh Safe (what a mouthful). I wasn’t sure whether to get the aluminum or steel hitch even after reading Aluminum Vs. Steel Hitches so I settled on that one because I couldn’t even find the steel equivalent. We’ll see how it holds up.


Categories: Linux, Programming

SFrame Resources

I have been learning about the SFrame tracing effort and figured I should document the resources I have reviewed. Indu Bhagat has been actively involved in the development of SFrame. This is one of her talks giving an overview of the objectives of SFrame. The overall idea is that profiling tools (e.g. perf) usually need to generate stack traces. She lists some methods used to generate stack traces, e.g. using frame pointers, EH frame, last branch record (LBR), and other heuristics. Each of these have their own advantages and pitfalls. SFrame encodes the minimal info required for stack tracing.

SFrame: The Simple Frame Stack Trace Format – Indu Bhagat, Oracle – YouTube

I found additional videos by searching for sframe indu (there are lots of unrelated sframe results out there). This one by Steven and Indu covers potential issues that need to be addressed for JITted code.

Implementing sframes – Steven Rostedt, Indu Bhagat (youtube.com)

There are various informative discussions about SFrame out there, e.g.

  1. SFrame based stack tracer for user space in the kernel [LWN.net]
  2. Fedora’s tempest in a stack frame [LWN.net]

Indu and Steven also talk about SFrame at the Linux Storage, Filesystem, MM & BPF Summit | LF Events (linuxfoundation.org). This video feels a bit more detailed than the previous one. In this video, it is explicitly outlined that frame pointers require setup in every function and increase register pressure. The cost of this cites the Fedora’s tempest in a stack frame [LWN.net] article. Steven also discusses how the Orc unwinder was created to solve the need for accurate stack unwinding. This is needed, for example, for live kernel patching (see What is Linux kernel live patching? (redhat.com) for more info). SFrame is based on orc unwinder but for user space.

Sframe – Steven Rostedt, Indu Bhagat – YouTube

These resources provide a high-level view of stack tracing/unwinding concerns. Tools like async-profiler/async-profiler: Sampling CPU and HEAP profiler for Java featuring AsyncGetCallTrace + perf_events (github.com) will probably look very different if they switch to using SFrame.


Categories: Equine

Horse Purchase Contracts

We decided to sell our horse a few months ago and buy another horse better suited to drill riding. The topic of which contract to use when buying a horse came up naturally. More specifically, the seller of the horse we were interested in wanted a right of first refusal (which I didn’t understand). My first go-to was the Horse purchase agreement – YouTube search. The video on Sales Fraud in the Horse Industry (youtube.com) was exactly what I needed. I’m summarizing the key points in this post so that I don’t have to watch the whole video again.

Sales Fraud in the Horse Industry

Some issues that horse buyers run into:

  1. Training and disposition are misrepresented. The buyer didn’t seek enough info, or the seller wasn’t clear/transparent (e.g. about horse vices)
  2. Horse is smaller/larger than represented, e.g. with minis where the seller doesn’t make a representation about the horse (or it is misrepresented).
  3. Horse being drugged during buyer’s evaluation.

She gives advice on reducing disputes by sellers ensuring advertisements are true and accurate. One of the behaviors mentioned (that buyers complain about) is cribbing, which I don’t think I have heard of before.

What is cribbing, and how to stop your horse from cribbing

Some recommendations from the video include:

  1. put things in writing
  2. avoid one-size-fits-all forms (e.g. are they valid in your state?)
  3. don’t leave major terms to guesswork
  4. specify the buyer, seller, price, terms, and the horse (registered date, foaling date)
  5. avoid underage contract signers (such contracts will not be enforceable in most places).

She also reviews the buyer perspective, the top tip being “if it’s important to you, get it in writing.” Any seller not willing to put things in writing (e.g. the horse is bomb proof) is a red flag. I had to research the meaning of this term but got the gist of it from Bombproof – Western Horseman and The Myth of the “Bomb-Proof” Horse – Horse Trail Chicks. Other things to consider putting in writing are medical conditions:

  1. if the horse has a medical colic.
  2. whether the horse needs to receive joint injections (that’s a thing?).

Other recommendations include:

  1. having the seller’s name and signature on the contract (thus ensuring promises are not just from a seller’s agent, who might not have really known the horse) and to specify who pays the seller’s agent’s commission.
  2. hiring an independent vet to examine the horse before buying (e.g. to avoid paying a lot for a horse that has been denerved).
  3. getting a drug screen.

She delves into the topic of releases, giving an example of a closed head injury that resulted in institutionalization of the rider, but the release didn’t use the language required to make it enforceable! Definitely caught my attention.

Other Recommendations

This was a great talk overall so I looked up the advertised references: My Horse University Online Horse Management and My Horse University | Facebook appear to have tons of resources on all matters equine. I’m also inspired to learn about types of horse insurance.


Categories: git

Viewing git file mode of bash scripts in Windows

I have been using bash scripts to run jtreg tests when working on my Windows desktop. The Git Bash environment does not care about whether the script has the executable mode set. However, running the same script on other platforms requires a chmod +x command. Since it is annoying to have to do this every time I switch platforms, I have decided to be fixing this before pushing scripts. How do I see the permission of a file in Git? – Stack Overflow recommends git ls-files -s. It’s only now that I’m learning (from the top voted answer) that Git only tracks the executable bit on files (Are file permissions and owner:group properties included in git commits? – Stack Overflow).

$ git ls-files -s
100755 a84afa2caa928a2cea6cf4ae1547f742e52c7078 0       run-foreign-abi-tests.sh
100644 24bf639dd98f99435db83012a7ba70200a09bea8 0       run-gtests.sh
100644 7137e3b5a8d85a8b24e7cb2ee4da2587d2dcde22 0       run-jtreg-test.sh
100644 1a4a0e9f7409b8b96af12516393a4cc5d9efd7e3 0       run-jtreg-tests.sh

chmod +x run-jtreg-test.sh does not change the file mode displayed by git ls-files -s. As per How to add chmod permissions to file in Git? – Stack Overflow, you can use this command starting in Git 2.9 (I’m running git version 2.45.2.windows.1)

git add --chmod=+x run-jtreg-test.sh

And now git ls-files -s shows the new file mode.

$ git ls-files -s
100755 a84afa2caa928a2cea6cf4ae1547f742e52c7078 0       run-foreign-abi-tests.sh
100644 24bf639dd98f99435db83012a7ba70200a09bea8 0       run-gtests.sh
100755 7137e3b5a8d85a8b24e7cb2ee4da2587d2dcde22 0       run-jtreg-test.sh
100644 1a4a0e9f7409b8b96af12516393a4cc5d9efd7e3 0       run-jtreg-tests.sh

Complexity Theory Research July 2024

Isomorphisms of Complexity Classes

Reading Reductions among polynomial isomorphism types is my first exposure to the concept of order types. More specifically, that paper proves this:

every polynomial many-one degree either consists of a single polynomial isomorphism type or else contains a collection of isomorphism types which has, under one-one, size-increasing, polynomially invertible reductions, the order type of the rationals.

Reductions among polynomial isomorphism types

P versus NP

I was telling my advisor that reading of these old papers makes me suspect that we are a very long way away from resolving the P vs NP question – we don’t appear to have made significant progress in this journey over the past few decades. This is obviously an uninformed gut feeling, not a scientific observation. I asked how researchers that have been at this for decades feel about the problem and these are some of the videos he shared. The speakers in this discussion on P vs NP covers issues such as:

  1. how to go from worst case to average case complexity of hard problems
  2. how to generate hard instances (with distinctions between puzzles and problems coming up in applications like cryptography)
  3. whether quantum mechanics can actually solve hard computational problems (with pessimism arising from potential inability to measure the results of parallel exploration). A naive approach will not work. Shor used the structure of the factoring problem. Does that structure exist in NP complete problems?
  4. whether current accepted axioms are not sufficient to resolve the problem.

There is also advice from Ron Fagin to spend some time (e.g. a couple of days each year) thinking about the hardest problem in their field. One of the most interesting questions to me was the one asking “what’s the most remarkable false proof of P vs NP proofs that you have come across“. Ron Fagin mentions the proof attempt by Vinay Deolalikar from HP Labs. This was addressed by Scott Aaronson in his post on Eight Signs A Claimed P≠NP Proof Is Wrong (scottaaronson.blog). The other comment (by Christos Papadimitriou) was about how some failed attempts have led to barriers showing that certain approaches will not work. Ron Fagin also recommends looking at the P-versus-NP page (tue.nl).

Beyond Computation: The P versus NP question (panel discussion)

Avi Wigderson’s talk on P vs NP got me to review the List of undecidable problems – Wikipedia (he specifically mentioned Hilbert’s tenth problem – Wikipedia).

Professor Avi Wigderson on the “P vs. NP” problem

Prefix-free Sets

One of the topics I have been learning about requires an understanding of prefix sets. Prefix-free Kolmogorov complexity is one of these areas. Some resources on this subject include:

  1. Lance Fortnow’s Kolmogorov complexity paper (kaikoura.pdf)
  2. Why don’t prefix-free Turing machines suffer from complexity dips? – Computer Science Stack Exchange
  3. computability – Construction of a universal prefix-free Turing machine – Mathematics Stack Exchange

I watched lecture 42 Kraft’s inequality (youtube.com) to get the basic idea behind Krafts inequality. The simpler proof was the one mapping prefix free codes subintervals of (0,1). Fortnow’s paper described these as intervals of real numbers whose dyadic expansion begins with 0.x for strings x in a prefix-free set A.

Kraft’s inequality

Error Correcting Codes

The XOR lemma used for worst case to average case hardness transformation has always seemed a bit mysterious to me. I had decided to dig into error correcting codes to better understand the list decoding approach to hardness amplification in the Pseudorandom Generators without the XOR Lemma paper. I started on this series last month and have found it extremely beneficial. It’s been much easier to read Venkatesan Guruswami’s List Decoding of Error-Correcting Codes thesis after going through this lecture series.

Algebraic Coding Theory by Mary Wootters


Categories: Equine

Horse Ownership Basics

Horse Trailers

We have been searching for horse trailer and wanted to learn the types of things people consider before buying one. We started with this video by Equine Helper:

HORSE TRAILER SHOPPING TIPS! (watch before buying)

The factors she mentioned include:

  1. Your vehicle’s towing capacity (trailer weight + the weight of the horses). This is the first time I’ve ever thought about how much a horse weighs. See How Much Does a Horse Weigh: Understanding Equine Weight – All About Horses.
  2. The length of your vehicle’s wheelbase (longer is better).
  3. The difference between the weight of the vehicle and the loaded trailer.
  4. The vehicle’s braking system, e.g. can it use the trailer’s brakes?
  5. The limits on the vehicle’s payload (weight of passengers & cargo inside the vehicle itself) when towing.
  6. Trailer style:
    • Straight load vs slant load vs stock trailers. Stock trailers don’t have support (e.g. for sudden braking), are open so projectiles can injure your horse
    • Trailer ventilation: (e.g. untreated ceilings can get really hot). Consider one with windows that can open, or ceilings that can be opened.
  7. The hitch on the towing vehicle needs to support the tongue weight of the trailer.
  8. The receiver needs to match the ball size of the trailer.

The video links to the Double D Trailers website, which has this relevant and detailed article: Horse Trailer Safety Standards Guide: Ensuring Equine Welfare – Double D Trailers. They also have various Bumper Pull Horse Trailers For Sale (which is interesting since I can see the price ranges of such new trailers).

This next video was helpful as well because it focuses on the practical considerations when buying a used horse trailer.

Consider this when buying a used horse trailer

Horseshoes

We were hiking recently when we came across some hoof-prints in the mud. The kids asked: why do horses need horseshoes? This page has a detailed explanation: Do horses need shoes? The pros and cons of shoeing (horseandcountry.tv). I also picked 2 videos from the do horses need shoes – YouTube search. The first was interesting because it showed the shoeing process.

Do Horses Need Shoes?

The next video was helpful since his perspective is informed by his profession.

To Shoe or Not to Shoe – Part 1 – Ask a Farrinarian

Cost of Horse Ownership

After having our horse reshoed more often than is reasonable, I looked ran this search: how much does it cost to own a horse – YouTube? It’s interesting how many of the results for these horse queries are from Equine Helper.

Some of the factors affecting the price of a horse include the:

  1. the amount/type of training a horse has
  2. the registration and pedigree: there are breed associations?? See Breed association| Britannica and Associations | Equine Science (iastate.edu), for example. For details on the pedigree, see Understanding Horse Bloodlines: The Basics of Pedigrees – Horse lovers With Passion.
  3. breed of the horse

Horse Breeds

The discussion in the previous video mentioned a number of breeds, none of which I’m familiar with. The List of horse breeds – Wikipedia is much longer than I expected! I look up horse breeds on YouTube and find this video of her favorite breeds. She lists these:

  1. the Appaloosa (which is also apparently prone to Equine recurrent uveitis).
  2. the Fjord horse (what is a draft horse?)
  3. Morgan horse (what is a quarter horse?)
  4. Miniature horse
  5. Icelandic horse
MY TOP 5 FAVORITE HORSE BREEDS (and why)

Our horse is a Thoroughbred and is 16 hands tall. This is another thing that has puzzled me. Why is horse height measured in hands? Looks like it goes back to before standardized units as explained in the video below.

Working with your horse

Our thoroughbred had a leg/foot injury. After a few weeks without being able to ride him, he was quite a handful when my wife hopped on him once more. She worked with him, and things are much better. However, I decided to learn what advice is given on how to not get thrown off a horse and what to do if it happens. This next video is one of the results I found on this. It includes tips like keeping your nose behind your belly button, learning how to do a one rein stop (while not leaning forward), and getting off as soon as you feel uncomfortable.

5 Tips To Keep You Safe Riding A Horse

A YouTube suggestion that came up afterwards was this video on gentle horse training. I find the approach interesting because she shows the frustration of the horse and the progression of the training.

Why gentle horse training beats high pressure everytime

One of the risks that came up when working with our horse was the fact that we didn’t have a round pen. I didn’t know why a round pen is used – and why is it round anyway? The Benefits of Using a Round Pen | Ride Magazine came to my rescue: you don’t have to teach the horse to stay out of the corners! The round pen size needs to match the speed/character of the horse as well.

…what is neat about a round pen, it is a small, quiet, safe environment and it makes it easier to get control of the feet.

The Benefits of Using a Round Pen | Ride Magazine

Horse Nutrition

One of the things I’ve found myself marveling at is how such an animal can be so big and muscular while just eating grass. Where does the protein come from? And this horse really likes alfalfa, which I had seen around the property without knowing what this plant was. I haven’t looked too keenly but there didn’t seem to be any videos addressing the breakdown of the nutritional value of grass.

The Ultimate Horse Diet | Health & Fitness

A search for videos on the nutritional value of hay – YouTube is closer to what I want. I like this next video for its overview of things hay growers consider:

Harvesting hay to feed our cattle. What makes for high quality alfalfa hay.

There are so many considerations in horse ownership. I’m happy to have my eyes opened into how this world is run, not to mention a new appreciation for some of the rodeo performances!


Categories: Java

Launching Freeplane in Eclipse

I was recently in a brainstorming session with a colleague about garbage collection scenarios. He suggested using mind mapping software, and I must confess that this was the first time I was hearing about this (as far as I can recall). We wanted a free program. Our search turned up freeplane (which can be downloaded on Sourceforge). As per my usual habits, I decided to build it from source myself (I especially get this urge when I run into Sourceforge download links). Fortunately, the sources are hosted on GitHub.

git clone https://github.com/freeplane/freeplane

Since this is a Java codebase, I searched for “eclipse java” to get an IDE. The Eclipse Packages page has the Eclipse IDE for Java Developers (I downloaded the Windows x64 version). The project uses gradle but I didn’t know how to open it in Eclipse so I asked Microsoft Copilot (Bing Chat) how to “open a gradle project in eclipse”. One of the results was from the Import existing Gradle Git project into Eclipse – Stack Overflow page, which points to the Buildship Gradle Integration plugin. I launched Eclipse, created a new workspace and tried to import the project but got an exception that ended with:

Caused by: java.lang.IllegalArgumentException: Unsupported class file major version 65
	at groovyjarjarasm.asm.ClassReader.<init>(ClassReader.java:199)
	at groovyjarjarasm.asm.ClassReader.<init>(ClassReader.java:180)
	at groovyjarjarasm.asm.ClassReader.<init>(ClassReader.java:166)
	at groovyjarjarasm.asm.ClassReader.<init>(ClassReader.java:287)
	... 180 more

The spring – Unsupported class file major version 65 with Java 21 “workaround” – Stack Overflow post is not particularly helpful. However, I have been interested in learning gradle for some time, so I might have finally found the opportunity I needed. I reread the Import existing Gradle Git project into Eclipse – Stack Overflow page then decided to look around the Eclipse IDE to find anything similar to the “Build Model” button mentioned in one of the replies. The “Refresh Tasks for All Projects” button did the trick! It populates the Gradle Tasks pane with an actual meaningful tree!

I was not sure how to run the application. I had unloaded it and reloaded it. I asked copilot “how to run an imported gradle project in eclipse” and the last step is to right-click on the project and choose Refresh to ensure Eclipse recognizes the changes. This time, the project explorer folders are rearranged, and version numbers are appended after each project!

I right clicked on the freeplane project, selected Run As… Java Application, then chose the Main (org.knopflerfish.framework) application and clicked OK.

This text appeared in the console but no application Window appeared.

Knopflerfish OSGi framework launcher, version <unknown>
Copyright 2003-2020 Knopflerfish. All Rights Reserved.
See http://www.knopflerfish.org for more information.

Created Framework: org.knopflerfish.framework, version=8.0.11.
Framework launched

I was not even sure how to terminate the application so I asked copilot “how to stop a running java program in eclipse”. Yes, it has been that long. Forgot about this button.

I then downloaded freeplane_bin-1.11.14.zip to explore the UI and see if I could track down the strings from the menus… I found some resource files that but that was not helpful, so I continued exploring other projects. I finally stumbled into the freeplane_framework project. Clicking on “Run As… > Java Application” revealed that it has a Launcher project! I had been looking for a project like this. It failed with this error in the console window:

Exception in thread "main" java.lang.UnsupportedOperationException: The Security Manager is deprecated and will be removed in a future release
	at java.base/java.lang.System.setSecurityManager(System.java:430)
	at org.freeplane.launcher.Launcher.launchWithoutUICheck(Launcher.java:291)
	at org.freeplane.launcher.Launcher.main(Launcher.java:88)

I manually set the disableSecurityManager field to true to bypass this issue. The application finally launched!!

Launching Freeplane in Eclipse on macOS

I decided to try this process on my M1 laptop. I downloaded the macOS AArch64 Eclipse IDE for Java Developers, accepted the default workspace path, and clicked on the “Import projects…” command in the package explorer. I selected the “Existing Gradle Project” import wizard then clicked Next.

For the “Project root directory”, I browsed to the freeplane repo: /Users/saint/repos/freeplane then clicked on “Finish”. There is a helpful message in the Gradle Tasks pane (which shows up by default): Click on the Refresh Tasks button to get the structure and the tasks for project.

A message in the status bar flashed by fast but nothing else happened in the IDE. I confirmed that “Buildship: Eclipse Plug-ins for Gradle” was listed in the Installation Details (Eclipse > About Eclipse).

I didn’t have the console view of what could have happened though so I enabled it via Window > Show View > Console. Aha, looks like the same error that happened the first time in Windows (not even sure how that got resolved at this point).

FAILURE: Build failed with an exception.

* What went wrong:
Could not open cp_init generic class cache for initialization script '/Users/saint/eclipse-workspace/.metadata/.plugins/org.eclipse.buildship.core/init.d/eclipsePlugin.gradle' (/Users/saint/.gradle/caches/8.1.1/scripts/2to4is5l87jn9v7vrcgka57e).
> BUG! exception in phase 'semantic analysis' in source unit '_BuildScript_' Unsupported class file major version 65

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Get more help at https://help.gradle.org

CONFIGURE FAILED in 568ms

Pasting the line starting with BUG! into copilot was helpful: class file major version 65 is jdk21 as per Chapter 4. The class File Format (oracle.com) and gradle supports Java 8 through 22 – see Compatibility Matrix (gradle.org). This matrix link is from macos – BUG! exception in phase ‘semantic analysis’ in source unit ‘_BuildScript_’ Unsupported class file major version 61 on Apple Arm – Stack Overflow.

Which version of gradle is the Buildship Eclipse plugin using? It must be older than 8.8 (the default on the compatibility matrix page). I’m running Eclipse Buildship: Eclipse Plug-ins for Gradle 3.1.9 | projects.eclipse.org. This links to buildship/docs/user/Faq.md at master · eclipse/buildship (github.com), but that FAQ doesn’t address this. However, there is a gradle/wrapper/gradle-wrapper.properties file as mentioned in macos – BUG! exception in phase ‘semantic analysis’ in source unit ‘_BuildScript_’ Unsupported class file major version 61 on Apple Arm – Stack Overflow! The gradle 7.4.2 Compatibility Matrix (gradle.org) shows that it supports only Java 8 through 17! I looked around for a setting that could let me change this. Started with Eclipse > Settings… and found a Gradle subtree in the Preferences window. Gradle distribution is set to “Gradle wrapper”.

The Specific Gradle version dropdown has many options. I selected 8.8 then clicked on Apply and Close. Refreshing the Gradle Tasks still didn’t do anything. I closed the project and reopened it and now all the tasks showed up in the Gradle Tasks pane. However, the Run As option on the freeplane_framework project only had a Run Configurations… option. After some poking around, I discovered that just like on Windows, I need to expand the project, right click on the build.gradle file, then select the Gradle > Refresh Gradle Project to get the Package Explorer to refresh. This hides the top level files in the project. Now I could right click on the project and use the Run As > Java Application command. Selecting the “Launcher” Java application resulted in previously mentioned error message so I manually set the disableSecurityManager field to true. Freeplane finally launched successfully!

Summary: How to launch Freeplane in Eclipse

  1. Clone the repo: git clone https://github.com/freeplane/freeplane
  2. Download the Eclipse IDE for Java Developers
  3. Set the gradle distribution to 8.8 (on Windows/Linux, use Window > Preferences, on macOS, use Eclipse > Preferences).
  4. Click on Import projects… in the Package Explorer.
  5. Select Existing Gradle Project then browse to the repo for the “Project root directory” (you can click Next to override the workspace settings to choose a specific Gradle version).
  6. Click on Refresh Gradle Tasks.
  7. Right click on the root build.gradle file and click on Refresh Tasks (if necessary, i.e. this file is shown at the root level of the package explorer)
  8. Manually set the disableSecurityManager field to true to bypass the UnsupportedOperationException.
  9. Run the “build” Gradle task in the Gradle Tasks window.
  10. Right click on the freeplane_framework project, Run As > Java Application then select the Launcher application.
  11. Select the freeplane dist option in the “Select Java Application” Window and click OK.
  12. Use the launched freeplane application!

Categories: Programming

Getting to “Hello World” in Dolphin Smalltalk

A colleague at work was telling me about the Smalltalk programming language this week. I have never used it so I asked for compiler recommendations for it. Dolphin smalltalk was one of the suggestions. I downloaded the ZIP of the latest release from https://github.com/dolphinsmalltalk/Dolphin/releases/tag/7.2.0 but unzipping it and launching it fails with a Fail to open image file 'C:\software\DolphinVM\DPRO.img7' error message. Downloading and running the Dolphin7Setup.exe installer got the Dolphin environment up and running.

Building Dolphin Smalltalk

I’m always interested in how different projects are built – this one stands out for being a Windows-only project. The repo I cloned was at commit 2cbc3e72cb.

git clone https://github.com/dolphinsmalltalk/Dolphin

Building Core/DolphinVM/DolphinVM.sln in Visual Studio takes less than a minute on my desktop. Pressing F5 shows an error dialog: Unable to start program 'C:\repos\Dolphin\Core\DolphinVM\Debug\DolphinVM8.dll'. Changing the startup project from VM to Launcher just shows the Fail to open image file 'C:\repos\Dolphin\Core\DolphinVM\Launcher\DPRO.img8' error. Looking at the repo home page, I think I have only built the virtual machine.

Building the Dolphin 8 Product Image

The instructions say to run git lfs pull but that doesn’t appear to do anything. Next step is to run BootDPRO.cmd. It calls Dolphin8 with the DBOOT.img8 argument. GitHub displays a note that this file is Stored with Git LFS. This note links to Managing large files – GitHub Docs and this is the first time I’m really looking at this. I don’t really understand why a file that’s less than 2MB needs this so I will skip this LFS detail for now. Running the command in BootDPRO.cmd in my MINGW shell does not do anything.

saint@desktop MINGW64 /c/repos/Dolphin (master)
$ ./Dolphin8.exe DBOOT.img8 DolphinProfessional

Switching to a Windows command prompt does the trick! I’m still in the dark about what this machine is and what exactly this image being compiled is.

C:\repos\Dolphin>BootDPRO.cmd
Dolphin Smalltalk Boot
Copyright (c) Object Arts Ltd, 1997-2021.
Boot started at 2024-06-27T21:33:14.671937943-06:00
Loading boot script...
Reloading BCL constants pools...
Updating ClassBuilder...
Reloading BCL class definitions...
Recompilation of OpcodePool required because class variables/constants are being added
...
Reloading 'Dolphin Message Box' ...
Loading source package 'Dolphin Message Box' from: C:\repos\Dolphin\Core\Object Arts\Dolphin\System\Win32\MessageBox\Dolphin Message Box.pax
Deleting obsolete boot image methods...
Removing obsolete boot image method Compiler class>>#notificationCallback:
...
Recompiling references to ICONDIR (size 22)...
Recompiling references to PROCESS_INFORMATION (size 16)...
Recompiling references to STARTUPINFOW (size 68)...
Boot completed at 2024-06-27T21:36:03.2736891-06:00, duration 2.81 minutes

Launching Dolphin8

We can now run Dolphin using this command:

Dolphin8.exe DPRO.img8

The program launches successfully. Note that Dolphin8.exe is the output of the Launcher project. To debug the application:

  1. Set Launcher as the startup project in Visual Studio.
  2. Open the Property Pages of the Launcher project.
  3. Navigate to the Configuration Properties > Debugging pane.
  4. Set the Command Arguments to “DPRO.img8”
  5. Set the Working Directory to the root of the git repo, e.g. “C:\repos\Dolphin”
  6. Press OK then launch the program.

Ideally, these steps should be built into the solution and the launcher project’s configuration but it was straightforward to figure out. This is what I get.

Dolphin Smalltalk Professional - DPRO.img8

Hello World in Smalltalk

I’m not quite sure where to start. I search for a smalltalk tutorial for beginners – Search (bing.com) decided to try the GNU Smalltalk User’s Guide: Saying hello by creating a new workspace then pasting 'Hello, world' printNl into it but it grumbles:

I ask Microsoft Copilot to “print hello world in dolphin smalltalk”. It suggests Transcript show: 'Hello World!'. explaining that

The Transcript class allows you to print messages by sending the show: message to it, with the string you want to display as the parameter

smalltalk Tutorial => Hello World in Smalltalk (riptutorial.com)

I evaluated this line, but nothing appeared to be happening. I reread the quote then search for the Transcript window. It is the System Transcript window shown below (whose icon is in the Dolphin Smalltalk Professional screenshot above). Sure enough, it contains the Hello World message.

System Transcript Window
System Transcript Window

As pointed out by my colleague and others like Dolphin Smalltalk 7 (randycoulman.com), picking up this language can make you a better programmer. I’ll need to find a decent program to implement in Smalltalk to learn about this programming language.


Categories: Manufacturing

Brief History of Engineering Drawings

I have been learning a little bit about print reading from this book: Print Reading for Industry: Brown, Walter C., Brown, Ryan K.: 9781645646723: Amazon.com: Books. It briefly mentions blueprints as one of the earliest printmaking processes, the diazo process, and finally wide-format printers (I think today is the first time I’m really noticing the latter). This piqued my interest in the history of blueprints. This video provided the highlights: famed polymath John Herschel invented blueprints using Potassium ferrocyanide and Ammonium ferric citrate. The method is called Cyanotype.

Why Blueprints are Blue

He mentions other methods developed by Alphonse Louis Poitevin – Wikipedia and others. Perhaps those are worth exploring at some point. The chemistry behind blueprints warrants deeper exploration but I’ll save that for another day. There are many videos on cyanotype, e.g. this one on How to Make Blueprints – Cyanotype (youtube.com):

How to Make Blueprints – Cyanotype

Engineering drawings are now commonly created using CAD software. Drawings commonly conform to standards such as ASME Y14.3-2012 or ISO 5456-2:1996. They are commonly transferred and viewed electronically in formats like PDF. This video summarizes the history of PDF format:

Adobe PDF History | Adobe Document Cloud

I’ll end this (super) brief history of engineering drawings with a video (from the search for blueprint history) from 1958. It attempts to motivate students about the importance of learning how to read prints (engineering drawings) and although it feels dated, the underlying points are still relevant.

Blueprints Technical Drawing 1958 Mechanical Drawing History