棧溢出問題的調查

上週在進行壓測時,某個調用hiredis庫的函數出現了coredump,調用棧以下:git

Program terminated with signal 11, Segmentation fault.
#0  0x000000000052c497 in wh::common::redis::RedisConn::HashMultiGet(std::string const&, std::vector<std::string, std::allocator<std::string> > const&, std::map<std::string, std::string, std::less<std::string>, std::allocator<std::pair<std::string const, std::string> > >&) ()
(gdb) bt
#0  0x000000000052c497 in wh::common::redis::RedisConn::HashMultiGet(std::string const&, std::vector<std::string, std::allocator<std::string> > const&, std::map<std::string, std::string, std::less<std::string>, std::allocator<std::pair<std::string const, std::string> > >&) ()
#1  0x00000000004cc418 in wh::server::user_activityHandler::getUserRating(wh::server::GetUserRatingResult&, std::vector<int, std::allocator<int> > const&) ()
#2  0x00000000004e54cf in wh::server::user_activityProcessor::process_getUserRating(int, apache::thrift::protocol::TProtocol*, apache::thrift::protocol::TProtocol*, void*) ()
#3  0x00000000004e3ad3 in wh::server::user_activityProcessor::dispatchCall(apache::thrift::protocol::TProtocol*, apache::thrift::protocol::TProtocol*, std::string const&, int, void*) ()

RedisConn中的HashMultiGet代碼以下:github

int RedisConn::HashMultiGet(
       const string&         key,
       const vector<string>& fields,
       map<string, string>&  fvs)
 {
   if(key.empty() || fields.empty())
     return 0;

   if ( !conn_ )
   {
     LOG(LOG_ERR, "ERROR!!! conn is NULL!!!");
     return kErrConnBroken;
   }

   size_t argc = fields.size() + 2;
   const char* argv[argc]; //在棧中直接分配內存
   size_t argvlen[argc];

   std::string cmd = "HMGET";
   argv[0]    = cmd.data();
   argvlen[0] = cmd.length();
   argv[1]    = key.data();
   argvlen[1] = key.length();
   size_t i = 2;
   for(vector< string >::const_iterator cit = fields.begin();
         cit != fields.end(); ++cit )
   {
     // put value into arg list
     argv[i] = cit->data();
     argvlen[i] = cit->length();
     ++i;
   }

   redisReply* reply = static_cast<redisReply*>( redisCommandArgv( conn_, argc, argv, argvlen ) );
   if ( !reply )
   {
     this->Release();
     LOG(LOG_ERR, "ERROR!!! Redis connection broken!!!");
     return kErrConnBroken;
   }

   int32_t ret = kErrOk;
   if ( reply->type != REDIS_REPLY_ARRAY )
   {
     this->CheckReply( reply );
     LOG(LOG_ERR, "RedisReply ERROR: %d %s", reply->type, reply->str);
     ret = kErrUnknown;
   }
...

其中出現問題的地方是構造hiredisredisCommandArgv請求時,構造的兩個參數都是直接在棧上分配。redis

const char* argv[argc]; //在棧中直接分配內存
   size_t argvlen[argc];

壓測時,HashMultiGet(key, fields, fvs)fields大小超過10萬,那麼在棧上分配的內存爲 10萬 * (8 + 8) = 160萬字節 = 1.6MB (64位系統),再加上以前分配的棧,將棧打爆了,致使了coredump.apache

爲何要將參數在棧上分配呢?一種多是:若是在堆上分配,就須要考慮free的問題。less

解決方法:
將argv和argvlen在堆上分配,畢竟堆的大小比棧大不少。函數

相關文章
相關標籤/搜索