mixxxdj / mixxx

Mixxx is Free DJ software that gives you everything you need to perform live mixes.
http://mixxx.org
Other
4.51k stars 1.28k forks source link

DEBUG ASSERT: "m_value < origValue" in function mixxx::Beats::ConstIterator #12071

Open Holzhaus opened 1 year ago

Holzhaus commented 1 year ago

Bug Description

Just encountered this. When I run the tests again, everything passes.

 91/808 Test #387: HotcueControlTest.SavedLoopActivate ........................................................***Exception: Interrupt  0.35 sec
QML debugging is enabled. Only use this in a safe environment.
info [0x55f4d12c7180] Warning: Ignoring XDG_SESSION_TYPE=wayland on Gnome. Use QT_QPA_PLATFORM=wayland to run on Wayland anyway.
Note: Google Test filter = HotcueControlTest.SavedLoopActivate
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from HotcueControlTest
[ RUN      ] HotcueControlTest.SavedLoopActivate
info [0x55f4d12c7180] SoundSourceFFmpeg - Disabling untested input formats: aa, aax, ac3, ace, acm, act, adf, adp, ads, adx, aea, afc, aix, alp, amr, amrnb, amrwb, anm, apac, apc, ape, apm, apng, aptx, aptx_hd, aqtitle, argo_asf, argo_brp, argo_cvg, asf, asf_o, ass, ast, au, av1, avi, avisynth, avr, avs, avs2, avs3, bethsoftvid, bfi, bin, bink, binka, bit, bitpacked, bmv, bfstm, brstm, boa, bonk, c93, caf, cavsvideo, cdg, cdxl, cine, codec2, codec2raw, concat, dash, data, daud, dcstr, derf, dfa, dfpwm, dhav, dirac, dnxhd, dsf, dsicin, dss, dts, dtshd, dv, dvbsub, dvbtxt, dxa, ea, ea_cdata, eac3, epaf, ffmetadata, filmstrip, fits, flac, flic, flv, live_flv, 4xm, frm, fsb, fwse, g722, g723_1, g726, g726le, g729, gdv, genh, gif, gsm, gxf, h261, h263, h264, hca, hcom, hevc, hls, hnm, ico, idcin, idf, iff, ifv, ilbc, image2, image2pipe, alias_pix, brender_pix, imf, ingenient, ipmovie, ipu, ircam, iss, iv8, ivf, ivr, jacosub, jv, kux, kvag, laf, lmlm4, loas, luodat, lrc, lvf, lxf, mca, mcc, matroska,webm, mgsts, microdvd, mjpeg, mjpeg_2000, mlp, mlv, mm, mmf, mods, moflex, mpc, mpc8, mpeg, mpegts, mpegtsraw, mpegvideo, mpjpeg, mpl2, mpsub, msf, msnwctcp, msp, mtaf, mtv, musx, mv, mvi, mxf, mxg, nc, nistsphere, nsp, nsv, nut, nuv, obu, ogg, oma, paf, alaw, mulaw, vidc, f64be, f64le, f32be, f32le, s32be, s32le, s24be, s24le, s16be, s16le, s8, u32be, u32le, u24be, u24le, u16be, u16le, u8, pjs, pmp, pp_bnk, pva, pvf, qcp, r3d, rawvideo, realtext, redspark, rka, rl2, rm, roq, rpl, rsd, rso, rtp, rtsp, s337m, sami, sap, sbc, sbg, scc, scd, sdns, sdp, sdr2, sds, sdx, film_cpk, ser, sga, shn, siff, simbiosis_imx, sln, smk, smjpeg, smush, sol, sox, spdif, srt, psxstr, stl, subviewer1, subviewer, sup, svag, svs, swf, tak, tedcaptions, thp, 3dostr, tiertexseq, tmv, truehd, tta, txd, tty, ty, v210, v210x, vag, vc1, vc1test, vividas, vivo, vmd, vobsub, voc, vpk, vplayer, vqf, w64, wady, wavarc, wc3movie, webm_dash_manifest, webvtt, wsaud, wsd, wsvqa, wtv, wve, xa, xbin, xmd, xmv, xvag, xwma, yop, yuv4mpegpipe, bmp_pipe, cri_pipe, dds_pipe, dpx_pipe, exr_pipe, gem_pipe, gif_pipe, hdr_pipe, j2k_pipe, jpeg_pipe, jpegls_pipe, jpegxl_pipe, pam_pipe, pbm_pipe, pcx_pipe, pfm_pipe, pgmyuv_pipe, pgm_pipe, pgx_pipe, phm_pipe, photocd_pipe, pictor_pipe, png_pipe, ppm_pipe, psd_pipe, qdraw_pipe, qoi_pipe, sgi_pipe, svg_pipe, sunrast_pipe, tiff_pipe, vbn_pipe, webp_pipe, xbm_pipe, xpm_pipe, xwd_pipe, libmodplug, libopenmpt
info [0x55f4d12c7180] faad2::LibLoader - Successfully loaded FAAD2 library "libfaad.so.2" version 2.10.1
info [0x55f4d12c7180] SoundSourceSndFile - Disabling OGG decoding for "libsndfile-1.2.2"
info [0x55f4d12c7180] SoundSourceProxy - SoundSource providers for file type "3g2"
info [0x55f4d12c7180] SoundSourceProxy - 1 (lowest) : "FFmpeg n6.0"
info [0x55f4d12c7180] SoundSourceProxy - SoundSource providers for file type "3gp"
info [0x55f4d12c7180] SoundSourceProxy - 1 (lowest) : "FFmpeg n6.0"
info [0x55f4d12c7180] SoundSourceProxy - SoundSource providers for file type "aac"
info [0x55f4d12c7180] SoundSourceProxy - 1 (lowest) : "FFmpeg n6.0"
info [0x55f4d12c7180] SoundSourceProxy - SoundSource providers for file type "aiff"
info [0x55f4d12c7180] SoundSourceProxy - 3 (default) : "libsndfile"
info [0x55f4d12c7180] SoundSourceProxy - 1 (lowest) : "FFmpeg n6.0"
info [0x55f4d12c7180] SoundSourceProxy - SoundSource providers for file type "caf"
info [0x55f4d12c7180] SoundSourceProxy - 2 (lower) : "libsndfile"
info [0x55f4d12c7180] SoundSourceProxy - SoundSource providers for file type "flac"
info [0x55f4d12c7180] SoundSourceProxy - 4 (higher) : "Xiph.org libFLAC"
info [0x55f4d12c7180] SoundSourceProxy - 2 (lower) : "libsndfile"
info [0x55f4d12c7180] SoundSourceProxy - SoundSource providers for file type "it"
info [0x55f4d12c7180] SoundSourceProxy - 3 (default) : "MODPlug"
info [0x55f4d12c7180] SoundSourceProxy - SoundSource providers for file type "m4a"
info [0x55f4d12c7180] SoundSourceProxy - 3 (default) : "Nero FAAD2"
info [0x55f4d12c7180] SoundSourceProxy - 1 (lowest) : "FFmpeg n6.0"
info [0x55f4d12c7180] SoundSourceProxy - SoundSource providers for file type "mj2"
info [0x55f4d12c7180] SoundSourceProxy - 1 (lowest) : "FFmpeg n6.0"
info [0x55f4d12c7180] SoundSourceProxy - SoundSource providers for file type "mod"
info [0x55f4d12c7180] SoundSourceProxy - 3 (default) : "MODPlug"
info [0x55f4d12c7180] SoundSourceProxy - SoundSource providers for file type "mov"
info [0x55f4d12c7180] SoundSourceProxy - 1 (lowest) : "FFmpeg n6.0"
info [0x55f4d12c7180] SoundSourceProxy - SoundSource providers for file type "mp3"
info [0x55f4d12c7180] SoundSourceProxy - 3 (default) : "MAD: MPEG Audio Decoder 0.15.1 (beta) FPM_64BIT"
info [0x55f4d12c7180] SoundSourceProxy - 1 (lowest) : "FFmpeg n6.0"
info [0x55f4d12c7180] SoundSourceProxy - SoundSource providers for file type "mp4"
info [0x55f4d12c7180] SoundSourceProxy - 3 (default) : "Nero FAAD2"
info [0x55f4d12c7180] SoundSourceProxy - 1 (lowest) : "FFmpeg n6.0"
info [0x55f4d12c7180] SoundSourceProxy - 1 (lowest) : "FFmpeg n6.0"
info [0x55f4d12c7180] SoundSourceProxy - SoundSource providers for file type "ogg"
info [0x55f4d12c7180] SoundSourceProxy - 4 (higher) : "Xiph.org OggVorbis"
info [0x55f4d12c7180] SoundSourceProxy - SoundSource providers for file type "okt"
info [0x55f4d12c7180] SoundSourceProxy - 3 (default) : "MODPlug"
info [0x55f4d12c7180] SoundSourceProxy - SoundSource providers for file type "opus"
info [0x55f4d12c7180] SoundSourceProxy - 4 (higher) : "Xiph.org libopusfile"
info [0x55f4d12c7180] SoundSourceProxy - SoundSource providers for file type "s3m"
info [0x55f4d12c7180] SoundSourceProxy - 3 (default) : "MODPlug"
info [0x55f4d12c7180] SoundSourceProxy - SoundSource providers for file type "stm"
info [0x55f4d12c7180] SoundSourceProxy - 3 (default) : "MODPlug"
info [0x55f4d12c7180] SoundSourceProxy - SoundSource providers for file type "wav"
info [0x55f4d12c7180] SoundSourceProxy - 3 (default) : "libsndfile"
info [0x55f4d12c7180] SoundSourceProxy - 1 (lowest) : "FFmpeg n6.0"
info [0x55f4d12c7180] SoundSourceProxy - SoundSource providers for file type "wv"
info [0x55f4d12c7180] SoundSourceProxy - 4 (higher) : "WavPack"
info [0x55f4d12c7180] SoundSourceProxy - 1 (lowest) : "FFmpeg n6.0"
info [0x55f4d12c7180] SoundSourceProxy - SoundSource providers for file type "xm"
info [0x55f4d12c7180] SoundSourceProxy - 3 (default) : "MODPlug"
warning [0x55f4d12c7180] ControlObject accessed via deprecated key "[Channel1]" "hotcue_1_enabled" - use "[Channel1]" "hotcue_1_status" instead
warning [Engine] Beats: Iterator would go out of possible range, capping at earliest possible position.
critical [Engine] DEBUG ASSERT: "m_value < origValue" in function mixxx::Beats::ConstIterator mixxx::Beats::ConstIterator::operator-=(difference_type) at /home/jan/Projects/mixxx/src/track/beats.cpp:123

