Valgrind를 공부(활용?)해봅니다.

진작에 알고 있고, 몇번 단순히 써봤지만, 규모가 조금 되는 프로그램에 ‘밸그린드'(https://valgrind.org/docs/manual/faq.html#faq.pronounce)를 실무에 활용하려니, 세세한 부분이 필요하더군요. 웹페이지에 설명이 잘되어 있지만, 역시 시간도 없고, AI라는 훌륭한 도구가 이제 도처에 널려 있으니, 써봅니다. 전 공짜를 좋아라 하므로 그냥 Google에게 물어봅니다.

Q how to trigger profiling in valgrind for specific attached application’s function run

답변에서 Profiling만 찾아주길래, 원래 원했던 memory leak check로 질문을 바꾸어 봅니다.

A) To profile specific functions in an already running application using Valgrind, use the Callgrind tool with conditional instrumentation. Start with instrumentation off (--instr-atstart=no), then use callgrind_control to enable profiling only during the target function execution. ValgrindValgrind +2

To profile specific functions in a running application, use the Callgrind tool with conditional instrumentation. Start with instrumentation off (--instr-atstart=no). Use callgrind_control to enable profiling during target function execution. ValgrindValgrind

Steps to Trigger Profiling on a Running Application

  1. Launch with Callgrind (Initially Disabled)
    Start the application with Callgrind. Instruct it not to start instrumentation immediately:bashvalgrind --tool=callgrind --instr-atstart=no ./your_app [args]
  2. Locate Process ID (PID)
    Find the PID of the running Valgrind process. This is usually the first child process.
  3. Trigger Profiling via Control Command
    When the application reaches the desired point, run this in another terminal:bashcallgrind_control -i on -p <PID>
  4. Stop Profiling
    Once the target function finishes, turn off instrumentation to minimize overhead:bashcallgrind_control -i off -p <PID>
  5. Dump Results
    Force a dump of the profile data:bashcallgrind_control -d -p <PID>  

Alternative: Programmatic Instrumentation (Recommended for accuracy)

Insert code directly into the application to control Valgrind: ValgrindValgrind

c

#include <valgrind/callgrind.h>

// Inside your code
CALLGRIND_START_INSTRUMENTATION;
// ... call the function you want to profile ...
target_function();
CALLGRIND_STOP_INSTRUMENTATION;

Compile with -I /path/to/valgrind/include and run without --instr-atstart=no

Useful Options

  • --dump-instr=yes: Provides assembly-level, instruction-by-instruction profiling (viewable in KCachegrind).
  • --collect-jumps=yes: Captures jump information for better performance analysis.
  • --fn-skip=funcprefix: Ignores irrelevant functions to reduce noise. ValgrindValgrind +1

The results can be analyzed using {Link: KCachegrind https://kcachegrind.sourceforge.net/}

Q) how to trigger profiling/leakage check in Valgrind only when specific attached application’s function run

이번에는 원본 코드를 수정해야한다고 하네요.. 이건 또 일단 원하는 바가 아닌데..

A) To trigger Valgrind Memcheck (leak check) or Callgrind (profiling) only during a specific function, use the Valgrind client request mechanism (VALGRIND_TOGGLE_CHECKING) in your C/C++ code. This allows enabling/disabling instrumentation dynamically, reducing performance overhead and focusing output. 

Steps to Implement Targeted Monitoring

  1. Include the Valgrind Header:
    Include <valgrind/valgrind.h> in your source file.
  2. Toggle Instrumentation in Code:
    Wrap the specific function call with toggling commands.c#include <valgrind/valgrind.h> void target_function() { // TURN ON VALGRIND VALGRIND_TOGGLE_CHECKING; // --- Code to profile/check for leaks --- do_work(); // TURN OFF VALGRIND VALGRIND_TOGGLE_CHECKING; }
  3. Compile and Run:
    • Compile with debugging symbols (-g).
    • Run with valgrind --tool=memcheck --log-file=valgrind.log ./your_programYouTubeYouTube +4

Alternative Method: Using GDB and vgdb 

If you cannot modify the source code, you can use vgdb to toggle instrumentation remotely.

  1. Run valgrind with: valgrind --vgdb-error=0 ./your_program.
  2. In another terminal, use vgdb to toggle:bashvgdb monitor toggle_checking # Run your specific function here vgdb monitor toggle_checking  ValgrindValgrind +2

