I recently noticed that the backtraces of my programs are not available when using 84codes/crystal:1.18.2-alpine (and later versions) OCI and the --static argument at build time.
I tried the 84codes/crystal:1.17.1-alpine OCI to build my program using --static and unlike the build using 84codes/crystal:1.18.2-alpine, it returns a backtrace referencing where it raised.
After hours of debugging, I discovered that OCIs based on Alpine 3.22, were able to return a backtrace just fine, but ones based on Alpine 3.23 just return ??? in their backtrace lines. 84codes/crystal:1.17.1-alpine is based on Alpine 3.22 and 84codes/crystal:1.18.2-alpine is based on Alpine 3.23
So I made my own OCIs for testing, this were the commands (inside the ./alpine directory):
docker build . --build-arg crystal_version=1.18.2 --build-arg shards_version=0.20.0 --build-arg gc_version=8.2.12 --build-arg llvm_version=21 --tag crystal-1.18.2-alpine-3.23-llvm-21 --build-arg alpine_version=3.23
(The LLVM version doesn't matter, I was just testing, if I remove --build-arg llvm_version=21 and use the default version (20), the issue will be the same)
docker build . --build-arg crystal_version=1.18.2 --build-arg shards_version=0.20.0 --build-arg gc_version=8.2.12 --tag crystal-1.18.2-alpine --build-arg alpine_version=3.22
Then I made a simple program to display a backtrace:
# throw.cr
begin
raise Exception.new("Throw!")
rescue ex
puts ex.inspect_with_backtrace
end
And here are the logs:
/tmp ❯❯❯ docker run -v "$(pwd)":/app -w /app --entrypoint "" -i -t crystal-1.18.2-alpine:latest sh -c 'crystal build throw.cr -o throw-1.18.2 --static -s -t -p' ✘ 130
Parse: 00:00:00.000032059 ( 1.16MB)
Semantic (top level): 00:00:00.290112032 ( 129.16MB)
Semantic (new): 00:00:00.001424468 ( 129.16MB)
Semantic (type declarations): 00:00:00.020435889 ( 129.16MB)
Semantic (abstract def check): 00:00:00.006934172 ( 145.16MB)
Semantic (restrictions augmenter): 00:00:00.007033280 ( 145.16MB)
Semantic (ivars initializers): 00:00:00.007105158 ( 145.16MB)
Semantic (cvars initializers): 00:00:00.133484277 ( 145.22MB)
Semantic (main): 00:00:00.042558743 ( 161.22MB)
Semantic (cleanup): 00:00:00.000280796 ( 161.22MB)
Semantic (recursive struct check): 00:00:00.000516952 ( 161.22MB)
Codegen (crystal): 00:00:00.157329363 ( 177.22MB)
Codegen (bc+obj): 00:00:00.278674221 ( 177.22MB)
Codegen (linking): 00:00:00.127641439 ( 177.22MB)
Codegen (bc+obj):
- no previous .o files were reused
/tmp ❯❯❯ ./throw-1.18.2
Throw! (Exception)
from /app/throw.cr:2:3 in '__crystal_main'
from /usr/share/crystal/src/crystal/main.cr:129:5 in 'main_user_code'
from /usr/share/crystal/src/crystal/main.cr:115:7 in 'main'
from /usr/share/crystal/src/crystal/system/unix/main.cr:9:3 in 'main'
from src/env/__libc_start_main.c:95:2 in 'libc_start_main_stage2'
/tmp ❯❯❯ docker run -v "$(pwd)":/app -w /app --entrypoint "" -i -t crystal-1.18.2-alpine-3.23-llvm-21:latest sh -c 'crystal build throw.cr -o throw-1.18.2 --static -s -t -p'
Parse: 00:00:00.000127268 ( 1.16MB)
Semantic (top level): 00:00:01.089720583 ( 129.49MB)
Semantic (new): 00:00:00.002192626 ( 129.49MB)
Semantic (type declarations): 00:00:00.055942273 ( 129.49MB)
Semantic (abstract def check): 00:00:00.021650070 ( 145.49MB)
Semantic (restrictions augmenter): 00:00:00.014922406 ( 145.49MB)
Semantic (ivars initializers): 00:00:00.024653824 ( 145.49MB)
Semantic (cvars initializers): 00:00:00.306904298 ( 145.55MB)
Semantic (main): 00:00:00.129445900 ( 161.55MB)
Semantic (cleanup): 00:00:00.000795617 ( 161.55MB)
Semantic (recursive struct check): 00:00:00.001323799 ( 161.55MB)
Codegen (crystal): 00:00:00.325639104 ( 177.55MB)
Codegen (bc+obj): 00:00:00.219256642 ( 177.55MB)
Codegen (linking): 00:00:00.136394361 ( 177.55MB)
Codegen (bc+obj):
- no previous .o files were reused
/tmp ❯❯❯ ./throw-1.18.2
Throw! (Exception)
from ???
from ???
from ???
from ???
from ???
from ???
from ???
from ???
from ???
/tmp ❯❯❯
The Crystal compiler OCI built with Alpine 3.23 returned from ???, and the one with Alpine 3.22, returned a proper backtrace. This also happens with the latest release 1.21.0, but I did all my testing with 1.17.0 and 1.18.2 since there is where I found the issue.
I recently noticed that the backtraces of my programs are not available when using
84codes/crystal:1.18.2-alpine(and later versions) OCI and the--staticargument at build time.I tried the
84codes/crystal:1.17.1-alpineOCI to build my program using--staticand unlike the build using84codes/crystal:1.18.2-alpine, it returns a backtrace referencing where it raised.After hours of debugging, I discovered that OCIs based on Alpine 3.22, were able to return a backtrace just fine, but ones based on Alpine 3.23 just return
???in their backtrace lines.84codes/crystal:1.17.1-alpineis based on Alpine 3.22 and84codes/crystal:1.18.2-alpineis based on Alpine 3.23So I made my own OCIs for testing, this were the commands (inside the
./alpinedirectory):(The LLVM version doesn't matter, I was just testing, if I remove
--build-arg llvm_version=21and use the default version (20), the issue will be the same)Then I made a simple program to display a backtrace:
And here are the logs:
The Crystal compiler OCI built with Alpine 3.23 returned
from ???, and the one with Alpine 3.22, returned a proper backtrace. This also happens with the latest release 1.21.0, but I did all my testing with 1.17.0 and 1.18.2 since there is where I found the issue.