Version

2.4

OS

Arch Linux

m0dB commented 1 year ago

I have also seen this once or twice, tried to reproduce it, looked at the code the see if I could understand what could trigger this, but no luck...

ronso0 commented 1 year ago

Just ran into this with main while looking into #12191 and could reliably reproduce it with two tracks:

Also, re-analysis is unexpected since the "Re-analyze if ..." options are unchecked. Mixxx should respect the beatmap.

fwcd commented 11 months ago

Ran into this too, should've probably searched the GitHub issues first (this bug was the main motivation for #12344 btw)...

I could also reliably reproduce it by reanalyzing with variable bpm, that seems to be the important part. Re-launching Mixxx and opening the previously variable-analyzed track also triggers the crash. Can confirm that this affects both main and 2.4.

Haven't bisected very far, but it seems to have been around since at least 5158e6259cfbba2f71e4f112d642259c48659354 from late 2021, so I am kind of surprised that we didn't hit this bug earlier. Building and running these 'old' versions for the bisect required patching together a slightly Frankensteinian script (unfortunately the prebuilt snapshots don't help us since they're built without debug assertions):

#!/bin/bash

for f in CMakeLists.txt cmake/modules/FindSQLite3.cmake; do
  sed -i '' 's/SQLite3::SQLite3/SQLite::SQLite3/g' "$f"