Key Considerations

Callgrind: For profiling, use CALLGRIND_TOGGLE_COLLECT from <valgrind/callgrind.h> to start/stop collection. Mantid ProjectMantid Project +1

Performance: While disabled, code runs much faster than fully instrumented, but still slower than natively.

Leaks: For memory leaks, note that memory allocated while monitoring is OFF but freed while ON may cause confusing reports.

세번째 질문을 합니다. 코드 수정 없이 하는 방법을 질문해봅니다.

Q) how to trigger profiling/leakage check in Valgrind only when specific attached application’s function run without modifying original target application

이젠 GDB가 대안으로 등장하기 시작합니다.

To trigger Valgrind profiling or memory leak checks in a target application only during a specific function’s execution without modifying the source, use valgrind --vgdb-error=0 to attach GDB, set a breakpoint at the target function, and issue manual leak checks (monitor leak_search) or start profiling when the breakpoint hits. This technique works by leveraging Valgrind’s GDB server. ValgrindValgrind +2

Steps to Trigger Profiling/Leakage Check on Demand

  1. Run with Valgrind and GDB server:
    Start the target application under Valgrind, enabling the GDB server:bashvalgrind --vgdb=yes --vgdb-error=0 --leak-check=full ./your_app --vgdb-error=0 makes Valgrind wait for a GDB client before executing the first instruction.
  2. Attach GDB in a new terminal:
    Find the PID of the process (Valgrind will display it) and attach GDB:bashgdb ./your_app (gdb) target remote | vgdb
  3. Set Breakpoint on Specific Function:
    Set a breakpoint on the target function where profiling or checking for leaks should start:bash(gdb) break your_function_name (gdb) continue
  4. Trigger Specific Actions via GDB (No Code Change):
    When the program hits your_function_name, GDB will stop. Then, instruct Valgrind:
    • To check for memory leaks at this point:bash(gdb) monitor leak_search
    • To start profiling (if using Callgrind):bash(gdb) monitor callgrind_control -i on
  5. Stop/Resume:
    Continue execution and trigger monitor leak_search again later. ValgrindValgrind +4

Alternative: Using valgrind/valgrind.h (Minimal Modification) 

If allowed to add one line to the code (#include <valgrind/valgrind.h>), insert VALGRIND_DO_LEAK_CHECK; directly inside the specific function. However, this requires recompilation. The GDB method requires no code modifications

GDB를 안쓸수 없냐고 하니,

Q) how to trigger profiling/leakage check in Valgrind only when specific attached application’s function run without modifying original target application and without usage of GDB

다른 방법은 없는 듯 하네요. Valgrind자체 vgdb라고 gdb처럼 쓸 수 있는 것을 대안처럼 이야기하지만 결국 GDB인듯 합니다.

You can achieve selective profiling or leakage checks in Valgrind without modifying the target application’s source code or using GDB by leveraging the callgrind_control utility or the vgdb command-line utility with monitor commands. Stack OverflowStack Overflow +1

For Profiling (Callgrind)

The callgrind_control utility is designed specifically for toggling instrumentation and collection during a program’s execution. Stack OverflowStack Overflow +2

  1. Launch your application under Valgrind’s Callgrind tool, but with instrumentation and collection turned off at the start:bashvalgrind --tool=callgrind --instr-atstart=no --collect-atstart=no ./your_executable [args...]
  2. Identify the application’s PID. Valgrind will print the PID when it starts.
  3. Trigger profiling start just before the function you are interested in is executed. You will need to switch to another terminal and run the callgrind_control command with the application’s PID:bashcallgrind_control -i on --pid=<PID>
  4. Trigger profiling stop after the function has completed its execution:bashcallgrind_control -i off --pid=<PID>  

This approach allows you to profile only the desired section of code, minimizing the overhead for uninteresting parts. Stack OverflowStack Overflow

For Leakage Checks (Memcheck)

For memory error and leak detection using the default Memcheck tool, there is no direct command-line option like callgrind_control to selectively start/stop checks without recompiling with client request macros. Stack OverflowStack Overflow

