1

我正在开发一个实现引用计数系统的库。在调试模式下,我打印出何时分配和删除值。编译测试程序并运行后,它并没有真正运行,而是实际上删除了自己并留下了一个stackdump文件。有趣的是,这只发生在我使用一个像基于范围的引用管理器一样实现的小型帮助模板类时(当对象帮助对象被销毁时减少引用计数)。

甚至 GDB 都无法帮助我,因为当我指示它运行时main.exe,它说它无法打开进程。

描述

因此,首先,我查看了导致所描述行为的主程序。

// snippet 1
void test1() {
    Handle<String> v = String::New("Fooo!");
    std::cout << v->Get() << "\n";
}

将其更改为以下内容会使程序再次运行:

// snippet 2
void test1() {
    String* v = String::New("Fooo!");
    std::cout << v->Get() << "\n";
    v->Decref();
}

但我知道Handle<T>该类实际上运行良好,因为以下函数再次运行良好,没有问题或未释放的值。(@agbinfo)String该类是Value. 该Handle模板旨在与该类一起使用,因此当不再需要该值时Value无需手动调用。Value::Decref()

// snippet 3
Value* test2() {
    Handle<HashMap> map = HashMap::New();
    Handle<Value> a = String::New("The Answer");
    Handle<Value> b = Int::New(42);
    map->Put(a, b);

    // It's easier to use the static getter functions when
    // using the Handle helper class because it makes headache
    // to cast them.
    a = map->AsString();
    std::cout << String::Get(a) << "\n";

    // Overwrite the existing entry in the map.
    a = String::New("The Answer");
    b = vv::null();
    map->Put(a, b);

    // But we could also get an actual pointer to the String. We
    // just have to make sure to decrease the reference count as
    // we don't have the Handle helper class that does that based
    // on the scope.
    String* str = map->AsString();
    std::cout << str->Get() << "\n";
    str->Decref();

    // The ownership of the returned Value* is transfered to the
    // caller with Handle<T>::Release()
    return a.Release();
}

有趣的是,当我替换return a.Release();intest2()return nullptr;invoke test1()but never ever test2()时,程序再次运行良好。

我恢复到程序崩溃的状态,并试图在代码更深的地方找到问题。我查看了vv::Value创建或删除对象时完成的日志记录。从中删除utils::typestr(Type())零件vv::Value::Free()会使程序再次运行!

class Value {

protected:

    /**
     * Must be invoked when an object was allocated and
     * constructed.
     */
    virtual void Init()
    {
        #ifdef VV_DEBUG
        if (vv::GetFlag(SystemFlags_LogGeneration))
            logging::LogValue(this, "Created " + utils::typestr(Type()));
        #endif
    }

    /**
     * Is invoked when the object is about to be deallocated
     * from vv::Value::Decref().
     */
    virtual void Free()
    {
        #ifdef VV_DEBUG
        if (vv::GetFlag(SystemFlags_LogGeneration))
            logging::LogValue(this, "Deleted " /* + utils::typestr(Type()) */);
        #endif
    }

public:

    // ...

};

现在,在 中vv::utils::typestr(),我通过将其字节解释为字符来将整数转换为字符串,因为可以从中返回的值vv::Value::Type()被声明为

enum {
    Type_Null = 'null',
    Type_Bool = 'bool',
    Type_Int = 'intg',
    Type_Double = 'dble',
    Type_String = 'strn',
    Type_Array = 'arry',
    Type_List = 'list',
    Type_HashMap = 'hmap',
};

所以代码typestr()

inline std::string typestr(int id)
{
    const char* buffer = reinterpret_cast<const char*>(&id);
    std::string result = "";
    for (size_t i=0; i < sizeof(int); i++) {
        result.push_back(buffer[i]);
    }

    // Reverse the string if we are on a little-endian system.
    if (vv::utils::bigendian()) {
        std::reverse(result.begin(), result.end());
    }
    return result;
}

堆栈转储

对于stackdump,不幸的是它不是每次都生成的,我现在无法重现它以获得最近的stackdump。但是其中一个生成的文件看起来很像这样:

Exception: STATUS_ACCESS_VIOLATION at eip=004022A8
eax=0028ABE4 ebx=0028AC6C ecx=00000001 edx=0000001C esi=00000000 edi=610071D0
ebp=0028AC48 esp=0028AC20 program=C:\Users\niklas\Desktop\cpp-vv\main.exe, pid 6412, thread main
cs=0023 ds=002B es=002B fs=0053 gs=002B ss=002B
Stack trace:
Frame     Function  Args
0028AC48  004022A8 (00000001, 0028AC6C, 80010100, 61007FDA)
0028ACF8  61008039 (00000000, 0028CD84, 610071D0, 00000000)
0028CD58  61005E84 (0028CD84, 00000000, 00000000, 00000000)
0028FF28  61005FF6 (610071D0, 00000000, 00000000, 00000000)
0028FF48  61006F54 (00402225, 00000000, 00000000, 00000000)
0028FF68  00402592 (00402225, 00000000, 00000000, 00000000)
0028FF88  00401015 (FFFDE000, 0028FFD4, 77C49F72, FFFDE000)
0028FF94  773F336A (FFFDE000, 76B6F19F, 00000000, 00000000)
0028FFD4  77C49F72 (00401000, FFFDE000, 00000000, 00000000)
0028FFEC  77C49F45 (00401000, FFFDE000, 00000000, 78746341)
End of stack trace

编译输出

g++ -std=c++11 -g -Wno-multichar -Iinclude/ -DVV_DEBUG src/main.cpp -c -oobj/src/main.o
g++ -std=c++11 -g -Wno-multichar -Iinclude/ -DVV_DEBUG src/vv/system.cpp -c -oobj/src/vv/system.o
g++ -std=c++11 -g -Wno-multichar -Iinclude/ -DVV_DEBUG src/vv/logging.cpp -c -oobj/src/vv/logging.o
g++ -std=c++11 -g -Wno-multichar -Iinclude/ -DVV_DEBUG src/vv/Types.cpp -c -oobj/src/vv/Types.o
g++ -std=c++11 -g -Wno-multichar -Iinclude/ -DVV_DEBUG src/vv/Value.cpp -c -oobj/src/vv/Value.o
g++ -Wno-multichar -Iinclude/ -DVV_DEBUG obj/src/main.o obj/src/vv/system.o obj/src/vv/logging.o obj/src/vv/Types.o obj/src/vv/Value.o -o main.exe

海合会详细信息

Windows 7 64 位上的 Cygwin

g++ (GCC) 4.8.2
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

到目前为止,这似乎使程序运行。但是你知道这个问题可能来自哪里吗?就我而言,我无法在typestr(). 也许作为经验丰富的开发人员,您可以识别并告诉我一些模式?

4

1 回答 1

4

只有在非常特殊的、看似任意的情况下,您的防病毒软件的检测启发式可能会错误地将您的程序识别为病毒。尝试暂时禁用实时系统保护。

于 2014-04-09T12:06:59.147 回答