【问题标题】:User Leak, libc++ leak or false positive用户泄漏、libc++ 泄漏或误报
【发布时间】:2015-04-04 04:04:46
【问题描述】:

我正在使用 clang 编译器和 libc++ 标准库在 C++11 的 mac 上构建一个动态库。当我在链接到动态库的测试代码上运行 valgrind 时,我得到一块肯定丢失的内存。这是 valgrind 报告:

==45659== 36 bytes in 1 blocks are definitely lost in loss record 57 of 228
==45659==    at 0x66BB: malloc (vg_replace_malloc.c:300)
==45659==    by 0x31EAB0: __Balloc_D2A (in /usr/lib/system/libsystem_c.dylib)
==45659==    by 0x31F2A5: __d2b_D2A (in /usr/lib/system/libsystem_c.dylib)
==45659==    by 0x31BED6: __dtoa (in /usr/lib/system/libsystem_c.dylib)
==45659==    by 0x3438A9: __vfprintf (in /usr/lib/system/libsystem_c.dylib)
==45659==    by 0x36A2DA: __v2printf (in /usr/lib/system/libsystem_c.dylib)
==45659==    by 0x34FF66: _vsnprintf (in /usr/lib/system/libsystem_c.dylib)
==45659==    by 0x34FFC5: vsnprintf_l (in /usr/lib/system/libsystem_c.dylib)
==45659==    by 0x34057A: snprintf_l (in /usr/lib/system/libsystem_c.dylib)
==45659==    by 0x10C75A: std::__1::num_put<char, std::__1::ostreambuf_iterator<char, std::__1::char_traits<char> > >::do_put(std::__1::ostreambuf_iterator<char, std::__1::char_traits<char> >, std::__1::ios_base&, char, double) const (in /usr/lib/libc++.1.dylib)
==45659==    by 0xF3221: std::__1::basic_ostream<char, std::__1::char_traits<char> >::operator<<(double) (in /usr/lib/libc++.1.dylib)
==45659==    by 0x12102: lmpsdata::header_data::write_dimension(std::__1::basic_ofstream<char, std::__1::char_traits<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) (header_data.cpp:75)
==45659== 
==45659== LEAK SUMMARY:
==45659==    definitely lost: 36 bytes in 1 blocks
==45659==    indirectly lost: 0 bytes in 0 blocks
==45659==      possibly lost: 0 bytes in 0 blocks
==45659==    still reachable: 18,340 bytes in 215 blocks
==45659==         suppressed: 25,274 bytes in 374 blocks

该特定代码部分不处理我动态分配的任何内存,仅使用 STL 对象和流。我附上了头文件和 valgrind 报告的导致泄漏的方法。有人可以解释一下这里发生了什么吗?我真的很困惑。

header_data.h

    #ifndef ____header_data__
#define ____header_data__

#include <string>
#include <fstream>
#include <cstdint>
#include <vector>
#include <map>

namespace lmpsdata {
class header_data
{//LAMMPS header information
    //only the point particle header information has been implemented
    //since the current atom base class is designed for point particles only
public:
    header_data():xdim(2), ydim(2), zdim(2), tiltdim(3) {}
    //methods
    void read(const std::string &, const std::string &);
    void write(std::ofstream &, const std::string &); //this write command will replace the one
    std::string check_header_keyword(const std::string &, bool &);
    uint64_t get(const std::string&);
    std::vector<double>& get_vector(const std::string&);
    void set(const std::string&, uint64_t);
    void set_vector(const std::string&, std::vector<double>&);

private:
    //methods
    void read_dimension(const std::string&, const std::string&);
    void write_dimension(std::ofstream&, const std::string&);

    //members
    uint64_t atomnum;
    uint64_t bondnum;
    uint64_t anglenum;
    uint64_t dihedralnum;
    uint64_t impropernum;
    uint64_t atomtypenum;
    uint64_t bondtypenum;
    uint64_t angletypenum;
    uint64_t dihedraltypenum;
    uint64_t impropertypenum;
    uint64_t extrabondnum;

    std::vector<double> xdim;
    std::vector<double> ydim;
    std::vector<double> zdim;
    std::vector<double> tiltdim;//for use with triclinic system

    std::map<std::string, uint64_t&> int_map  {
        {"atoms", atomnum},
        {"bonds", bondnum},
        {"angles", anglenum},
        {"dihedrals", dihedralnum},
        {"impropers", impropernum},
        {"atom types", atomtypenum},
        {"bond types", bondtypenum},
        {"angle types", angletypenum},
        {"dihedral types", dihedraltypenum},
        {"improper types", impropertypenum},
        {"extra bond per atom", extrabondnum},
    };
    std::map<std::string, std::vector<double>&> v_map {
        {"xlo xhi", xdim},
        {"ylo yhi", ydim},
        {"zlo zhi", zdim},
        {"xy xz yz", tiltdim}
    };
};
}
#endif /* defined(____header_data__) */

lmpsdata.cpp 仅显示 write_dimension 方法和文件开头

#include "header_data.h"
#include <stdexcept>
using namespace lmpsdata;
void header_data::write_dimension(std::ofstream &file, const std::string& keyword)
{
    std::vector<double>& data = v_map.at(keyword);
    for (auto value: data) {
        file << value <<  " ";
    }
}

如果需要更多信息,请告诉我。

【问题讨论】:

  • 您是否确保传入header_data::write_dimension() 的ofstream 被正确关闭并在之后被删除?另外,您是否尝试过将语句 file &lt;&lt; value &lt;&lt; " "; 拆分为多个语句和行,以找出问题是否与 ofstream 或格式化命令有关?另外,您使用的是最新的 Valgrind 版本吗?
  • 上述 valgrind 报告发生在 ostream 关​​闭和打开的情况下。第一次运行 valgrind 时,我忘记了关闭流,我认为这就是问题所在。但问题仍然存在。
  • 我正在使用 valgrind-3.10.1。我稍后会检查格式。

标签: c++ memory-leaks clang valgrind libc++


【解决方案1】:

您没有提到您正在使用的 OSX 和 valgrind 的确切版本。我无法在我正在使用的版本(OSX 10.10;valgrind HEAD==Valgrind-3.11.0.SVN)上完全重现这一点。

这不在C++ 标准库中,而是在C 库中。您应该能够使用简单的代码(几乎相同)重现它:

#include <stdio.h>
#include <xlocale.h>

int
main(int argc, char **argv)
{
    locale_t loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0);
    double d = 22.22;
    char buffer[1024];
    snprintf_l(buffer, 1024, loc, "%f\n", d);
    printf("%s", buffer);
    freelocale(loc);
}

当使用valgrind --show-leak-kinds=all --leak-check=full ./leak 运行时,我看到一些“仍然可以访问”的泄漏(在这种情况下,您应该显示实际的泄漏):

==26151== 32 bytes in 1 blocks are still reachable in loss record 28 of 85
==26151==    at 0x10000850B: malloc (in /usr/local/Cellar/valgrind/HEAD/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==26151==    by 0x1002BC7DF: __Balloc_D2A (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x1002B9533: __rv_alloc_D2A (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x1002B9B3A: __dtoa (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x1002E1D52: __vfprintf (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x10030A9AE: __v2printf (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x1002EF154: _vsnprintf (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x1002EF1B3: vsnprintf_l (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x1002DF5F7: snprintf_l (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x100000ECA: main (leak.cpp:10)
==26151== 
==26151== 36 bytes in 1 blocks are still reachable in loss record 30 of 85
==26151==    at 0x10000850B: malloc (in /usr/local/Cellar/valgrind/HEAD/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==26151==    by 0x1002BC7DF: __Balloc_D2A (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x1002BD055: __d2b_D2A (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x1002B986B: __dtoa (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x1002E1D52: __vfprintf (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x10030A9AE: __v2printf (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x1002EF154: _vsnprintf (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x1002EF1B3: vsnprintf_l (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x1002DF5F7: snprintf_l (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x100000ECA: main (leak.cpp:10)
==26151== 
==26151== 80 bytes in 1 blocks are still reachable in loss record 47 of 85
==26151==    at 0x10000850B: malloc (in /usr/local/Cellar/valgrind/HEAD/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==26151==    by 0x1002BC736: __Balloc_D2A (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x1002BD055: __d2b_D2A (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x1002B986B: __dtoa (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x1002E1D52: __vfprintf (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x10030A9AE: __v2printf (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x1002EF154: _vsnprintf (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x1002EF1B3: vsnprintf_l (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x1002DF5F7: snprintf_l (in /usr/lib/system/libsystem_c.dylib)
==26151==    by 0x100000ECA: main (leak.cpp:10)

我会对此进行压制,因为它几乎毫无疑问不是真正的泄漏,并且完全不受您的控制。

查看已发布的OSX libc source,我看不到错误出现在哪里——320 和后来的一切看起来都还可以;底层代码似乎在必要时分配和释放。不过,该错误可能已被引入并修复,因为我没有对所有来源进行详尽的拖网;有问题的文件是vfprintf.c,您正在查看dtoaresult assignments & frees。

要确定您的 libc 版本,您可以:

$ otool -l /usr/lib/system/libsystem_c.dylib | grep -A5 ID_

在我的系统上,我得到了输出:

          cmd LC_ID_DYLIB
      cmdsize 64
         name /usr/lib/system/libsystem_c.dylib (offset 24)
   time stamp 1 Thu Jan  1 01:00:01 1970
      current version 1044.10.1
compatibility version 1.0.0

997,我认为,是小牛队(10.9)。我不知道他们是否在小牛队时间范围内的某个时间点在代码中引入了泄漏然后修复它 - __vfprintf 例程中似乎没有代码路径实际上在任何已发布的源中泄漏.

【讨论】:

  • 我在小牛(10.9),libsystem_c.dylib的版本是997.90.3。当我在 valgrind 中运行代码时,我没有得到任何明确丢失的泄漏,只有 4 个仍然可以访问,并且它们与您的相同。我认为你的权利这可能不是真正的泄漏。我想知道这是否是 valgrind-3.10.1 和 C++11 的问题。
  • 这很容易成为 valgrind 3.10.1 错误报告泄漏的问题,因为它无法正确跟踪分配 - 我见过其中的一些,通常使用系统级抑制文件以防止报告这些泄漏。
  • 谢谢,我会研究如何设置抑制文件。
  • 传入 --gen-suppressions=yes 以获得相应的抑制,并直接从代码中删除行,以使其在出现在不同代码路径下时更具可回收性
猜你喜欢
  • 2011-11-02
  • 1970-01-01
  • 2011-07-21
  • 1970-01-01
  • 1970-01-01
  • 2016-06-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-09
相关资源
最近更新 更多