done
sed -i '' 's/    \(AVCodec\* pDecoder = nullptr\)/    const \1/g' src/sources/soundsourceffmpeg.cpp
git checkout main -- cmake/modules/{FindPortAudio,FindJACK}.cmake

rm -rf build-2.4
cmake --preset <my 2.4 build preset>
cmake --build build-2.4 --target mixxx

git reset --hard

build/mixxx <path to the variable-bpm-analyzed track>
fwcd commented 11 months ago

Some insights from debugging, here's the first debug assert I am getting after launching Mixxx and loading a variable BPM track:

DEBUG ASSERT: "m_value < origValue" in function Beats::ConstIterator mixxx::Beats::ConstIterator::operator-=(Beats::ConstIterator::difference_type) at src/track/beats.cpp:133

The thread where this happens is CachingReaderWorker and here's the call stack:

mixxx_debug_assert(char const*, char const*, int, char const*) (src/util/assert.h:9)
mixxx::Beats::ConstIterator::operator-=(int) (.cold.1) (src/track/beats.cpp:133)
mixxx_debug_assert(char const*, char const*, int, char const*) (src/util/assert.h:0)
mixxx::Beats::ConstIterator::operator-=(int) (src/track/beats.cpp:133)
mixxx::Beats::getBpmAroundPosition(mixxx::audio::FramePos, int) const (src/track/beats.cpp:598)
BpmControl::updateLocalBpm() (src/engine/controls/bpmcontrol.cpp:1024)
SyncControl::trackLoaded(std::__1::shared_ptr<Track>) (src/engine/sync/synccontrol.cpp:382)
EngineBuffer::notifyTrackLoaded(std::__1::shared_ptr<Track>, std::__1::shared_ptr<Track>) (src/engine/enginebuffer.cpp:636)
EngineBuffer::slotTrackLoaded(std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double) (src/engine/enginebuffer.cpp:562)
QtPrivate::FunctorCall<QtPrivate::IndexesList<0, 1, 2>, QtPrivate::List<std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double>, void, void (EngineBuffer::*)(std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double)>::call(void (EngineBuffer::*)(std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double), EngineBuffer*, void**) (vcpkg/installed/arm64-osx/include/Qt6/QtCore/qobjectdefs_impl.h:135)
void QtPrivate::FunctionPointer<void (EngineBuffer::*)(std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double)>::call<QtPrivate::List<std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double>, void>(void (EngineBuffer::*)(std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double), EngineBuffer*, void**) (vcpkg/installed/arm64-osx/include/Qt6/QtCore/qobjectdefs_impl.h:172)
void doActivate<false>(QObject*, int, void**) (@void doActivate<false>(QObject*, int, void**):335)
CachingReader::trackLoaded(std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double) (build/mixxx-lib_autogen/include/moc_cachingreader.cpp:247)
QtPrivate::FunctorCall<QtPrivate::IndexesList<0, 1, 2>, QtPrivate::List<std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double>, void, void (CachingReader::*)(std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double)>::call(void (CachingReader::*)(std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double), CachingReader*, void**) (vcpkg/installed/arm64-osx/include/Qt6/QtCore/qobjectdefs_impl.h:135)
void QtPrivate::FunctionPointer<void (CachingReader::*)(std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double)>::call<QtPrivate::List<std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double>, void>(void (CachingReader::*)(std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double), CachingReader*, void**) (vcpkg/installed/arm64-osx/include/Qt6/QtCore/qobjectdefs_impl.h:172)
void doActivate<false>(QObject*, int, void**) (@void doActivate<false>(QObject*, int, void**):335)
CachingReaderWorker::trackLoaded(std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double) (build/mixxx-lib_autogen/include/moc_cachingreaderworker.cpp:247)
CachingReaderWorker::loadTrack(std::__1::shared_ptr<Track> const&) (src/engine/cachingreader/cachingreaderworker.cpp:252)
CachingReaderWorker::run() (src/engine/cachingreader/cachingreaderworker.cpp:123)
QThreadPrivate::start(void*) (@QThreadPrivate::start(void*):86)

