gpt4 book ai didi

android - 在 JNI/OpenGL ES 加载代码期间,非常规和狡猾的 Android 崩溃

转载 作者:塔克拉玛干 更新时间:2023-11-02 22:59:02 26 4
gpt4 key购买 nike

赏金

因为这对我来说是一个重要的问题,所以我一直悬赏。我不是在寻找确切的答案——任何能让我解决这个问题的答案都会得到赏金。请确保您已经看到下面的编辑。

编辑:我已经设法在 Gdb 死机时捕获它的崩溃(通过“adb shell setprop debug.db.uid 32767”),并注意到这与 this post 中提到的问题完全相同。在 Google 网上论坛上。显示的回溯与我的崩溃线程相同(精确地址除外)。我承认,我不是调试工具向导,所以如果您对我应该寻找什么有任何想法,请告诉我。

快速而肮脏的破败

我删除了大部分相当大的应用程序代码,以便应用程序执行以下操作:通过 JNI 包装器(来自 C++ --> Java)加载一堆纹理,以便 Java 库为我处理解码,使 OpenGL 纹理脱离它们,并将屏幕清除为相当漂亮但模拟的深蓝色。它正在 libc 中消亡,但只有十分之一。

更糟糕的是,它甚至看起来与我编写的任何代码都没有关系——它似乎以延迟的方式发生,但它似乎与一些方便指责的东西无关作为垃圾收集器。我自己的代码中没有发生崩溃的特定点 - 它似乎在每次运行的基础上转移。

更长的故事

我最终得到了一个带有堆栈的标准崩溃转储,它告诉我几乎什么都没有,因为它有两个条目,一个是 libc,一个是看起来像无效或空堆栈帧的条目。 libc 中解析的符号是 pthread_mutex_unlock。我什至不再自己使用这个函数,因为我已经去掉了对多线程的需求。 ( native 代码在表面 View 中调用并仅呈现。)

pthread_mutex_unlock 会导致段错误,通常在地址 0,但有时是一个小值(小于 0x200)而不是 0。Bionic 中的默认(也是最常见的)互斥锁只有一个可以段错误的指针,这就是指向pthread_mutex_t 结构本身。但是,更复杂的互斥体(有多个选项)可能会使用额外的指针。所以,很可能 libc 很好,而 libdvm 有问题(假设我可以信任我的堆栈跟踪,甚至那么远)。

让我注意这个问题似乎只有在我执行以下两件事之一时才会重现:禁用图像数据部分的加载(但仍读取格式/尺寸信息),并使我用于将纹理加载到 OpenGL 的缓冲区未初始化,或者通过仅禁用最终的 glTexImage2D 调用来禁用 OpenGL 纹理的创建。

请注意,上述用于将纹理加载到 OpenGL 的缓冲区仅创建一次并销毁一次。我试过扩大它并确定我没有受到特定于该缓冲区的缓冲区溢出问题的困扰。

