6

I am getting a Native Crash signal 11 (SIGSEGV), code 1 (SEGV_MAPERR) randomly in my App. The App loops over files and analyses them in the C++ code and return an array of floats. This is done in an AsyncTask which runs for a while while processing the files. What am I doing wrong in the code which is leading to the crash? Or is it a Superpowered issue? Thank you.

This is the AsyncTask doInBackground function:

protected String doInBackground(Object... urls) {

            for (int i = 0; i < songFiles.size(); i++) {
                SongFile temp = songFiles.get(i);
                try {
                    float[] f = Analyser.getInfo(temp.getPath());
                        if (f != null && f.length > 1) {
                            ...save to DB
                        }
                    }

                } catch (Exception e) {
                }
            }
            return "";
        } 

The Function between the Java and C++ code:

extern "C" JNIEXPORT jfloatArray Java_com_superpowered_SuperpoweredPlayer_getInfo(JNIEnv *env, jobject instance,jstring filepath) {
    jfloatArray ret;
    char *Path= (char *) env->GetStringUTFChars(filepath, JNI_FALSE);

    ret = (jfloatArray)env->NewFloatArray(2);

    float *values = superpoweredPlayer->getKey(Path);

    env->SetFloatArrayRegion(ret, 0, 2, values);
    env->ReleaseStringUTFChars(filepath, Path);
    return ret;

}

The C++ function getKey:

float *SuperpoweredPlayer::getKey(char *url) {

    SuperpoweredDecoder *decoder = new SuperpoweredDecoder();

    //decoder initialize from the URL input
    const char *openError = decoder->open(url, false, 0, 0);
    if (openError) {
        delete decoder;
        return NULL;
    };

    // Create the analyzer.
    SuperpoweredOfflineAnalyzer *analyzer = new SuperpoweredOfflineAnalyzer(decoder->samplerate, 0, decoder->durationSeconds);

    // Create a buffer for the 16-bit integer samples coming from the decoder.
    short int *intBuffer = (short int *)malloc(decoder->samplesPerFrame * 2 * sizeof(short int) + 16384);
    // Create a buffer for the 32-bit floating point samples required by the effect.
    float *floatBuffer = (float *)malloc(decoder->samplesPerFrame * 2 * sizeof(float) + 1024);

    // Processing.
    while (true) {

        // Decode one frame. samplesDecoded will be overwritten with the actual decoded number of samples.
        unsigned int samplesDecoded = decoder->samplesPerFrame;
        if (decoder->decode(intBuffer, &samplesDecoded) == SUPERPOWEREDDECODER_ERROR) break;
        if (samplesDecoded < 1) break;

        // Convert the decoded PCM samples from 16-bit integer to 32-bit floating point.
        SuperpoweredShortIntToFloat(intBuffer, floatBuffer, samplesDecoded);

        // Submit samples to the analyzer.
        analyzer->process(floatBuffer, samplesDecoded);

        // Update the progress indicator.
        // progress = (double)decoder->samplePosition / (double)decoder->durationSamples;
    };



    // Get the result.
    unsigned char *averageWaveform = NULL, *lowWaveform = NULL, *midWaveform = NULL, *highWaveform = NULL, *peakWaveform = NULL, *notes = NULL;
    int waveformSize, overviewSize, keyIndex;
    char *overviewWaveform = NULL;
    float loudpartsAverageDecibel, peakDecibel, bpm, averageDecibel, beatgridStartMs = 0;
    analyzer->getresults(&averageWaveform, &peakWaveform, &lowWaveform, &midWaveform, &highWaveform, &notes, &waveformSize, &overviewWaveform, &overviewSize, &averageDecibel, &loudpartsAverageDecibel, &peakDecibel, &bpm, &beatgridStartMs, &keyIndex);

    float *ret;
    ret=(float*)malloc(2*sizeof(float));
    ret[0] = bpm;
    ret[1] = keyIndex;

    // Cleanup.
    delete decoder;
    delete analyzer;
    free(intBuffer);
    free(floatBuffer);

    // Done with the result, free memory.
    if (averageWaveform) free(averageWaveform);
    if (lowWaveform) free(lowWaveform);
    if (midWaveform) free(midWaveform);
    if (highWaveform) free(highWaveform);
    if (peakWaveform) free(peakWaveform);
    if (notes) free(notes);
    if (overviewWaveform) free(overviewWaveform);

    return ret;

}
Ziad Halabi
  • 964
  • 11
  • 31
  • 1
    Stack trace could help. Using [ndk-stack](https://developer.android.com/ndk/guides/ndk-stack.html) you can point to the line in C++ that failed. – Alex Cohn Jan 11 '17 at 19:20
  • 1
    The fact that it works sometime and sometime it does not, is an indication with the value that is obtained at runtime. It can be the path that is being passed from Java --the "url" value that is used here: const char *openError = decoder->open(url, false, 0, 0); if (openError) { delete decoder; return NULL; }; In such case, this line can potentially cause SIGSEGV: env->SetFloatArrayRegion(ret, 0, 2, values); Sprinkle the code with log messages to isolate the problem. That may pinpoint the problem. – user2995358 Jan 16 '17 at 06:13
  • 1
    @user2995358 Thank you for your response. You have a point regarding the url. However, it is never null (the check is in the Java code). Also, it happens randomly at runtime but not on the same "url" item. My bet is that I am not handling memory properly so a SIGSEGV is occurring. – Ziad Halabi Jan 16 '17 at 11:19
  • 1
    @ZiadHalabi It need not be null, but the url may not be of the right format. SIGSEGV happens because you are accessing a memory not belonging to you or you are using uninitialized variables. Check for null for all the pointers that are supposed to get values from your decoder/analyzer methods. For ex openError is not null, initialize bpm and keyIndex before using it etc. – user2995358 Jan 17 '17 at 03:56

1 Answers1

3

Please, double check that you are releasing all the memory, you are allocating AND that you release memory/delete objects in the reverse order e.g. if you created object A then B, you should delete B then A.

  1. Memory leak: float* values isn't freed in Java_com_superpowered_SuperpoweredPlayer_getInfo()
  2. I would free memory allocated in analyzer->getresults() first, then floatBuffer and intBuffer, then deleted analyzer and then decoder.

Are you sure it is correct (and safe) to free memory allocated in analyzer->getresults() with free(), because free in the superpowered may be different from free in your code (if either is linked statically to standard C library)?

ivan_onys
  • 2,282
  • 17
  • 21
  • Seems to me that `ret=(float*)malloc(2*sizeof(float));` in SuperpoweredPlayer::getKey is never being freed. (e.g after `env->SetFloatArrayRegion(ret, 0, 2, values);`) – Yoni Gross Jan 21 '17 at 00:57
  • It is item 1 in the answer – ivan_onys Jan 21 '17 at 03:37
  • Yes. I think it's not so clear that you are pointing to what is defiantly a memory leak. Maybe replace "values" with `float *values`, and add that this is a memory leak? – Yoni Gross Jan 21 '17 at 11:15
  • @YoniGross Done – ivan_onys Jan 21 '17 at 12:24
  • @ivan_onys Thank you for your answer. Can you please tell me why would the order of the memory freeing matter? – Ziad Halabi Feb 03 '17 at 09:01
  • Analyzer and decoder may still be using buffers, but buffers are already freed. One should keep buffers allocated until users of those buffers exist. The most straightforward way is to delete objects / deallocate memory in the reverse order of their allocation / creation. – ivan_onys Feb 03 '17 at 16:41