Digging a bit into the implementation, this looks unsafe:

https://github.com/mixxxdj/mixxx/blob/2c2e706b4419794e1b29f23f124ce65f49b88bc7/src/track/beats.cpp#L63-L69

Signed integer overflow is undefined behavior and we really shouldn't assume that integers just wrap around. Instead we should probably check if m_beatsOffset is < std::numeric_limits::max() - n. Likewise here:

https://github.com/mixxxdj/mixxx/blob/2c2e706b4419794e1b29f23f124ce65f49b88bc7/src/track/beats.cpp#L111-L117

Removing the DEBUG_ASSERTs from the iterator methods triggers another debug assert:

DEBUG ASSERT: "frames > 0" in function static mixxx::Bpm BeatUtils::calculateAverageBpm(int, mixxx::audio::SampleRate, mixxx::audio::FramePos, mixxx::audio::FramePos) at src/track/beatutils.cpp:26

...which also happens on CachingReaderWorker:

mixxx_debug_assert(char const*, char const*, int, char const*) (src/util/assert.h:9)
BeatUtils::calculateAverageBpm(int, mixxx::audio::SampleRate, mixxx::audio::FramePos, mixxx::audio::FramePos) (.cold.1) (src/track/beatutils.cpp:26)
mixxx_debug_assert(char const*, char const*, int, char const*) (src/util/assert.h:0)
BeatUtils::calculateAverageBpm(int, mixxx::audio::SampleRate, mixxx::audio::FramePos, mixxx::audio::FramePos) (src/track/beatutils.cpp:26)
mixxx::Beats::getBpmAroundPosition(mixxx::audio::FramePos, int) const (src/track/beats.cpp:617)
BpmControl::updateLocalBpm() (src/engine/controls/bpmcontrol.cpp:1024)
SyncControl::trackLoaded(std::__1::shared_ptr<Track>) (src/engine/sync/synccontrol.cpp:382)
EngineBuffer::notifyTrackLoaded(std::__1::shared_ptr<Track>, std::__1::shared_ptr<Track>) (src/engine/enginebuffer.cpp:636)
EngineBuffer::slotTrackLoaded(std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double) (src/engine/enginebuffer.cpp:562)
QtPrivate::FunctorCall<QtPrivate::IndexesList<0, 1, 2>, QtPrivate::List<std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double>, void, void (EngineBuffer::*)(std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double)>::call(void (EngineBuffer::*)(std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double), EngineBuffer*, void**) (vcpkg/installed/arm64-osx/include/Qt6/QtCore/qobjectdefs_impl.h:135)
void QtPrivate::FunctionPointer<void (EngineBuffer::*)(std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double)>::call<QtPrivate::List<std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double>, void>(void (EngineBuffer::*)(std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double), EngineBuffer*, void**) (vcpkg/installed/arm64-osx/include/Qt6/QtCore/qobjectdefs_impl.h:172)
void doActivate<false>(QObject*, int, void**) (@void doActivate<false>(QObject*, int, void**):335)
CachingReader::trackLoaded(std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double) (build/mixxx-lib_autogen/include/moc_cachingreader.cpp:247)
QtPrivate::FunctorCall<QtPrivate::IndexesList<0, 1, 2>, QtPrivate::List<std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double>, void, void (CachingReader::*)(std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double)>::call(void (CachingReader::*)(std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double), CachingReader*, void**) (vcpkg/installed/arm64-osx/include/Qt6/QtCore/qobjectdefs_impl.h:135)
void QtPrivate::FunctionPointer<void (CachingReader::*)(std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double)>::call<QtPrivate::List<std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double>, void>(void (CachingReader::*)(std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double), CachingReader*, void**) (vcpkg/installed/arm64-osx/include/Qt6/QtCore/qobjectdefs_impl.h:172)
void doActivate<false>(QObject*, int, void**) (@void doActivate<false>(QObject*, int, void**):335)
CachingReaderWorker::trackLoaded(std::__1::shared_ptr<Track>, mixxx::audio::SampleRate, double) (build/mixxx-lib_autogen/include/moc_cachingreaderworker.cpp:247)
CachingReaderWorker::loadTrack(std::__1::shared_ptr<Track> const&) (src/engine/cachingreader/cachingreaderworker.cpp:252)
CachingReaderWorker::run() (src/engine/cachingreader/cachingreaderworker.cpp:123)
QThreadPrivate::start(void*) (@QThreadPrivate::start(void*):86)

So it seems like there's something fishy with the beatmap we're loading. One interesting find was that while constant beatgrids seem to deserialize as BeatGrid-2.0:

debug [Main] Successfully deserialized Beats ("BeatGrid-2.0")

...the variable BPM grid, that subsequently crashes to due the debug assert, is a BeatMap-1.0:

debug [Main] Successfully deserialized Beats ("BeatMap-1.0")

So we're likely going down different codepaths depending on the beatgrid format. Perhaps there's a bug in the way BeatMap-1.0 is read/written? Wouldn't be surprised if #12191 is indeed related.

fwcd commented 11 months ago

I've pushed a fix for the overflow checks in #12349 which seems to fix this issue completely. The BeatMap-1.0 serialization might be something to investigate anyway, more details on that in #12349.

JoergAtGithub commented 9 months ago

Unfortunately this assert occured for me again.

ronso0 commented 9 months ago

Did you note the track or can you otherwise repdocuce it?