代码之家  ›  专栏  ›  技术社区  ›  Michael Chinen

为什么我的价值不能增加?内存跺脚/堆栈故障?

  •  1
  • Michael Chinen  · 技术社区  · 15 年前

    我遇到了一只非常迷人的虫子,我想把我的头绕起来。

    我觉得我以前见过这种情况,但这次我想知道为什么会发生这种情况。

    我有:

    int i;
    int debug = 0;
    for(i = 0; i < buf_end; i++) {
        do_some_buffer_work();
        if(something_is_true()) {
             do_something_important();
             printf("debug is %i, i is %i", debug++, i);
        }
    }
    

    printf(“结束\n”);

    我得到输出:

    debug is 1, i is 55
    debug is 2, i is 55
    

    所以有一个点,循环被执行了两次,其中i的值相同。 我在循环中所做的任何事情都不会直接接触到我,而且我怀疑这是传统的内存跺脚,因为它的值总是相同的。我怀疑这是导致程序计数器移动的原因(因为有时链接不好会导致类似的错误),直到我执行以下测试:

    int i;
    static int debug;
    for(i = 0; i < buf_end; i++) {
        do_some_buffer_work();
        if(something_is_true()) {
             do_something_important();
             printf("debug is %i, i is %i\n", debug++, i++);
             printf("debug is %i, i is %i\n", debug++, i++);
             printf("debug is %i, i is %i\n", debug++, i++);
        }
    }
    printf("end\n");
    

    我得到了这个有趣的输出:

    debug is 0, i is 55
    debug is 1, i is 56
    debug is 2, i is 57
    debug is 3, i is 55
    debug is 4, i is 56
    debug is 5, i is 57
    end
    

    很明显,我已经执行了两次相同的完整迭代,但是调试变量没有受到影响。由于某种原因,值似乎正在被缓存和还原。我有预感,把调试变量改成了非静态的,得到了:

    int i;
    int debug = 0;
    for(i = 0; i < buf_end; i++) {
        do_some_buffer_work();
        if(something_is_true()) {
             do_something_important();
             printf("debug is %i, i is %i\n", debug++, i++);
             printf("debug is %i, i is %i\n", debug++, i++);
             printf("debug is %i, i is %i\n", debug++, i++);
        }
    }
    printf("end\n");
    

    我得到了这个有趣的输出:

    debug is 0, i is 55
    debug is 1, i is 56
    debug is 2, i is 57
    debug is 0, i is 55
    debug is 1, i is 56
    debug is 2, i is 57
    end
    

    所以看起来栈中的变量被重置为第55个变量的开头。 迭代。

    我确信这个bug在其中一个do-something _-important()调用中——处理缓冲区读取——但是这个bug有它自己的特点,我想在我压扁它之前我应该先了解它的本质。 所以,请不要试图帮助我解决它,如果你有一些线索,告诉我它发生的原因。更具体地说,在程序状态中什么可以更改为“重置”值?

    编辑:如果有人因为我遗漏了这些功能而感到恼火,我很抱歉。它们非常大,并且引用了其他函数,但是我之所以不考虑它,主要是因为我不关心如何解决这个问题;我想知道如何以最简单的方式重新创建它。

    第二次编辑:这是蓝调发生的直接功能。包括引用的函数以及所有子函数和定义,大概有500行左右,所以我不在这里做。

    static int find_headers_search(FCALParseContext *fpc, uint8_t *buf, int buf_size,
                                   int search_start)
    
    {
        FCALFrameInfo fi;
        int end_offset = -1, size = 0, i;
        uint8_t *header_buf;
    
        int debug = 0;
        for (i = 0; i < buf_size - 1; i++) {
            if ((AV_RB16(buf + i) & 0xFFFE) == 0xFFF8) {
                    av_log(NULL,AV_LOG_DEBUG,"predebug%i i %i\n",debug, i);
                header_buf = fcal_fifo_read_wrap(fpc, search_start + i,
                                                 MAX_FRAME_HEADER_SIZE,
                                                 &fpc->wrap_buf,
                                                 &fpc->wrap_buf_allocated_size);
    
                if (frame_header_is_valid(header_buf, &fi)) {
                    av_log(NULL,AV_LOG_DEBUG,"frame num %u bufstart %u, size %u, end %u i %i\n", (unsigned int)fi.frame_or_sample_num,
                           search_start, buf_size, search_start + buf_size -1, i);
                    FCALHeaderMarker **end_handle = &fpc->headers;
    
                    size = 0;
                    while (*end_handle) {
                        end_offset =  (*end_handle)->offset;
                        end_handle = &(*end_handle)->next;
                        size++;
                    }
    
                    *end_handle = av_mallocz(sizeof(FCALHeaderMarker));
                    if (!*end_handle) {
                        av_log(fpc->avctx, AV_LOG_ERROR,
                               "couldn't allocate FCALHeaderMarker\n");
                        return AVERROR(ENOMEM);
                    }
                    (*end_handle)->fi     = fi;
                    (*end_handle)->offset = search_start + i;
                    /* The actual size of the linked list is now size + 1 */
                    update_sequences(fpc, size - FCAL_MAX_SEQUENTIAL_HEADERS,
                                     FFMIN(size, FCAL_MAX_SEQUENTIAL_HEADERS),
                                     *end_handle);
                    fpc->nb_headers_found++;
                    size++;
                    av_log(NULL,AV_LOG_DEBUG,"debug%i i %i\n",debug++, i++);
                    size = 0;
                    while (*end_handle) {
                        end_offset =  (*end_handle)->offset;
                        end_handle = &(*end_handle)->next;
                        size++;
                    }
    
                    *end_handle = av_mallocz(sizeof(FCALHeaderMarker));
                    if (!*end_handle) {
                        av_log(fpc->avctx, AV_LOG_ERROR,
                               "couldn't allocate FCALHeaderMarker\n");
                        return AVERROR(ENOMEM);
                    }
                    (*end_handle)->fi     = fi;
                    (*end_handle)->offset = search_start + i;
                    /* The actual size of the linked list is now size + 1 */
                    update_sequences(fpc, size - FCAL_MAX_SEQUENTIAL_HEADERS,
                                     FFMIN(size, FCAL_MAX_SEQUENTIAL_HEADERS),
                                     *end_handle);
                    fpc->nb_headers_found++;
                    size++;
                    av_log(NULL,AV_LOG_DEBUG,"debug%i i %i\n",debug++, i++);
                    av_log(NULL,AV_LOG_DEBUG,"debug%i i %i\n",debug++, i++);
                    av_log(NULL,AV_LOG_DEBUG,"debug%i i %i\n",debug++, i++);
                }
            }
        }
        return size;
    }
    
    4 回复  |  直到 14 年前
        1
  •  3
  •   Hans Passant    15 年前

    有一个缓冲区,有一个缓冲区,有一些“缓冲工作”。在片段中都不可见。显然,有一些不可见的代码在缓冲区的末尾写入,从而导致局部变量被踩踏。 调试 . 在缓冲区端设置一个数据断点,通常在一两分钟内就能找到它。

        2
  •  2
  •   Clifford    15 年前

    对我来说,它看起来像递归/重入。以下假设将解释观察到的行为:

    do_some_buffer_work() 在某些情况下,调用包含此代码的函数。这将解释静态一致性和局部重启。你看到它的原因只是为了 i 是因为 something_is_true() 仅对以下值有效 .

    您可能应该在调试器中单步执行代码。观察printf()上断点处的调用堆栈将快速确定是否发生了递归。

        3
  •  1
  •   Michael Burr    15 年前

    这可能有帮助,也可能没有帮助(不知道 do_some_buffer_work() , something_is_true() do_something_important() 有点妨碍分析。但是可以尝试在不同的地方使用2个循环计数器变量,并检查它们是否不同:

    int i;
    static int ii;
    
    int debug = 0;
    for(i = 0, ii = 0; i < buf_end; i++, ii++) {
        if (i != ii) debugBreak();
    
        do_some_buffer_work();
        if (i != ii) debugBreak();
    
        if(something_is_true()) {
            if (i != ii) debugBreak();
             do_something_important();
             printf("debug is %i, i is %i", debug++, i);
        }
    
        if (i != ii) debugBreak();
    }
    

    这可能会让你更清楚地了解事情何时会变得糟糕。

        4
  •  0
  •   Michael Chinen    14 年前

    这个问题是因为用max(a,b)宏重新评估了一个副作用函数。 它在这个函数的客户机代码中。