0

I made a sample project using ndk + openGL. Idea of the app is simple, open the camera when you tap on the screen AR object appear on place where you tapped.

When I worked in debug mode all was ok, but after I switched in release mode, compilation is ok, I can open the camera, but when I tap on the screen the app crashed.

Of course there any logs... So, to open the logs option (in order to find out the reason of the crash) I added such line debuggable true in release section.

But, now when I tap on the screen the app don't crash...

What is the possible reason of crash? How can I check it if I get crash only with debuggable false and can't see logs...?

Here my gradle

apply plugin: 'com.android.application'

/*
The arcore aar library contains the native shared libraries.  These are
extracted before building to a temporary directory.
*/
def arcore_libpath = "${buildDir}/arcore-native"

// Create a configuration to mark which aars to extract .so files from
configurations { natives }

android {
compileSdkVersion 27
defaultConfig {
    applicationId "com.google.ar.core.examples.c.helloar"

    // 24 is the minimum since ARCore only works with 24 and higher.
    minSdkVersion 24
    targetSdkVersion 27
    versionCode 1
    versionName "1.0"

    signingConfig signingConfigs.debug

    externalNativeBuild {
        cmake {
            cppFlags "-std=c++11", "-Wall"
            arguments "-DANDROID_STL=c++_static",
                    "-DARCORE_LIBPATH=${arcore_libpath}/jni",
                    "- 
 DARCORE_INCLUDE=${project.rootDir}
/../../libraries/include"
        }
    }
    ndk {
        abiFilters "arm64-v8a", "armeabi-v7a", "x86"
    }
}
compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
    release {
        debuggable true
        minifyEnabled false
        proguardFiles getDefaultProguardFile
('proguard-android.txt'), 'proguard-rules.pro'
    }
}
externalNativeBuild {
    cmake {
        path "CMakeLists.txt"
    }
}
}

dependencies {
// ARCore library
implementation 'com.google.ar:core:1.9.0'
natives 'com.google.ar:core:1.9.0'

implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support:design:27.1.1'
}

// Extracts the shared libraries from aars in the natives configuration.
// This is done so that NDK builds can access these libraries.
task extractNativeLibraries() {
// Always extract, this insures the native libs 
are updated if the version changes.
outputs.upToDateWhen { false }
doFirst {
    configurations.natives.files.each { f ->
        copy {
            from zipTree(f)
            into arcore_libpath
            include "jni/**/*"
        }
    }
}
}

tasks.whenTaskAdded {
task-> if (task.name.contains("external") && !task.name.contains("Clean")) {
    task.dependsOn(extractNativeLibraries)
}
}

and app gradle

// Top-level build file where you can add 
configuration options common to all sub-projects/modules.

buildscript {
repositories {
    google()
    jcenter()
}
dependencies {
    classpath 'com.android.tools.build:gradle:3.3.0'
    // NOTE: Do not place your application dependencies here; they belong
    // in the individual module build.gradle files
}
}

allprojects {
repositories {
    google()
    jcenter()
    mavenLocal()
}
}

task clean(type: Delete) {
delete rootProject.buildDir
}

EDIT

Here I got some log output

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'samsung/crownltexx/crownlte:9/PPR1.180610.011/N960FXXU2CSDE:user/release-keys'
Revision: '28'
ABI: 'arm64'
pid: 11411, tid: 11600, name: GLThread 82400    >>> com.myapp.ar <<<
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
Abort message: 'terminating with uncaught exception of type std::bad_alloc: std::bad_alloc'
        x0    0000000000000000    x1    0000000000002d50    x2    0000000000000006    x3    0000000000000008
        x4    fefefefefefefeff    x5    fefefefefefefeff    x6    fefefefefefefeff    x7    7f7f7f7f7f7f7f7f
        x8    0000000000000083    x9    0000007f5c9fa890    x10 fffffff87ffffbdf    x11 0000000000000001
        x12 0000000000000018    x13 000000005d0788d4    x14 00057c28f4018340    x15 00001ef146270368
        x16 0000007f5ca312b0    x17 0000007f5c970958    x18 0000000000000000    x19 0000000000002c93
        x20 0000000000002d50    x21 0000000000000083    x22 ffffff80ffffffc8    x23 0000007e94b00cb0
        x24 0000007e94b00b90    x25 0000007e94b00bd0    x26 0000000000000016    x27 ffffffffffffffb0
        x28 0000007ebfc24e9c    x29 0000007e94b00b00
        sp    0000007e94b00ac0    lr    0000007f5c963da0    pc    0000007f5c963dcc
