Skip to content

PIQP win diag: set +e so loop survives failing attempt #23

PIQP win diag: set +e so loop survives failing attempt

PIQP win diag: set +e so loop survives failing attempt #23

name: Reproduce macos-14 py3.14 test_DMcrash SIGSEGV with EXACT failing artifact
# Mac py3.14 test-python keeps failing with:
# test_DMcrash (matrix.Matrixtests.test_DMcrash) ... Segmentation fault: 11
# despite the SWIG owning-bytes fix landing in nightly-abi3 (linux verified
# locally with debug py3.14 — passes cleanly).
#
# This workflow pulls the EXACT artifact that the failing test-python job
# consumed (run 25970736288, artifact casadi-osx-arm64-py311) and runs
# the same test path with `-X faulthandler` to capture the C-frame trace
# that bare CI doesn't expose.
#
# Also runs against the nightly-abi3 release-asset (.whl) as a control —
# if the release-asset wheel passes but the workflow-artifact .zip fails,
# we know they're not byte-identical despite both originating from the
# same python-osx job.
on: [push, workflow_dispatch]
jobs:
diag:
runs-on: macos-14
steps:
- name: Checkout casadi/casadi (abi3) for the test/python dir
uses: actions/checkout@v6.0.2
with:
repository: casadi/casadi
ref: abi3
- name: Download FAILING CI artifact (casadi-osx-arm64-py311 from run 25970736288)
uses: actions/download-artifact@v8.0.1
with:
name: casadi-osx-arm64-py311
run-id: 25970736288
github-token: ${{ secrets.GITHUB_TOKEN }}
repository: casadi/casadi
- name: Layout
run: |
ls -la
ls -la *.zip || true
- name: Unpack artifact like CI (mirror binaries.yml line 1100)
run: |
unzip -q casadi-osx-arm64-py311.zip -d artifact
ls artifact/casadi/ | head -10
echo "--- md5 of artifact-derived _casadi.so ---"
md5 artifact/casadi/_casadi.so
- name: Also download the nightly-abi3 release-asset .whl (control)
run: |
curl -sL -o release.whl \
https://github.com/casadi/casadi/releases/download/nightly-abi3/casadi-3.7.2.dev%2Babi3-cp311-abi3-macosx_11_0_arm64.whl
mkdir -p release && unzip -q release.whl -d release
echo "--- md5 of release-asset _casadi.so ---"
md5 release/casadi/_casadi.so
echo "--- diff metadata ---"
ls -la artifact/casadi/_casadi.so release/casadi/_casadi.so
- name: Inspect mach-o headers and dependencies
run: |
F=artifact/casadi/_casadi.so
file $F
echo "--- otool -L (linked dylibs) ---"
otool -L $F | head -20
echo "--- nm: PyUnicode_AsUTF8String? (the fix's call) ---"
nm -gU $F 2>/dev/null | grep -i PyUnicode | head -10 || true
nm -u $F 2>/dev/null | grep -i PyUnicode | head -10 || true
- name: Set up conda py3.14 (matches CI)
uses: conda-incubator/setup-miniconda@77236efeba76d591229f44c36f2469426cc33dec
with:
python-version: 3.14
activate-environment: py3.14
auto-update-conda: true
channels: pkgs/main, conda-forge, conda-forge/label/python_rc
- name: "Install deps (mirror CI: pip + numpy scipy pandas looseversion pyright)"
shell: bash -el {0}
run: |
conda install -y pip
pip install numpy scipy pandas looseversion pyright
- name: Try import (artifact wheel)
continue-on-error: true
shell: bash -el {0}
env:
PYTHONPATH: ${{ github.workspace }}/artifact
run: |
set -x
which python && python -V
python -c 'import casadi; print("import OK", casadi.__version__)' 2>&1 | tail -10
- name: Reproduce test_DMcrash on ARTIFACT wheel under -X faulthandler
continue-on-error: true
shell: bash -el {0}
env:
PYTHONPATH: ${{ github.workspace }}/artifact
run: |
set -x
python -X faulthandler -c "
from casadi import DM
print('starting test_DMcrash path')
try:
DM([DM([1,2]), DM([1,2])])
print('NO EXCEPTION (unexpected)')
except Exception as e:
print('caught:', type(e).__name__)
print('msg:', repr(str(e))[:300])
a = DM([DM([1]), DM([2])])
print('second DM OK, value:', a)
print('all clear')
" 2>&1 | tail -80
echo "exit=$?"
- name: Run full matrix.py suite (matches the actual failing test path)
continue-on-error: true
shell: bash -el {0}
env:
PYTHONPATH: ${{ github.workspace }}/artifact
run: |
set -x
cd test/python
python -X faulthandler -c "
import sys, unittest
sys.path.insert(0, '.')
import matrix
res = unittest.TextTestRunner(verbosity=2).run(
unittest.TestLoader().loadTestsFromModule(matrix))
print('tests run:', res.testsRun, 'failures:', len(res.failures), 'errors:', len(res.errors))
" 2>&1 | tail -50
echo "exit=$?"
- name: Reproduce test_DMcrash on RELEASE-ASSET wheel under -X faulthandler (control)
continue-on-error: true
shell: bash -el {0}
env:
PYTHONPATH: ${{ github.workspace }}/release
run: |
set -x
python -X faulthandler -c "
from casadi import DM
try:
DM([DM([1,2]), DM([1,2])])
print('NO EXCEPTION')
except Exception as e:
print('caught:', type(e).__name__, str(e)[:200])
a = DM([DM([1]), DM([2])])
print('release-asset all clear')
" 2>&1 | tail -40
echo "exit=$?"
- name: Try with MallocStackLogging + MallocScribble for UAF detection
continue-on-error: true
shell: bash -el {0}
env:
PYTHONPATH: ${{ github.workspace }}/artifact
MallocScribble: "1"
MallocPreScribble: "1"
MallocGuardEdges: "1"
run: |
set -x
python -X faulthandler -c "
from casadi import DM
try:
DM([DM([1,2]), DM([1,2])])
except Exception as e:
print('caught:', type(e).__name__, str(e)[:200])
print('mallocscribble clear')
" 2>&1 | tail -40
# ===== CONTROL: same wheel + py3.11 (to isolate py3.14-specific bug) =====
- name: Set up conda py3.11 (control)
uses: conda-incubator/setup-miniconda@77236efeba76d591229f44c36f2469426cc33dec
with:
python-version: "3.11"
activate-environment: py3.11
auto-update-conda: true
channels: pkgs/main, conda-forge
- name: "Install deps in py3.11 (pip + numpy)"
shell: bash -el {0}
run: |
conda activate py3.11
conda install -y pip
pip install numpy
- name: Reproduce test_DMcrash under py3.11 (control) — same wheel
continue-on-error: true
shell: bash -el {0}
env:
PYTHONPATH: ${{ github.workspace }}/artifact
run: |
conda activate py3.11
which python && python -V
set -x
python -X faulthandler -c "
from casadi import DM
try:
DM([DM([1,2]), DM([1,2])])
except Exception as e:
print('py3.11 caught:', type(e).__name__, str(e)[:200])
a = DM([DM([1]), DM([2])])
print('py3.11 all clear, a =', a)
" 2>&1 | tail -40
echo "py3.11 exit=$?"
# ===== lldb attach for C-frame capture (py3.14 again) =====
- name: Capture crash with lldb on py3.14
continue-on-error: true
shell: bash -el {0}
env:
PYTHONPATH: ${{ github.workspace }}/artifact
run: |
conda activate py3.14
which python && python -V
# Generate a tiny script first (lldb is happier with file input)
cat > /tmp/crashprobe.py <<'PYEOF'
import sys
print('about to call DM([DM([1,2]), DM([1,2])])', flush=True)
from casadi import DM
DM([DM([1,2]), DM([1,2])])
print('unreachable: did not crash', flush=True)
PYEOF
# Run under lldb, dump backtrace on SIGSEGV
lldb -o "settings set target.process.stop-on-exec false" \
-o "run /tmp/crashprobe.py" \
-o "thread backtrace all" \
-o "register read" \
-o "image lookup --address \$pc" \
-o "quit" \
-- $(which python) 2>&1 | tail -120 || true
# ===== Refcount instrumentation: print sys.getrefcount before/around the crash =====
- name: Refcount probe (does PyTuple_SET_ITEM in funpack steal a borrowed ref?)
continue-on-error: true
shell: bash -el {0}
env:
PYTHONPATH: ${{ github.workspace }}/artifact
run: |
conda activate py3.14
set -x
python -X faulthandler -c "
import sys
from casadi import DM
a = DM([1,2])
b = DM([1,2])
arg_list = [a, b]
print('before call: refcount(a)=', sys.getrefcount(a), 'refcount(arg_list)=', sys.getrefcount(arg_list), flush=True)
try:
DM(arg_list)
except Exception as e:
print('caught:', type(e).__name__, str(e)[:200], flush=True)
print('after call: refcount(a)=', sys.getrefcount(a), 'refcount(arg_list)=', sys.getrefcount(arg_list), flush=True)
" 2>&1 | tail -40
echo "refcount-probe exit=$?"