Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

suspicious segfault in CRN compression with one main thread and one helper thread #74

Open
illwieckz opened this issue Oct 8, 2024 · 2 comments

Comments

@illwieckz
Copy link
Member

illwieckz commented Oct 8, 2024

I previously noticed that using different value of -helperThreads produce different files. I wondered if it was due to a bug.

I recently wrote a patch in the illwieckz/max-threads branch to set the maximum amount of threads to be usable at build time.

Given crunch is supposed to work with a main thread and some helper threads, the amount of helper threads is assumed to be the maximum amount of threads minus one, so it is assumed the tool should work with only two threads.

Building crunch with an arbitrary maximum amount of threads can be done like this with the illwieckz/max-threads branch:

mkdir buikd && cd build
cmake .. -DUSE_MAX_THREADS=2 && ninja

When I run a conversion from PNG to TGA it works:

./crunch -noTitle -nostats -noprogress -file ../test/unvanquished_64.png -out unvanquished_64.tga

When I run a conversion from PNG to TGA it segfaults, like if it stumbled on some memory issues. It doesn't crash when spawning a thread, it crashes when reading data:

./crunch -noTitle -nostats -noprogress -file ../test/unvanquished_64.png -out unvanquished_64.crn
Thread 1 "crunch" received signal SIGSEGV, Segmentation fault.
0x000055555562d1df in crnlib::crn_comp::pack_blocks (this=this@entry=0x5555556f2ae0, group=group@entry=0, 
    clear_histograms=clear_histograms@entry=true, pCodec=pCodec@entry=0x0, pColor_endpoint_remap=<optimized out>, 
    pColor_selector_remap=<optimized out>, pAlpha_endpoint_remap=0x5555556f4760, pAlpha_selector_remap=0x5555556f4810)
    at crunch/crnlib/crn_comp.cpp:279
279	          uint index = (*endpoint_remap[c])[m_endpoint_indices[b].component[c]];

Thread 8 (Thread 0x7ffff76006c0 (LWP 1053043) "crunch"):
#0  0x00007ffff7898d61 in __futex_abstimed_wait_common64 (private=<optimized out>, cancel=true, abstime=0x0, op=393, expected=0, futex_word=0x5555556ea120) at ./nptl/futex-internal.c:57
#1  __futex_abstimed_wait_common (cancel=true, private=<optimized out>, abstime=0x0, clockid=0, expected=0, futex_word=0x5555556ea120) at ./nptl/futex-internal.c:87
#2  __GI___futex_abstimed_wait_cancelable64 (futex_word=futex_word@entry=0x5555556ea120, expected=expected@entry=0, clockid=clockid@entry=0, abstime=abstime@entry=0x0, private=<optimized out>) at ./nptl/futex-internal.c:139
#3  0x00007ffff78a4f0f in do_futex_wait (sem=sem@entry=0x5555556ea120, abstime=0x0, clockid=0) at ./nptl/sem_waitcommon.c:111
#4  0x00007ffff78a4fa8 in __new_sem_wait_slow64 (sem=0x5555556ea120, abstime=0x0, clockid=0) at ./nptl/sem_waitcommon.c:183
#5  0x000055555562a405 in crnlib::semaphore::wait (this=<optimized out>, milliseconds=<optimized out>) at crunch/crnlib/crn_threading_pthreads.cpp:173
#6  0x000055555562a7cd in crnlib::task_pool::thread_func (pContext=0x5555556f2ae8) at crunch/crnlib/crn_threading_pthreads.cpp:384
#7  0x00007ffff789ca94 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:447
#8  0x00007ffff7929c3c in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:78

