valgrind обнаруживает утечку памяти после вызова asprintf даже при вызове free

Мой код выглядит следующим образом:

bool
amflinkd_apteryx_set_action (const char *address, char *action)
{
    bool ret = false;
    char *path = NULL;

    if (asprintf (&path, "%s/%s", AMFLINKD_IP_ADDRESS_PATH, address) > 0)
    {
        ret = apteryx_set_string (path, "action", action);
    }

    free (path);
    return ret;
}

Затем я запускаю свой модульный тест на нем, который выполняет valgrind на нем. Мой выход:

np: running: "amflinkd_apteryx_unit_tests.amflinkd_apteryx_set_action_true"
EVENT SLMATCH err: SET: Not initialised
at 0x805B512: np::spiegel::describe_stacktrace (np/spiegel/spiegel.cxx)
by 0x804BAD3: np::event_t::with_stack (np/event.cxx)
by 0x8067688: np::mock_syslog (isyslog.c)
by 0x8079EA8: np::spiegel::platform::intercept_tramp (np/spiegel/platform/linux_x86.cxx)
by 0x412BC71:

==4== 33 bytes in 1 blocks are definitely lost in loss record 323 of 525
==4==    at 0x402B19B: malloc (vg_replace_malloc.c:299)
==4==    by 0x47ECAA7: vasprintf (vasprintf.c:73)
==4==    by 0x47CFEEA: asprintf (asprintf.c:35)
==4==    by 0x804B392: amflinkd_apteryx_set_action (amflinkd_apteryx.c:33)
==4==    by 0x804B28F: test_amflinkd_apteryx_set_action_true (amflinkd_apteryx_unit_tests.c:38)
==4==    by 0x805B0F1: np::spiegel::function_t::invoke(std::vector<np::spiegel::value_t, std::allocator<np::spiegel::value_t> >) const (spiegel.cxx:606)
==4==    by 0x804F68A: np::runner_t::run_function(np::functype_t, np::spiegel::function_t*) (runner.cxx:526)
==4==    by 0x804FEF6: np::runner_t::run_test_code(np::job_t*) (runner.cxx:650)
==4==    by 0x805019D: np::runner_t::begin_job(np::job_t*) (runner.cxx:710)
==4==    by 0x804E694: np::runner_t::run_tests(np::plan_t*) (runner.cxx:147)
==4==    by 0x8050374: np_run_tests (runner.cxx:822)
==4==    by 0x804BA7D: main (main.c:108)
==4== 
{
   <insert_a_suppression_name_here>
   Memcheck:Leak
   match-leak-kinds: definite
   fun:malloc
   fun:vasprintf
   fun:asprintf
   fun:amflinkd_apteryx_set_action
   fun:test_amflinkd_apteryx_set_action_true
   fun:_ZNK2np7spiegel10function_t6invokeESt6vectorINS0_7value_tESaIS3_EE
   fun:_ZN2np8runner_t12run_functionENS_10functype_tEPNS_7spiegel10function_tE
   fun:_ZN2np8runner_t13run_test_codeEPNS_5job_tE
   fun:_ZN2np8runner_t9begin_jobEPNS_5job_tE
   fun:_ZN2np8runner_t9run_testsEPNS_6plan_tE
   fun:np_run_tests
   fun:main
}
==4== 40 bytes in 1 blocks are definitely lost in loss record 335 of 525
==4==    at 0x402B19B: malloc (vg_replace_malloc.c:299)
==4==    by 0x47ECAA7: vasprintf (vasprintf.c:73)
==4==    by 0x47CFEEA: asprintf (asprintf.c:35)
==4==    by 0x412D10B: apteryx_cas_string (apteryx.c:530)
==4==    by 0x412D1BC: apteryx_set_string (apteryx.c:544)
==4==    by 0x804B3B0: amflinkd_apteryx_set_action (amflinkd_apteryx.c:35)
==4==    by 0x804B28F: test_amflinkd_apteryx_set_action_true (amflinkd_apteryx_unit_tests.c:38)
==4==    by 0x805B0F1: np::spiegel::function_t::invoke(std::vector<np::spiegel::value_t, std::allocator<np::spiegel::value_t> >) const (spiegel.cxx:606)
==4==    by 0x804F68A: np::runner_t::run_function(np::functype_t, np::spiegel::function_t*) (runner.cxx:526)
==4==    by 0x804FEF6: np::runner_t::run_test_code(np::job_t*) (runner.cxx:650)
==4==    by 0x805019D: np::runner_t::begin_job(np::job_t*) (runner.cxx:710)
==4==    by 0x804E694: np::runner_t::run_tests(np::plan_t*) (runner.cxx:147)
==4== 
{
   <insert_a_suppression_name_here>
   Memcheck:Leak
   match-leak-kinds: definite
   fun:malloc
   fun:vasprintf
   fun:asprintf
   fun:apteryx_cas_string
   fun:apteryx_set_string
   fun:amflinkd_apteryx_set_action
   fun:test_amflinkd_apteryx_set_action_true
   fun:_ZNK2np7spiegel10function_t6invokeESt6vectorINS0_7value_tESaIS3_EE
   fun:_ZN2np8runner_t12run_functionENS_10functype_tEPNS_7spiegel10function_tE
   fun:_ZN2np8runner_t13run_test_codeEPNS_5job_tE
   fun:_ZN2np8runner_t9begin_jobEPNS_5job_tE
   fun:_ZN2np8runner_t9run_testsEPNS_6plan_tE
}
EVENT VALGRIND 73 bytes of memory leaked


