I was reviewing jtreg test failures in some GitHub actions last week. Since I was only interested in the failures, I decided to write a script to extract the failure details from the log files I had downloaded from GitHub. This looked like another task for the VS Code agent so I wrote the algorithm for extracting the failure details into individual text files, which would be easier to review. Below is the prompt I used.
write a python script that processes all the text files in a user-specified folder. For each text file:
- split it into sections using "--------------------------------------------------" as a separator
- generate a filename for the section from the first line after the separator. To do so, use the text after "TEST: " and replace /, #, and . with underscores and append the ".txt" extension.
- if the section contains the string "test jdk:" then write the content of the entire section to a file with the generated name.
I thought I would need to do at least some debugging of the script but I did not! The script worked flawlessly. I didn’t even need to execute it myself because the flow of using the agent included running the script on my raw folder and then generating a README file! See the script and the README at Add scripts generated by Claude Sonnet 4 VS Code agent · swesonga/scratchpad@f5e8057. The agent was using the Claude Sonnet 4 model.
delete all lines from ":: jdk21u x64"
replace 2025-07 with a variable called psu
replace aarch64 and x64 with a variable called arch
replace jdk21u with a variable called jdkrepo
replace jdk-21.0.6+7 with a variable called jdktag
replace D:\java\binaries\jdk\%arch%\%psu%\windows-%jdkrepo% with a variable called psudir
output a message showing the psudir after setting it
make the variables user-specified variables
make the variables command line arguments instead
I pasted the screenshots of the result of each prompt into the slideshow below. Notice that I was using Agent mode with Anthropic’s Claude Sonnet 4 model. I was not exactly saving much time in this scenario using the VSCode agent, but I was certainly understanding its capabilities.
I created a duplicate directory with the initial .bat file then used a single prompt with all the lines above. I was pleased that I got the same result!
I was a bit confused the 2nd time I worked through these prompts because the behavior of the agent was different! I didn’t notice that I had somehow switched from Agent mode to Ask mode but I have now learned the difference between these two modes.
What is the torch.varkeepDim parameter used for? p101
What about the correction parameter (formerly unbiased)? How does it relate to Bessel’s_correction? p103
Explain the difference between layer normalization and batch normalization. p104
When is layer normalization advantageous? p105
What is a GELU? How can it lead to better optimization properties during training (compared to a ReLU)?
What is one advantage of small non-zero outputs on negative inputs to a GELU? p106
Explain the role of a FeedForward module in enhancing the model’s ability to learn from and generalize data. Compare with Feedforward neural network. p108
Why were shortcut connections originally proposed for deep networks in computer vision? p109
Which PyTorch method computes loss gradients? p112
Explain Pre-LayerNorm and Post-LayerNorm. p115
What does preservation of shape throughout the transformer block architecture enable? p116
Explain weight-tying as used in the original GPT-2 architecture and its advantages. p121
This is the author’s video corresponding to chapter 4 of the book.
Build an LLM from Scratch 4: Implementing a GPT model from Scratch To Generate Text
Math Concepts
The author mentioned that OpenAI used the biased variance option when training their GPT-2 model. The reasons why Bessel’s correction is usually used in statistics is explained well in this video:
Why We Divide by N-1 in the Sample Variance (The Bessel’s Correction)
I didn’t think the reason we need activation functions was expounded upon. This final video provides a great explanation.
Why Do We Need Activation Functions in Neural Networks?
As part of the jdk11u release process, I needed to run some shell tests on my Windows desktop to determine whether they failed due to a product issue or an environment issue. I defaulted to using my Git Bash environment instead of Cygwin. This post shares some errors I ran into as a result of the different shell environment. The key takeaway was to run such tests in Cygwin whenever I encountered path errors.
I confirmed that rmic.exe exists in the bin directory of the jdk.
$ ls -1 /d/java/binaries/jdk/x64/2025-07/windows-jdk11u/jdk-11.0.28+6/bin/rmic*
/d/java/binaries/jdk/x64/2025-07/windows-jdk11u/jdk-11.0.28+6/bin/rmic.exe*
Changing the test to directly refer to rmic.exe instead of rmic (as shown in the diff below) resulted in the test passing on my machine. I concluded that this specific issue must therefore be a test bug (i.e. the test should avoid this issue on Windows). See Shell Tests in jtreg for possible ways to fix this. This is one reason why shell scripts are being discouraged for OpenJDK testing.
STDOUT:
Warning: generation and use of skeletons and static stubs for JRMP
is deprecated. Skeletons are unnecessary, and static stubs have
been superseded by dynamically generated stubs. Users are
encouraged to migrate away from using rmic to generate skeletons and static
stubs. See the documentation for java.rmi.server.UnicastRemoteObject.
This warning is output by the sun.rmi.rmic.Main.compile method. The arguments to rmic.exe are parsed by Main.parseArgs. The -v1.2 argument specifies the non-default generator to use. My local machine must be using a different default generator (or so I thought).
So what does the failing line do? I asked Copilot: what does this command do in a bash script? . ${TESTSRC-.}/common.sh
. (dot command) This is shorthand for source, which executes the contents of a file in the current shell environment rather than in a subshell. This means any variables, functions, or changes made by the script will persist in the current shell.
${TESTSRC-.} This is a parameter expansion. It means:
Use the value of the TESTSRC environment variable if it is set.
If TESTSRC is not set, use . (the current directory) as the default.
/common.sh This appends /common.sh to the path determined above.
This error was a Windows path issue again. Notepad++ could open D:/java/ms/openjdk-jdk11u/test/jdk/sun/tools/jrunscript/common.sh so the path itself was fine. I used this workaround:
diff --git a/test/jdk/sun/tools/jrunscript/jrunscript-eTest.sh b/test/jdk/sun/tools/jrunscript/jrunscript-eTest.sh
index 6a3d4e76ff..94001967b0 100644
--- a/test/jdk/sun/tools/jrunscript/jrunscript-eTest.sh
+++ b/test/jdk/sun/tools/jrunscript/jrunscript-eTest.sh
@@ -30,10 +30,11 @@
# @run shell jrunscript-eTest.sh
# @summary Test that output of 'jrunscript -e' matches the dash-e.out file
-. ${TESTSRC-.}/common.sh
+comn=`/mnt/c/software/msys64/usr/bin/cygpath.exe ${TESTSRC-.}/common.sh`
+. "/mnt$comn"
setup
-${JAVA} ${TESTVMOPTS} ${TESTJAVAOPTS} -cp ${TESTCLASSES} CheckEngine
+${JAVA}.exe ${TESTVMOPTS} ${TESTJAVAOPTS} -cp ${TESTCLASSES} CheckEngine
if [ $? -eq 2 ]; then
echo "No js engine found and engine not required; test vacuously passes."
exit 0
This got me to the actual test error:
STDOUT:
Output of jrunscript -e differ from expected output. Failed.
STDERR:
Warning: Nashorn engine is planned to be removed from a future JDK release
diff: D:/java/ms/openjdk-jdk11u/test/jdk/sun/tools/jrunscript/dash-e.out: No such file or directory
I couldn’t see which command generated the output though, so I added set -ex to the top of the script (like run.sh in the previous test). This was the resulting output:
Aha! Notice the root cause of the filename issues: OS=Linux! This also confirmed that I was using the same diff command. I didn’t get the error message at jdk11u/test/jdk/sun/tools/jrunscript/jrunscript-eTest.sh at jdk-11.0.28+5 · openjdk/jdk11u because because of set -ex (the e means exit immediately if any command fails, which does not match the behavior this test requires). After removing the e, I got this output:
$ ls -1 `cygpath D:/java/ms/openjdk-jdk11u/test/jdk/sun/tools/jrunscript/dash-e.out`
/d/java/ms/openjdk-jdk11u/test/jdk/sun/tools/jrunscript/dash-e.out
I patched the script as shown in the next diff:
diff --git a/test/jdk/sun/tools/jrunscript/jrunscript-eTest.sh b/test/jdk/sun/tools/jrunscript/jrunscript-eTest.sh
index 6a3d4e76ff..4c7130857e 100644
--- a/test/jdk/sun/tools/jrunscript/jrunscript-eTest.sh
+++ b/test/jdk/sun/tools/jrunscript/jrunscript-eTest.sh
@@ -30,10 +30,13 @@
# @run shell jrunscript-eTest.sh
# @summary Test that output of 'jrunscript -e' matches the dash-e.out file
-. ${TESTSRC-.}/common.sh
+set -x
+
+comn=`/mnt/c/software/msys64/usr/bin/cygpath.exe ${TESTSRC-.}/common.sh`
+. "/mnt$comn"
setup
-${JAVA} ${TESTVMOPTS} ${TESTJAVAOPTS} -cp ${TESTCLASSES} CheckEngine
+${JAVA}.exe ${TESTVMOPTS} ${TESTJAVAOPTS} -cp ${TESTCLASSES} CheckEngine
if [ $? -eq 2 ]; then
echo "No js engine found and engine not required; test vacuously passes."
exit 0
@@ -44,7 +47,9 @@ fi
rm -f jrunscript-eTest.out 2>/dev/null
${JRUNSCRIPT} -J-Dnashorn.args.prepend=--no-deprecation-warning -J-Djava.awt.headless=true -l nashorn -e "println('hello')" > jrunscript-eTest.out 2>&1
-$golden_diff jrunscript-eTest.out ${TESTSRC}/dash-e.out
+diffarg=`/mnt/c/software/msys64/usr/bin/cygpath.exe ${TESTSRC}/dash-e.out`
+
+$golden_diff jrunscript-eTest.out "/mnt$diffarg"
if [ $? != 0 ]
then
echo "Output of jrunscript -e differ from expected output. Failed."
Avoiding Path Issues
This madness (in the diff above) that made me realize that I needed to fix the path issues and that perhaps Cygwin was the better environment for these tests. Sure enough, the test passed the first time I executed it in Cygwin:
Executing: D:/java/binaries/jdk/x64/2025-07/windows-jdk11u/jdk-11.0.28+6/bin/java -Xmx512m -jar C:/java/binaries/jtreg/jtreg-7.4+1/lib/jtreg.jar -agentvm -ignore:quiet -automatic -xml -vmoption:-Xmx512m -timeoutFactor:4 -concurrency:1 -testjdk:D:/java/binaries/jdk/x64/2025-07/windows-jdk11u/jdk-11.0.28+6 -verbose:fail,error,summary -nativepath:D:/java/binaries/jdk/x64/2025-07/windows-jdk11u/jdk-11.0.28+6-test-image/hotspot/jtreg/native test/jdk/sun/tools/jrunscript/jrunscript-eTest.sh
XML output to D:\java\ms\dups\openjdk-jdk11u\JTwork
Passed: sun/tools/jrunscript/jrunscript-eTest.sh
Test results: passed: 1
Moral of the story: run OpenJDK shell tests on Windows in the Cygwin environment! At this point, the only outstanding question is about the difference between the local machine and the test environment the build was executed in. I thought that the fact that I didn’t get the rmic.jrmp.stubs.deprecated warning meant that a different default generator was used on my machine. However, I realized that the stdout messages were not being displayed! That warning was present in the output but I needed to open JTwork\sun\rmi\rmic\defaultStubVersion\run.jtr to see it!
----------System.out:(11/743)----------
Warning: generation and use of skeletons and static stubs for JRMP
is deprecated. Skeletons are unnecessary, and static stubs have
been superseded by dynamically generated stubs. Users are
encouraged to migrate away from using rmic to generate skeletons and static
stubs. See the documentation for java.rmi.server.UnicastRemoteObject.
Warning: generation and use of skeletons and static stubs for JRMP
is deprecated. Skeletons are unnecessary, and static stubs have
been superseded by dynamically generated stubs. Users are
encouraged to migrate away from using rmic to generate skeletons and static
stubs. See the documentation for java.rmi.server.UnicastRemoteObject.
TEST PASSED: default output identical to -v1.2 output
----------System.err:(8/706)----------
+ defdir=./default_output
+ refdir=./reference_output
+ rm -rf ./default_output ./reference_output
+ mkdir ./default_output ./reference_output
+ D:/java/binaries/jdk/x64/2025-07/windows-jdk11u/jdk-11.0.28+6/bin/rmic -classpath D:/java/ms/openjdk-jdk11u/JTwork/classes/sun/rmi/rmic/defaultStubVersion/run.d -keep -nowrite -d ./default_output G1Impl
+ D:/java/binaries/jdk/x64/2025-07/windows-jdk11u/jdk-11.0.28+6/bin/rmic -classpath D:/java/ms/openjdk-jdk11u/JTwork/classes/sun/rmi/rmic/defaultStubVersion/run.d -keep -nowrite -d ./reference_output -v1.2 G1Impl
+ diff -r ./default_output ./reference_output
+ echo 'TEST PASSED: default output identical to -v1.2 output'
This was sufficient for me to confirm that the build behaves as expected in this scenario.
Once I had finished Setting up my First 3D Printer, I wanted to design and print custom models. The Anycubic Slicer Next software can only open 3mf files. Being new to this space means that I had never even heard of this format before. Here are some useful resources for learning more about it:
Download the latest release from the releases page. This is a .zip archive.
In Blender, go to Edit -> Preferences and open the Add-ons tab on the left.
Click on the Install… button at the top. Navigate to the .zip you downloaded.
Under the “Community” category, find the add-on called “Import-Export: 3MF format”. Make sure that it’s enabled. (Note: If searching with the search box, exclude the “Import-Export” text since this is the category, not part of the add-on name. Just search for “3MF” instead.)
As promised on the plugins page, the export option is now available:
Note that there is a “Scale” option on the export dialog. I just ensured my dimensions were real world dimensions. Here is the simple blender model I made in the Anycubic Slicer Next software. I was able to successfully print it.
Anycubic Slicer Next with a 3mf exported from Blender
The AI advances of the past couple of years have streamlined many mundane tasks. A recent example for me was having Copilot generate scripts to crop images. I wanted to crop the top portion of my screenshots of the Anycubic app (which I used in my post on Setting up my First 3D Printer). I figured I might as well have one post showing my evolution of prompts for this task:
Prompt 1 – Crop my PNGs
Write a python script that takes a number p and crops the top p pixels from a PNG
copilot:
Sure thing! Here's a Python script that uses the Pillow library to crop the top p pixels from a PNG image:
Setup Tip: You'll need to install Pillow if you haven’t already:
pip install pillow
Let me know if you'd like to expand it—for example, batch processing multiple images or adding a GUI.
from PIL import Image
def crop_top_pixels(image_path, p, output_path):
# Open the image
img = Image.open(image_path)
# Ensure p isn't larger than the image height
width, height = img.size
p = min(p, height)
# Define the crop box (left, upper, right, lower)
crop_box = (0, p, width, height)
# Crop and save
cropped_img = img.crop(crop_box)
cropped_img.save(output_path)
print(f"Cropped image saved to {output_path}")
# Example usage
crop_top_pixels("input.png", 50, "output.png")
Pillow Installation
The only notable thing about installing pillow is that I still need this --break-system-packages flag. I will save that investigation for another day. I will upgrade pip though!
saint@MacBookPro Kobra3-app % pip3 install pillow
[notice] A new release of pip is available: 24.3.1 -> 25.1.1
[notice] To update, run: python3.13 -m pip install --upgrade pip
error: externally-managed-environment
× This environment is externally managed
╰─> To install Python packages system-wide, try brew install
xyz, where xyz is the package you are trying to
install.
...
note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.
saint@MacBookPro Kobra3-app % pip3 install pillow --break-system-packages
Collecting pillow
Downloading pillow-11.2.1-cp313-cp313-macosx_11_0_arm64.whl.metadata (8.9 kB)
Downloading pillow-11.2.1-cp313-cp313-macosx_11_0_arm64.whl (3.0 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.0/3.0 MB 27.0 MB/s eta 0:00:00
Installing collected packages: pillow
Successfully installed pillow-11.2.1
[notice] A new release of pip is available: 24.3.1 -> 25.1.1
[notice] To update, run: python3.13 -m pip install --upgrade pip
Prompt 2 – Invoking via Bash
write a bash script to pass all PNG images to the Python script
copilot:
You got it! Here's a simple Bash script that loops through all .png files in a directory and passes each one to the Python script you created:
#!/bin/bash
# Number of pixels to crop from the top
CROP_HEIGHT=50
# Directory containing PNG images (current directory)
IMAGE_DIR="."
# Python script name
PYTHON_SCRIPT="crop.py"
# Output directory
OUTPUT_DIR="./cropped"
# Create output directory if it doesn't exist
mkdir -p "$OUTPUT_DIR"
# Loop through all PNG files
for img in "$IMAGE_DIR"/*.png; do
filename=$(basename "$img")
output_path="$OUTPUT_DIR/$filename"
echo "Cropping $img..."
python3 "$PYTHON_SCRIPT" "$img" "$CROP_HEIGHT" "$output_path"
done
copilot (continued):
To use this setup:
- Make sure your Python script (crop.py) accepts arguments like this:
- Save the Bash script to a file like batch_crop.sh, make it executable with chmod +x batch_crop.sh, and run it from the terminal:
./batch_crop.sh
Prompt 3 – User Configurable Crop Dimensions
make bash script take number of pixels as a command line argument
copilot:
You got it, Saint. Here's the updated Bash script that takes the number of pixels to crop (CROP_HEIGHT) as a command-line argument:
#!/bin/bash
# Check for a crop height argument
if [ $# -lt 1 ]; then
echo "Usage: $0 <crop_height>"
exit 1
fi
CROP_HEIGHT=$1
IMAGE_DIR="."
PYTHON_SCRIPT="crop.py"
OUTPUT_DIR="./cropped"
mkdir -p "$OUTPUT_DIR"
for img in "$IMAGE_DIR"/*.png; do
filename=$(basename "$img")
output_path="$OUTPUT_DIR/$filename"
echo "Cropping $img by $CROP_HEIGHT pixels..."
python3 "$PYTHON_SCRIPT" "$img" "$CROP_HEIGHT" "$output_path"
done
copilot (continued):
How to run it:
./batch_crop.sh 50
Let me know if you want it to support additional flags (like input/output dirs or image formats) to make it more flexible!
Prompt 4 – Scale Down the Images
modify python script to support scaling each image down to a configurable width
copilot:
Absolutely! Here's how you can update your Python script to not only crop the top p pixels but also scale each image down to a configurable target width while preserving the aspect ratio:
import sys
from PIL import Image
def crop_and_scale(image_path, crop_pixels, output_path, target_width=None):
# Open the image
img = Image.open(image_path)
width, height = img.size
# Crop the top p pixels
crop_pixels = min(crop_pixels, height)
cropped_img = img.crop((0, crop_pixels, width, height))
# Resize while maintaining aspect ratio
if target_width is not None and target_width < cropped_img.width:
aspect_ratio = cropped_img.height / cropped_img.width
target_height = int(target_width * aspect_ratio)
cropped_img = cropped_img.resize((target_width, target_height), Image.LANCZOS)
# Save the result
cropped_img.save(output_path)
if __name__ == "__main__":
if len(sys.argv) < 4:
print("Usage: python crop.py <input_path> <crop_pixels> <output_path> [target_width]")
sys.exit(1)
img_path = sys.argv[1]
crop_amount = int(sys.argv[2])
output_path = sys.argv[3]
width = int(sys.argv[4]) if len(sys.argv) > 4 else None
crop_and_scale(img_path, crop_amount, output_path, width)
copilot (continued):
Example command to run it:
python3 crop.py input.png 50 output.png 300
This will crop the top 50 pixels, then scale the image to 300px wide while maintaining the aspect ratio.
Let me know if you want to add height constraints or enforce padding for uniform size outputs!
Prompt 5 – No Bash Required
merge the functionality of the bash script into the python script
copilot:
You got it, Saint. Here's a standalone Python script that handles batch processing of all PNGs in the current directory, crops the top p pixels, and optionally scales them to a target width—all from the command line:
import os
import sys
from PIL import Image
def crop_and_scale(image_path, crop_pixels, output_path, target_width=None):
img = Image.open(image_path)
width, height = img.size
# Crop top p pixels
crop_pixels = min(crop_pixels, height)
cropped = img.crop((0, crop_pixels, width, height))
# Resize if target width is specified
if target_width is not None and target_width < cropped.width:
aspect_ratio = cropped.height / cropped.width
new_height = int(target_width * aspect_ratio)
cropped = cropped.resize((target_width, new_height), Image.LANCZOS)
cropped.save(output_path)
def process_directory(crop_pixels, target_width=None):
input_dir = "."
output_dir = "./cropped"
os.makedirs(output_dir, exist_ok=True)
for filename in os.listdir(input_dir):
if filename.lower().endswith(".png"):
input_path = os.path.join(input_dir, filename)
output_path = os.path.join(output_dir, filename)
print(f"Processing {filename}...")
crop_and_scale(input_path, crop_pixels, output_path, target_width)
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python batch_crop.py <crop_pixels> [target_width]")
sys.exit(1)
crop_pixels = int(sys.argv[1])
target_width = int(sys.argv[2]) if len(sys.argv) > 2 else None
process_directory(crop_pixels, target_width)
copilot (continued):
Example usage:
python batch_crop.py 50 # Crop top 50 pixels only
python batch_crop.py 50 300 # Crop top 50 pixels and scale to 300px wide
Want to add input/output directory flags, process JPGs too, or make it a little GUI app for drag-and-drop convenience? I’d be glad to help with any of that.
Prompt 6 – Renaming Files
modify the script to replace user specified filename prefix with a custom user specified prefix
copilot:
Sure thing! Here's the updated Python script that allows the user to specify:
- The number of pixels to crop (crop_pixels)
- An optional scaling width (target_width)
- A filename prefix to replace
- A new prefix to substitute in its place
import os
import sys
from PIL import Image
def crop_and_scale(image_path, crop_pixels, output_path, target_width=None):
img = Image.open(image_path)
width, height = img.size
# Crop
crop_pixels = min(crop_pixels, height)
cropped = img.crop((0, crop_pixels, width, height))
# Scale
if target_width is not None and target_width < cropped.width:
aspect_ratio = cropped.height / cropped.width
new_height = int(target_width * aspect_ratio)
cropped = cropped.resize((target_width, new_height), Image.LANCZOS)
cropped.save(output_path)
def process_directory(crop_pixels, target_width, old_prefix, new_prefix):
input_dir = "."
output_dir = "./cropped"
os.makedirs(output_dir, exist_ok=True)
for filename in os.listdir(input_dir):
if filename.lower().endswith(".png"):
if not filename.startswith(old_prefix):
continue # Skip files that don't match the prefix
# Replace old prefix with new
new_filename = new_prefix + filename[len(old_prefix):]
input_path = os.path.join(input_dir, filename)
output_path = os.path.join(output_dir, new_filename)
print(f"Processing {filename} → {new_filename}")
crop_and_scale(input_path, crop_pixels, output_path, target_width)
if __name__ == "__main__":
if len(sys.argv) < 4:
print("Usage: python batch_crop.py <crop_pixels> <old_prefix> <new_prefix> [target_width]")
sys.exit(1)
crop_pixels = int(sys.argv[1])
old_prefix = sys.argv[2]
new_prefix = sys.argv[3]
target_width = int(sys.argv[4]) if len(sys.argv) > 4 else None
process_directory(crop_pixels, target_width, old_prefix, new_prefix)
Example usage:
python batch_crop.py 40 IMG_ thumb_ 300
If you have files like IMG_001.png, the output will be named thumb_001.png.
Let me know if you’d like suffix support, regex matching, or nested folder traversal next!
This final script enabled me to crop, scale down, and renamed all the PNGs from my phone with minimal effort. Here is an example of how I used it:
The assembly process took me just under a week since I did just a little bit each day. The manual has a QR code for the installation instructions video, Assembly for Anycubic Kobra 3 Combo:
The assembly process was smooth overall, especially because the video clarified some of the instructions. I wasn’t sure how to install the filament tubes into the print head because I did not know how far they should go into the print head. I noticed in the video that their filament tubes had a black ring on the tubes (which mine didn’t) but that was enough to let me know it was safe to push them further into the print head until they were firmly in. I also didn’t pay attention to the orientation of the filament when I loaded it into the ACE (Anycubic Color Engine), but it still worked fine in with my 180-degree rotation around the z-axis.
The print head and print bed did not seem to be shaking so I didn’t adjust the hexagonal isolation columns located underneath the print head/bed respectively. Another instruction in the “Check before use” section was to manually move the print head and print platform then if there is any difficulty or abnormal noise during the movement, adjust the tensioners to ensure smooth sliding of the print head or platform. I had no idea what the right tension was. I hazarded a guess and proceeded to powering on the machine.
Printing my First Model
After powering up, the Kobra 3 displayed a QR code on its screen linking to the Anycubic app. The next step was to add the Kobra 3 printer after the app was installed and I registered a new account. I tried scanning the QR code on the printer screen, but it failed with an error about checking the server selection on the printer. I wasn’t sure what that was referring to since I had just connected the printer to my Wi-Fi without any “cloud” setup. I think I ended up selecting the printer by model in the app but still had problems connecting to it. I decided to install the desktop software to see if I could make progress that way. I think it was after I set up the Anycubic Slicer Next software on my desktop that I was able to connect to the printer in the app on my phone.
I looked around for what to make for my first ever print run and settled for this metric screw and nut gauge (whatever it is). Unfortunately, the printer kept moving closer and closer to the y-axis edge. It looked like it was going to try to print off the build platform so I canceled the task.
I decided to print a different model, a simpler one, to see if perhaps the model is at issue. This (mostly) circular fidget toy seemed ideal because its dimensions meant that it had to be centered on the build platform.
3D-printed Downloaded Fidget Toy
The first time I tried to print this model, it failed with the printer almost overrunning the build platform (just like it did with the previous model):
Broken 3D-printed Downloaded Fidget Toy
I cancelled that task and upgraded the printer’s firmware:
The same thing happened again so I cancelled the printing task. I had set up the 3D printer on a stand-up desk which had a little bit of swaying motion when the printer was at work so I moved the 3D printer onto the floor to see if this could have been the problem. The same broken model was being printed with the printer on the floor so I cancelled the task again. However, this enabled me to rule out the stability of the print platform. The tasks I cancelled are in the “End” tab, along with their start and end times in the images below.
At this point, the remaining question was whether the build platform’s Y-axis was too lose. That could be the cause of the drifting that occurred as the build progressed. I tightened the Y-axis belt tensioner more than what I had initially considered reasonable then printed the model again. This time, the print head appeared to correctly stay within the bounds of the model! The fidget toy was now printed correctly! It was such a relief to know that my unit was functioning correctly and so I didn’t have to start dealing with tech support on a new product.
Desktop Software Installation and Usage
The printer’s accompanying USB stick has the Anycubic Slicer Next installer. I set it up when my initial attempts to connect to the printer on my phone failed.
The only change I made in the startup sequence was to disable the other 3D printers. I installed the updates when prompted then logged in after Anycubic Slicer Next loaded.
After logging in, I went to the Makeronline tab and found the fidget toy (this was when I had realized that a simpler model would greatly aid my troubleshooting efforts). The firmware updates message on the Workbench page are what prompted me to update the firmware (as phone earlier in the app screenshots from my phone). My first run through this program also had the wrong printer selected. The corresponding error is included in the images below.
I needed to switch from the Anycubic Kobra S1 0.4 nozzle. Why is this one selected anyway if I only checked mine in the initial startup prompts? Switching to the right printer enabled the print task to start:
The job started above is the first fidget task I manually ended in the “End” tab of the Tasks page in an earlier screenshot of the app on my phone. After I installed the firmware update using my phone, there was still a “New firmware version available for update” message in the Printer section but it turned out the update was for the ACE Pro this time:
With both devices’ firmware up to date, I started the second attempt of the fidget toy (which failed due to the aforementioned lose Y-axis belt). One error that popped up was this tangled material error alert.
Tangled Material Dialog
This was when I realized that the orientation of the filament when I loaded it into the ACE (Anycubic Color Engine) was off by 180-degrees around the z-axis. The link is to CODE: 11519 Filament tangle detected, please organize the filament and resume printing | Anycubic Wiki. I re-adjusted the roll and was able to resume printing. The dialog below marked the completion of the final print (after the Y-axis belt was tightened):
Print Finished Dialog
Modeling Software Options
Now that I could print models from Makeronline, the remaining key component was the software for creating my own models. I learned that one of the programs people use is Tinkercad, which is a free web app. Having used SolidWorks in the past, I decided to stick with it. I needed to renew my license though, which turned out to be more involved than I expected (see the post on Troubleshooting my SOLIDWORKS 2025 Installation for details). In the midst of troubleshooting those issues, I realized that I could probably use Blender as well! In conclusion, there are many options for modeling software for exploration and 3D printing.
I installed SOLIDWORKS 2023 Student Edition on my desktop about 2 years ago but did not renew the license after it expired last year. Having just set up a 3D printer, I decided to renew my SOLIDWORKS license to make new models I could print. Since I bought the latest version (2025) from SOLIDWORKS for Students | SOLIDWORKS. I uninstalled SOLIDWORKS 2023 and installed SOLIDWORKS 2025 SP2.0. However, I could not start SOLIDWORKS 2025! It failed with this error:
---------------------------
SOLIDWORKS Product Activation
---------------------------
The current computer identifier (0) does not match the previously used computer identifier (1). You can continue, but you might have activation or deactivation issues in the future.
Please contact you support representative for support.
---------------------------
OK
---------------------------
The OK button then leads to another dialog with this message:
[Window Title]
SOLIDWORKS
[Content]
This Student Edition has expired.
Please browse to the following web location to find out about the new version of SOLIDWORKS Student Edition:
www.solidworks.com/goEDU
[OK]
These is the first set of instructions I got (slightly paraphrased):
Go to the FlexNet Folder (C:\ProgramData\FLEXnet) and rename all ‘SW_D’ files to something like ‘SW_D_old’. Launch SOLIDWORKS again and test the behavior. By default, the “ProgramData” folder is hidden. Either you can activate the “Show hidden folders” option in the “Folder” options, or you can directly enter C:\ProgramData in the address bar to access this folder.
If the above steps do not help, try the steps below
Go to Control Panel\All Control Panel Items\Programs and Features
Search for SOLIDWORKS <version> then right click on it and click ‘Change’
The SOLIDWORKS installation manager will pop up. Click on ‘Repair your installation’.
Once repair is complete, launch SOLIDWORKS and test the behavior.
If the issue still persists, please share the Installation Manager logs on the 3DSupport App for investigation. To go to the Installation log folder, you can type %appdata% at Start > RUN and press Enter, and it will open the “Roaming” folder. In the “Roaming” folder, go to the “SOLIDWORKS” folder. Under the “SOLIDWORKS” folder, you will find a folder named “Installation log.” Zip this complete folder and provide it for investigation.
These steps didn’t work so I uploaded my installation logs. The next set of instructions addressed the problem. Here they are (slightly paraphrased):
To resolve this issue, I recommend uninstalling all SOLIDWORKS versions currently present on your system. Once that is done, then perform the installation again and be sure to enter your new license number [1234…] manually on the serial number page in the “3D Design > SOLIDWORKS” checkbox. If you have purchased the Student Edition, then your new serial number will be present in the post-purchase email. Please follow these installation steps:
Uninstall all SOLIDWORKS versions currently present on your system. Please follow these steps:
Go to Control Panel\All Control Panel Items\Programs and Features
Search for SOLIDWORKS (Version) then right click on it and select ‘Uninstall’.
In the Installation Manager window, under ‘Advanced Options’, select ‘Change’.
By default, the ‘Program files and Folders’ will be selected. Select the remaining items (‘Registry Entries’ and ‘Data Files and Folders’) as well.
Click ‘Back to Summary’.
Click on ‘Remove Items’ and let Installation Manager complete the uninstallation.
Once uninstallation is complete, restart your system.
Download the setup file from: www.solidworks.com/SEK
Right-click on the setup file and select ‘Run as administrator’.
On Welcome page > Select ‘Install on this computer’ and click ‘Next’.
On the Serial Number screen, enter your latest serial number [1234…] manually in the 3D Design > SOLIDWORKS checkbox only. Please leave all the other product’s [like composer, Visual, simulation, etc..] serial number checkboxes blank and unchecked. See the attached image for your reference.
On the Summary screen > Under Products, click ‘change’ > untick the unwanted
On the Summary screen > Download Option, click ‘change” > select ‘Download and Install’.
On the Summary screen, click ‘Install Now’.
Uninstalling SOLIDWORKS 2025
The first time I uninstalled SOLIDWORKS 2025, I didn’t adjust the “Advanced Options”:
After uninstalling it, the SOLIDWORKS Login Manager was still listed as installed in the control panel. I uninstalled it as well then later realized that I missed other products from Dassault Systemes.
I entered the correct serial number on the installation this time. I also noticed that the install path is in C:\dev (which also has SOLIDWORKS 2023 files). I didn’t remember how I created that directory the last time I installed SOLIDWORKS (nothing else would automatically go into my \dev directory). After all this, the same error message is displayed when trying to start SOLIDWORKS. This was because I skipped the most important steps in the uninstallation. I paid careful attention to the options the next time I uninstalled it! Before uninstalling it, I poked around and found that I the license is in HKEY_LOCAL_MACHINE\SOFTWARE\SolidWorks\Licenses\Serial Numbers. The slideshow below shows the advanced options that need to be selected for a full uninstall.
These are the files and folders that were to be removed on my desktop:
Program Files and Folder:
C:\Program Files\SOLIDWORKS Corp\SOLIDWORKS CAM\
C:\Program Files\SOLIDWORKS Corp\SOLIDWORKS Visualize\
C:\Program Files\SOLIDWORKS Corp\SOLIDWORKS Plastics\
C:\Program Files\SOLIDWORKS Corp\SOLIDWORKS Flow Simulation\
C:\Program Files\SOLIDWORKS Corp\eDrawings\
C:\Program Files\SOLIDWORKS Corp\SOLIDWORKS\
Registry Entries:
HKCU\Software\SolidWorks
HKLM\Software\Wow6432Node\SolidWorks
HKLM\Software\SolidWorks
HKCU\Software\SRAC
HKLM\Software\SRAC
HKCU\Software\eDrawings
HKLM\Software\eDrawings
Data Files and Folder:
C:\software\SOLIDWORKS Data (2)
C:\ProgramData\SolidWorks
C:\ProgramData\COSMOS Applications
C:\Users\Public\Documents\SolidWorks
C:\ProgramData\DassaultSystemes
Download Files and Folder:
C:\dev\Downloads\SolidWorksFiles\SOLIDWORKS 2023 SP02.1
C:\dev\Downloads\SolidWorksFiles\SOLIDWORKS 2025 SP02
After uninstallation completed, I decided to inspect the registry (using the reg query command) to see what was left. 6 of the 7 registry entries returned “ERROR: The system was unable to find the specified registry key or value.” Only the HKEY_LOCAL_MACHINE\SOFTWARE\SolidWorks key still contained data.
C:\>reg query "HKEY_LOCAL_MACHINE\SOFTWARE\SolidWorks" /s
HKEY_LOCAL_MACHINE\SOFTWARE\SolidWorks\AddIns
HKEY_LOCAL_MACHINE\SOFTWARE\SolidWorks\AddIns\{219180B0-7183-4FE2-B167-4E2BFE534004}
(Default) REG_DWORD 0x1
Description REG_SZ Access to digital manufacturers worldwide, sourceable 3D components, and more. v6.33.2049
Title REG_SZ 3DEXPERIENCE Marketplace
Icon Path REG_SZ C:\Program Files\Common Files\SOLIDWORKS Shared\3DEXPERIENCE Marketplace\I_3DEXPERIENCEPlatform_16.png
HKEY_LOCAL_MACHINE\SOFTWARE\SolidWorks\AddIns\{fb5ac345-200b-44d2-9ffa-69b7d44fc36f}
(Default) REG_DWORD 0x0
Description REG_SZ Work with a package of content from a 3DEXPERIENCE source, such as SOLIDWORKS Connected or the Design with SOLIDWORKS app.
Title REG_SZ 3DEXPERIENCE Exchange
Icon Path REG_SZ C:\Program Files\Common Files\SOLIDWORKS Shared\3DEXPERIENCE Exchange\Resources\3dexperience_exchange-20px.png
The path “C:\Program Files\SOLIDWORKS Corp” now contained 0 files but still contained 2 folders: “SOLIDWORKS Electrical” and “eDrawings” (which looked harmless). The other directories had been removed. There was still a “C:\ProgramData\SOLIDWORKS Electrical” directory though. I could not remove it because “C:\ProgramData\SOLIDWORKS Electrical\MSSQL15.TEW_SQLEXPRESS\MSSQL\Binn\batchparser.dll” was open in the SQL Server (TEW_SQLEXPRESS) service. The path to its executable was "C:\ProgramData\SOLIDWORKS Electrical\MSSQL15.TEW_SQLEXPRESS\MSSQL\Binn\sqlservr.exe" -sTEW_SQLEXPRESS. The disabled agent’s path was "C:\ProgramData\SOLIDWORKS Electrical\MSSQL15.TEW_SQLEXPRESS\MSSQL\Binn\SQLAGENT.EXE" -i TEW_SQLEXPRESS and the CEIP service executable path was "C:\ProgramData\SOLIDWORKS Electrical\MSSQL15.TEW_SQLEXPRESS\MSSQL\Binn\sqlceip.exe" -Service TEW_SQLEXPRESS. See the properties of these services below.
I stopped the SQL Server (TEW_SQLEXPRESS) and SQL Server CEIP service (TEW_SQLEXPRESS) services. I could now delete the “C:\ProgramData\SOLIDWORKS Electrical” directory. Notice that there was also a SolidWorks Licensing Service but it was stopped. The path to its executable was "C:\Program Files (x86)\Common Files\SolidWorks Shared\Service\SolidWorksLicensing.exe" and it was the only file under the “SolidWorks Shared” directory. I deleted it (even though this left the service entry). I didn’t recall how I set up the 2023 version but I have setup files in C:\dev\Downloads\SolidWorks so I just manually deleted that entire folder.
Installing SOLIDWORKS 2025
After the full uninstall described above, there was no serial number prepopulated into the installer this time! The default Download to path was in my documents folder, which is on OneDrive. Aha, this must be why I had changed it to C:\dev\Downloads\SolidWorks – no need to store a couple of GBs of installers on OneDrive.
Once installation completed, all I needed to do is enter my email and click “next” to activate the product.
SOLIDWORKS 2025 now started up successfully! The root cause of all my problems was that I didn’t do a full uninstall of SOLIDWORKS 2023!
time /cygdrive/c/repos/scratchpad/scripts/java/cygwin/build-jdk.sh windows x86_64 slowdebug zero
The build will fail with Error: Failed to load D:\java\forks\dups11\openjdk\jdk\build\windows-x86_64-zero-slowdebug\jdk\bin\zero\jvm.dll. However, the build is still usable (all the binaries and symbols should be present). I am saving that investigation for another day. For now, verify that the build works by running this command:
$ build/windows-x86_64-zero-slowdebug/jdk/bin/java.exe -version
java version "25-internal" 2025-09-16
Java Runtime Environment (slowdebug build 25-internal-adhoc.USERsaint.jdk)
Java HotSpot 64-Bit Zero VM (slowdebug build 25-internal-adhoc.USERsaint.jdk, interpreted mode)
checking for --enable-hsdis-bundling... disabled, default
checking what hsdis backend to use... 'none', hsdis will not be built
checking if hsdis should be bundled... no
checking for --enable-libffi-bundling... disabled, default
checking for LIBFFI... checking for ffi.h... no
configure: error: Could not find libffi!
configure exiting with result code 1
I took at look at the source of the error message in jdk/make/autoconf/lib-ffi.m4 and realized that I need to have the repo.
git clone https://github.com/libffi/libffi.git
cd libffi
git checkout v3.4.8
I then added the --with-libffi argument to the configure script.
This didn’t address the failure but closer inspection of the .m4 led me to realize that I need to build libffi. This was the genesis of the post on Building libffi for Windows x64 with Visual C++. Once I had built libffi and created the .lib file in the expected location of its repo, the configure script succeeded. I started the build with this command:
time /cygdrive/c/repos/scratchpad/scripts/java/cygwin/build-jdk.sh windows x86_64 slowdebug zero
The build failed!
* For target hotspot_variant-zero_libjvm_gtest_objs_BUILD_GTEST_LIBJVM_pch.obj:
BUILD_GTEST_LIBJVM_pch.cpp
d:\java\forks\dups11\openjdk\jdk\src\hotspot\share\runtime/globals.hpp(35): fatal error C1083: Cannot open include file: 'globals_windows_zero.hpp': No such file or directory
... (rest of output omitted)
* For target support_gensrc_java.base__SocketOptionRegistry.java:
/*
* Copyright (c) 2008, 2020, Oracle and/or its affiliates. All rights reserved.
*
...
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
... (rest of output omitted)
* All command lines available in /cygdrive/d/java/forks/dups11/openjdk/jdk/build/windows-x86_64-zero-slowdebug/make-support/failure-logs.
=== End of repeated output ===
There was a .log file and a .cmd file in the failure-logs folder. build\windows-x86_64-zero-slowdebug\make-support\failure-logs\support_gensrc_java.base__SocketOptionRegistry.java.log contained this:
/*
* Copyright (c) 2008, 2020, Oracle and/or its affiliates. All rights reserved.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
/cygdrive/d/java/forks/dups11/openjdk/jdk/make/scripts/fixpath.sh: line 486: c:\progra~1\mib055~1\2022\enterp~1\vc\tools\msvc\1444~1.352\bin\hostx64\x64\cl.exe: command not found
There were several warnings (e.g. warning C4267: ‘initializing’: conversion from ‘size_t’ to ‘int’, possible loss of data) and a build error that I fixed in Fix MSVC warnings and build errors · swesonga/jdk@bd519ea. I later realized that I could have used the --disable-warnings-as-errors configure argument but it was good for me to be aware of which issues I could run into later when using the zero interpreter. At this point, the build failed with this message:
* For target buildtools_create_symbols_javac__the.COMPILE_CREATE_SYMBOLS_batch:
Error: Failed to load D:\java\forks\dups11\openjdk\jdk\build\windows-x86_64-zero-slowdebug\jdk\bin\zero\jvm.dll
* All command lines available in /cygdrive/d/java/forks/dups11/openjdk/jdk/build/windows-x86_64-zero-slowdebug/make-support/failure-logs.
That error in the only line in the build\windows-x86_64-zero-slowdebug\make-support\failure-logs\buildtools_create_symbols_javac__the.COMPILE_CREATE_SYMBOLS_batch.log file. This was the .cmdline file (next to the .log file):
That meant that I didn’t need to look any further into this error (at least not right away) so I moved on to building for the ARM64 platform. I initially tried cross compiling but this didn’t work. This is the configure command I tried for cross compiling.
The linker failed with a series of 19 unresolved symbols and a warning that the library machine type ‘ARM64’ conflicts with target machine type ‘x64’. Notice that the === Output from failing command(s) repeated here === section did not contain the linker warning LNK4272 since it omits the rest of the input after showing the first 14 lines. Scanning the build log is important in such cases where some of the output is omitted at the end.
...
fallbackLinker.o : error LNK2019: unresolved external symbol __imp_ffi_type_double referenced in function Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1double
fallbackLinker.o : error LNK2019: unresolved external symbol __imp_ffi_type_pointer referenced in function Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1pointer
d:\repos\dups\libffi\lib\libffi.lib : warning LNK4272: library machine type 'ARM64' conflicts with target machine type 'x64'
d:\java\forks\dups11\openjdk\jdk\build\windows-aarch64-zero-slowdebug\buildjdk\support\modules_libs\java.base\fallbackLinker.dll : fatal error LNK1120: 19 unresolved externals
At this point, I switched to my ARM64 machine (to avoid these mismatch issues) and copied the the libffi files onto it (after cloning the libffi repo). The --openjdk-target argument is no longer necessary for a native build on Windows ARM64. The build failed since the CONTEXT (x86 64-bit) struct (defined in C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um\winnt.h is a platform specific struct. This fix was needed for ARM64: Fix build errors on Windows AArch64 · swesonga/jdk@d3ec3c7. OpenJDK now built successfully, well, until the same error about being unable to load jvm.dll.
I moved on to testing the build but this time it didn’t work! I added some debug output in Display GetLastError() on failure to load DLL · swesonga/jdk@ce7143e. GetLastError was 126, i.e. “the specified module could not be found” as per the System Error Codes (0-499) (WinError.h). I verified that the path to jvm.dll was valid then used Process Monitor to record events on the system while running java.exe. Turns out I needed to copy libffi-8.dll into the directory containing jvm.dll.
The Microsoft.CodeCoverage.Console tool can be used to collect code coverage for C++ code. To use it, open the “Developer Command Prompt” – it should be in the path by default. Once the OpenJDK build completes, instrument the DLLs and EXEs as follows:
Run start output.coverage to open the code coverage file. You can use the search filter to narrow down the coverage results, e.g. by searching for “interpreter”.
Instrumenting Fails on Windows AArch64
There are warnings when instrumenting some of the DLLs on the AArch64 platform. The warning below is from running Microsoft.CodeCoverage.Console instrument java.dll in the ARM64 Native Tools Command Prompt:
My list from last year had these 3 files, which are no longer present in the build so keep in mind that you need to ensure your list is current and exhaustive:
$ alias ls
alias ls='ls -F --color=auto --show-control-chars'
$ which ls
/usr/bin/ls
$ /usr/bin/ls -1 *.exe
jabswitch.exe
jaccessinspector.exe
jaccesswalker.exe
...