However, you can use the vgdb command-line utility to send “monitor commands” to the Valgrind gdbserver, although this is more complex and typically used in conjunction with GDB. The user can achieve some level of control this way, but it is not as straightforward as the callgrind_control method. Stack OverflowStack Overflow +2

A common approach for Memcheck is to use suppressions files to ignore errors in known library functions or parts of the code you do not control. This does not trigger the check only for a specific function but helps in filtering out irrelevant results. SourcewareSourceware

For precise control over Memcheck without source code modification, the use of GDB to set breakpoints and issue monitor commands for starting/stopping error detection remains the most reliable method. Stack OverflowStack Overflow +3

아무래도 source code를 수정하는 방법이 좀더 나을 듯 하여. 세부 질문으로 들어가봅니다.

Q) how to trigger profiling/leakage check in Valgrind only when specific attached application’s function run with minimal source code change, show the exact working example of source code. example shall include two functions, one has leakage and other has no leakage, and it shall run in daemon style

일단 제법 길게 이것저것 제시를 해주긴합니다. 예제코드도 그럴듯해보이고

You can control Valgrind’s memory checking for specific functions using the Valgrind client requests VALGRIND_START_MEMCHECK and VALGRIND_STOP_MEMCHECK macros within your code. This requires including the valgrind/valgrind.h header and linking against the Valgrind client library. 

Below is an exact C source code example that demonstrates this approach in a daemon-style program (using a simple infinite loop) with one function that leaks memory and another that does not. 

Source Code Example (daemon_leak_example.c

c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // For sleep function
#include <valgrind/valgrind.h> // Required for Valgrind client requests

// Function with a definite memory leak
void function_with_leak(void) {
    char *ptr;
    // Start Valgrind memory checking specifically for this function
    VALGRIND_START_MEMCHECK;

    ptr = malloc(1024); // Allocate memory
    if (ptr == NULL) {
        perror("malloc failed");
        return;
    }
    // No free call, so this is a leak

    printf("Leak function ran, allocated 1024 bytes (will be reported by Valgrind).\n");

    // Stop Valgrind memory checking
    VALGRIND_STOP_MEMCHECK;
}

// Function with no memory leak
void function_without_leak(void) {
    char *ptr;
    // Start Valgrind memory checking
    VALGRIND_START_MEMCHECK;

    ptr = malloc(1024); // Allocate memory
    if (ptr == NULL) {
        perror("malloc failed");
        return;
    }
    free(ptr); // Free the allocated memory

    printf("No leak function ran, allocated and freed 1024 bytes (will not be reported).\n");

    // Stop Valgrind memory checking
    VALGRIND_STOP_MEMCHECK;
}

int main(void) {
    printf("Daemon-style application starting...\n");
    printf("Valgrind should only monitor inside the instrumented functions.\n");

    while (1) { // Simulate a daemon's infinite loop
        function_without_leak();
        sleep(2); // Wait a bit
        function_with_leak();
        sleep(2); // Wait a bit
    }

    // This part is unreachable in a true infinite loop, but a real daemon
    // would handle termination signals and clean up here. Valgrind reports
    // leaks at exit, so the process must eventually terminate for a final summary.
    return 0;
}

How to Compile and Run

  1. Install Valgrind Development Headers: Make sure you have the necessary headers installed (e.g., valgrind-devel or valgrind-dbg package in most Linux distributions).
  2. Compile: Compile your code with the -g flag for line numbers and link the Valgrind client requests library using the -lvalgrind flag.bashgcc -g daemon_leak_example.c -o daemon_leak_example -lvalgrind
  3. Run with Valgrind: Run the executable with Valgrind’s memcheck tool. To only track memory within the instrumented sections and see a final leak summary at exit, use --leak-check=full and potentially suppress initial errors with --instr-atstart=no (though the client requests handle the start/stop manually).Important: Valgrind’s final leak summary is reported when the program terminates. For a daemon, you would typically send a termination signal (SIGINT or SIGTERM) to allow a graceful shutdown and leak report. In this example, the infinite loop is a simplification.To observe the effects, you can interrupt the running program after a few iterations (using Ctrl+C or kill) to see the current memory state, but the most comprehensive leak report happens at the program’s normal exit.bashvalgrind --leak-check=full --show-leak-kinds=definite ./daemon_leak_example  University of Wisconsin–MadisonUniversity of Wisconsin–Madison +4

Expected Output

When you run the program and eventually terminate it, Valgrind’s output will focus on the leaks that occurred within the VALGRIND_START_MEMCHECK/VALGRIND_STOP_MEMCHECK blocks that were not freed, specifically from function_with_leak. The memory allocated and freed in function_without_leak will not show up as a leak in the final report. 

The output will include a summary similar to:

==...== definite lost: 1,024 bytes in 1 blocks from function_with_leak ...

This confirms Valgrind only checked the specific code sections you marked. 

이제 쓸만한 정보와 예제들이 있으니 실습시간입니다. 하.지.만.

예제코드로 해봤지만, ubuntu 24.04에서는 valgrind-dbg패키지는 설치가 안되고, 그래도 apt install valgrind로도 /usr/include/valgrind는 설치가 되고 memcheck.h도 보이길래 확인해봤더니 VALGRIND_START/STOP_MEMCHECK는 어디에도 정의가 없어서, 버전 문제인가 싶어 찾아보니 설치된건 3.22이고, 혹시 몰라 물어봤더니 3.26에서 지원한다고 하길래 Valgrind 소스코드를 받아서 빌드해서 설치해봤더니 여전히 없네요.. 참.. 사기를 잘치는 군요.. 중간에 제대로 된거 아니냐고 따지니 두번째 거짓말로 VALGRIND_ENABLE_ADDRCHECK, 이런걸 쓰라고 알려주네요.. 제대로 버전까지 잡아서 이야기하니 이제, 실토합니다. 대안을 내놓으라고 질문하니 아래를 내놓네요

Q) even valgrind 3.26 does not support VALGRIND_START_MEMCHECK macro. don’t lie. So provide proper control method from client application how to start and stop memory leak detection feature on specific function run.

