用户泄漏、libc++ 泄漏或误报
User Leak, libc++ leak or false positive
我正在使用 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 << " ";
}
}
如果需要更多信息,请告诉我。
您没有提到您正在使用的 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
我看到一些 'still reachable' 泄漏(在这种情况下你的应该显示实际泄漏):
==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, I can't see where the bug is present - 320 and later all look OK; with the underlying code appearing to allocate and free where necessary. The bug may have been introduced and fixed, though, as I didn't perform an exhaustive trawl of all the sources; the file in question is vfprintf.c,您正在查看 dtoaresult
分配和释放。
要确定您的 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
例程中似乎没有代码路径实际上在任何已发布来源。
我正在使用 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 << " ";
}
}
如果需要更多信息,请告诉我。
您没有提到您正在使用的 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
我看到一些 'still reachable' 泄漏(在这种情况下你的应该显示实际泄漏):
==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, I can't see where the bug is present - 320 and later all look OK; with the underlying code appearing to allocate and free where necessary. The bug may have been introduced and fixed, though, as I didn't perform an exhaustive trawl of all the sources; the file in question is vfprintf.c,您正在查看 dtoaresult
分配和释放。
要确定您的 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
例程中似乎没有代码路径实际上在任何已发布来源。