2

我正在为一种小型计算机语言制作虚拟机。该虚拟机是使用 GNU 实用程序 Flex 用 C 语言开发的。因此,项目编译是使用 GNU GCC,然后是 Flex。

在这个虚拟机中,我有一个 GC Stop & Copy。在我更改之前,GC 的工作内存无法扩大 - 例如,如果第一次翻转没有优化用于进行新分配的空间,则从 512 字节变为 1024 字节。

这种改变似乎奏效了。事实上,我不知道自从这些更改后它是否真的有效,但现在我有一个错误。它刚刚出现在第一个翻转中。事实上,当涉及到复制数据时,我有一个不断变化的变量。但是这个变量很重要,因为它指向我要复制的项目。在 Stop & Copy 中,此变量用于更改插槽(此处SLOT_FORWARD)以通知数据在内存中的新位置(以防我们仍然要复制)。

所以我有一个循环,它复制前一个容器的每个盒子,其在内存中的位置由变量指定old。我有一个从addr位置填充的新容器。但是旧值在迭代过程中发生了变化!并且在复制之后,我想将插槽向前更改以放置新容器的地址。但是随着 old 的变化,你可以想象我在错误的地方记录了这个值。

所以我花了很长时间调试这种情况很少发生的案例(它在 2 次翻转后发生了 3 到 4 个容器)。我使用 GDB 来了解我的一个调试函数中更改的值(同时她也通过添加调试函数进行了修改)。然后我更改了编译器(clang 到 gcc)以重新启动 GDB,并看到它是一个大括号字符(仍在调试函数中)改变了值......最后,我const尽可能地为所有函数设置了所有参数,现在我被告知文件iofwrite.c第 37 行中的值已更改。因此这是另一个世界的错误。

有问题的代码在哪里出现错误:

static t_case
copy(t_dono *dono, const t_case old)
{
  t_case  addr;
  t_case  size;
  t_case  temp;
  int     i;

  temp = old;

  if (mem[old + SLOT_FORWARD] >= ns
      && mem[old + SLOT_FORWARD] <= ts)
    return (mem[old + SLOT_FORWARD]);
  else
    {
      addr = mp;
      size = mem[old + SLOT_SIZE];
      i = 0;

      fprintf(stderr, "change:\t");
      dump(stderr, mem, old);

      assert(old == temp);

      while (i < size)
        {
          fprintf(stderr, "!!!COPY:\t");
          dump(stderr, mem, old);
          assert(old == temp);
          mem[addr + i] = mem[old + i]; /* BUG IS HERE */
          i = i + 1;
        }

      mem[old + SLOT_FORWARD] = addr;
      fprintf(stderr, "change:\t");
      dump(stderr, mem, old);
      assert(old == temp);
      mp = mp + size;

      return (addr);
    }
}

正如你所看到的,我做了很多调试来定位错误,我得到了这个日志文件:

ref:            [ 0005 0001 0003 0004 0035 ]
copy:           [ 0007 0001 0003 0004 0075 0001 00f9 ]
change:         [ 0007 0001 0003 0004 0075 0001 00f9 ]
!!!COPY:        [ 0007 0001 0003 0004 0075 0001 00f9 ]
!!!COPY:        [ 0007 0001 0003 0004 0075 0001 00f9 ]
!!!COPY:        [ 0007 0001 0003 0004 0075 0001 00f9 ]
!!!COPY:        [ 0003 0001 0003 ]
!!!COPY:        [ 0003 0004 0003 ]
!!!COPY:        [ 0003 0004 0075 ]
!!!COPY:        [ 0003 0004 0075 ]
change:         [ 0003 0033 0075 ]

我还使用了 Valgrind,它告诉我有很多错误,但只是在这个错误之后(这是正常的,因为 GC 现在将访问随机数据)。在此变量更改期间,我绝对没有错误。

我们可以看到通过函数 copy (l: 662) 的其他容器没有得到这种未定义的行为(请参阅第 10、48、54、66、82、120、126 和 134 行的日志文件)。只有在执行时一切都会出错,这当然会导致所有 GC 数据出错。

代码真的很长(大约 1000 行),因为目标是在单个文件 C 中运行 VM。很抱歉,我无法使代码更清晰。但问题只是神奇地出现了,我无法更进一步,制作出超越 Python 的未来语言(笑话)。

存储库的链接是: git.osau.re
更改的链接是:ompldr

亲切的问候。

4

1 回答 1

3

追踪随机内存破坏者(听起来就是这样)非常困难。我的方法是确定编译器何时决定将更改的变量(可能在堆栈上的某个位置)放置,然后为该位置设置一个观察点。这应该告诉您代码在哪里修改值——您可能会发现它在某个被调用的函数中,该函数正在向后退栈以踩到您的变量。然后你需要弄清楚为什么会这样。

于 2013-03-22T21:38:29.967 回答