Thread 1 (Thread 0x7ffff7e9d780 (LWP 1053001) "crunch"):
#0  0x000055555562d1df in crnlib::crn_comp::pack_blocks (this=this@entry=0x5555556f2ae0, group=group@entry=0, clear_histograms=clear_histograms@entry=true, pCodec=pCodec@entry=0x0, pColor_endpoint_remap=<optimized out>, pColor_selector_remap=<optimized out>, pAlpha_endpoint_remap=0x5555556f4760, pAlpha_selector_remap=0x5555556f4810) at crunch/crnlib/crn_comp.cpp:279
#1  0x00005555556339e6 in crnlib::crn_comp::compress_internal (this=this@entry=0x5555556f2ae0) at crunch/crnlib/crn_comp.cpp:1238
#2  0x0000555555633e1e in crnlib::crn_comp::compress_pass (this=0x5555556f2ae0, params=..., pEffective_bitrate=0x7fffffffcab0) at crunch/crnlib/crn_comp.cpp:1297
#3  0x00005555556212f9 in crnlib::create_compressed_texture (params=..., comp_data=..., pActual_quality_level=pActual_quality_level@entry=0x7fffffffca90, pActual_bitrate=pActual_bitrate@entry=0x7fffffffcab0) at crunch/crnlib/crn_texture_comp.cpp:60
#4  0x00005555555b985f in crnlib::mipmapped_texture::write_comp_texture (this=0x7fffffffcf70, pFilename=0x5555556e9310 "build/test/png-to-all/unvanquished_64.crn", orig_comp_params=..., pActual_quality_level=0x7fffffffca90, pActual_bitrate=0x7fffffffcab0) at crunch/crnlib/crn_mipmapped_texture.cpp:2942
#5  0x00005555556254f4 in crnlib::texture_conversion::write_compressed_texture (stats=..., perceptual=<optimized out>, progress_state=..., dst_format=crnlib::PIXEL_FMT_A8R8G8B8, comp_params=..., params=..., work_tex=...) at crunch/crnlib/crn_texture_conversion.cpp:396
#6  crnlib::texture_conversion::process (params=..., stats=...) at crunch/crnlib/crn_texture_conversion.cpp:621
#7  0x0000555555568e38 in crunch::convert_file (this=this@entry=0x7fffffffdcf0, file_index=file_index@entry=0, num_files=<optimized out>, pSrc_filename=<optimized out>, pDst_filename=0x5555556e93f0 "build/test/png-to-all/unvanquished_64.crn", out_file_type=out_file_type@entry=crnlib::texture_file_types::cFormatCRN) at crunch/crunch/crunch.cpp:1114
#8  0x000055555556a64b in crunch::process_files (this=this@entry=0x7fffffffdcf0, files=...) at crunch/crunch/../crnlib/crn_vector.h:106
#9  0x000055555556e194 in crunch::convert (this=this@entry=0x7fffffffdcf0) at crunch/crunch/crunch.cpp:381
#10 0x000055555556e875 in crunch::convert (this=this@entry=0x7fffffffdcf0, pCommand_line=<optimized out>) at crunch/crunch/crunch.cpp:330
#11 0x0000555555566427 in main_internal (argc=argc@entry=10, argv=argv@entry=0x7fffffffdf88) at crunch/crunch/crunch.cpp:1201
#12 0x000055555555d798 in main (argc=10, argv=0x7fffffffdf88) at crunch/crunch/crunch.cpp:1228

Thanks to this branch I more curious behaviors. I already noticed that using -helperThreads 3 with the default max amount of threads being 16, crn build was reproducible. It was not with amounts of helper threads higher than 3.

But with that branch, I can test different maximum amount of threads:

  • with default 16 max threads and -helperThreads 3, the image conversion is reproducible.
  • with default 16 max threads and -helperThreads 4, I get 6 different files (some DDS, CRN and KTX files).
  • with default 16 max threads and -helperThreads 7, the image conversion is reproducible.
  • with default 16 max threads and -helperThreads 14, I get 7 different files (some DDS, CRN and KTX files).
  • with default 16 max threads and -helperThreads 15, I get 1 different file (a DDS file).
  • with 4 max threads and -helperThreads 3, the image conversion is reproducible.
  • with 8 max threads and -helperThreads 3, the image conversion is reproducible.
  • with 8 max threads and -helperThreads 4, I get 6 different files (some DDS, CRN and KTX files).
  • with 8 max threads and -helperThreads 7, the image conversion is reproducible.
  • with 32 max threads and -helperThreads 3, the image conversion is reproducible.
  • with 32 max threads and -helperThreads 4, I get 6 different files (some DDS, CRN and KTX files).
  • with 32 max threads and -helperThreads 8, the image conversion is reproducible.
  • with 32 max threads and -helperThreads 9, I get 1 different file (a DDS file).
  • with 32 max threads and -helperThreads 10, I get 1 different file (a DDS file).
  • with 32 max threads and -helperThreads 11, the image conversion is reproducible.
  • with 32 max threads and -helperThreads 12, the image conversion is reproducible.
  • with 32 max threads and -helperThreads 13, the image conversion is reproducible.
  • with 32 max threads and -helperThreads 14, I get 7 different files (some DDS, CRN and KTX files).
  • with 32 max threads and -helperThreads 15, I get 1 different file (a DDS file).
  • with 32 max threads and -helperThreads 16, I get 5 different files (some DDS, CRN and KTX files).
  • with 32 max threads and -helperThreads 17, the image conversion is reproducible.
  • with 32 max threads and -helperThreads 18, the image conversion is reproducible.
  • with 32 max threads and -helperThreads 19, the image conversion is reproducible.
  • with 32 max threads and -helperThreads 24, the image conversion is reproducible.
  • with 32 max threads and -helperThreads 30, the image conversion is reproducible.
  • with 64 max threads and -helperThreads 14, I get 7 different files (some DDS, CRN and KTX files).
  • with 64 max threads and -helperThreads 15, I get 1 different file (a DDS file).

So I suspect there is a memory corruption or memory allocation error somewhere.

When there are different images being produced, it's always the same conversion that produces something different.

@illwieckz illwieckz changed the title suspicious segfault in CRN compression with one main thread and mone helper thread suspicious segfault in CRN compression with one main thread and one helper thread Oct 8, 2024
@illwieckz
Copy link
Member Author

It is also worth noting the mismatches only occurs with image formats produced by crnlib (DDS, CRN and KTX files).

@illwieckz
Copy link
Member Author

How to test for the mismatch:

  1. Make a build tailored for reproducibility: cmake -S. -Bbuild -DUSE_FAST_MATH=OFF && cmake --build build
  2. Edit test/test.py to customize -helperThreads with problematic values.
  3. Run test/test.py

When using the illwieckz/max-threads branch, it's possible to set -DUSE_MAX_THREADS=64 (or other values) as CMake argument to customize the maximum amount of threads.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant