2013-03-22 108 views
2

我正在为小型计算机语言制作虚拟机。该虚拟机使用GNU实用程序Flex以C开发。因此,项目编译与GNU GCC然后是Flex。如何解决奇怪的更改值?

在此虚拟机中,我有一个GC Stop &复制。在我的更改之前,GC的工作内存无法扩大 - 例如,如果第一个翻转没有优化用于创建新分配的空间,则从512字节到1024字节。

此更改似乎工作。事实上,我不知道这些改变是否真的起作用,但现在我有一个错误。它刚刚出现在第一次翻转。事实上,当它复制数据,我有一个常数变化变量。但是这个变量很重要,因为它指向了我想要复制的项目。在Stop &复制中,此变量用于更改插槽(此处为SLOT_FORWARD),以通知内存中数据的新位置(如果我们仍将复制)。

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

因此,我花了很长时间调试很少发生这种情况的案例(发生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现在将访问随机数据)。在这个变量的变化过程中,我完全没有错误。

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

代码非常长(大约1000行),因为目标是在单个文件中运行虚拟机C.我很抱歉无法让代码更清晰。但是这个问题看起来很神奇,我无法走得更远,使未来的语言超越Python(笑话)。

库的链接是:git.osau.re
变化的链接是:ompldr

亲切的问候。

回答

3

追踪随机内存clobbers(这听起来像)是非常困难的。我的方法是找出编译器何时决定放置更改变量(可能位于堆栈的某处),然后设置该位置的观察点。这应该告诉你代码在哪里修改值 - 你可能会发现它在某个被称为函数的函数中,它正在逐步备份堆栈以对变量进行加密。那么你需要弄清楚为什么会发生这种情况。