Crash only on Library Build

So I’m getting reports that one of my modules crashes. Four Track module v 2.0.0 crashes Rack when added to a patch. · Issue #2 · patheros/JPManuals · GitHub

I’ve tested the version from the library and it does indeed crash for me. But I can’t reproduce it on builds I make locally or builds I make using the github action which I believe is using the rack plugin tool chain.

One of the users in the issue has a useful stack trace that points to a possible place the crash might be happening in my code. But I’m not sure how to test any fixes here without resubmitting to the library. Does anyone have any ideas on why the library build might be different or ideas on how I could debug the library build to see what’s going on?

@cschol

Here is the full github action I am using

name: Build and Push to Google Drive
on: workflow_dispatch

env:
  rack-sdk-version: 2.4.1
  rack-plugin-toolchain-dir: /home/build/rack-plugin-toolchain
  
  # important to be unique between different plugins, otherwise this action will move the old files
  build-prefix: "JPLab" 

defaults:
  run:
    shell: bash

jobs:

  modify-plugin-version:
    name: Modify plugin version
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/cache@v3
        id: plugin-version-cache
        with:
          path: plugin.json
          key: ${{ github.sha }}-${{ github.run_id }}
      - run: |
          gitrev=`git rev-parse --short HEAD`
          pluginversion=`jq -r '.version' plugin.json`
          echo "Set plugin version from $pluginversion to $pluginversion-$gitrev"
          cat <<< `jq --arg VERSION "$pluginversion-$gitrev" '.version=$VERSION' plugin.json` > plugin.json
        # only modify plugin version if no tag was created
        # if: "! startsWith(github.ref, 'refs/tags/v')"

  build:
    name: ${{ matrix.platform }}
    needs: modify-plugin-version
    runs-on: ubuntu-latest
    container:
      image: ghcr.io/qno/rack-plugin-toolchain-win-linux
      options: --user root
    strategy:
      matrix:
        platform: [win-x64, lin-x64]
    steps:
      - uses: actions/checkout@v3
        with:
          submodules: recursive
      - uses: actions/cache@v3
        id: plugin-version-cache
        with:
          path: plugin.json
          key: ${{ github.sha }}-${{ github.run_id }}
      - name: Build plugin
        run: |
          export PLUGIN_DIR=$GITHUB_WORKSPACE
          pushd ${{ env.rack-plugin-toolchain-dir }}
          make plugin-build-${{ matrix.platform }}
      - name: Upload artifact
        uses: actions/upload-artifact@v3
        with:
          path: ${{ env.rack-plugin-toolchain-dir }}/plugin-build
          name: ${{ matrix.platform }}

  build-mac:
    name: mac
    needs: modify-plugin-version
    runs-on: macos-latest
    strategy:
      fail-fast: false
      matrix:
        platform: [x64, arm64]
    steps:
      - uses: actions/checkout@v3
        with:
          submodules: recursive
      - uses: actions/cache@v3
        id: plugin-version-cache
        with:
          path: plugin.json
          key: ${{ github.sha }}-${{ github.run_id }}
      - name: Get Rack-SDK
        run: |
          pushd $HOME
          wget -O Rack-SDK.zip https://vcvrack.com/downloads/Rack-SDK-${{ env.rack-sdk-version }}-mac-${{ matrix.platform }}.zip
          unzip Rack-SDK.zip
      - name: Build plugin
        run: |
          CROSS_COMPILE_TARGET_x64=x86_64-apple-darwin
          CROSS_COMPILE_TARGET_arm64=arm64-apple-darwin
          export RACK_DIR=$HOME/Rack-SDK
          export CROSS_COMPILE=$CROSS_COMPILE_TARGET_${{ matrix.platform }}
          make dep
          make dist
      - name: Upload artifact
        uses: actions/upload-artifact@v3
        with:
          path: dist/*.vcvplugin
          name: mac-${{ matrix.platform }}

  upload:
    name: Upload To Google Drive
    runs-on: ubuntu-latest
    needs: [build, build-mac]
    steps:
      - uses: actions/checkout@v2
      - uses: actions/download-artifact@v2
        with:
          path: _artifacts
      - name: Zip Plugins    
        working-directory: ./_artifacts
        run: for i in */; do zip -r "${i%/}.zip" "$i"; done
      - name: Upload release assets
        uses: adityak74/google-drive-upload-git-action@main
        with:
          credentials: ${{ secrets.gd_upload_credentials }}
          namePrefix: "${{ env.build-prefix }}-" 
          filename: _artifacts/*.zip
          folderId: ${{ secrets.gd_upload_folder_id }}
          overwrite: "true" # optional boolean

And the Makefile

# If RACK_DIR is not defined when calling the Makefile, default to two directories above
RACK_DIR ?= ../..

# FLAGS will be passed to both the C and C++ compiler
FLAGS +=
CFLAGS +=
CXXFLAGS +=

# Careful about linking to shared libraries, since you can't assume much about the user's environment and library search path.
# Static libraries are fine, but they should be added to this plugin's build system.
LDFLAGS +=

# Add .cpp files to the build
SOURCES += $(wildcard src/*.cpp)

# Add files to the ZIP package when running `make dist`
# The compiled plugin and "plugin.json" are automatically added.
DISTRIBUTABLES += res
DISTRIBUTABLES += res/subdiv
DISTRIBUTABLES += $(wildcard LICENSE*)
DISTRIBUTABLES += $(wildcard presets)

# Include the Rack plugin Makefile framework
include $(RACK_DIR)/plugin.mk

I should also clarity this plugin is closed source, so it went through Andrew Belt through support@vcvrack.com

1 Like

Same happens to me. That is why I use the build tool chain and not the GitHub action. I’ll bet if you build with the tool chain you will repro.

Oh, closed source. I know nothing about that process.

My understanding is the way I have the github action set up IS using the docker image from the rack-plugin-toolchain. See Automated building and releasing Plugins on Github with Github Actions - #40 by qno

You can run rack under a debugger with your library build.

It should be possible to configure the debugger with where your source code is. Configuring gdb in vscode is an exceedingly poorly documented dark art, and most gdb info I see points to gdb docs that are 404, and I couldn’t figure it out in a few minutes.

I was able set a working breakpoint on my library module process method and see disassembly. In your case you have a crash, so it should get you to the right function and be able to get a sense of what’s happening.

Thanks Paul. I actually haven’t ever gotten a debugger attached to VCV, but luckily one of my Linux users posted a stack trace, so I believe I know which function is crashing. I’m also assuming the line numbers are stripped from the production build so there won’t be any more resolution beyond that.

The Linux crash also said “Floating point exception” which confuses me since the function in question is only dealing with integers, but after staring at that method I have one theory. The function is performing modulo operations and for one frame, the divisor is 0. So my guess is that is causing the crash. Apparently modulo 0 is undefined behavior. I guess working on one build but not another is pretty undefined.

I summited an update to VCV. Hopefully they can test the fix before releasing it. I also added more logging to help track down what’s wrong, in case this doesn’t fix it.

Return Value of Modulus Operator

  • If y completely divides x, the result of the expression is 0.
  • If x is not completely divisible by y, then the result will be the remainder in the range [0, y-1]
  • (x % y) < (x / 2) ………if (x >= y)
  • (x % y) = x ……… if (x < y)
  • If y is 0, then division by zero is a compile-time error.

How on earth can division by 0 be a compile time error? I suppose a compiler could detect division by a constant 0. But division by a calculated or user entered 0 would by definition be a run time error, yes?

2 Likes

According to stack overflow, which we all know is the 100% accurate sarcasm, the behavior is undefined

image

1 Like

good point.

haha - yes I appreciate the sarcasm. That said, I tend to trust what I find there :wink:

1 Like

For others wanting to try debugging this kind of scenario:

If using VSCode, you can add a debugger configuration that launches your non-development Rack. Here we’re launching Rack, not attaching, but it’s not hard to add an attach configuration, although I’ve never found attaching very reliable for reaching breakpoitns, so I always launch. You don’t get line numbers of course, because it’s stripped, but you do get (mangled) function names, so you can see the function boundaries in the disassembly.

(Here, I’m on Windows, so the paths will differ on other platforms).

launch.json (fragment):

{
    "name": "(gdb) Launch retail",
    "program": "C:/Program Files/VCV/Rack2Free/Rack.exe",
    "args": [], // set any command-line args needed
    "environment": [ {"name": "RACK_DIR", "value":"C:/Program Files/VCV/Rack2Free"} ],
...
}
1 Like