curl 사용 시 mem leak 관련

zer0boy의 이미지

안녕하세요.

몇주 동안 혼자 씨름하다 혹시나 여기에 제가 해결하지 못한 문제 해결해 주실 분이 있으실까 해서 문의 남깁니다.

C로 소스가 구성되어 있구요..
curl 로 https 통신해서 결과 받아오는 프로그램 입니다.
서버, curl / openssl 버전은 아래와 같습니다.
- server : solaris 10
- cURL : 7.64.1
- OpenSSL : 1.0.2R

통신 자체는 크게 문제가 없는데 memory leak 이 발생을 하고 있습니다.
아주 서서히 증가하는게 아니라 큰 폭으로 memory가 증가하여 프로세스 정상 구동에도 문제가 되는 상태입니다.
이런 식으로 계속 돌다가 보면 프로세스가 죽는 현상도 있는데 이때 생기는 core 파일 내용은 아래 확인 부탁 드립니다.

#0 0xfefb84d8 in Curl_hash_clean_with_criterium () from /usr/local/lib/libcurl.so.4
#1 0xfef92b24 in hostcache_prune () from /usr/local/lib/libcurl.so.4
#2 0xfef92b80 in Curl_hostcache_prune () from /usr/local/lib/libcurl.so.4
#3 0xfefb8e04 in multi_done () from /usr/local/lib/libcurl.so.4
#4 0xfefbab1c in multi_runsingle () from /usr/local/lib/libcurl.so.4
#5 0xfefbade8 in curl_multi_perform () from /usr/local/lib/libcurl.so.4
#6 0xfefb40b8 in easy_transfer () from /usr/local/lib/libcurl.so.4
#7 0xfefb4254 in easy_perform () from /usr/local/lib/libcurl.so.4

가장 최근 core 파일인데 거의 대부분 easy_perform 호출 후 signal을 받습니다.
대략적인 소스는 형태는 multi thread로 되어 있으며 간단히 소스는 아래에 붙이겠습니다.

struct MemoryStruct
{
    char *memory;
    size_t size;
};
 
size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
{
    size_t realsize = size * nmemb;
    struct MemoryStruct *mem = (struct MemoryStruct *)data;
 
    mem->memory = (char *)myrealloc(mem->memory, mem->size + realsize + 1);
    if (mem->memory)
    {
        memcpy(&(mem->memory[mem->size]), ptr, realsize);
        mem->size += realsize;
        mem->memory[mem->size] = 0;
    }
    return realsize;
}
 
void *myrealloc(void *ptr, size_t size)
{
    if(ptr)
        return realloc(ptr, size);
    else
        return malloc(size);
}
 
// 실제 소스에서 curl 부분만 대략적으로 가져온 부분 입니다.
.........................................................
    curl = curl_easy_init();
    {
        printf("curl_easy_init() Fail\n");
        return(-100);
    }
    else
    {
        headerList = curl_slist_append(headerList, headerContentType);
        ..........................
        ..........................
 
        jsonReq = cJSON_CreateObject();
        category = cJSON_CreateArray();
 
        //set jsonReq
        cJSON_AddStringToObject(jsonReq, "flag", "0");
        cJSON_AddStringToObject(jsonReq, "type", "1");
        ..........................
        ..........................
        szReq = cJSON_PrintUnformatted(jsonReq);
        cJSON_Delete(jsonReq);
 
        curl_easy_setopt(curl, CURLOPT_URL, URL);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);     
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);     
        curl_easy_setopt(curl, CURLOPT_POST, 1L);               
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerList); 
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, szReq);      
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);      
        curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1);        
        curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);           
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
 
        CURLcode res = curl_easy_perform(curl);
 
        if(res != CURLE_OK || chunk.size == 0)
        {
            ..........................
            ..........................
        }
        curl_easy_cleanup(curl);
 
        if (szReq)
            free(szReq);
 
        free(chunk.memory);
        chunk.memory = NULL ;
        chunk.size   = 0;
.........................................................

위 소스는 각 thread에서 구동이 되는 부분 입니다.
curl이나 openssl 구글링을 해보니까 mem leak 관련 글들은 많은데 해결책이 정확히 보이지 않아
글을 남깁니다.
혹시 curl쪽 잘 아시는 분 있으시면 한번 확인 부탁 드립니다.

그럼 이만 줄이겠습니다.

yhsuk의 이미지

curl을 잘 아는 건 아니지만, 다음 3가지가 좀 의심되네요. 점검해 보세요.
(curl이 잘 사용해도 leak이 있는 경우가 있긴 합니다만...)

1. slist에 대해서 다음이 불려지고 있는지?

curl_slist_free_all(slist)

2. 이 if문 안에서 혹시 리소스 해제를 하지 않고 return 하진 않는지?

        if(res != CURLE_OK || chunk.size == 0)
        {
            ..........................
            ..........................
        }

3. 쓰레드 사용을 시작 하기 전에 main에서 다음을 호출하고 있는지 (한번만 호출되도록)

curl_global_init();

Signature :) - "여유를 갖고 행동하되 게을러지지 말자"

zer0boy 의 이미지

답변이 늦어 죄송합니다.
제가 만든 소스에서 얘기하신 1번, 2번은 문제가 없어 보입니다.
1번 같은 경우 호출 하고 있고 2번의 경우 해당 경우에 리소스 해제하고 있습니다.
(현재 테스트에서는 if문에 걸리지 않고 무조건 성공 처리되도록 해서 테스트하고 있습니다.)
3번의 경우 제가 알기로는 curl_easy_init 호출 시 내부에서 global_init이 호출 되는 것으로 알고 있는데요..
multi thread 의 경우 꼭 curl_global_init을 호출해줘야 하는 건가요??

yhsuk의 이미지

https://curl.haxx.se/libcurl/c/curl_easy_init.html
https://m.blog.naver.com/PostView.nhn?blogId=milennium9&logNo=20155299555&proxyReferer=https%3A%2F%2Fwww.google.com%2F

여길 읽어보시면 자동으로 호출해 주긴 하는데 curl_global_init이 thread-safe가 아니어서 치명적이라고 되어 있습니다.
쓰레드들이 시작되더라도 최초에는 한 쓰레드만 curl_easy_init을 호출하게 되면 이 다음부터는 문제가 아니겠지요.
하지만 만일 쓰레드들이 시작되었을 때 처음부터 쓰레드들이 curl_easy_init을 동시에 호출할 가능성이 있으면 문제일 수 있습니다.

Signature :) - "여유를 갖고 행동하되 게을러지지 말자"

Polar11111의 이미지

저도 curl 개발 중에 비슷한 문제가 있는데... 혹시 해결하셨나요..?

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.