我正在运行此代码(为了保持点而简化):
int main(int argc, char** argv) {
char* videoPath = args[1];
DataSource* dataSource;
std::thread dataSourceThread;
dataSource = new FileDataSource(0.5f, true);
dataSourceThread = std::thread(&FileDataSource::start, (FileDataSource*)dataSource, videoPath, 50);
logger->info("FileDataSource started");
float invScale = 1.f / dataSource->getScale();
cv::Mat frame;
unsigned long bufferIndexPrev = 0;
unsigned long t;
cv::Mat projMat(3, 4, CV_32F);
cv::Mat rVec, tVec;
while (!dataSource->isRunning()) std::this_thread::yield();
logger->info("Data source running");
Tracker tracker;
tracker.dataSource = dataSource;
std::thread trackerThread(&Tracker::start, &tracker);
while (tracker.stateBuffer == nullptr) std::this_thread::yield();
logger->info("Tracker started");
while (true) {
int bufferIndex = tracker.stateBuffer->bufferIndex;
if (bufferIndex == bufferIndexPrev) {
if (dataSource->isRunning()) {
// std::this_thread::sleep_for(std::chrono::milliseconds(1));
std::this_thread::yield();
continue;
}
else {
break;
}
}
bufferIndexPrev = bufferIndex;
State& state = tracker.stateBuffer->buffer[bufferIndex];
Data data = state.data;
int lastProcessedFrameNum = tracker.stateBuffer->buffer[bufferIndex].dataPacket.frameNumber;
dataSource->getOriginalFrameByFrameNum(lastProcessedFrameNum, frame);
cv::putText(frame, "Processing", cv::Point(20, 30), CV_FONT_HERSHEY_PLAIN, 2, CV_RGB(255, 0, 0));
// ^^^^^^^^^ this line causes the crash
cv::putText(frame, std::to_string(lastProcessedFrameNum), cv::Point(200, 30), CV_FONT_HERSHEY_PLAIN, 2, CV_RGB(255, 255, 0));
std::vector<cv::KeyPoint> keypoints = state.keypoints;
std::vector<int> indices = state.indices;
for (int i = 0; i < keypoints.size(); i++) {
cv::Point2f detectedPoint = keypoints[i].pt * invScale;
if (state.indices[i] >= 0)
cv::drawMarker(frame, detectedPoint, CV_RGB(0, 255, 0), cv::MARKER_CROSS);
else
cv::drawMarker(frame, detectedPoint, CV_RGB(255, 0, 0), cv::MARKER_CROSS);
}
cv::imshow("Video", frame);
int key = cv::waitKey(10);
if (key == 32) {
tracker.nextPhase();
}
}
logger->info("Exited main loop");
if (trackerThread.joinable()) trackerThread.join();
if (dataSourceThread.joinable()) dataSourceThread.join();
return 0;
}
现在,程序有时在分段错误(SIGSEGV)上崩溃
cv::putText(frame, "Processing", cv::Point(20, 30), CV_FONT_HERSHEY_PLAIN, 2, CV_RGB(255, 0, 0));
我使用GDB调试它。我看到它的方式,唯一可能导致问题的是矩阵对象frame
。
我也可以确认这一点,因为如果我放置cv::Mat
的另一个实例而不是frame
,代码永远不会崩溃。
数据源正在运行自己的获取视频帧并将其放入循环缓冲区的循环。主程序在需要时从该缓冲区中获取视频帧。
void FileDataSource::start(char* fileVideoPath, int fps) {
logger->info("FileDataSource opening FileVideo {}", fileVideoPath);
FileVideo video(fileVideoPath);
unsigned long period = 1.e+6 / fps; // microseconds
std::chrono::microseconds periodDuration(period);
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
while (true) {
int nextBufferIndex = (bufferIndex + 1) % bufferSize;
cv::Mat& originalFrame = originalFrameBuffer[nextBufferIndex];
bool success = video.getFrame(originalFrame);
if (!success) break;
cv::Mat& reducedFrame = reducedFrameBuffer[nextBufferIndex];
cvtColor(originalFrame, reducedFrame, CV_BGR2GRAY);
if (!this->presized) resize(reducedFrame, reducedFrame, cv::Size(), scale, scale);
frameNumBuffer[nextBufferIndex] = frameNumBuffer[bufferIndex] + 1;
tBuffer[nextBufferIndex] = tBuffer[bufferIndex] + period;
bufferIndex = nextBufferIndex;
this->running = true;
std::this_thread::sleep_until(now + periodDuration);
now = std::chrono::system_clock::now();
}
this->running = false;
}
void FileDataSource::getOriginalFrameByFrameNum(unsigned long frameNum, cv::Mat& frame) {
int lBufferIndex = this->bufferIndex;
for (int i = 0; i < bufferSize; i++)
{
if (frameNumBuffer[lBufferIndex] == frameNum) break;
lBufferIndex = (lBufferIndex - 1 + bufferSize) % bufferSize;
}
frame = originalFrameBuffer[lBufferIndex];
}
tracker
既不会读取也不会写入originalFrameBuffer
,只会从reducedFrameBuffer
读取,但这不会显示在代码中,因为帖子足够大。
从another SO question我了解分段错误的本质:
分段错误是一种由访问引起的特定错误 记忆“不属于你。”它是一种帮助机制 使您免于破坏内存并引入难以调试 内存错误。每当你遇到段错误时,你就知道自己在做什么 内存有问题 - 访问已经存在的变量 释放,写入内存的只读部分等。
我真的不明白这是如何适用于我的情况的。有什么想法吗?
GDB回溯显示:
Thread 1 "videoGTKExample" received signal SIGSEGV, Segmentation fault.
0x00007fffed095281 in malloc_consolidate () from /lib/libc.so.6
(gdb) bt
#0 0x00007fffed095281 in malloc_consolidate () at /lib/libc.so.6
#1 0x00007fffed096d2a in _int_malloc () at /lib/libc.so.6
#2 0x00007fffed098d44 in malloc () at /lib/libc.so.6
#3 0x00007fffed966a78 in operator new(unsigned long) (sz=8192) at /build/gcc-multilib/src/gcc/libstdc++-v3/libsupc++/new_op.cc:50
#4 0x00007fffeed3d684 in cv::putText(cv::_InputOutputArray const&, cv::String const&, cv::Point_<int>, int, double, cv::Scalar_<double>, int, int, bool) ()
at /usr/local/lib/libopencv_imgproc.so.3.1
#5 0x00000000004a920b in main(int, char**) (argc=4, argv=0x7fffffffe0d8) at /home/andro/tracker/examples/videoGTKExample.cpp:180
P.S。 如果我用valgrind运行代码,我无法重现错误,因为它执行得慢得多,并且线程的并发性似乎不再是问题。
更新
我通过以下方式设法重现了SIGSEGV崩溃:
valgrind --tool=exp-sgcheck
生成的日志为here。我不确定这是否是同样的错误,因为它发生在没有定期发生的行。我是valgrind的新手,不知道这个日志中是否有任何有用的东西。我所看到的只是一个堆栈跟踪,类似于我在正常的gdb会话中看到的。
更新2:
在gdb
我有时(很少)会遇到以下崩溃:
Thread 1 "videoGTKExample" received signal SIGABRT, Aborted.
0x00007fffed05104f in raise () from /lib/libc.so.6
(gdb) bt
#0 0x00007fffed05104f in raise () at /lib/libc.so.6
#1 0x00007fffed05247a in abort () at /lib/libc.so.6
#2 0x00007fffed08ec50 in __libc_message () at /lib/libc.so.6
#3 0x00007fffed094fe6 in malloc_printerr () at /lib/libc.so.6
#4 0x00007fffed09536c in malloc_consolidate () at /lib/libc.so.6
#5 0x00007fffed096d2a in _int_malloc () at /lib/libc.so.6
#6 0x00007fffed098d44 in malloc () at /lib/libc.so.6
#7 0x00007fffed966a78 in operator new(unsigned long) (sz=8192) at /build/gcc-multilib/src/gcc/libstdc++-v3/libsupc++/new_op.cc:50
#8 0x00007fffeed3d684 in cv::putText(cv::_InputOutputArray const&, cv::String const&, cv::Point_<int>, int, double, cv::Scalar_<double>, int, int, bool) ()
at /usr/local/lib/libopencv_imgproc.so.3.1
#9 0x00000000004a951b in main(int, char**) (argc=4, argv=0x7fffffffe0d8) at /home/andro/stypevisualirtracker/examples/videoGTKExample.cpp:180
从此question:
abort()向调用进程发送SIGABRT信号,这是怎么回事 abort()基本上有效。
abort()通常由检测a的库函数调用 内部错误或一些严重破坏的约束。例如 如果内部结构被a损坏,malloc()将调用abort() 堆溢出。
所以这肯定是由损坏的堆引起的。