Search code examples
linuxmemory-leakssystem-callsstrace

strace output not showing malloc and free system call in a multi threaded application


I am trying to simulate a memory leak problem with the following code and then investigate the system calls resulting in memory leak.

include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
#include <ctime>

class SharedObject {
public:
    std::string currentTime;

    SharedObject() {
        // Capture current time as string
        auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
        currentTime = std::ctime(&now); // Convert time to string
    }

    void displayTime() {
        std::cout << "Current Time: " << currentTime;
    }
};

void threadFunction(const std::string& threadName, int totalIterations) {
    for (int iteration = 1; iteration <= totalIterations; ++iteration) {
        SharedObject* obj = new SharedObject(); // Create object
        std::cout << threadName << " created an object at iteration " << iteration << std::endl;
        obj->displayTime();

        // Every 100 iterations, forget to delete the object (simulating memory leak)
        if (iteration % 2 == 1) {
            std::cout << threadName << " forgot to delete the object at iteration " << iteration << std::endl;
        } else {
            delete obj; // Delete object
            std::cout << threadName << " deleted the object at iteration " << iteration << std::endl;
        }

        // Sleep for 512 milliseconds
        std::this_thread::sleep_for(std::chrono::milliseconds(512));
    }
    std::cout << threadName << " completed all iterations.\n";
}int main() {
    const int totalIterations = 100000;

    // Launch two threads
    std::thread thread1(threadFunction, "Thread 1", totalIterations);
    std::thread thread2(threadFunction, "Thread 2", totalIterations);

    // Wait for threads to finish
    thread1.join();
    thread2.join();

    std::cout << "Both threads completed execution.\n";
    return 0;
}

I have compiled and running the program in Linux soumajit-HP-Pavilion-Desktop-590-p0xxx 5.4.0-150-generic #167~18.04.1-Ubuntu SMP Wed May 24 00:51:42 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux system

Post that I am trying to trace malloc and free system call using strace.
Following is the output

[pid 18031] <... stat resumed> {st_mode=S_IFREG|0644, st_size=312, ...}) = 0
[pid 18032] write(1, "Current Time: Tue Jan 14 23:08:3"..., 39) = 39
[pid 18031] futex(0x7fcf11c08c20, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 18032] write(1, "Thread 2 deleted the object at i"..., 46) = 46
[pid 18031] <... futex resumed> )       = 0
[pid 18032] nanosleep({tv_sec=0, tv_nsec=512000000},  <unfinished ...>
[pid 18031] write(1, "Thread 1 created an object at it"..., 45) = 45
[pid 18031] write(1, "Current Time: Tue Jan 14 23:08:3"..., 39) = 39
[pid 18031] write(1, "Thread 1 forgot to delete the ob"..., 55) = 55
[pid 18031] nanosleep({tv_sec=0, tv_nsec=512000000},  <unfinished ...>
[pid 18032] <... nanosleep resumed> 0x7fcf10c7ace0) = 0
[pid 18032] stat("/etc/localtime",  <unfinished ...>
[pid 18031] <... nanosleep resumed> 0x7fcf1147bce0) = 0
[pid 18032] <... stat resumed> {st_mode=S_IFREG|0644, st_size=312, ...}) = 0
[pid 18031] futex(0x7fcf11c08c20, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 18032] futex(0x7fcf11c08c20, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 18031] <... futex resumed> )       = -1 EAGAIN (Resource temporarily unavailable)
[pid 18032] <... futex resumed> )       = 0
[pid 18031] stat("/etc/localtime",  <unfinished ...>
[pid 18032] write(1, "Thread 2 created an object at it"..., 45 <unfinished ...>
[pid 18031] <... stat resumed> {st_mode=S_IFREG|0644, st_size=312, ...}) = 0
[pid 18032] <... write resumed> )       = 45
[pid 18032] write(1, "Current Time: Tue Jan 14 23:08:3"..., 39 <unfinished ...>
[pid 18031] futex(0x7fcf11c08c20, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 18032] <... write resumed> )       = 39
[pid 18031] <... futex resumed> )       = 0
[pid 18032] write(1, "Thread 2 forgot to delete the ob"..., 55 <unfinished ...>
[pid 18031] futex(0x7fcf11c088c0, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 18032] <... write resumed> )       = 55
[pid 18032] futex(0x7fcf11c088c0, FUTEX_WAKE_PRIVATE, 1) = 1
[pid 18031] <... futex resumed> )       = 0
[pid 18032] nanosleep({tv_sec=0, tv_nsec=512000000},  <unfinished ...>
[pid 18031] futex(0x7fcf11c088c0, FUTEX_WAKE_PRIVATE, 1) = 0
[pid 18031] write(1, "Thread 1 created an object at it"..., 45) = 45
[pid 18031] write(1, "Current Time: Tue Jan 14 23:08:3"..., 39) = 39
[pid 18031] write(1, "Thread 1 deleted the object at i"..., 46) = 46
[pid 18031] nanosleep({tv_sec=0, tv_nsec=512000000}, 

Why does the memory allocation and de-allocation calls not captured ?
[![ldd binary][1]][1] [![ldd binary and ltrace version][1]][1]

enter image description here


Solution

  • strace is short for "system call/signal trace". There are some system calls related to memory management (sbrk/brk/mmap) but those are low-level and not useful to debug memory leaking issues.

    If you want to trace memory leaks you can use something like the AddressSanitizer available in g++/clang++:

    $ g++ -fsanitize=address -g -o code code.cc
    $ ./code
    ...
    Thread 2 completed all iterations.
    Thread 1 completed all iterations.
    Both threads completed execution.
    
    =================================================================
    ==1153114==ERROR: LeakSanitizer: detected memory leaks
    
    Direct leak of 320 byte(s) in 10 object(s) allocated from:
        #0 0x75f22d59e548 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:95
        #1 0x56ef3081f76d in threadFunction(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, int) /tmp/x.cc:24
        #2 0x56ef30822e7a in void std::__invoke_impl<void, void (*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, int), char const*, int>(std::__invoke_other, void (*&&)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, int), char const*&&, int&&) /usr/include/c++/13/bits/invoke.h:61
        #3 0x56ef30822c4b in std::__invoke_result<void (*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, int), char const*, int>::type std::__invoke<void (*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, int), char const*, int>(void (*&&)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, int), char const*&&, int&&) /usr/include/c++/13/bits/invoke.h:96
        #4 0x56ef30822b6a in void std::thread::_Invoker<std::tuple<void (*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, int), char const*, int> >::_M_invoke<0ul, 1ul, 2ul>(std::_Index_tuple<0ul, 1ul, 2ul>) /usr/include/c++/13/bits/std_thread.h:292
        #5 0x56ef30822b03 in std::thread::_Invoker<std::tuple<void (*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, int), char const*, int> >::operator()() /usr/include/c++/13/bits/std_thread.h:299
        #6 0x56ef30822ae3 in std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, int), char const*, int> > >::_M_run() /usr/include/c++/13/bits/std_thread.h:244
        #7 0x75f22d30edb3  (/lib/x86_64-linux-gnu/libstdc++.so.6+0xecdb3) (BuildId: ca77dae775ec87540acd7218fa990c40d1c94ab1)
        #8 0x75f22d4fea41 in asan_thread_start ../../../../src/libsanitizer/asan/asan_interceptors.cpp:234
        #9 0x75f22cf95a93 in start_thread nptl/pthread_create.c:447
    

    (I limited the number of iterations to 10)

    More info here.