Skip to content

Images built with Alpine <=3.23 do not include backtrace in result binaries. #41

Description

@Fijxu

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions