reliability of proc/statm for finding memory leak

2019-09-19 06:43发布

问题:

I am trying to find a slow memory leak in a large application.

ps shows the VSZ growing slowly until the application crashes after running for 12-18 hours. Unfortunately, valgrind, leakcheak, etc have not been useful (Valgrind fails with illegal instruction).

Alternatively, I've been printing the contents of /proc/statm over time, and approximately every 10s I see the first field of statm (total program size) increase by 20-30 bytes.

I've tracked it down to one function but it doesn't make sense. The offending function reads a directory and performs a clear() on a std::set. What in the function would increase the memory footprint? And... why doesn't the memory reduce once the directory is closed?

Trace Output:

DvCfgProfileList::obtainSystemProfileList() PRE MEMORY USAGE: 27260 11440 7317 15 0 12977 0
DvCfgProfileList::obtainSystemProfileList() MID 1 MEMORY USAGE: 27296 11440 7317 15 0 13013 0
DvCfgProfileList::obtainSystemProfileList() MID 2 MEMORY USAGE: 27296 11443 7317 15 0 13013 0
DvCfgProfileList::obtainSystemProfileList POST MEMORY USAGE: 27288 11443 7317 15 0 13005 0

The Big Question

Can I rely on reading /proc/statm for an immediate reading of process memory? This Unix/Linux Posting says it is "updated on every access".

If true, then why does it indicate that obtainSystemProfileList() is leaking?

EDIT I

I added the link to the Unix/Linux post. So if reads of /proc/.../statm result in a direct and immediate kernel call, then is there some time delay in the kernel updating its own internal results? If indeed there is no memory leak in the code fragment, then what else explains the change in mem values across a few lines of code?

EDIT II

Would calling getrusage() provide a more immediate and accurate view of process memory use? (or does it just make the same, potentially delayed, kernel calls as reading /proc/.../statm ?

Kernel is 32-bit 3.10.80-1 if that makes any difference...

Code Fragment:

bool
DvCfgProfileList::obtainSystemProfileList()
{
    TRACE(("DvCfgProfileList::obtainSystemProfileList() PRE "));
    DvComUtil::printMemoryUsage();

    DIR *pDir = opendir(SYSTEM_PROFILE_DIRECTORY);
    if (pDir == 0)
    {
        mkdir(SYSTEM_PROFILE_DIRECTORY, S_IRWXU | S_IRWXG | S_IRWXO);
        pDir = opendir(SYSTEM_PROFILE_DIRECTORY);
        if (pDir == 0)
        {
            TRACE(("%s does not exist or cannot be created\n", SYSTEM_PROFILE_DIRECTORY));
            return false;
        }
    }

    TRACE(("DvCfgProfileList::obtainSystemProfileList() MID 1 "));
    DvComUtil::printMemoryUsage();

    mpProfileList->clearSystemProfileList(); // calls (std::set) mProfileList.clear()

    TRACE(("DvCfgProfileList::obtainSystemProfileList() MID 2 "));
    DvComUtil::printMemoryUsage();

    struct dirent *pEntry;
    while ((pEntry = readdir(pDir)) != 0)
    {
        if (!strcmp(pEntry->d_name, ".") || !strcmp(pEntry->d_name, ".."))
            continue;

        TRACE(("Profile name = %s\n", pEntry->d_name));
        mpProfileList->addSystemProfile(std::string(pEntry->d_name));
    }
    closedir(pDir);

    printf("DvCfgProfileList::obtainSystemProfileList POST ");
    DvComUtil::printMemoryUsage();

    return true;
}


/* static */ void
DvComUtil::printMemoryUsage()
{
    char fname[256], line[256];
    sprintf(fname, "/proc/%d/statm", getpid());

    FILE *pFile = fopen(fname, "r");
    if (!pFile)
        return;

    fgets(line, 255, pFile);
    fclose(pFile);
    printf("MEMORY USAGE: %s", line);
}