添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement . We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

我的一个区块链项目正在使用你开发的rapidjson库, 在使用过程中,出现了一个段错误问题,我一直没有找着问题根源,简单表述下,在全局类Blockchain类中定义如下成员:

class Blochchain
void start();
void tst_doc();
rapidjson::Value m_lcdata;

当启动项目时,会从一个leveldb库中,读取一个json串,然后Parse解析到doc,最后需要将doc["data"]保存到 m_lcdata中:
m_lcdata = doc["data"];
赋完值后,如果紧接着在上面这句赋值下面,输出其内容,完全正常:
rapidjson::StringBuffer buffer;
rapidjson::Writerrapidjson::StringBuffer writer(buffer);
m_lcdata.Accept(writer);
LOG_DEBUG_INFO("tst_doc........, lcdata: %s", buffer.GetString());

可以正常打印出以下内容:
tst_doc........, lcdata:{"id":28,"utc":1540720518,"version":1,"zero_bits":12,"pre_hash":"AAcTmeq8lp9eH5GE9mgl/OBYxI809V6Zl4fTTqw/aTE=","miner":"BH6PNUv9anrjG9GekAd+nus+emyYm1ClCT0gIut1O7A3w6uRl7dAihcD8HvKh+IpOopcgQAzkYxQZ+cxT+32WdM=","tx_ids":[],"nonce":[0,0,0,748]}

但是,当前启动过程结束后,在另一个函数比如 tst_doc() 中再次直接打印该m_lcdata内容时(此时不再赋值,因为之前在start()中已对m_lcdata赋过值了),报段错误,gdb堆栈信息:
[root@localhost tst1]#
[root@localhost tst1]# gdb askcoin
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-100.el7_4.1
License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html ;
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
http://www.gnu.org/software/gdb/bugs/ ;...
Reading symbols from /root/repos/askcoin/build/bin/askcoin...done.
(gdb) r
Starting program: /root/repos/askcoin/build/bin/tst1/askcoin
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Detaching after fork from child process 37285.
2018-10-29 10:02:17.668145 [utc:1540778537] |info| src/askcoin.cpp:132 start askcoin, version: 0.0.1, verno: 1, git_sha1:
2018-10-29 10:02:17.668272 [utc:1540778537] |info| src/askcoin.cpp:53 Using the 'standard' SHA256 implementation
2018-10-29 10:02:17.732174 [utc:1540778537] |info| src/blockchain.cpp:1521 verify asic_resistant_data ok
2018-10-29 10:02:17.763468 [utc:1540778537] |info| src/blockchain.cpp:1644 genesis block: {"data":{"id":0,"utc":1518926400,"version":10000,"zero_bits":0,"intro":"This coin is a gift for those who love freedom.","init_account":{"account":"lichuan","id":1,"avatar":1,"pubkey":"BH6PNUv9anrjG9GekAd+nus+emyYm1ClCT0gIut1O7A3w6uRl7dAihcD8HvKh+IpOopcgQAzkYxQZ+cxT+32WdM="},"author":{"name":"Chuan Li","country":"China","github":" https://github.com/lichuan","mail":"[email protected]","belief":"In the beginning, God created the heavens and the earth."}},"children":["OozHT+JRJcqvik0U44kavezGasWN1wHQ8jet2QeuFPg="],"hash":"00IU34oWhhi75KhL3E8k0IueYCrseZ50Dzg+mHkc8/4=","sign":"MEQCIEtAgYI0zpVZFyQZGpJj3AcAGTrPGIC7J4r682oND2MaAiBs3N62gRc/mOMEA3tPdQfi2vquHTQzNkMbVB/8iGeQ7g=="}
2018-10-29 10:02:18.189280 [utc:1540778538] |info| src/blockchain.cpp:3000 load block finished, zero_bits: 12, cur_block_id: 28, cur_block_hash: AABlvFN34P3FF4KDkreGCRxlrMg6sQynXeRblluBQMc= (hex: 000065bc5377e0fdc517828392b786091c65acc83ab10ca75de45b965b8140c7)
2018-10-29 10:02:18.189444 [utc:1540778538] |info| src/blockchain.cpp:3025 peer score data from leveldb: {"utc":1540729140,"peers":[{"host":"192.168.0.122","port":18051,"score":999997095}]}
[New Thread 0x7ffff535f700 (LWP 37286)]
[New Thread 0x7ffff4b5e700 (LWP 37287)]
[New Thread 0x7ffff435d700 (LWP 37288)]