VALGRIND_ENABLE_ERROR_REPORTING시리즈를 내줍니다.. 이것도 함 해보죠.

To control Valgrind’s Memcheck from a C/C++ application without using the unsupported VALGRIND_START_MEMCHECK macro, use VALGRIND_DISABLE_ERROR_REPORTING and VALGRIND_ENABLE_ERROR_REPORTING macros from <valgrind/memcheck.h>. These stop reporting errors, though they do not pause instrumentation overhead. ValgrindValgrind +2

Proper Control Method

To analyze a specific function, wrap the code with these macros to silence Valgrind during unnecessary sections.

c

#include <valgrind/memcheck.h>
#include <stdlib.h>

void function_to_test() {
    // START CHECKING
    VALGRIND_ENABLE_ERROR_REPORTING;
    
    int *x = malloc(10 * sizeof(int));
    // Memory leak: x is not freed
    
    // STOP CHECKING
    VALGRIND_DISABLE_ERROR_REPORTING;
}

int main() {
    // Initial, unmonitored work
    function_to_test();
    return 0;
}

Key Considerations

Note: These macros still incur the performance cost of running under Valgrind, they simply tell the tool to stop reporting the errors. ValgrindValgrind +4

Compilation: Compile with -g for debug symbols and include valgrind.h and memcheck.h.

Alternative for Specific Leaks: Use VALGRIND_DO_LEAK_CHECK to force a leak report at a specific, targeted point in the code, rather than waiting for program termination.

Run Command: valgrind --tool=memcheck --leak-check=full ./your_program.

뭔가 컴파일도 되고 어떤 효과는 있는 듯 하지만. 원하는 기능은 아닌듯 합니다.. 프로그램 실행 종료시점에서만, 단순히 측정결과가 부정확하다는 것 밖에는 아무런 도움이 안되네요. 레포트가 바로 찍히는 것도 아니고, longterm으로 관측시 초기 한번 체크하고, 한참뒤에 켜서 다시 보는 식인 것 같네요. 여전히 아래 DO_LEAK_CHECK와 함께 사용하는게 좋은 듯 합니다.
Document를 보면 VALGRIND_DO_LEAK_CHECK, VALGRIND_DO_ADDED_LEAK_CHECK정도가 그나마 존재하는 매크로로 보입니다. 해서 정리하면 아래정도겠습니다. 예제 C코드는 이전과 크게 바꾸지 않고
VALGRIND_START_MEMCHECK –> VALGRIND_DO_LEAK_CHECK로
VALGRIND_STOP_MEMCHECK –> VALGRIND_DO_ADDED_LEAK_CHECK로 일단 넣고, 빌드시에는 -l 옵션은 굳이 없어도 기본 ubuntu apt로 설치한 Valgrind 3.22로 잘 빌드가 되고, 실행 커맨드를 아래처럼 하면

