【问题标题】:Attempt at proof of work reading/writing Graphviz DAG values causes segfault尝试读取/写入 Graphviz DAG 值的工作证明会导致段错误
【发布时间】:2018-07-18 02:51:25
【问题描述】:

更新:在使用 -g 编译并检查有问题的每一行之后,它现在已经设法在系统级头文件中出现段错误:

Program received signal SIGSEGV, Segmentation fault.
__gnu_cxx::new_allocator<char*>::construct<char*, char* const&> (this=0x7fffffffdec0, 
__p=0x5555557702f0, __args#0=<error reading variable>)
at /usr/include/c++/8.1.1/ext/new_allocator.h:136
136             { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }

这是我以前从未见过的。

所以我有一个非常简单的 C++ 程序,它至少尝试同时写入和读取文件,以确保 DAG 中绝对没有重复值(变量名称更改为保护个人商业秘密):

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <vector>

struct parsedline {
    unsigned long long pre_edge;
    unsigned long long post_edge;
};

std::vector<parsedline> readfile(std::string name) {
    //create array of parsedlines for later use
    std::vector<parsedline> edges;

    //count lines and create array of lines
    int linecount = 0;
    std::vector<char*> lines;
    std::ifstream dag(name);
    std::string ul;

    while (std::getline(dag, ul)) {

        //convert to c_str immediately to avoid errors with atoi
        std::istringstream thisline;
        thisline.str(ul);
        lines[linecount] = const_cast<char*>(thisline.str().c_str());
        ++linecount;

    }

    //drop block definition in graphviz file representing DAG
    int max_readable_line = sizeof(lines) - 2;
    std::vector<char*> readable_lines;

    for (int i = 0; i < max_readable_line; i++) {

        //shift index to skip line 1
        int rlindex = i+1;
        readable_lines[i] = lines[rlindex];

    }

    //read integers from each line, store them in array of parsedline objects
    for (int j = 0; j < max_readable_line; j++) {

        int edge_begin, edge_end;
        if (std::sscanf(readable_lines[j], "   %d -> %d", &edge_begin, &edge_end)) {
            edges[j].pre_edge = edge_begin;
            edges[j].post_edge = edge_end;
        }

    }
    return edges;
}

int main (int argc, char **argv) {

    unsigned long long minperrank = atoi(argv[1]);
    unsigned long long maxperrank = atoi(argv[2]);
    unsigned long long minranks = atoi(argv[3]);
    unsigned long long maxranks = atoi(argv[4]);
    double edgechance = atof(argv[5]);
    std::string filedest(argv[6]);

    unsigned long long i,j,k,l,nodes = 0;
    std::srand(time(NULL));

    unsigned long long ranks = minranks + (std::rand() % (maxranks - minranks + 1));

    std::ofstream dagout(filedest, std::ofstream::out);
    std::vector<parsedline> lines = readfile(filedest);

    dagout << "digraph {" << std::endl;

    for(i = 0; i < ranks; i++) {
        unsigned long long newnodes = minperrank + (rand() % (maxperrank - minperrank + 1));

        for (j = 0; j < nodes; j++) {

            for (k = 0; k < newnodes; k++) {

                unsigned long long checkval = std::rand() % 100;

                if (checkval < edgechance) {

                    for (l = 0; l < sizeof(lines); l++) {

                        //each new member must be a nonce
                        unsigned long long pree = lines[l].pre_edge;
                        unsigned long long poste = lines[l].post_edge;
                        if (checkval != pree) {

                            if (checkval != poste) {

                                dagout << "   " << j << " -> " << k+nodes << ";" << std::endl;

                            }

                        }

                    }

                }

            }

        }

        nodes += newnodes;

    }

    dagout << "}" << std::endl;
    return 0;
}

这编译得很好,但是当我尝试运行它时:

[realkstrawn93@archlinux Desktop]$ ./gendag 10 10 10 10 0.1 test.gv
Segmentation fault (core dumped)
[realkstrawn93@archlinux Desktop]$ 

这是 gdb 的输出 —— 它说它在 readfile 函数中,但没有具体说明它在该函数中的位置:

[realkstrawn93@archlinux Desktop]$ gdb gendag
GNU gdb (GDB) 8.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
<http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from gendag...(no debugging symbols found)...done.
(gdb) r 10 10 10 10 0.1 test.gv
Starting program: /home/realkstrawn93/Desktop/gendag 10 10 10 10 0.1 test.gv

Program received signal SIGSEGV, Segmentation fault.
0x0000555555555675 in readfile(std::__cxx11::basic_string<char, 
std::char_traits<char>, std::allocator<char> >) ()

我需要知道段错误在该函数中的确切位置。

【问题讨论】:

标签: c++ linux graphviz directed-acyclic-graphs


【解决方案1】:
//count lines and create array of lines
int linecount = 0;
std::vector<char*> lines;  <--
std::ifstream dag(name);
std::string ul;

while (std::getline(dag, ul)) {

    //convert to c_str immediately to avoid errors with atoi
    std::istringstream thisline;
    thisline.str(ul);
    lines[linecount] = const_cast<char*>(thisline.str().c_str()); <--
    ++linecount;

}

您在初始化之前访问lines[linecount]。尽管向量被初始化为空,但它的元素没有被初始化。使用

lines.push_back(const_cast<char*>(thisline.str().c_str()));

如果您只需要将一个元素附加到向量的末尾,则不要使用访问器运算符 ([])。

否则,如果您必须按位置访问元素,请首先通过向构造函数传递参数进行初始化,如 in

std::vector&lt;char*&gt; lines(max_lines);

这将初始化向量以包含max_lines 空对象。

当您的程序访问不属于它的内存时,会发生分段错误。由于向量总是在堆上分配,访问任何超出其末尾的数据都会导致段错误。

【讨论】:

  • 原因同理,你访问readable_lines[i]没有初始化。当我关注你的程序时,我只注意到这个问题的第一次出现。
  • 抱歉耽搁了,不得不构建你的代码来验证。 thisline.str().c_str()const char* 类型,而你的向量是 char* 类型。这使得它无法分配,因为它没有找到有效的构造函数。只需将向量更改为 std::vector&lt;const char*&gt; lines 即可。
  • 但是请注意,如果您打算返回结果 char*,则 c_str() 不可用。一旦创建它的字符串超出范围,该方法返回的指针就会失效。
  • 最后一件事,您使用sizeof(lines) 来获取向量的长度是错误的。 sizeof 返回字节大小,这不是您想要的。你想要的是lines.size()
  • 别忘了更改另一个实例,std::vector&lt;const char*&gt; readable_lines; 在使用 g++ 和 msbuild 进行这些更改后,我可以正常构建和运行代码,没有错误。
【解决方案2】:

事实证明,在继续之前,我必须检查数组的大小是否为 0。否则就是空指针解引用:

if (lines.size() == 0) {

    dagout << "   " << j << " -> " << k+nodes << ";" << std::endl;

} else {

    //each new member must be a nonce
    unsigned long long pree = lines.at(l).pre_edge;
    unsigned long long poste = lines.at(l).post_edge;
    if (std::rand() % 100 != pree) {

        if (std::rand() % 100 != poste) {

            dagout << "   " << j << " -> " << k+nodes << ";" << std::endl;

        }

    }

}

不过,现在我遇到了一些无限循环问题——这是一个完全不相关的问题,我可以自己解决。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-12-12
    • 2011-02-06
    • 2015-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多