我能想到的罪魁祸首是:

  • 我没有正确使用 JNI,它对堆栈做了一些讨厌的事情。
  • 我在破坏堆栈帧的地方有一个逐一错误。
  • 我正在传递 OpenGL ES 一些不好的东西,它正在做一些同样糟糕的事情(tm)。
  • 我的自定义滚动内存分配器运行不正常。

  • 几天来,我一直在为这些罪魁祸首(以及更多!)梳理我的代码。我对使用调试器犹豫不决,因为这次崩溃似乎对时间很敏感。但是,在启用调试选项的情况下,我仍然可以使用我自己的 native 代码完全未优化的崩溃。 (gdb 本身以爬行方式运行,应用程序连接时也是如此)

    我做过的事
  • 使用 CheckJNI。
  • 尽可能多地精简代码,直到它停止崩溃。
  • 编写了一个信号处理程序并编写了一个小型日志记录系统来转储在抛出信号之前完成的最后一件事。
  • 试图(但失败)加剧了问题。
  • 用金丝雀在两端填充 native 堆数组。他们从未改变。
  • 对代码路径中的代码进行了 100% 的审计。 (我只是没有看到问题。)
  • 当我修复一个小错误时,我认为问题神奇地消失了,运行了 50 次代码以确保是这样,然后第二天我第一次运行时就崩溃了。 (哦,我以前从未对错误如此生气!)

  • 以下是来自 LogCat 的常见 native 崩溃信息的 fragment :
    I/DEBUG   ( 5818): signal 11 (SIGSEGV), fault addr 00000000
    I/DEBUG ( 5818): r0 0000006e r1 00000080 r2 fffffc5e r3 100ffe58
    I/DEBUG ( 5818): r4 00000000 r5 00000000 r6 00000000 r7 00000000
    I/DEBUG ( 5818): r8 00000000 r9 8054f999 10 10000000 fp 0013e768
    I/DEBUG ( 5818): ip 3b9aca00 sp 100ffe58 lr afd10640 pc 00000000 cpsr 60000010
    I/DEBUG ( 5818): d0 643a64696f72646e d1 6472656767756265
    I/DEBUG ( 5818): d2 8083297880832965 d3 8083298880832973
    I/DEBUG ( 5818): d4 8083291080832908 d5 8083292080832918
    I/DEBUG ( 5818): d6 8083293080832928 d7 8083294880832938
    I/DEBUG ( 5818): d8 0000000000000000 d9 0000000000000000
    I/DEBUG ( 5818): d10 0000000000000000 d11 0000000000000000
    I/DEBUG ( 5818): d12 0000000000000000 d13 0000000000000000
    I/DEBUG ( 5818): d14 0000000000000000 d15 0000000000000000
    I/DEBUG ( 5818): d16 0000000000000000 d17 3fe999999999999a
    I/DEBUG ( 5818): d18 42eccefa43de3400 d19 3fe00000000000b4
    I/DEBUG ( 5818): d20 4008000000000000 d21 3fd99a27ad32ddf5
    I/DEBUG ( 5818): d22 3fd24998d6307188 d23 3fcc7288e957b53b
    I/DEBUG ( 5818): d24 3fc74721cad6b0ed d25 3fc39a09d078c69f
    I/DEBUG ( 5818): d26 0000000000000000 d27 0000000000000000
    I/DEBUG ( 5818): d28 0000000000000000 d29 0000000000000000
    I/DEBUG ( 5818): d30 0000000000000000 d31 0000000000000000
    I/DEBUG ( 5818): scr 80000012
    I/DEBUG ( 5818):
    I/DEBUG ( 5818): #00 pc 00000000
    I/DEBUG ( 5818): #01 pc 0001063c /system/lib/libc.so
    I/DEBUG ( 5818):
    I/DEBUG ( 5818): code around pc:
    I/DEBUG ( 5818):
    I/DEBUG ( 5818): code around lr:
    I/DEBUG ( 5818): afd10620 e1a01008 e1a02007 e1a03006 e1a00005
    I/DEBUG ( 5818): afd10630 ebfff95d e1a05000 e1a00004 ebffff46
    I/DEBUG ( 5818): afd10640 e375006e 03a0006e 13a00000 e8bd81f0
    I/DEBUG ( 5818): afd10650 e304cdd3 e3043240 e92d4010 e341c062
    I/DEBUG ( 5818): afd10660 e1a0e002 e24dd008 e340300f e1a0200d
    I/DEBUG ( 5818):
    I/DEBUG ( 5818): stack:
    I/DEBUG ( 5818): 100ffe18 00000000
    I/DEBUG ( 5818): 100ffe1c 00000000
    I/DEBUG ( 5818): 100ffe20 00000000
    I/DEBUG ( 5818): 100ffe24 ffffff92
    I/DEBUG ( 5818): 100ffe28 100ffe58
    I/DEBUG ( 5818): 100ffe2c 00000000
    I/DEBUG ( 5818): 100ffe30 00000080
    I/DEBUG ( 5818): 100ffe34 8054f999 /system/lib/libdvm.so
    I/DEBUG ( 5818): 100ffe38 10000000
    I/DEBUG ( 5818): 100ffe3c afd10640 /system/lib/libc.so
    I/DEBUG ( 5818): 100ffe40 00000000
    I/DEBUG ( 5818): 100ffe44 00000000
    I/DEBUG ( 5818): 100ffe48 00000000
    I/DEBUG ( 5818): 100ffe4c 00000000
    I/DEBUG ( 5818): 100ffe50 e3a07077
    I/DEBUG ( 5818): 100ffe54 ef900077
    I/DEBUG ( 5818): #01 100ffe58 00000000
    I/DEBUG ( 5818): 100ffe5c 00000000
    I/DEBUG ( 5818): 100ffe60 00000000
    I/DEBUG ( 5818): 100ffe64 00000000
    I/DEBUG ( 5818): 100ffe68 00000000
    I/DEBUG ( 5818): 100ffe6c 00000000
    I/DEBUG ( 5818): 100ffe70 00000000
    I/DEBUG ( 5818): 100ffe74 00000000
    I/DEBUG ( 5818): 100ffe78 00000000
    I/DEBUG ( 5818): 100ffe7c 00000000
    I/DEBUG ( 5818): 100ffe80 00000000
    I/DEBUG ( 5818): 100ffe84 00000000
    I/DEBUG ( 5818): 100ffe88 00000000
    I/DEBUG ( 5818): 100ffe8c 00000000
    I/DEBUG ( 5818): 100ffe90 00000000
    I/DEBUG ( 5818): 100ffe94 00000000
    I/DEBUG ( 5818): 100ffe98 00000000
    I/DEBUG ( 5818): 100ffe9c 00000000

    使用 ndk r6,Android 平台 2.2(API 级别 8),编译时使用 -Wall -Werror,仅限 ARM 模式。

    我正在研究任何想法,尤其是那些可以确定性方式验证的想法。如果更多信息有帮助,请发表评论(如果不能,请回答),我会尽快更新我的问题。感谢您阅读到这里!

    JNI接口(interface)

    有 j2n 和 n2j 调用。现在唯一的 j2n 调用在这里:
    private static class Renderer implements GLSurfaceView.Renderer {
    public void onDrawFrame(GL10 gl) {
    GraphicsLib.graphicsStep();
    }

    public void onSurfaceChanged(GL10 gl, int width, int height) {
    GraphicsLib.graphicsInit(width, height);
    }

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    // Do nothing.
    }
    }

    这段代码通过这个接口(interface):
    public class GraphicsLib {

    static {
    System.loadLibrary("graphicslib");
    }

    public static native void graphicsInit(int width, int height);
    public static native void graphicsStep();
    }

    在 native 方面,它看起来像:
    extern "C" {
    JNIEXPORT void JNICALL FN(graphicsInit)(JNIEnv* env, jobject obj, jint width, jint height);
    JNIEXPORT void JNICALL FN(graphicsStep)(JNIEnv* env, jobject obj);
    };

    函数定义本身以原型(prototype)的副本开始。

    graphicsInit 只是存储了它传递的尺寸并稍微设置了 OpenGL,没有任何特别有趣的东西。 graphicsStep 将屏幕清除为漂亮的颜色并调用 LoadSprites(env) .

    更复杂的一面由 LoadSprites() 中使用的 n2j 调用组成,它每帧加载一个 Sprite 。不是一个优雅的解决方案,但它一直在工作,但这次崩溃除外。

    LoadSprites 的工作方式如下:
    GameAssetsInfo gai;
    void LoadSprites(JNIEnv* env)
    {
    InitGameAssets(gai, env);
    CatchJNIException(env, "j0");
    ...
    static int z = 0;
    if (z < numSprites)
    {
    CatchJNIException(env, "j1");
    OpenGameImage(gai, SpriteIDFromNumber(z));
    CatchJNIException(env, "j2");
    unsigned int actualWidth = GetGameImageWidth(gai);
    CatchJNIException(env, "j3");
    unsigned int actualHeight = GetGameImageHeight(gai);
    CatchJNIException(env, "j4");
    ...
    jint i;
    int r = 0;
    CatchJNIException(env, "j5");
    do {
    CatchJNIException(env, "j6");
    i = ReadGameImage(gai);
    CatchJNIException(env, "j7");
    if (i > 0)
    {
    // Deal with the pure data chunk -- One line at a time.
    CatchJNIException(env, "j8");
    StoreGameImageChunk(gai, (int*)sprites[z].data + r, 0, i);
    ...
    r += sprites[z].width;
    CatchJNIException(env, "j9");
    UnreadGameImage(gai);
    CatchJNIException(env, "j10");
    } else {
    break;
    }
    } while (true);

    CatchJNIException(env, "j11");
    CloseGameImage(gai);
    CatchJNIException(env, "j12");

    ... OpenGL ES calls ...

    glTexImage2D( ... );

    z++;
    }

    CatchJNIException(env, "j13");
    }

    CatchJNIException 在哪里(并且 从不 为我打印任何内容):
    void CatchJNIException(JNIEnv* env, const char* str)
    {
    jthrowable exc = env->ExceptionOccurred();
    if (exc) {
    jclass newExcCls;
    env->ExceptionDescribe();
    env->ExceptionClear();
    newExcCls = env->FindClass(
    "java/lang/IllegalArgumentException");
    if (newExcCls == NULL) {
    // Couldn't find the exception class.. Uuh..
    LOGE("Failed to catch JNI exception entirely -- could not find exception class.");
    return;
    abort();
    }
    LOGE("Caught JNI exception. (%s)", str);
    env->ThrowNew( newExcCls, "thrown from C code");
    // abort();
    }
    }

    而 GameAssetInfo 的相关部分和相关代码仅从 native 代码中调用,其工作方式如下:
    void InitGameAssets(GameAssetsInfo& gameasset, JNIEnv* env)
    {
    CatchJNIException(env, "jS0");
    FST;
    char str[64];
    sprintf(str, "%s/GameAssets", ROOTSTR);

    gameasset.env = env;
    CatchJNIException(gameasset.env, "jS1");
    gameasset.cls = gameasset.env->FindClass(str);
    CatchJNIException(gameasset.env, "jS2");
    gameasset.openAsset = gameasset.env->GetStaticMethodID(gameasset.cls, "OpenAsset", "(I)V");
    CatchJNIException(gameasset.env, "jS3");
    gameasset.readAsset = gameasset.env->GetStaticMethodID(gameasset.cls, "ReadAsset", "()I");
    CatchJNIException(gameasset.env, "jS4");
    gameasset.closeAsset = gameasset.env->GetStaticMethodID(gameasset.cls, "CloseAsset", "()V");
    CatchJNIException(gameasset.env, "jS5");
    gameasset.buffID = gameasset.env->GetStaticFieldID(gameasset.cls, "buff", "[B");

    CatchJNIException(gameasset.env, "jS6");
    gameasset.openImage = gameasset.env->GetStaticMethodID(gameasset.cls, "OpenImage", "(I)V");
    CatchJNIException(gameasset.env, "jS7");
    gameasset.readImage = gameasset.env->GetStaticMethodID(gameasset.cls, "ReadImage", "()I");
    CatchJNIException(gameasset.env, "jS8");
    gameasset.closeImage = gameasset.env->GetStaticMethodID(gameasset.cls, "CloseImage", "()V");
    CatchJNIException(gameasset.env, "jS9");
    gameasset.buffIntID = gameasset.env->GetStaticFieldID(gameasset.cls, "buffInt", "[I");
    CatchJNIException(gameasset.env, "jS10");
    gameasset.imageWidth = gameasset.env->GetStaticFieldID(gameasset.cls, "imageWidth", "I");
    CatchJNIException(gameasset.env, "jS11");
    gameasset.imageHeight = gameasset.env->GetStaticFieldID(gameasset.cls, "imageHeight", "I");
    CatchJNIException(gameasset.env, "jS12");
    gameasset.imageHasAlpha = gameasset.env->GetStaticFieldID(gameasset.cls, "imageHasAlpha", "I");
    CatchJNIException(gameasset.env, "jS13");
    }

    void OpenGameAsset(GameAssetsInfo& gameasset, int rsc)
    {
    FST;
    CatchJNIException(gameasset.env, "jS14");
    gameasset.env->CallStaticVoidMethod(gameasset.cls, gameasset.openAsset, rsc);
    CatchJNIException(gameasset.env, "jS15");
    }

    void CloseGameAsset(GameAssetsInfo& gameasset)
    {
    FST;
    CatchJNIException(gameasset.env, "jS16");
    gameasset.env->CallStaticVoidMethod(gameasset.cls, gameasset.closeAsset);
    CatchJNIException(gameasset.env, "jS17");
    }

    int ReadGameAsset(GameAssetsInfo& gameasset)
    {
    FST;
    CatchJNIException(gameasset.env, "jS18");
    int ret = gameasset.env->CallStaticIntMethod(gameasset.cls, gameasset.readAsset);
    CatchJNIException(gameasset.env, "jS19");
    if (ret > 0)
    {
    CatchJNIException(gameasset.env, "jS20");
    gameasset.obj = gameasset.env->GetStaticObjectField(gameasset.cls, gameasset.buffID);
    CatchJNIException(gameasset.env, "jS21");
    gameasset.arr = reinterpret_cast<jbyteArray*>(&gameasset.obj);
    }
    return ret;
    }

    void UnreadGameAsset(GameAssetsInfo& gameasset)
    {
    FST;
    CatchJNIException(gameasset.env, "jS22");
    gameasset.env->DeleteLocalRef(gameasset.obj);
    CatchJNIException(gameasset.env, "jS23");
    }

    void StoreGameAssetChunk(GameAssetsInfo& gameasset, void* store, int offset, int length)
    {
    FST;
    CatchJNIException(gameasset.env, "jS24");
    gameasset.env->GetByteArrayRegion(*gameasset.arr, offset, length, (jbyte*)store);
    CatchJNIException(gameasset.env, "jS25");
    }

    void OpenGameImage(GameAssetsInfo& gameasset, int rsc)
    {
    FST;
    CatchJNIException(gameasset.env, "jS26");
    gameasset.env->CallStaticVoidMethod(gameasset.cls, gameasset.openImage, rsc);
    CatchJNIException(gameasset.env, "jS27");
    gameasset.l_imageWidth = (int)gameasset.env->GetStaticIntField(gameasset.cls, gameasset.imageWidth);
    CatchJNIException(gameasset.env, "jS28");
    gameasset.l_imageHeight = (int)gameasset.env->GetStaticIntField(gameasset.cls, gameasset.imageHeight);
    CatchJNIException(gameasset.env, "jS29");
    gameasset.l_imageHasAlpha = (int)gameasset.env->GetStaticIntField(gameasset.cls, gameasset.imageHasAlpha);
    CatchJNIException(gameasset.env, "jS30");
    }

    void CloseGameImage(GameAssetsInfo& gameasset)
    {
    FST;
    CatchJNIException(gameasset.env, "jS31");
    gameasset.env->CallStaticVoidMethod(gameasset.cls, gameasset.closeImage);
    CatchJNIException(gameasset.env, "jS32");
    }

    int ReadGameImage(GameAssetsInfo& gameasset)
    {
    FST;
    CatchJNIException(gameasset.env, "jS33");
    int ret = gameasset.env->CallStaticIntMethod(gameasset.cls, gameasset.readImage);
    CatchJNIException(gameasset.env, "jS34");
    if ( ret > 0 )
    {
    CatchJNIException(gameasset.env, "jS35");
    gameasset.obj = gameasset.env->GetStaticObjectField(gameasset.cls, gameasset.buffIntID);
    CatchJNIException(gameasset.env, "jS36");
    gameasset.arrInt = reinterpret_cast<jintArray*>(&gameasset.obj);
    }
    return ret;
    }

    void UnreadGameImage(GameAssetsInfo& gameasset)
    {
    FST;
    CatchJNIException(gameasset.env, "jS37");
    gameasset.env->DeleteLocalRef(gameasset.obj);
    CatchJNIException(gameasset.env, "jS38");
    }

    void StoreGameImageChunk(GameAssetsInfo& gameasset, void* store, int offset, int length)
    {
    FST;
    CatchJNIException(gameasset.env, "jS39");
    gameasset.env->GetIntArrayRegion(*gameasset.arrInt, offset, length, (jint*)store);
    CatchJNIException(gameasset.env, "jS40");
    }

    int GetGameImageWidth(GameAssetsInfo& gameasset) { return gameasset.l_imageWidth; }
    int GetGameImageHeight(GameAssetsInfo& gameasset) { return gameasset.l_imageHeight; }
    int GetGameImageHasAlpha(GameAssetsInfo& gameasset) { return gameasset.l_imageHasAlpha; }

    它在 Java 方面得到了支持:
    public class GameAssets {
    static public Resources res = null;
    static public InputStream is = null;
    static public byte buff[];
    static public int buffInt[];
    static public final int buffSize = 1024;
    static public final int buffIntSize = 2048;

    static public int imageWidth;
    static public int imageHeight;
    static public int imageHasAlpha;
    static public int imageLocX;
    static public int imageLocY;
    static public Bitmap mBitmap;
    static public BitmapFactory.Options decodeResourceOptions = new BitmapFactory.Options();

    public GameAssets(Resources r) {
    res = r;
    buff = new byte[buffSize];
    buffInt = new int[buffIntSize];
    decodeResourceOptions.inScaled = false;
    }
    public static final void OpenAsset(int id) {
    is = res.openRawResource(id);
    }
    public static final int ReadAsset() {
    int num = 0;
    try {
    num = is.read(buff);
    } catch (Exception e) {
    ;
    }
    return num;
    }
    public static final void CloseAsset() {
    try {
    is.close();
    } catch (Exception e) {
    ;
    }
    is = null;
    }

    // We want all the advantages that BitmapFactory can provide -- reading
    // images of compressed image formats -- so we provide our own interface
    // for it.
    public static final void OpenImage(int id) {
    mBitmap = BitmapFactory.decodeResource(res, id, decodeResourceOptions);
    imageWidth = mBitmap.getWidth();
    imageHeight = mBitmap.getHeight();
    imageHasAlpha = mBitmap.hasAlpha() ? 1 : 0;
    imageLocX = 0;
    imageLocY = 0;
    }
    public static final int ReadImage() {
    if (imageLocY >= imageHeight) return 0;
    int numReadPixels = buffIntSize;
    if (imageLocX + buffIntSize >= imageWidth)
    {
    numReadPixels = imageWidth - imageLocX;
    mBitmap.getPixels(buffInt, 0, imageWidth, imageLocX, imageLocY, numReadPixels, 1);
    imageLocY++;
    }
    else
    {
    mBitmap.getPixels(buffInt, 0, imageWidth, imageLocX, imageLocY, numReadPixels, 1);
    imageLocX += numReadPixels;
    }
    return numReadPixels;
    }
    public static final void CloseImage() {
    }
    }

    请注意游戏 Assets 代码中明显缺乏线程安全性。

    让我知道更多信息是否有用。

    最佳答案

    我无法回复:(,我刚刚遇到了类似的问题,并注意到某处的 java 文档说何时有线程(如果您正在使用 OpenGL,则有线程)。您需要小心第一个参数(env)和第二个参数(作业对象)。您不能在不同的线程上共享,因为它们是特定于线程的。

    就我而言,有事件线程和渲染线程。我有一个全局 env 和 jself 变量,它们是调用 jni 时传入的 2 个参数。我更改了代码以确保只有渲染线程接触 env/jself 变量。在事件线程中,我传入原始数据,只需注意需要完成的操作,因此不需要 env/jself 变量。当然,我使用互斥锁来锁定我的事件结构。

    看起来你在这里设置 env 可能是全局的
    游戏 Assets .env = env;

    如果 gameasset 是全局的或者被不同的线程使用,那么简单地通过互斥锁/锁定共享 env 或 jobject 类变量是行不通的(它们是线程特定的)。

    TL:博士;从java调用jni方法时,我只访问env变量,渲染线程上的jobject第二个变量,没有其他地方,到目前为止已经缓解了我的问题。

    关于android - 在 JNI/OpenGL ES 加载代码期间,非常规和狡猾的 Android 崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6937001/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com