backtrace:
        #00 pc 0000000000021dcc    /system/lib64/libc.so (abort+124)
        #01 pc 00000000000cccac    /data/app/com.myapp.ar-jewGNQDDH4aeZ8zLn7v2aA==/lib/arm64/libhello_ar_native.so
        #02 pc 00000000000cce0c    /data/app/com.myapp.ar-jewGNQDDH4aeZ8zLn7v2aA==/lib/arm64/libhello_ar_native.so
        #03 pc 00000000000c9bf0    /data/app/com.myapp.ar-jewGNQDDH4aeZ8zLn7v2aA==/lib/arm64/libhello_ar_native.so
        #04 pc 00000000000c92f8    /data/app/com.myapp.ar-jewGNQDDH4aeZ8zLn7v2aA==/lib/arm64/libhello_ar_native.so
        #05 pc 00000000000c9278    /data/app/com.myapp.ar-jewGNQDDH4aeZ8zLn7v2aA==/lib/arm64/libhello_ar_native.so (__cxa_throw+120)
        #06 pc 00000000000cc588    /data/app/com.myapp.ar-jewGNQDDH4aeZ8zLn7v2aA==/lib/arm64/libhello_ar_native.so (operator new(unsigned long)+84)
        #07 pc 000000000006c3c0    /data/app/com.myapp.ar-jewGNQDDH4aeZ8zLn7v2aA==/lib/arm64/libhello_ar_native.so (std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char>>::append(char const*, unsigned long)+264)
        #08 pc 000000000006e59c    /data/app/com.myapp.ar-jewGNQDDH4aeZ8zLn7v2aA==/lib/arm64/libhello_ar_native.so (FrameManager::LoadImage(std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char>>)+280)
        #09 pc 0000000000050ed8    /data/app/com.myapp.ar-jewGNQDDH4aeZ8zLn7v2aA==/lib/arm64/libhello_ar_native.so (hello_ar::VideoRender::thread_task2(std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char>>, std::__ndk1::shared_ptr<FrameManager>)+60)
        #10 pc 000000000006c800    /data/app/com.myapp.ar-jewGNQDDH4aeZ8zLn7v2aA==/lib/arm64/libhello_ar_native.so (_ZNSt6__ndk114__thread_proxyINS_5tupleIJNS_10unique_ptrINS_15__thread_structENS_14default_deleteIS3_EEEEMN8hello_ar11VideoRenderEFvNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEENS_10shared_ptrI12FrameManagerEEEPS8_SE_SH_EEEEEPvSM_+132)
        #11 pc 0000000000084df8    /system/lib64/libc.so (__pthread_start(void*)+208)
        #12 pc 0000000000023ac4    /system/lib64/libc.so (__start_thread+68)

EDIT

LoadImage method

