Docker容器中多线程AlignGraph程序抛出std::length_error求助
问题定位与修复建议:Docker中AlignGraph触发std::length_error异常
问题概述
在Docker容器中运行多线程C++基因组比对工具AlignGraph时,触发std::length_error异常,异常发生在某线程的std::string::append字符串操作环节。该工具在容器外本地运行正常,已排查编译问题、尝试小输入数据,错误仍触发。
运行命令
Starting program: /opt/AlignGraph/AlignGraph/AlignGraph --read1 sample1.fasta --read2 sample2.fasta --contig /data/run/plentyofbugs_out/assembly/contigs.fasta --genome /data/reference_genome/GCA_013814885.1_ASM1381488v1_genomic.fna --distanceLow 100 --distanceHigh 1000 --extendedContig extendedContigs.fa --remainingContig remainingContigs.fa
调试栈回溯信息
Thread 3 "AlignGraph" received signal SIGABRT, Aborted. [Switching to Thread 0x7ffff72016c0 (LWP 91)] __pthread_kill_implementation (no_tid=0, signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:44 warning: 44 ./nptl/pthread_kill.c: No such file or directory (gdb) backtrace #0 __pthread_kill_implementation (no_tid=0, signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:44 #1 __pthread_kill_internal (signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:78 #2 __GI___pthread_kill (threadid=<optimized out>, signo=signo@entry=6) at ./nptl/pthread_kill.c:89 #3 0x00007ffff7b3f26e in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26 #4 0x00007ffff7b228ff in __GI_abort () at ./stdlib/abort.c:79 #5 0x00007ffff7ddeffe in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6 #6 0x00007ffff7df3e9c in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6 #7 0x00007ffff7ddea49 in std::terminate() () from /lib/x86_64-linux-gnu/libstdc++.so.6 #8 0x00007ffff7df4128 in __cxa_throw () from /lib/x86_64-linux-gnu/libstdc++.so.6 #9 0x00007ffff7de2381 in std::__throw_length_error(char const*) () from /lib/x86_64-linux-gnu/libstdc++.so.6 #10 0x0000555555562b51 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_check_length (__s=0x555555580071 "basic_string::append", __n2=6, __n1=0, this=0x7ffff7200b30) at /usr/include/c++/13/bits/allocator.h:184 #11 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::append (__s=0x5555555802ce ".delta", this=0x7ffff7200b30) at /usr/include/c++/13/bits/basic_string.h:1473 #12 std::operator+<char, std::char_traits<char>, std::allocator<char> > (__rhs=0x5555555802ce ".delta", __lhs=...) at /usr/include/c++/13/bits/basic_string.h:3690 #13 task1 (arg=<optimized out>) at AlignGraph.cpp:3640 #14 0x00007ffff7b96a94 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:447 #15 0x00007ffff7c23c3c in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:78
对应代码片段(AlignGraph.cpp第3631-3656行)
3631 //NUCMER 3632 for(chromosomeID = 0; chromosomeID < ins.numChromosomes; chromosomeID ++) 3633 { 3634 command = "nucmer tmp/_genome." + itoa(chromosomeID) + ".fa tmp/_contigs.fa -p tmp/_contigs_genome." + itoa(chromosomeID) + " > nucmer_doc.txt 2> nucmer_doc.txt"; 3635 if(system(command.c_str()) != 0) 3636 { 3637 command = "touch tmp/_contigs_genome." + itoa(chromosomeID) + ".delta"; 3638 system(command.c_str()); 3639 } 3640 command = "tmp/_contigs_genome." + itoa(chromosomeID) + ".delta"; 3641 delta2psl(command); 3642 } 3643 } 3644 else 3645 { 3646 for(chromosomeID = 0; chromosomeID < ins.numChromosomes; chromosomeID ++) 3647 { 3648 command = "pblat tmp/_genome." + itoa(chromosomeID) + ".fa tmp/_contigs.fa -noHead tmp/_contigs_genome." + itoa(chromosomeID) + ".psl -fastMap -threads=8 > blat_doc.txt 2> blat_doc.txt"; 3649 if(system(command.c_str()) != 0) 3650 { 3651 command = "blat tmp/_genome." + itoa(chromosomeID) + ".fa tmp/_contigs.fa -noHead tmp/_contigs_genome." + itoa(chromosomeID) + ".psl -fastMap > blat_doc.txt 2> blat_doc.txt"; 3652 if(system(command.c_str()) != 0) {cout << "BLAT CALL FAILED!" << endl; exit(-1);} 3653 } 3654 } 3655 } 3656 }
根因分析
核心问题在于代码中使用了非标准C++函数itoa:
itoa并非C++标准库函数,属于平台相关的扩展函数。本地环境(可能是Windows或特定Linux发行版)的libc提供了该函数,但Docker容器内的glibc通常没有实现itoa,或者其实现返回的是指向栈内存的指针。- 多线程环境下,栈内存会被频繁覆盖,当
itoa返回的栈指针在后续字符串拼接(std::string::append)时被访问,会读取到无效的内存内容,导致字符串长度校验失败,触发std::length_error异常。 - 本地运行正常是因为本地libc的
itoa实现可能使用了静态缓冲区或其他安全机制,而容器内的实现不具备该特性。
代码修复方案
将所有itoa(chromosomeID)替换为标准C++字符串转换方法std::to_string(chromosomeID),同时确保代码头部包含<string>头文件(如果尚未包含)。
修复后的核心代码片段:
3634 command = "nucmer tmp/_genome." + std::to_string(chromosomeID) + ".fa tmp/_contigs.fa -p tmp/_contigs_genome." + std::to_string(chromosomeID) + " > nucmer_doc.txt 2> nucmer_doc.txt"; // ... 3637 command = "touch tmp/_contigs_genome." + std::to_string(chromosomeID) + ".delta"; // ... 3640 command = "tmp/_contigs_genome." + std::to_string(chromosomeID) + ".delta"; // ... 3648 command = "pblat tmp/_genome." + std::to_string(chromosomeID) + ".fa tmp/_contigs.fa -noHead tmp/_contigs_genome." + std::to_string(chromosomeID) + ".psl -fastMap -threads=8 > blat_doc.txt 2> blat_doc.txt"; // ... 3651 command = "blat tmp/_genome." + std::to_string(chromosomeID) + ".fa tmp/_contigs.fa -noHead tmp/_contigs_genome." + std::to_string(chromosomeID) + ".psl -fastMap > blat_doc.txt 2> blat_doc.txt";
调试步骤
- 验证
itoa可用性:在Docker容器内编译并运行如下测试程序,检查itoa是否存在及返回值是否有效:#include <stdio.h> int main() { int num = 1; char* str = itoa(num); printf("%s\n", str); return 0; } - 替换
itoa后重新编译AlignGraph,在容器内运行测试,确认异常是否消失。 - 使用gdb在容器内调试,查看替换前
itoa返回的字符串内容,确认是否存在内存无效的情况。
最小复现示例建议
- 编写多线程测试程序,模拟原代码的字符串拼接逻辑:
在Docker容器内编译运行该程序,观察是否触发类似的#include <iostream> #include <string> #include <pthread.h> void* thread_func(void* arg) { int id = *(int*)arg; std::string cmd = "test." + itoa(id) + ".txt"; // 使用itoa std::cout << cmd << std::endl; return nullptr; } int main() { pthread_t threads[5]; int ids[5] = {0,1,2,3,4}; for(int i=0; i<5; i++) { pthread_create(&threads[i], nullptr, thread_func, &ids[i]); } for(int i=0; i<5; i++) { pthread_join(threads[i], nullptr); } return 0; }std::length_error异常。 - 使用最小规模的基因组和contig文件,仅保留NUCMER分支的逻辑,简化原程序进行测试。
内容的提问来源于stack exchange,提问作者somilsharma
相关产品推荐
相关产品推荐