EVENT VALGRIND 2 unsuppressed errors found by valgrind


FAIL amflinkd_apteryx_unit_tests.amflinkd_apteryx_set_action_true

Первая утечка, предположительно, указатель «путь», но я освобождаю его перед возвращением.
Я понятия не имею, что это за вторая утечка.

Это функция модульного теста, вызывающая функцию amflinkd_apteryx_set_action() :

void
test_amflinkd_apteryx_set_action_true (void)
{
    printf("Running test_amflinkd_apteryx_set_action_true test.");
    const char *address = {"192.168.1.5"};
    char *action = {"drop"};
    bool res = amflinkd_apteryx_set_action (address, action);
    NP_ASSERT_TRUE (res);
}

Вот код для apteryx_set_string()

bool
apteryx_cas_string (const char *path, const char *key, const char *value, uint64_t ts)
{
    char *full_path;
    size_t len;
    bool res = false;

    /* Create full path */
    if (key)
        len = asprintf (&full_path, "%s/%s", path, key);
    else
        len = asprintf (&full_path, "%s", path);
    if (len)
    {
        res = apteryx_cas (full_path, value, ts);
        free (full_path);
    }
    return res;
}

bool
apteryx_set_string (const char *path, const char *key, const char *value)
{
    return apteryx_cas_string (path, key, value, UINT64_MAX);
}

I retried my amflinkd_apteryx_set_action():

bool
amflinkd_apteryx_set_action (const char *address, char *action)
{
    bool ret = false;
    char *path = NULL;

    if (asprintf (&path, "%s/%s", AMFLINKD_IP_ADDRESS_PATH, address) > 0)
    {
        ret = true; //apteryx_set_string (path, "action", action);
    }

    free (path);
    return ret;
}

Это прошло без утечки памяти.
Тогда я попытался :

bool
amflinkd_apteryx_set_action (const char *address, char *action)
{
    bool ret = false;
    char *path = NULL;

    if (asprintf (&path, "%s/%s", AMFLINKD_IP_ADDRESS_PATH, address) > 0)
    {
        ret = apteryx_test_string (path, "action", action);
    }

    free (path);
    return ret;
}

с

bool
apteryx_test_string (const char *path, const char *key, const char *value)
{
    char *full_path;
    size_t len;
    bool res = false;

    /* Create full path */
    if (key)
        len = asprintf (&full_path, "%s/%s", path, key);
    else
        len = asprintf (&full_path, "%s", path);
    if (len)
    {
        res = true;
        free (full_path);
    }
    return res;
}

Это также прошло без утечки памяти. Это должно быть что-то в apteryx_set_string (), что препятствует освобождению памяти или потере ссылок.

Я понял это!
Код попал в ASSERT

ASSERT ((ref_count > 0), return false, "SET: Not initialisedn");

два вызова глубже в функции apteryx_set_string (). Из-за assert свободный не был вызван, и valgrind принял это как утечку памяти.
Спасибо за вашу помощь.

2 ответа

  1. Возможно, это вопрос масштаба. Попробуйте освободить блок if.

    Переменные, создаваемые внутри оператора if, насколько я помню, живут только внутри блока if. Таким образом, вы потеряли бы его закрывающей скобкой блока if.

    В то время как вы объявили переменную вне блока, распределение не произошло до вызова asprintf() во время оператора if.

    Редактировать
    Чтобы предоставить больше информации, моя мысль заключается в том, что asprintf-это тот, который объявляет фактическую переменную, на которую просто указывает указатель, который ей дается. Если это так, то объявление переменной, которая теряется, находится в операторе if

    Второе Редактирование
    Я проверил со следующим и не смог воспроизвести:

    #include <stdlib.h>
    #include <stdio.h>
    
    void test() {
      char *str = NULL;
    
      if (asprintf(&str, "%s", "foo") > 0) {
        printf("%s\n", str);
      }
    
      free(str);
    }
    
    int main() {
      test();
      return 0;
    }
    

    Командная строка GCC:

    gcc -g -D_GNU_SOURCE -I/usr/include asprintf.c
    

    Выход:

    valgrind ./a.out
    ==2461== Memcheck, a memory error detector
    ==2461== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
    ==2461== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
    ==2461== Command: ./a.out
    ==2461==
    foo
    ==2461==
    ==2461== HEAP SUMMARY:
    ==2461==     in use at exit: 0 bytes in 0 blocks
    ==2461==   total heap usage: 3 allocs, 3 frees, 1,128 bytes allocated
    ==2461==
    ==2461== All heap blocks were freed -- no leaks are possible
    ==2461==
    ==2461== For counts of detected and suppressed errors, rerun with: -v
    ==2461== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
    
  2. Я понял это!
    Код попал в ASSERT

    ASSERT ((ref_count > 0), return false, "SET: Not initialised\n");

    два вызова глубже в функции apteryx_set_string (). Из-за assert свободный не был вызван, и valgrind принял это как утечку памяти.
    Спасибо за вашу помощь.