void FrameManager::LoadImage(std::string const & path)
{
std::ifstream file(path, std::ios::binary);

const int PKM_HEADER_SIZE = 16;
char pkmHeader[PKM_HEADER_SIZE];
file.read(pkmHeader, sizeof(pkmHeader));

std::string magicNumber = "";
magicNumber.append("" + ((char) pkmHeader[0]));
magicNumber.append("" + ((char) pkmHeader[1]));
magicNumber.append("" + ((char) pkmHeader[2]));
magicNumber.append("" + ((char) pkmHeader[3]));
m_image_width = ((int) pkmHeader[9]) | (((int) pkmHeader[8]) << 8);
m_image_height = ((int) pkmHeader[11]) | (((int) pkmHeader[10]) << 8);
const int BLOCK_DIM = 4;
const int BLOCK_PIXELS = BLOCK_DIM * BLOCK_DIM;
const int BLOCK_BITS = 64;
int size = (BLOCK_BITS / 8) * m_image_width * m_image_height /
 (BLOCK_PIXELS);

m_imageData.resize(size);
int pos = 0;

while (size > 0)
{
    file.read((char *) &m_imageData[pos], size);
    pos += (int) file.gcount();
    size -= (int) file.gcount();
}

file.close();
}
Sirop4ik
  • 4,543
  • 2
  • 54
  • 121
  • 1
    Sounds like a calling card of undefined behavior to me. Could be anything from trying to use invalid pointers/iterators to overflow or loop issues. All of these can have significant changes in behavior when optimised. – UKMonkey Jun 17 '19 at 10:37
  • Try integrating `Fabric` to your application. Using this tool, you can track exact line of crash on `Fabric` console. Then perhaps we can help more. Issue could be related to your `Proguard` file as well. Hence, integrate `Fabric` to know the root cause. https://get.fabric.io/# – Apurv Jun 17 '19 at 10:39
  • 3
    Usually, variables (including pointers) in debug mode are initialized to zero (or NULL in case of pointers) but in the release mode, they can be garbage. Probably some null-check did not catch an uninitialized pointer in your code. – Elviss Strazdins Jun 17 '19 at 10:51
  • @Apurv I think also Crashlitics can make the same, right? – Sirop4ik Jun 17 '19 at 11:05
  • Firebase Crashlytics will be better option, Since it can log native crashes as well@AlekseyTimoshchenko – user3135923 Jun 17 '19 at 11:07
  • @AlekseyTimoshchenko yes, you can use Crashlytics as well. – Apurv Jun 17 '19 at 11:24
  • @ElvissStrazdins I updated my question with log output. It look like problem with allocation... – Sirop4ik Jun 17 '19 at 12:38
  • Алексей, Your code is most probably the issue. Code would help us so much more in helping you than your gradle build files. – nada Jun 17 '19 at 13:55
  • Are you running out of memory, or corrupting the heap with a buffer overflow? You're getting a `std::bad_alloc` thrown from what looks like an `operator new(unsigned long)` call in `std::string::append(char const*, unsigned long)`. – Andrew Henle Jun 17 '19 at 14:22
  • 1
    @AlekseyTimoshchenko my guess is that some variable (size) is being initialized to 0 in debug mode and you increment it by the needed amount and then allocate memory e.g. `new Foo[size]`. In release mode, the variable is not initialized to 0 (has some huge garbage value) and you get `std::bad_alloc`. – Elviss Strazdins Jun 17 '19 at 14:49
  • Analyze your `FrameManager::LoadImage()`. If you give `6e59c` to **addr2line** utility, you will get [the specific line number that caused the problem](https://stackoverflow.com/a/5317759/192373). – Alex Cohn Jun 18 '19 at 07:05
  • @AndrewHenle added `LoadImage` method to question – Sirop4ik Jun 18 '19 at 11:28
  • @ElvissStrazdins added `LoadImage` method to question – Sirop4ik Jun 18 '19 at 11:28
  • @AlexCohn added `LoadImage` method to question – Sirop4ik Jun 18 '19 at 11:28
  • @AlekseyTimoshchenko you take an empty string literal "" and then append some value to it and then pass it to `magicNumber.append`. It treats your pointer as a null-terminated string and if there is no null byte there (in release mode it probably isn't there, but it is there in the debug mode) then it tries to resize the string until a bad_alloc is thrown. – Elviss Strazdins Jun 18 '19 at 13:04
  • how did you fix? – Ken Zira Jul 20 '20 at 14:22

1 Answers1

3

"" + ((char) pkmHeader[0]) is a const char*, and therefore std::string::append() can receive it. But the value of this const char* is not "A" (assuming that pkmHeader[0] == 'A'). The result of "" + ((char) pkmHeader[0]) is a pointer to (preallocated) "" constant with offset of 65 from that pointer. For me,

printf("%p %p\n", "", "" + ((char) 'A') );

prints:

0x7f3cbf201025 0x7f3cbf201066

Note that 0x7f3cbf201066 - 0x7f3cbf201025 = 0x41, the value of 'A'.

To fix your code, use

magicNumber.append(pkmHeader, 4);
Alex Cohn
  • 56,089
  • 9
  • 113
  • 307