valgrind –tool=memcheck –leak-check=full –show-leak-kinds=all ./d

==23072== Memcheck, a memory error detector
==23072== Copyright (C) 2002-2022, and GNU GPL’d, by Julian Seward et al.
==23072== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
==23072== Command: ./d
==23072==
Daemon-style application starting…
Valgrind should only monitor inside the instrumented functions.
==23072== 1,024 bytes in 1 blocks are still reachable in loss record 1 of 1
==23072== at 0x4846828: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==23072== by 0x48F01B4: _IO_file_doallocate (filedoalloc.c:101)
==23072== by 0x4900523: _IO_doallocbuf (genops.c:347)
==23072== by 0x48FDF8F: _IO_file_overflow@@GLIBC_2.2.5 (fileops.c:745)
==23072== by 0x48FEAAE: _IO_new_file_xsputn (fileops.c:1244)
==23072== by 0x48FEAAE: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1197)
==23072== by 0x48F2C5C: puts (ioputs.c:40)
==23072== by 0x1096C7: main (daemon_leak_example.c:46)
==23072==
==23072== LEAK SUMMARY:
==23072== definitely lost: 0 bytes in 0 blocks
==23072== indirectly lost: 0 bytes in 0 blocks
==23072== possibly lost: 0 bytes in 0 blocks
==23072== still reachable: 1,024 bytes in 1 blocks
==23072== suppressed: 0 bytes in 0 blocks
==23072==
No leak function ran, allocated and freed 1024 bytes (will not be reported).
==23072== LEAK SUMMARY:
==23072== definitely lost: 0 (+0) bytes in 0 (+0) blocks
==23072== indirectly lost: 0 (+0) bytes in 0 (+0) blocks
==23072== possibly lost: 0 (+0) bytes in 0 (+0) blocks
==23072== still reachable: 1,024 (+0) bytes in 1 (+0) blocks
==23072== suppressed: 0 (+0) bytes in 0 (+0) blocks
==23072==
==23072== 1,024 bytes in 1 blocks are still reachable in loss record 1 of 1
==23072== at 0x4846828: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==23072== by 0x48F01B4: _IO_file_doallocate (filedoalloc.c:101)
==23072== by 0x4900523: _IO_doallocbuf (genops.c:347)
==23072== by 0x48FDF8F: _IO_file_overflow@@GLIBC_2.2.5 (fileops.c:745)
==23072== by 0x48FEAAE: _IO_new_file_xsputn (fileops.c:1244)
==23072== by 0x48FEAAE: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1197)
==23072== by 0x48F2C5C: puts (ioputs.c:40)
==23072== by 0x1096C7: main (daemon_leak_example.c:46)
==23072==
==23072== LEAK SUMMARY:
==23072== definitely lost: 0 bytes in 0 blocks
==23072== indirectly lost: 0 bytes in 0 blocks
==23072== possibly lost: 0 bytes in 0 blocks
==23072== still reachable: 1,024 bytes in 1 blocks
==23072== suppressed: 0 bytes in 0 blocks
==23072==
Leak function ran, allocated 1024 bytes (will be reported by Valgrind).
==23072== 1,024 (+1,024) bytes in 1 (+1) blocks are still reachable in new loss record 2 of 2
==23072== at 0x4846828: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==23072== by 0x1094E9: function_with_leak (daemon_leak_example.c:13)
==23072== by 0x1096EA: main (daemon_leak_example.c:52)
==23072==
==23072== LEAK SUMMARY:
==23072== definitely lost: 0 (+0) bytes in 0 (+0) blocks
==23072== indirectly lost: 0 (+0) bytes in 0 (+0) blocks
==23072== possibly lost: 0 (+0) bytes in 0 (+0) blocks
==23072== still reachable: 2,048 (+1,024) bytes in 2 (+1) blocks
==23072== suppressed: 0 (+0) bytes in 0 (+0) blocks
==23072==
==23072== 1,024 bytes in 1 blocks are still reachable in loss record 2 of 3
==23072== at 0x4846828: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==23072== by 0x48F01B4: _IO_file_doallocate (filedoalloc.c:101)
==23072== by 0x4900523: _IO_doallocbuf (genops.c:347)
==23072== by 0x48FDF8F: _IO_file_overflow@@GLIBC_2.2.5 (fileops.c:745)
==23072== by 0x48FEAAE: _IO_new_file_xsputn (fileops.c:1244)
==23072== by 0x48FEAAE: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1197)
==23072== by 0x48F2C5C: puts (ioputs.c:40)
==23072== by 0x1096C7: main (daemon_leak_example.c:46)
==23072==
==23072== 1,024 bytes in 1 blocks are definitely lost in loss record 3 of 3
==23072== at 0x4846828: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==23072== by 0x1094E9: function_with_leak (daemon_leak_example.c:13)
==23072== by 0x1096EA: main (daemon_leak_example.c:52)
==23072==
==23072== LEAK SUMMARY:
==23072== definitely lost: 1,024 bytes in 1 blocks
==23072== indirectly lost: 0 bytes in 0 blocks
==23072== possibly lost: 0 bytes in 0 blocks
==23072== still reachable: 1,024 bytes in 1 blocks
==23072== suppressed: 0 bytes in 0 blocks
==23072==
No leak function ran, allocated and freed 1024 bytes (will not be reported).
==23072== LEAK SUMMARY:
==23072== definitely lost: 1,024 (+0) bytes in 1 (+0) blocks
==23072== indirectly lost: 0 (+0) bytes in 0 (+0) blocks
==23072== possibly lost: 0 (+0) bytes in 0 (+0) blocks
==23072== still reachable: 1,024 (+0) bytes in 1 (+0) blocks
==23072== suppressed: 0 (+0) bytes in 0 (+0) blocks
==23072==
==23072== 1,024 bytes in 1 blocks are still reachable in loss record 1 of 2
==23072== at 0x4846828: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==23072== by 0x48F01B4: _IO_file_doallocate (filedoalloc.c:101)
==23072== by 0x4900523: _IO_doallocbuf (genops.c:347)
==23072== by 0x48FDF8F: _IO_file_overflow@@GLIBC_2.2.5 (fileops.c:745)
==23072== by 0x48FEAAE: _IO_new_file_xsputn (fileops.c:1244)
==23072== by 0x48FEAAE: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1197)
==23072== by 0x48F2C5C: puts (ioputs.c:40)
==23072== by 0x1096C7: main (daemon_leak_example.c:46)
==23072==
==23072== 1,024 bytes in 1 blocks are definitely lost in loss record 2 of 2
==23072== at 0x4846828: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==23072== by 0x1094E9: function_with_leak (daemon_leak_example.c:13)
==23072== by 0x1096EA: main (daemon_leak_example.c:52)
==23072==
==23072== LEAK SUMMARY:
==23072== definitely lost: 1,024 bytes in 1 blocks
==23072== indirectly lost: 0 bytes in 0 blocks
==23072== possibly lost: 0 bytes in 0 blocks
==23072== still reachable: 1,024 bytes in 1 blocks
==23072== suppressed: 0 bytes in 0 blocks
==23072==
Leak function ran, allocated 1024 bytes (will be reported by Valgrind).
==23072== 1,024 (+1,024) bytes in 1 (+1) blocks are still reachable in new loss record 2 of 3
==23072== at 0x4846828: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==23072== by 0x1094E9: function_with_leak (daemon_leak_example.c:13)
==23072== by 0x1096EA: main (daemon_leak_example.c:52)
==23072==
==23072== LEAK SUMMARY:
==23072== definitely lost: 1,024 (+0) bytes in 1 (+0) blocks
==23072== indirectly lost: 0 (+0) bytes in 0 (+0) blocks
==23072== possibly lost: 0 (+0) bytes in 0 (+0) blocks
==23072== still reachable: 2,048 (+1,024) bytes in 2 (+1) blocks
==23072== suppressed: 0 (+0) bytes in 0 (+0) blocks
==23072==
==23072== 1,024 bytes in 1 blocks are still reachable in loss record 2 of 3
==23072== at 0x4846828: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==23072== by 0x48F01B4: _IO_file_doallocate (filedoalloc.c:101)
==23072== by 0x4900523: _IO_doallocbuf (genops.c:347)
==23072== by 0x48FDF8F: _IO_file_overflow@@GLIBC_2.2.5 (fileops.c:745)
==23072== by 0x48FEAAE: _IO_new_file_xsputn (fileops.c:1244)
==23072== by 0x48FEAAE: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1197)
==23072== by 0x48F2C5C: puts (ioputs.c:40)
==23072== by 0x1096C7: main (daemon_leak_example.c:46)
==23072==
==23072== 2,048 bytes in 2 blocks are definitely lost in loss record 3 of 3
==23072== at 0x4846828: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==23072== by 0x1094E9: function_with_leak (daemon_leak_example.c:13)
==23072== by 0x1096EA: main (daemon_leak_example.c:52)
==23072==
==23072== LEAK SUMMARY:
==23072== definitely lost: 2,048 bytes in 2 blocks
==23072== indirectly lost: 0 bytes in 0 blocks
==23072== possibly lost: 0 bytes in 0 blocks
==23072== still reachable: 1,024 bytes in 1 blocks
==23072== suppressed: 0 bytes in 0 blocks
==23072==

