valgrind报告使用std :: string

时间:2018-09-11 06:43:39

标签: c++ c++11 gcc valgrind

我正在研究在树莓派3上运行的代码。在我的日志记录类中收到以下错误。

==1297== Invalid read of size 8
==1297==    at 0x4865D1C: ??? (in /usr/lib/arm-linux-gnueabihf/libarmmem.so)
==1297==  Address 0x4c8d45c is 100 bytes inside a block of size 107 alloc'd
==1297==    at 0x4847DA4: operator new(unsigned int) (vg_replace_malloc.c:328)
==1297==    by 0x49C3D9B: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::reserve(unsigned int) (in /usr/lib/arm-linux-gnueabihf/libstdc++.so.6.0.22)
==1297==    by 0x4AE65: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > std::operator+<char, std::char_traits<char>, std::allocator<char> >(char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (basic_string.tcc:1155)
==1297==    by 0xF82B5: Log::Book::addField(std::unique_ptr<Log::Entry, std::default_delete<Log::Entry> >&, unsigned int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (LogBook.cpp:149)
==1297==    by 0xF7CCB: Log::Book::record(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int, unsigned int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long long, std::ratio<1ll, 1000000000ll> > >) (LogBook.cpp:87)

GCC版本:gcc版本6.3.0 20170516(Raspbian 6.3.0-18 + rpi1 + deb9u1)
valgrind版本:valgrind-3.13.0
 我似乎无法找到问题所在,因为函数Log :: Book :: record()通过传递值来获取它的值。我也可以说在调用函数时并不总是显示此错误。从错误显示在哪一行,错误显示在哪行的意义上说,它是确定性的。任何人都可以指导我解决这个问题以及解决的方法吗?下面的代码段带有注释的行。

/** log message */
void Book::record(std::string file, const int line, const unsigned int level, Identifier id, const std::string message,
                  const std::chrono::high_resolution_clock::time_point timeStamp)
{
    if (!(fileLevels & level) && !(consoleLevels & level)) { return; }

    auto now = Time::keeper->now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(timeStamp - Time::globalEpoch);

    //generate message
    auto entry = std::make_unique<Entry>(level);

    // Time since startup
    addField(entry, 0, std::to_string(duration.count()));

    //UTC Time
    addField(entry, 1, now.dateTime());

    // File
    std::string stringFile;
    if (!file.empty())
    {
        stringFile = URL{file}.lastPathComponent();
    }
    addField(entry, 2, stringFile);

    //Line number
    addField(entry, 3, std::to_string(line));

    //ID
    addField(entry, 4, id);

    //Message
    std::string stringMessage;
    if(!message.empty())
    {
        addField(entry, 5, message); //this is line LogBook.cpp:87
    }
    else
    {
        addField(entry, 5, " empty message.");
    }
    *entry << ";";

    //queue message
    this->append(std::move(entry));
}
void Book::addField(std::unique_ptr<Entry> &entry, unsigned int index, const std::string &text)
{
    std::string textOutput;

    if ((spacings.at(index) != 0) && (text.length() > (spacings.at(index) - 1)))
    {
        spacings.at(index) = (uint8_t) (text.length() + 2);
    }

    entry->setWidth(spacings.at(index));

    if(entry->empty())
        textOutput = text;
    else
        textOutput = ";" + text;   //This is line LogBook.cpp:149

    if(!textOutput.empty())
        (*entry) << textOutput;
}

调用此函数并发生此问题的代码。

auto node = child(items, "item", index);
auto enabled = boolValue(node, "enabled", false);
auto file = pathValue(node, key::path);
auto name = stringValue(node, "name", "");
auto type = stringValue(node, "type");

CLOG(CLOG::WARNING, "Yard item " + name + " not enabled, path:" + file.path());

更新1: 我用带有选项的cmake进行编译。并增加了额外的选择。这些没有解决问题。

add_compile_options(-ggdb)
add_compile_options(-O1)

#Extra disable vectorization
add_compile_options(-fno-tree-vectorize)
add_compile_options(-fno-tree-loop-vectorize)
add_compile_options(-fno-tree-slp-vectorize)

更新2:
我找到了另一个使用字符串连接并且valgrind报告相同错误的地方


更新3:
一些时间和有趣的发现。 共享库libarmmem.so中发生错误。它会动态加载,因此始终位于其他地址上。使用gdb和valgrind组合可在发生错误时中断。
gdb使用起始地址加载了共享库。

(gdb) info sharedlibrary
From        To          Syms Read   Shared Object Library
0x0483246c  0x04832750  Yes         /usr/local/lib/valgrind/vgpreload_core-arm-linux.so
0x04846e60  0x04850c10  Yes         /usr/local/lib/valgrind/vgpreload_memcheck-arm-linux.so
0x04863588  0x048672fc  Yes (*)     /usr/lib/arm-linux-gnueabihf/libarmmem.so
...

valgrind报告的错误。

==9442== Invalid read of size 8
==9442==    at 0x4865D34: ??? (in /usr/lib/arm-linux-gnueabi/libarmmem.so)

我们从libarmmem.readread得知,.text节从588开始。memcpy位于710。在此断点处的反汇编表明我们在memcpy中,地址为0x04863710。如果我们检查范围,例如:0x04863588-0x04863710 =188。188 + 588(.text的起始地址)=710。
拆卸表明它发生在装配线上。 vldmia是用于加载向量浮点寄存器的指令。

0x04865d34 <+9764>: vldmia  r1!, {d9}

还没有解决办法。

1 个答案:

答案 0 :(得分:3)

大多数情况下,libarmem.so中的代码已被矢量化,以至于只有在读取完整的8字节块之后才意识到存在终止字符。这不会触发处理器异常(因为算法可确保指针对齐并因此停留在同一页面上),但会导致Valgrind等工具报告误报。

随着时间的流逝,此类问题变得越来越严重,这使得Valgrind在实践中的用处不大。进行详细讨论,请参见Valgrind vs Optimising Compilers;有关实际示例,请参见this bug in diff;有关更多示例,请参见my Debian suppression list