-1

我正在使用 Rapidjson 并注意到当我在 g++ (-O1/-O2/-O3) 中打开优化时,我遇到了分段错误。我想我已经追踪到了 rapidjson 中的 GenericValue& AddMember() 函数。

GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) {
    RAPIDJSON_ASSERT(IsObject());
    RAPIDJSON_ASSERT(name.IsString());

    Object& o = data_.o;
    if (o.size >= o.capacity) {
        if (o.capacity == 0) {
            o.capacity = kDefaultObjectCapacity;
            o.members = reinterpret_cast<Member*>(allocator.Malloc(o.capacity * sizeof(Member)));
        }
        else {
            SizeType oldCapacity = o.capacity;
            o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5
            o.members = reinterpret_cast<Member*>(allocator.Realloc(o.members, oldCapacity * sizeof(Member), o.capacity * sizeof(Member)));
        }
    }
    o.members[o.size].name.RawAssign(name);
    o.members[o.size].value.RawAssign(value);
    o.size++;
    return *this;
}

调试时,我可以看到 kDefaultObjectCapacity ( 正在优化(这是一个静态的 const SizeType kDefaultObjectCapacity = 16)

因此行“o.capacity = kDefaultObjectCapacity;” 没有被执行,并且 malloc 正在分配 0 字节然后尝试强制转换它。

为什么要删除此静态常量?

我曾尝试使 Object& o 既易变又静态,但均未奏效。有任何想法吗?

谢谢威尔

编辑:我无法在嵌入式平台上轻松运行测试,rapidjson 目前是使用 buildroot 构建的。我尝试了单元测试,但无法让它们达到目标。

我可以看看提供程序集,但它是大型应用程序的一部分,因此可能很难找到合适的位。

有关信息,这是调用 rapidjson 代码的方法,这似乎是问题所在:

int16_t FrontEndJSONHandlers::get_run_cfg_packer(JSONEngine& json_engine, char *message, int32_t *length)
{
Document doc;

// Need to pack an empty request to get the data
doc.SetObject();
doc.AddMember(JSONRPC_MEMBER, JSONRPC_VERSION, doc.GetAllocator());
doc.AddMember(METHOD_MEMBER, JSON_RPC_METH_GET_RUN_CFG, doc.GetAllocator());
doc.AddMember(ID_MEMBER, json_engine.GetNextMessageID(), doc.GetAllocator());

// Format the message
json_engine.FormatMessageAndRegisterResponseHandler(&doc, &message, &length, get_run_cfg_handler);

return 0;
}

如果我将 Document doc 设为静态,它不会出现段错误 - 不确定这是否是最好的解决方法?

4

2 回答 2

1

我遇到了类似的问题,使用 GCC 4.9 编译:指定优化标志 (-On) 时出现分段错误。很容易复制,代码如下:

//...
#include "extern/rapidjson/document.h"
//...
void* MyThread(void*)
{
   std::cout << "Start of MyThread" << std::endl;

   rapidjson::Document doc;
   doc.Parse("{ \"NAME\": \"Peter\", \"AGE\": 38, \"Male\": true}");

   std::cout << "End!";

   return NULL;
}

int main(int argc, char** argv) 
{
   pthread_t thread;

   pthread_create(&thread, NULL, &MyThread, NULL);

   std::string str;
   std::cout << "Press <Return>"  << std::endl;
   std::getline(std::cin, str);

   return 0;
 }

这里的关键是在单独的线程中创建和解析的文档。如果我们直接在 main() 中调用“MyThread(NULL)”,我们不会得到任何错误。

Visual Studio 2012 的类似代码经过优化,运行良好。我运行了 VS 代码分析,没有发现错误。唯一的区别是线程:

std::thread th([] { MyThread(NULL); }); 
th.join();

================================================

该问题已在 1.0.2 版本之后得到修复。使用最新的代码可以正常工作。

于 2016-02-16T01:45:34.550 回答
1

您的代码看起来非常错误,并且以某种方式让我怀疑您也误解了您在调试器中看到的内容。先尝试修复错误,然后再担心优化器可能在做什么:

看来您的部分意图是 o.capacity 最终>(或者您可能打算>=) o.size

但是您是否考虑过o.size > kDefaultObjectCapacityo.capacity 从零开始的情况?更严重的是,对于降低容量的代码,您关于增加 1.5 倍的评论是错误的。你可能打算oldCapacity*3/2

if (o.size >= o.capacity) {
    if (o.capacity == 0) {
        o.capacity = kDefaultObjectCapacity;
        o.members = reinterpret_cast<Member*>(allocator.Malloc(o.capacity * sizeof(Member)));
    }
    else {
        SizeType oldCapacity = o.capacity;
        o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5
        o.members = reinterpret_cast<Member*>(allocator.Realloc(o.members, oldCapacity * sizeof(Member), o.capacity * sizeof(Member)));
    }
}

编辑:我对上述大部分内容都误解了。但我仍然认为对调试器中所见内容的误解更有可能解释为优化器破坏了该代码。

提到的“90”是十进制的,还是十六进制的?优化器当然可以跳过乘以sizeof(Member)直接得到该乘法的结果。

于 2015-09-23T13:44:11.160 回答