Program received signal SIGSEGV, Segmentation fault.
WriteString (length=4294967251, str=0x1d4a410 "u", this=0x7fffffffd5f0) at depend/fly/depend/rapidjson/include/rapidjson/writer.h:400
400 const Ch c = is.Peek();
Missing separate debuginfos, use: debuginfo-install glibc-2.17-196.el7_4.2.x86_64 libgcc-4.8.5-16.el7_4.2.x86_64 libstdc++-4.8.5-16.el7_4.2.x86_64 openssl-libs-1.0.2k-8.el7.x86_64 zlib-1.2.7-17.el7.x86_64
(gdb) bt
#0 WriteString (length=4294967251, str=0x1d4a410 "u", this=0x7fffffffd5f0) at depend/fly/depend/rapidjson/include/rapidjson/writer.h:400
#1 String (copy=, length=4294967251, str=0x1d4a410 "u", this=0x7fffffffd5f0) at depend/fly/depend/rapidjson/include/rapidjson/writer.h:208
#2 Key (copy=, length=, str=, this=0x7fffffffd5f0) at depend/fly/depend/rapidjson/include/rapidjson/writer.h:223
#3 rapidjson::GenericValue<rapidjson::UTF8, rapidjson::MemoryPoolAllocatorrapidjson::CrtAllocator >::Accept<rapidjson::Writer<rapidjson::GenericStringBuffer<rapidjson::UTF8, rapidjson::CrtAllocator>, rapidjson::UTF8, rapidjson::UTF8, rapidjson::CrtAllocator, 0u> > (
this=0x1cf07d0 <fly::base::Singleton::instance()::instance+1904>, handler=...) at depend/fly/depend/rapidjson/include/rapidjson/document.h:1851
#4 0x000000000041f394 in Blockchain::tst_doc (this=0x1cf0060 <fly::base::Singleton::instance()::instance>) at src/blockchain.cpp:1473
#5 0x000000000040a536 in Askcoin::main (this=) at src/askcoin.cpp:184
#6 0x00007ffff6b9cc05 in __libc_start_main () from /lib64/libc.so.6
#7 0x000000000040c83d in _start ()
(gdb)

上面的堆栈中,src/blockchain.cpp:1473 附近的代码是:

void Blockchain::tst_doc()
LOG_DEBUG_INFO("tst_doc begin......................");

rapidjson::StringBuffer buffer; rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); m_lcdata.Accept(writer); LOG_DEBUG_INFO("tst_doc........, lcdata: %s", buffer.GetString());

1473行,对应这句代码: m_lcdata.Accept(writer);
按我的理解,m_lcdata在start()启动时,赋值完的,可以一直有效才对,为什么在另一个tst_doc中却无效?
刚开始我还认为是原始的doc造成, 因为在start()中, doc局部变量是栈上: rapidjson::Document doc; doc.Parse(json_string);
后来,我修改为如下: std::shared_ptrrapidjson::Document doc_ptr = std::make_sharedrapidjson::Document();
auto &doc = *doc_ptr;
这样,doc就是直接由堆上来分配, 但上面的段错误问题依旧存在,只要在另一个函数中,打印该m_lcdata就会出错,百思不得其解,请求rapidjson的原作者来答疑, 万分感谢!!!

你的 shared_ptr 也是定义成局部变量么?是的话同样会有这个问题。

我看过一些rapidjson的代码,如果将doc["data"]赋值给,成员变量m_lcdata, 就算原来的doc释放了,但m_lcdata这个成员变量还存在,所以按理不该释放, 以下是rapidjson::value的operator=的定义:
GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT {
RAPIDJSON_ASSERT(this != &rhs);
this->~GenericValue();
RawAssign(rhs);
return *this;

而RawAssign定义如下:
//! Assignment without calling destructor
void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT {
data_ = rhs.data_;
// data_.f.flags = rhs.data_.f.flags;
rhs.data_.f.flags = kNullFlag;

所以,在启动时, doc["data"]会赋值到m_lcdata, 相当于它的data_也相应的赋值给了m_lcdata

https://github.com/Tencent/rapidjson/blob/master/doc/tutorial.md#deep-copy-value-deepcopyvalue

Thanks, I have seen, but it's better if rapidjson value support ref count.

RapidJson在解析方面做了大量的优化,因此在使用中也引入了一些不便和限制,这是一种权衡。 比如Document中包含的分配器就是一例,分配内存时,Allocator内部先获取大块内存,然后再小块使用,通常比直接调用运行库的new快一些,但是Allocator析构时,内部的内存就必须整块释放。因此在释放Document时,所有值都无效了。

也许这更适合你的场景:使用RapidJson解析,但是用其他具有完全值语义的Json库的来存储所需的字段。

RapidJson在解析方面做了大量的优化,因此在使用中也引入了一些不便和限制,这是一种权衡。 比如Document中包含的分配器就是一例,分配内存时,Allocator内部先获取大块内存,然后再小块使用,通常比直接调用运行库的new快一些,但是Allocator析构时,内部的内存就必须整块释放。因此在释放Document时,所有值都无效了。

也许这更适合你的场景:使用RapidJson解析,但是用其他具有完全值语义的Json库的来存储所需的字段。

也许是rapidjson为了极致性能,放弃了易用性吧,我的askcoin项目在很多地方都应用了rapidjson,当初也是看重它的运行效率,后期会开源,也感谢各位的答疑!