위처럼 나오는데, 이미 실행흐름을 알고 있고, 대강의 지점을 알고 있다면, ADDED로만 확인해봐도 좋을 듯 하네요. 아래로 예제 코드를 조금더 변경해서 각 위치에서 확인가능한것을 보았습니다. 그리고 초반에 IO작업있는 부분도 일단은 Leak로 두고 있다가 Exit으로 풀려나갈때 빼주는 것 같네요.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // For sleep function
#include <valgrind/valgrind.h> // Required for Valgrind client requests
#include <valgrind/memcheck.h> // Required for Valgrind client requests

// Function with a definite memory leak
void function_with_leak(void) {
    char *ptr;
    // Start Valgrind memory checking specifically for this function
    VALGRIND_PRINTF("CHECK before Leak Function\n");
	VALGRIND_DO_ADDED_LEAK_CHECK;

    ptr = malloc(1024); // Allocate memory
    if (ptr == NULL) {
        perror("malloc failed");
        return;
    }
    // No free call, so this is a leak

    printf("Leak function ran, allocated 1024 bytes (will be reported by Valgrind).\n");

    // Stop Valgrind memory checking
    VALGRIND_DO_ADDED_LEAK_CHECK;
    VALGRIND_PRINTF_BACKTRACE("LEAK OCCURRED\n");
}

// Function with no memory leak
void function_without_leak(void) {
    char *ptr;
    // Start Valgrind memory checking
    VALGRIND_PRINTF("CHECK before non-Leak Function\n");
    VALGRIND_DO_ADDED_LEAK_CHECK;

    ptr = malloc(1024); // Allocate memory
    if (ptr == NULL) {
        perror("malloc failed");
        return;
    }
    free(ptr); // Free the allocated memory

    printf("No leak function ran, allocated and freed 1024 bytes (will not be reported).\n");

    // Stop Valgrind memory checking
    VALGRIND_PRINTF("CHECK after non-Leak Function\n");
    VALGRIND_DO_ADDED_LEAK_CHECK;
}

int main(void) {
    printf("Daemon-style application starting...\n");
    printf("Valgrind should only monitor inside the instrumented functions.\n");

    do { // Simulate a daemon's infinite loop
        function_without_leak();
        sleep(2); // Wait a bit
        function_with_leak();
        sleep(2); // Wait a bit
    } while(0);

    VALGRIND_PRINTF("Exit CHECK\n");
    VALGRIND_DO_ADDED_LEAK_CHECK;
    // This part is unreachable in a true infinite loop, but a real daemon
    // would handle termination signals and clean up here. Valgrind reports
    // leaks at exit, so the process must eventually terminate for a final summary.
    return 0;
}


게시됨

카테고리

작성자