Crash dump analysis for multi threaded c++ application crash [closed] - c++

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
This is a question on generic scenario. I have a multi threaded c++ application which crashed and I have the crash dump. There might be hundreds of thread running and any of them might have caused the crash.
What would be the good approach to start analyzing the crash dump.
Among many threads (already logged information under dump file) how to find any specific thread causing the crash. Should I look for any specific criteria because I cannot go ahead and analyze all the threads and their stacks.
Any other useful information/clue you want to suggest.
Thank you very much in advance

We call the following code a minimum reproducible example and you should provide it as part of your question in the future.
It creates 100 threads, synchronizes them so that they all start running at the same time (needs C++ 20). One of the threads will generate an exception randomly, so that we don't know which one it was.
#include <random>
#include <thread>
#include <vector>
using namespace std::chrono_literals;
std::random_device rd;
std::mt19937 twister(rd());
std::uniform_int_distribution<int> dist(0, 100);
std::counting_semaphore<100> synchronizer(0);
void randomCrash()
{
synchronizer.acquire();
if (dist(twister) < 2)
{
throw std::exception();
}
std::this_thread::sleep_for(1000ms); // Ensure the thread is still there when we analyze the dump
}
int main()
{
std::vector<std::thread> threads;
for (int i = 0; i < 100; i++)
{
std::thread t(&randomCrash);
threads.push_back(std::move(t)); // Threads can't be copied, so move it
}
std::cout << "Created 100 threads.\r\n";
synchronizer.release(100);
std::cout << "100 threads running now.\r\n";
for (std::thread& th : threads)
{
if (th.joinable())
{
th.join();
}
}
std::cout << "Done. Ooops ... no exception happened? Well, that's randomness.\r\n";
}
If we now open the crash dump, we can see that it has already switched to thread 92 which caused the exception by looking at the prompt:
0:092>
But let's pretend that didn't work by using the command ~0s, so we're back on the main thread.
0:092> ~0s
ntdll!NtWaitForSingleObject+0x14:
00007ffc`cbcacc94 c3 ret
0:000>
Using the ~ command, you can identify the thread(s) which caused an exception:
0:000> ~
. 0 Id: 3edc.cc8 Suspend: 1 Teb: 0000004a`fb029000 Unfrozen
[...]
91 Id: 3edc.38d0 Suspend: 1 Teb: 0000004a`fb0df000 Unfrozen
# 92 Id: 3edc.2418 Suspend: 1 Teb: 0000004a`fb0e1000 Unfrozen
93 Id: 3edc.4788 Suspend: 1 Teb: 0000004a`fb0e3000 Unfrozen
[...]
103 Id: 3edc.43e4 Suspend: 1 Teb: 0000004a`fb0f7000 Unfrozen
The current thread has a dot (.) and the threads with an exception have a hash (#). Note that the dot may hide the hash if the current thread is the one which threw the exception. So you can easily switch to the thread
0:000> ~92s
ucrtbase!abort+0x4e:
00007ffc`c960286e cd29 int 29h
and look at the call stack
0:092> k
# Child-SP RetAddr Call Site
00 0000004a`80efe500 00007ffc`c9601f9f ucrtbase!abort+0x4e
01 0000004a`80efe530 00007ffc`b6e01aab ucrtbase!terminate+0x1f
02 0000004a`80efe560 00007ffc`b6e02317 VCRUNTIME140_1!FindHandler<__FrameHandler4>+0x45b [D:\...\frame.cpp # 693]
03 0000004a`80efe730 00007ffc`b6e040d9 VCRUNTIME140_1!__InternalCxxFrameHandler<__FrameHandler4>+0x267 [D:\...\frame.cpp # 357]
04 0000004a`80efe7d0 00007ffc`cbcb1f6f VCRUNTIME140_1!__CxxFrameHandler4+0xa9 [D:\...\risctrnsctrl.cpp # 306]
05 0000004a`80efe840 00007ffc`cbc61454 ntdll!RtlpExecuteHandlerForException+0xf
06 0000004a`80efe870 00007ffc`cbcb0a9e ntdll!RtlDispatchException+0x244
07 0000004a`80efef80 00007ffc`c96bd759 ntdll!KiUserExceptionDispatch+0x2e
08 0000004a`80eff6b0 00007ffc`a9f36480 KERNELBASE!RaiseException+0x69
09 0000004a`80eff790 00007ff7`49ec13fd VCRUNTIME140!_CxxThrowException+0x90 [D:\...\throw.cpp # 75]
0a 0000004a`80eff7f0 00007ff7`49ec1ecb WhichThreadCrashes!randomCrash+0x1bd [C:\...\WhichThreadCrashes.cpp # 19]
0b (Inline Function) --------`-------- WhichThreadCrashes!std::invoke+0x2 [C:\...\type_traits # 1585]
0c 0000004a`80eff850 00007ffc`c95b1bb2 WhichThreadCrashes!std::thread::_Invoke<std::tuple<void (__cdecl*)(void)>,0>+0xb [C:\...\thread # 55]
0d 0000004a`80eff880 00007ffc`cb7c7034 ucrtbase!thread_start<unsigned int (__cdecl*)(void *),1>+0x42
0e 0000004a`80eff8b0 00007ffc`cbc62651 kernel32!BaseThreadInitThunk+0x14
0f 0000004a`80eff8e0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
So we can see it crashes in randomCrash().
Once you know how it works, you can also switch to the thread with the exception directly by using ~#s:
0:092> ~0s
ntdll!NtWaitForSingleObject+0x14:
00007ffc`cbcacc94 c3 ret
0:000> ~#s
ucrtbase!abort+0x4e:
00007ffc`c960286e cd29 int 29h
0:092>
Also, !analyze -v should give you
0:000> !analyze -v
[...]
STACK_COMMAND: ~92s ; .ecxr ; kb

Related

Initializing plog::RollingFileAppender on Windows XP Triggers Access Violation (Null Pointer)

When using [plog][1] on Windows XP. In this case, the code is:
void LogInit(void)
{
static plog::RollingFileAppender<plog::TxtFormatter> fileAppender("log.log");
Using Visual Studio 2019 but the project uses the platform toolset Visual Studio 2017 - Windows XP (v141_XP)
The output assembly is:
; COMDAT _LogInit
_TEXT SEGMENT
_status$1$ = -516 ; size = 4
_appender$66 = -516 ; size = 4
$T65 = -512 ; size = 256
$T64 = -512 ; size = 256
$T62 = -512 ; size = 256
$T60 = -512 ; size = 256
$T58 = -256 ; size = 256
$T57 = -256 ; size = 256
$T41 = -256 ; size = 256
_LogInit PROC ; COMDAT
; 108 : {
00000 55 push ebp
00001 8b ec mov ebp, esp
00003 83 e4 f8 and esp, -8 ; fffffff8H
; 109 : static plog::RollingFileAppender<plog::TxtFormatter> fileAppender("log.log");
00006 64 a1 00 00 00
00 mov eax, DWORD PTR fs:__tls_array
0000c 81 ec 04 02 00
00 sub esp, 516 ; 00000204H
00012 8b 0d 00 00 00
00 mov ecx, DWORD PTR __tls_index
00018 53 push ebx
00019 56 push esi
0001a 8b 34 88 mov esi, DWORD PTR [eax+ecx*4]
The null pointer is because EAX (__tls_array) and ECX (__tls_index) area both null. Output from WinDbg:
TGLOBALFLAG: 70
APPLICATION_VERIFIER_FLAGS: 0
CONTEXT: (.ecxr)
eax=00000000 ebx=00000000 ecx=00000000 edx=7c90e4f4 esi=0012f624 edi=00000000
eip=1000366a esp=001afda4 ebp=001affb4 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010216
LogTest!LogInit+0x1a:
1000366a 8b3488 mov esi,dword ptr [eax+ecx*4] ds:0023:00000000=????????
Resetting default scope
EXCEPTION_RECORD: (.exr -1)
ExceptionAddress: 1000366a (LogTest!LogInit+0x0000001a)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000000
Parameter[1]: 00000000
Attempt to read from address 00000000
PROCESS_NAME: notepad.exe
READ_ADDRESS: 00000000
ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.
EXCEPTION_CODE_STR: c0000005
EXCEPTION_PARAMETER1: 00000000
EXCEPTION_PARAMETER2: 00000000
FAULTING_LOCAL_VARIABLE_NAME: fileAppender
STACK_TEXT:
001affb4 7c80b713 00000000 00000000 0012f624 LogTest!LogInit+0x1a
001affec 00000000 10003650 00000000 00000000 kernel32!BaseThreadStart+0x37
STACK_COMMAND: ~1s; .ecxr ; kb
FAULTING_SOURCE_LINE: d:\test\logtest.cpp
FAULTING_SOURCE_FILE: d:\test\logtest.cpp
FAULTING_SOURCE_LINE_NUMBER: 109
FAULTING_SOURCE_CODE:
105:
106: // This is an example of an exported function.
107: LogInit_API void LogInit(void)
108: {
> 109: static plog::RollingFileAppender<plog::TxtFormatter> fileAppender(";pg.log");
110: plog::init(plog::info, &fileAppender);
111:
112:
113:
114:
SYMBOL_NAME: LogTest!LogInit+1a
MODULE_NAME: LogTest
IMAGE_NAME: LogTest.dll
FAILURE_BUCKET_ID: NULL_POINTER_READ_c0000005_LogTest.dll!LogInit
OS_VERSION: 5.1.2600.5512
BUILDLAB_STR: xpsp
OSPLATFORM_TYPE: x86
OSNAME: Windows XP
FAILURE_ID_HASH: {0218fa42-bce4-328f-5683-a7e3657927fc}
Followup: MachineOwner
---------
Code for affected class is:
namespace plog
{
template<class Formatter, class Converter = NativeEOLConverter<UTF8Converter> >
class PLOG_LINKAGE_HIDDEN RollingFileAppender : public IAppender
{
public:
RollingFileAppender(const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0)
: m_fileSize()
, m_maxFileSize()
, m_maxFiles(maxFiles)
, m_firstWrite(true)
{
setFileName(fileName);
setMaxFileSize(maxFileSize);
}
#ifdef _WIN32
RollingFileAppender(const char* fileName, size_t maxFileSize = 0, int maxFiles = 0)
: m_fileSize()
, m_maxFileSize()
, m_maxFiles(maxFiles)
, m_firstWrite(true)
{
setFileName(fileName);
setMaxFileSize(maxFileSize);
}
#endif
virtual void write(const Record& record)
{
util::MutexLock lock(m_mutex);
if (m_firstWrite)
{
openLogFile();
m_firstWrite = false;
}
else if (m_maxFiles > 0 && m_fileSize > m_maxFileSize && static_cast<size_t>(-1) != m_fileSize)
{
rollLogFiles();
}
size_t bytesWritten = m_file.write(Converter::convert(Formatter::format(record)));
if (static_cast<size_t>(-1) != bytesWritten)
{
m_fileSize += bytesWritten;
}
}
void setFileName(const util::nchar* fileName)
{
util::MutexLock lock(m_mutex);
util::splitFileName(fileName, m_fileNameNoExt, m_fileExt);
m_file.close();
m_firstWrite = true;
}
#ifdef _WIN32
void setFileName(const char* fileName)
{
setFileName(util::toWide(fileName).c_str());
}
#endif
void setMaxFiles(int maxFiles)
{
m_maxFiles = maxFiles;
}
void setMaxFileSize(size_t maxFileSize)
{
m_maxFileSize = (std::max)(maxFileSize, static_cast<size_t>(1000)); // set a lower limit for the maxFileSize
}
void rollLogFiles()
{
m_file.close();
util::nstring lastFileName = buildFileName(m_maxFiles - 1);
util::File::unlink(lastFileName.c_str());
for (int fileNumber = m_maxFiles - 2; fileNumber >= 0; --fileNumber)
{
util::nstring currentFileName = buildFileName(fileNumber);
util::nstring nextFileName = buildFileName(fileNumber + 1);
util::File::rename(currentFileName.c_str(), nextFileName.c_str());
}
openLogFile();
m_firstWrite = false;
}
private:
void openLogFile()
{
util::nstring fileName = buildFileName();
m_fileSize = m_file.open(fileName.c_str());
if (0 == m_fileSize)
{
size_t bytesWritten = m_file.write(Converter::header(Formatter::header()));
if (static_cast<size_t>(-1) != bytesWritten)
{
m_fileSize += bytesWritten;
}
}
}
util::nstring buildFileName(int fileNumber = 0)
{
util::nostringstream ss;
ss << m_fileNameNoExt;
if (fileNumber > 0)
{
ss << '.' << fileNumber;
}
if (!m_fileExt.empty())
{
ss << '.' << m_fileExt;
}
return ss.str();
}
private:
util::Mutex m_mutex;
util::File m_file;
size_t m_fileSize;
size_t m_maxFileSize;
int m_maxFiles;
util::nstring m_fileExt;
util::nstring m_fileNameNoExt;
bool m_firstWrite;
};
}
Is there code or compiler settings that can be modified to fix/remove the references to __tls_array / __tls_index.
This occurs in both debug & release builds.
[1]: https://github.com/SergiusTheBest/plog
Setting compiler option /Zc:threadSafeInit- removes the references to __tls_array and __tls_index and stops the access violation crash.
Microsoft documentation here mentions:
In the C++11 standard, block scope variables with static or thread
storage duration must be zero-initialized before any other
initialization takes place. Initialization occurs when control first
passes through the declaration of the variable. If an exception is
thrown during initialization, the variable is considered
uninitialized, and initialization is re-attempted the next time
control passes through the declaration. If control enters the
declaration concurrently with initialization, the concurrent execution
blocks while initialization is completed. The behavior is undefined if
control re-enters the declaration recursively during initialization.
By default, Visual Studio starting in Visual Studio 2015 implements
this standard behavior. This behavior may be explicitly specified by
setting the /Zc:threadSafeInit compiler option.
The /Zc:threadSafeInit compiler option is on by default. The
/permissive- option does not affect /Zc:threadSafeInit.
Thread-safe initialization of static local variables relies on code
implemented in the Universal C run-time library (UCRT). To avoid
taking a dependency on the UCRT, or to preserve the non-thread-safe
initialization behavior of versions of Visual Studio prior to Visual
Studio 2015, use the /Zc:threadSafeInit- option. If you know that
thread-safety is not required, use this option to generate slightly
smaller, faster code around static local declarations.
Thread-safe static local variables use thread-local storage (TLS)
internally to provide efficient execution when the static has already
been initialized. The implementation of this feature relies on Windows
operating system support functions in Windows Vista and later
operating systems. Windows XP, Windows Server 2003, and older
operating systems do not have this support, so they do not get the
efficiency advantage. These operating systems also have a lower limit
on the number of TLS sections that can be loaded. Exceeding the TLS
section limit can cause a crash. If this is a problem in your code,
especially in code that must run on older operating systems, use
/Zc:threadSafeInit- to disable the thread-safe initialization code.

Boost ASIO segfault in release mode

I have made this small illustrative code that exhibits the same issues the program I'm writing does: namely, it works fine in debug mode, segfaults in release. The problem seems to be that the ui_context, in release mode, when being called to run the work it has assigned, is nullptr.
Running in Fedora 33, with g++ (GCC) 10.2.1 20201125 (Red Hat 10.2.1-9) and clang version 11.0.0 (Fedora 11.0.0-2.fc33) . Both compilers behave in the same way. Boost version is 1.75 .
Code:
#include <iostream>
#include <vector>
#include <memory>
#include <chrono>
#include <thread>
#include <boost/asio.hpp>
#include <boost/signals2.hpp>
constexpr auto MAX_LOOP_COUNT = 100;
class network_client : public std::enable_shared_from_this<network_client>
{
private:
using Signal = boost::signals2::signal<void(int)>;
public:
network_client(boost::asio::io_context &context) :
strand(boost::asio::make_strand(context))
{
std::cout << "network client created" << std::endl;
}
void doNetworkWork()
{
std::cout << "doing network work" << std::endl;
boost::asio::post(strand,std::bind(&network_client::onWorkComplete,shared_from_this()));
}
void onWorkComplete()
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::cout << "signalling completion" << " from thread id:" << std::this_thread::get_id() << std::endl;
signal(42);
}
void workCompleteHandler(const typename Signal::slot_type &slot)
{
signal.connect(slot);
}
private :
boost::asio::strand<boost::asio::io_context::executor_type> strand;
Signal signal;
};
class network_client_producer
{
public :
network_client_producer() : work(boost::asio::make_work_guard(context))
{
using run_function = boost::asio::io_context::count_type (boost::asio::io_context::*)();
for (int i = 0; i < 2; i++)
{
context_threads.emplace_back(std::bind(static_cast<run_function>(&boost::asio::io_context::run), std::ref(context)));
}
}
~network_client_producer()
{
context.stop();
for(auto&& thread : context_threads)
{
if(thread.joinable())
{
thread.join();
}
}
}
using NetworkClientPtr = std::shared_ptr<network_client>;
NetworkClientPtr makeNetworkClient()
{
return std::make_shared<network_client>(context);
}
private :
boost::asio::io_context context;
std::vector<std::thread> context_threads;
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work;
};
class desktop : public std::enable_shared_from_this<desktop>
{
public:
desktop(const boost::asio::io_context::executor_type &executor):executor(executor)
{
}
void doSomeNetworkWork()
{
auto client = client_producer.makeNetworkClient();
client->workCompleteHandler([self = shared_from_this()](int i){
//post work into the UI thread
std::cout << "calling into the uiThreadWork with index " << i << " from thread id:" << std::this_thread::get_id() << std::endl;
boost::asio::post(self->executor, std::bind(&desktop::uiThreadWorkComplete, self, i));
});
client->doNetworkWork();
}
void showDesktop()
{
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
public:
void uiThreadWorkComplete(int i)
{
std::cout << "Called in the UI thread with index:" << i << ", on thread id:" << std::this_thread::get_id() << std::endl;
}
private:
const boost::asio::io_context::executor_type& executor;
network_client_producer client_producer;
};
int main()
{
std::cout << "Starting application. Main thread id:"<<std::this_thread::get_id() << std::endl;
int count = 0;
boost::asio::io_context ui_context;
auto work = boost::asio::make_work_guard(ui_context);
/*auto work = boost::asio::require(ui_context.get_executor(),
boost::asio::execution::outstanding_work.tracked);*/
auto ui_desktop = std::make_shared<desktop>(ui_context.get_executor());
ui_desktop->doSomeNetworkWork();
while(true)
{
ui_context.poll_one();
ui_desktop->showDesktop();
if (count >= MAX_LOOP_COUNT)
break;
count++;
}
ui_context.stop();
std::cout << "Stopping application" << std::endl;
return 0;
}
Compiling it with g++ -std=c++17 -g -o main -pthread -O3 main.cpp and
running it in gdb I get this:
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Starting application. Main thread id:140737348183872
[New Thread 0x7ffff7a51640 (LWP 27082)]
[New Thread 0x7ffff7250640 (LWP 27083)]
network client created
doing network work
signalling completion from thread id:140737348179520
calling into the uiThreadWork with index 42 from thread id:140737348179520
Thread 2 "main" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff7a51640 (LWP 27082)]
0x000000000040b7b8 in boost::asio::io_context::basic_executor_type<std::allocator<void>, 0u>::execute<std::_Bind<void (desktop::*(std::shared_ptr<desktop>, int))(int)> >(std::_Bind<void (desktop::*(std::shared_ptr<desktop>, int))(int)>&&) const (this=<optimized out>, f=...) at /usr/local/include/boost/asio/impl/io_context.hpp:309
309 io_context_->impl_.post_immediate_completion(p.p,
While compiling without any optimizations g++ -std=c++17 -g -o main -pthread -O0 main.cpp works as expected.
I tried to keep it as close as I can to the real program that actually does network IO, which is why I have that strand in there.
It's obvious that I'm doing something horribly wrong here. The question is: what is the problem?
Thank you for any pointers.
Add the sanitizers -fsanitize=undefined,address:
Starting application. Main thread id:139902898299968
network client created
doing network work
signalling completion from thread id:139902399940352
calling into the uiThreadWork with index 42 from thread id:139902399940352
=================================================================
==29084==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7ffc8393d7c0 at pc 0x000000507ce0 bp 0x7f3d90d9e3d0 sp 0x7f3d90d9e3c8
READ of size 8 at 0x7ffc8393d7c0 thread T1
#0 0x507cdf in boost::asio::io_context::basic_executor_type<std::allocator<void>, ...
#1 0x507cdf in boost::asio::detail::initiate_post_with_executor<boost::asio::io_co...
#2 0x507cdf in auto boost::asio::post<boost::asio::io_context::basic_executor_type...
#3 0x5077cf in desktop::doSomeNetworkWork()::'lambda'(int)::operator()(int) const ...
#4 0x518ce2 in boost::function1<void, int>::operator()(int) const /home/sehe/custo...
#5 0x518481 in boost::signals2::detail::void_type boost::signals2::detail::call_wi...
#6 0x518481 in boost::signals2::detail::void_type boost::signals2::detail::variadi...
#7 0x517f43 in boost::signals2::detail::slot_call_iterator_t<boost::signals2::deta...
#8 0x516397 in void boost::signals2::optional_last_value<void>::operator()<boost::...
#9 0x516397 in void boost::signals2::detail::combiner_invoker<void>::operator()<bo...
#10 0x516397 in boost::signals2::detail::signal_impl<void (int), boost::signals2::...
#11 0x50d9d4 in network_client::onWorkComplete() /home/sehe/Projects/stackoverflow...
#12 0x51021d in void std::_Bind<void (network_client::* (std::shared_ptr<network_c...
#13 0x51021d in void boost::asio::asio_handler_invoke<std::_Bind<void (network_cli...
#14 0x51021d in void boost_asio_handler_invoke_helpers::invoke<std::_Bind<void (ne...
#15 0x51021d in boost::asio::detail::executor_op<std::_Bind<void (network_client::...
#16 0x51188e in boost::asio::detail::strand_executor_service::invoker<boost::asio:...
#17 0x514311 in void boost::asio::asio_handler_invoke<boost::asio::detail::strand_...
#18 0x514311 in void boost_asio_handler_invoke_helpers::invoke<boost::asio::detail...
#19 0x514311 in boost::asio::detail::executor_op<boost::asio::detail::strand_execu...
#20 0x4d8704 in boost::asio::detail::scheduler::do_run_one(boost::asio::detail::co...
#21 0x4d70dc in boost::asio::detail::scheduler::run(boost::system::error_code&) /h...
#22 0x523a6e in boost::asio::io_context::run() /home/sehe/custom/boost_1_75_0/boos...
#23 0x5258ef in unsigned long std::_Bind<unsigned long (boost::asio::io_context::*...
#24 0x5258ef in unsigned long std::__invoke_impl<unsigned long, std::_Bind<unsigne...
#25 0x5258ef in std::__invoke_result<std::_Bind<unsigned long (boost::asio::io_con...
#26 0x5258ef in unsigned long std::thread::_Invoker<std::tuple<std::_Bind<unsigned...
#27 0x7f3da660bd7f (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0xd0d7f)
#28 0x7f3da5f856da in start_thread (/lib/x86_64-linux-gnu/libpthread.so.0+0x76da)
#29 0x7f3da5a9671e in clone /build/glibc-S7xCS9/glibc-2.27/misc/../sysdeps/unix/sy...
Address 0x7ffc8393d7c0 is located in stack of thread T0 at offset 224 in frame
#0 0x4cb30f in main /home/sehe/Projects/stackoverflow/test.cpp:109
This frame has 6 object(s):
[32, 40) 'ref.tmp.i85' (line 96)
[64, 80) 'ref.tmp.i'
[96, 112) 'ui_context' (line 113)
[128, 152) 'work' (line 114)
[192, 208) 'ui_desktop' (line 117)
[224, 240) 'ref.tmp' (line 117) <== Memory access at offset 224 is inside this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-use-after-scope /home/sehe/custom/boost_1_75_0/boost/asio/io_context.hpp:678:25 in boost::asio::io_context::basic_executor_type<std::allocator<void>, 0u>::basic_executor_type(boost::asio::io_context::basic_executor_type<std::allocator<void>, 0u> const&)
Shadow bytes around the buggy address:
0x10001071faa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10001071fab0: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 f8 f2 f2 f2
0x10001071fac0: f8 f2 f2 f2 00 f2 f2 f2 00 00 f3 f3 00 00 00 00
0x10001071fad0: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1
0x10001071fae0: 00 f2 f2 f2 f8 f8 f2 f2 00 00 f2 f2 00 00 00 f2
=>0x10001071faf0: f2 f2 f2 f2 00 00 f2 f2[f8]f8 f3 f3 00 00 00 00
0x10001071fb00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10001071fb10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10001071fb20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10001071fb30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10001071fb40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
Thread T1 created by T0 here:
#0 0x483a6a in pthread_create (/home/sehe/Projects/stackoverflow/sotest+0x483a6a)
#1 0x7f3da660c014 in std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std...
#2 0x5241d4 in void std::vector<std::thread, std::allocator<std::thread> >::_M_realloc_ins...
#3 0x523517 in std::thread& std::vector<std::thread, std::allocator<std::thread> >::emplac...
==29084==ABORTING
There's your culprit.
Searching The Culprit
This frame has 6 object(s):
[32, 40) 'ref.tmp.i85' (line 96)
[64, 80) 'ref.tmp.i'
[96, 112) 'ui_context' (line 113)
[128, 152) 'work' (line 114)
[192, 208) 'ui_desktop' (line 117)
[224, 240) 'ref.tmp' (line 117) <== Memory access at offset 224 is inside this variable
What variable is that? Apparently in the line
auto ui_desktop = std::make_shared<desktop>(ui_context.get_executor());
there's a temporary that is being kept a reference to. It must be ui_context.get_executor() because ui_desktop is named and has "obvious" lifetime.
Sure enough, desktop declares its executor member by reference:
const boost::asio::io_context::executor_type& executor;
This is a clear error. Executors are not services nor execution contexts, and are designed to be cheaply copyable and passed by value. The fix is trivial:
boost::asio::io_context::executor_type executor;
BONUS
As a bonus, here's a simplified version that runs the demo for half a second. Notes:
using a thread_pool instead of hand-rolling a flawed one
consider not using .stop() on execution contexts, or forgetting about the redundant word guards?
Live On Compiler Explorer
#include <iostream>
#include <chrono>
#include <iomanip>
#include <memory>
#include <thread>
#include <boost/asio.hpp>
#include <boost/signals2.hpp>
namespace {
using namespace std::chrono_literals;
auto now = std::chrono::high_resolution_clock::now;
auto elapsed = [start=now()] { return (now()-start)/1ms; };
inline std::string thread_hash() {
static constexpr std::hash<std::thread::id> h{};
std::ostringstream oss;
oss << std::hex << std::setw(2) << std::setfill('0')
<< h(std::this_thread::get_id()) % 0xff;
return oss.str();
}
auto trace = [](auto const&... args) {
std::cout << "thread #" << thread_hash() << " at t+" << std::setw(3) << elapsed() << "ms\t";
(std::cout << ... << args) << std::endl;
};
} // namespace
struct network_client : std::enable_shared_from_this<network_client> {
explicit network_client(const boost::asio::any_io_executor& context) : strand(make_strand(context)) {
trace("network client created");
}
void doNetworkWork() {
trace("doing network work");
post(strand, std::bind(&network_client::onWorkComplete, shared_from_this()));
}
void onWorkComplete() {
std::this_thread::sleep_for(10ms);
trace("signalling completion");
signal(42);
}
template <typename F> void workCompleteHandler(F slot) {
signal.connect(std::move(slot));
}
private:
boost::asio::strand<boost::asio::any_io_executor> strand;
using Signal = boost::signals2::signal<void(int)>;
Signal signal;
};
struct network_client_producer {
auto makeNetworkClient() {
return std::make_shared<network_client>(context_threads.get_executor());
}
private :
boost::asio::thread_pool context_threads {2};
};
struct desktop : std::enable_shared_from_this<desktop> {
explicit desktop(boost::asio::io_context::executor_type executor) : executor(std::move(executor)) {}
void doSomeNetworkWork() {
auto client = client_producer.makeNetworkClient();
client->workCompleteHandler([this, self = shared_from_this()](int i) {
// post work into the UI thread
trace("calling into the uiThreadWork with index ", i);
post(executor, std::bind(&desktop::uiThreadWorkComplete, self, i));
});
client->doNetworkWork();
}
static void showDesktop() {
trace("showDesktop");
std::this_thread::sleep_for(20ms);
}
void uiThreadWorkComplete(int i) const {
trace("Called in the UI thread with index:", i);
}
private:
boost::asio::io_context::executor_type executor;
network_client_producer client_producer;
};
int main() {
trace("Starting application. Main thread is #", thread_hash());
boost::asio::io_context ui_context;
auto work = boost::asio::make_work_guard(ui_context);
/*auto work = boost::asio::require(ui_context.get_executor(),
boost::asio::execution::outstanding_work.tracked);*/
auto ui_desktop = std::make_shared<desktop>(ui_context.get_executor());
ui_desktop->doSomeNetworkWork();
for (auto deadline = now() + 0.5s; now() < deadline;) {
ui_context.poll_one();
ui_desktop->showDesktop();
}
trace("Stopping application");
work.reset();
ui_context.run();
// ui_context.stop();
trace("Bye\n");
}
Prints
thread #97 at t+ 0ms Starting application. Main thread is #97
thread #97 at t+ 0ms network client created
thread #97 at t+ 1ms doing network work
thread #97 at t+ 1ms showDesktop
thread #2d at t+ 11ms signalling completion
thread #2d at t+ 11ms calling into the uiThreadWork with index 42
thread #97 at t+ 21ms Called in the UI thread with index:42
thread #97 at t+ 21ms showDesktop
thread #97 at t+ 41ms showDesktop
thread #97 at t+ 61ms showDesktop
thread #97 at t+ 81ms showDesktop
thread #97 at t+101ms showDesktop
thread #97 at t+122ms showDesktop
thread #97 at t+142ms showDesktop
thread #97 at t+162ms showDesktop
thread #97 at t+182ms showDesktop
thread #97 at t+202ms showDesktop
thread #97 at t+222ms showDesktop
thread #97 at t+242ms showDesktop
thread #97 at t+262ms showDesktop
thread #97 at t+282ms showDesktop
thread #97 at t+302ms showDesktop
thread #97 at t+323ms showDesktop
thread #97 at t+343ms showDesktop
thread #97 at t+363ms showDesktop
thread #97 at t+383ms showDesktop
thread #97 at t+403ms showDesktop
thread #97 at t+423ms showDesktop
thread #97 at t+443ms showDesktop
thread #97 at t+463ms showDesktop
thread #97 at t+483ms showDesktop
thread #97 at t+503ms Stopping application
thread #97 at t+504ms Bye
The problem is that your executor is a reference to a temporary object. In your main method, you call ui_context.get_executor(), which returns a temporary object. You pass the temporary to the desktop constructor, which stores a reference to this object in the member variable executor. After the auto ui_desktop = ... line in main has completed, the temporary goes out-of-scope and the reference held by executor becomes invalid.
This problem is also detected when compiling your program with address sanitization enabled (-fsanitize=address):
==24629==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7ffe8bee0270 at pc 0x5584b2d89b0c bp 0x7f3ac42fd970 sp 0x7f3ac42fd960
READ of size 8 at 0x7ffe8bee0270 thread T1
#0 0x5584b2d89b0b in boost::asio::io_context::basic_executor_type<std::allocator<void>, 0u>::basic_executor_type(boost::asio::io_context::basic_executor_type<std::allocator<void>, 0u> const&) /usr/include/boost/asio/io_context.hpp:678
...
I would suspect that in your debug build, the temporary object gets to live slightly longer, i.e. the stack memory that was occupied by the temporary object is not reclaimed immediately after the temporary goes out-of-scope. Whereas in the release build, more aggressive optimizations are applied, which results in the memory being reclaimed sooner, thereby invalidating the reference sooner, and then crashing your program once the reference is accessed.
To fix this, you have to ensure that the executor returned by get_executor does not go out-of-scope, so that the reference held by the ui_desktop object remains valid. For example, you could assign the result of get_executor to a variable in your main:
auto executor{ui_context.get_executor()};
auto ui_desktop = std::make_shared<desktop>(executor);

C++ Read file stream from terminal line by line with only one command

Short: I want to read data from the terminal into a variable and compare it with a string.
Long: I am using a TI AM3358 development board from GOEMBED which runs embedded linux. I use this kit to read data from a canbus. To read the data from the canbus into the terminal I use socketcan.
When I run the following command into the terminal I get a datastream of can messages from the bus.
candump can0
I wrote some code which execute the above command and returns the output.
string GetCmdOutput(const char * cmd)
{
char buffer[128];
string result = "";
string different;
FILE* pipe = popen(cmd,"r");
int counter=0;
if(!pipe) throw runtime_error("popen() failed!");
try {
if(fgets(buffer,128,pipe) !=NULL){
while(counter<1){
result +=buffer;
counter++;
}
}
}catch(...){
pclose(pipe);
throw;
}
pclose(pipe);
return result;
}
In int main() I run the following code which compares the terminal output to a string:
string dump = "candump can0";
const char *senddump;
senddump = dump.c_str();
string newOutput;
int senddata = 0;
int i = 0;
int x = 0;
int amountS = 0;
int y = 0;
string datas;
while(i<1)
{
newOutput = GetCmdOutput(senddump);
cout<<newOutput + "\n";
if(newOutput==" can0 000 [2] 01 12\n")
{
canWrite(busn,baudrate, sendID, dlcn,data[0],data[1],data[2],data[3],data[4],data[5],data[6],data[7]);
}
}
The code itself works, but I miss a lot of data from the canbus. I think the reason for this is that I always need to reinitialize the canbus (cmd --> candump can0).
Now my question is: How to change my code that I only need to run the command "candump can0" ones and that the code always compares the next messafes to the string. If this is possible, I think I will gain already a lot of perfomance.
If you have other commands or other options to improve the performancy, pleas do not hesitate to give constructive critisisme. (I'm not a professional! I try to learn)
/ EDIT 1 /
The following datastream is how it is outputted into the terminal
can0 712 [1] 05
can0 192 [6] 1C 0F 19 00 00 00
can0 70B [1] 00
can0 70B [1] 85
can0 703 [1] 00
can0 707 [1] 00
can0 709 [1] 00
Thanks in advance,
Kind regards,
TMO
If there is continual output from candump you could simply pipe the output to your program like this:
$ candump can0 | myprogram
Where myprogram.cpp looks a bit like this:
#include <iostream>
#include <string>
int main()
{
std::string message;
while(std::getline(std::cin, message))
{
// process message here ... eg. ...
if(message == "can0 000 [2] 01 12")
{
// do something specific ...
}
}
}

Create an Arduino ESP8266 library

I would like to create a Arduino library for an ESP8266or ESP32 microcontroller. I wrote a test library which running on an Arduino Nano board with no problem. Here the library cpp file:
#include "Test.h"
Test::Test(){
}
uint32_t Test::libTest(strcttest* t){
uint32_t w;
w = t->a;
return w;
}
Here's the the header file :
#include <Arduino.h>
typedef struct {
uint32_t a;
uint32_t b;
}strcttest;
class Test
{
public:
Test();
uint32_t libTest(strcttest* t);
private:
};
And last but not least the Arduino ino file:
#include <Test.h>
//Instante Test
Test t;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
Serial.println("Start");
}
void loop() {
// put your main code here, to run repeatedly:
//Create structure
strcttest *tt;
tt->a=1;
tt->b=2;
//Output result
Serial.println (t.libTest(tt));
delay(1000);
}
Every compile fine with an Arduino Nano board as well as with ESP8266/ESP32 boards. When I run it on the Nano Board i get the expected result:
Start
1
1
1
1
1
1
1
1
1
...
When I run it on the ESP8266 board I get the following crash result:
l*⸮⸮⸮⸮CI>⸮⸮⸮HB⸮⸮Start
Exception (28):
epc1=0x402024f8 epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000000 depc=0x00000000
ctx: cont
sp: 3ffef7d0 end: 3ffef9a0 offset: 01a0
>>>stack>>>
3ffef970: feefeffe 00000000 3ffee950 40201eb4
3ffef980: feefeffe feefeffe 3ffee96c 40202340
3ffef990: feefeffe feefeffe 3ffee980 40100108
<<<stack<<<
7!a!*6⸮⸮⸮Start
Exception (28):
epc1=0x402024f8 epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000000 depc=0x00000000
ctx: cont
sp: 3ffef7d0 end: 3ffef9a0 offset: 01a0
>>>stack>>>
3ffef970: feefeffe 00000000 3ffee950 40201eb4
3ffef980: feefeffe feefeffe 3ffee96c 40202340
3ffef990: feefeffe feefeffe 3ffee980 40100108
<<<stack<<<
ĜBs⸮`⸮"⸮⸮Start
...
And last but not least on the ESP Development board I receive:
i%M/⸮`⸮i%M7
⸮⸮%Q=qU=\Md⸮aGd<$⸮Start
Guru Meditation Error of type LoadProhibited occurred on core 1. Exception was unhandled.
Register dump:
PC : 0x400dde93 PS : 0x00060030 A0 : 0x800d0570 A1 : 0x3ffc7390
A2 : 0x3ffc1c30 A3 : 0x00000000 A4 : 0x0800001c A5 : 0xffffffff
A6 : 0xffffffff A7 : 0x00060d23 A8 : 0x800832e9 A9 : 0x3ffc7380
A10 : 0x00000003 A11 : 0x00060023 A12 : 0x00060020 A13 : 0x00000003
A14 : 0x00000001 A15 : 0x00000000 SAR : 0x0000001f EXCCAUSE: 0x0000001c
EXCVADDR: 0x00000000 LBEG : 0x400014fd LEND : 0x4000150d LCOUNT : 0xffffffff
Backtrace: 0x400dde93:0x3ffc7390 0x400d0570:0x3ffc73b0 0x400d79b0:0x3ffc73d0
CPU halted.
So my question is: What I am doing wrong. What do i miss. What I haven't understood with Arduino IDE, cpp, pointers, etc.
Sorry I forgot: I use Arduino IDE 1.8.2
strcttest *tt; is your problem. You're not allocating memory for and creating an object of type strcttest - you're merely allocating memory for a pointer to an object of that type. Basically, the code should crash everywhere when your code gets to the line tt->a=1; The fact that it doesn't when run on the Nano is basically dumb luck..
Think of the case where you have a char* variable and then try to copy a string to it - it will crash too, since you dont have any storage space for the string itself - you only have a few bytes allocated that store the address of the string.
The following is a more reasonable implementation of your void loop() function:
void loop() {
// put your main code here, to run repeatedly:
//Create structure
strcttest tt;
tt.a=1;
tt.b=2;
//Output result
Serial.println (t.libTest(&tt));
delay(1000);
}
Another (slower, due to use of new and delete) implementation may look like this:
void loop() {
// put your main code here, to run repeatedly:
//Create structure
strcttest *tt = new strcttest;
tt->a=1;
tt->b=2;
//Output result
Serial.println (t.libTest(tt));
delete tt;
delay(1000);
}
For ESP32 and ESP8266, have a excellent tool to help in crashes situations,
like that You report.
This integrates to Arduino IDE
See it in: https://github.com/me-no-dev/EspExceptionDecoder

Is std::async broken in gcc 4.7 on linux? [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I'm testing std::async in isolation before using it in real code, to verify that it works correctly on my platform (which is ubuntu 12.10 64-bit).
It works (somewhat rarely) and usually just hangs. If it works for you, don't jump to conclusions. Try a few more times, it will probably hang.
If I remove the pthread_mutex test, it doesn't hang. This is the smallest code I can get to reproduce the hang. Is there some reason that you can't mix C pthread code with c++ async code?
#include <iostream>
#include <pthread.h>
#include <chrono>
#include <future>
#include <iomanip>
#include <sstream>
#include <type_traits>
template<typename T>
std::string format_ns(T &&value)
{
std::stringstream s;
if (std::is_floating_point<T>::value)
s << std::setprecision(3);
if (value >= 1000000000)
s << value / 1000000000 << "s";
else if (value >= 1000000)
s << value / 1000000 << "ms";
else if (value >= 1000)
s << value / 1000 << "us";
else
s << value << "ns";
return s.str();
}
template<typename F>
void test(const std::string &msg, int iter, F &&lambda)
{
std::chrono::high_resolution_clock clock;
auto st = clock.now();
int i;
for (i = 0; i < iter; ++i)
lambda();
auto en = clock.now();
std::chrono::nanoseconds dur = std::chrono::duration_cast<
std::chrono::nanoseconds>(en-st);
std::cout << msg << format_ns(dur.count() / i) << std::endl;
}
int test_pthread_mutex()
{
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
test("pthread_mutex_lock/pthread_mutex_unlock: ", 1000000000,
[&]()
{
pthread_mutex_lock(&m);
pthread_mutex_unlock(&m);
});
pthread_mutex_destroy(&m);
return 0;
}
int test_async()
{
test("async: ", 100,
[&]()
{
auto asy = std::async(std::launch::async, [](){});
asy.get();
});
return 0;
}
int main()
{
test_pthread_mutex();
test_async();
}
Here is the build command line:
g++ -Wextra -Wall --std=c++11 -pthread mutexperf/main.cpp
There are no build output messages.
Here is the output of g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.7/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.7.2-2ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-4.7/README.Bugs --enable-languages=c,c++,go,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.7 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.7 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --enable-objc-gc --disable-werror --with-arch-32=i686 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.7.2 (Ubuntu/Linaro 4.7.2-2ubuntu1)
I tried it on a few different computers and found that it indeed worked fine like #johan commented. I investigated the machine I was using and found evidence that the hard drive is beginning to fail. It has some bad sectors and also saw dmesg report several "hard resets" of the HDD after an unusual 4 second freeze. Odd, I hadn't seen any issues before I posted the question. It's probably some subtle/intermittent corruption when compiling/linking or perhaps when loading the executable.
[44242.380936] ata3.00: exception Emask 0x10 SAct 0x0 SErr 0x800000 action 0x6 frozen
[44242.380942] ata3.00: irq_stat 0x08000000, interface fatal error
[44242.380946] ata3: SError: { LinkSeq }
[44242.380950] sr 2:0:0:0: CDB:
[44242.380952] Get event status notification: 4a 01 00 00 10 00 00 00 08 00
[44242.380965] ata3.00: cmd a0/00:00:00:08:00/00:00:00:00:00/a0 tag 0 pio 16392 in
[44242.380965] res 50/00:03:00:08:00/00:00:00:00:00/a0 Emask 0x10 (ATA bus error)
[44242.380968] ata3.00: status: { DRDY }
[44242.380974] ata3: hard resetting link
[44242.700025] ata3: SATA link up 1.5 Gbps (SStatus 113 SControl 300)
[44242.704849] ata3.00: configured for UDMA/100
[44242.720055] ata3: EH complete
[44970.117542] ata3.00: exception Emask 0x10 SAct 0x0 SErr 0x800100 action 0x6 frozen
[44970.117547] ata3.00: irq_stat 0x08000000, interface fatal error
[44970.117551] ata3: SError: { UnrecovData LinkSeq }
[44970.117555] sr 2:0:0:0: CDB:
[44970.117557] Get event status notification: 4a 01 00 00 10 00 00 00 08 00
[44970.117570] ata3.00: cmd a0/00:00:00:08:00/00:00:00:00:00/a0 tag 0 pio 16392 in
[44970.117570] res 50/00:03:00:08:00/00:00:00:00:00/a0 Emask 0x10 (ATA bus error)
[44970.117573] ata3.00: status: { DRDY }
[44970.117579] ata3: hard resetting link
[44970.436662] ata3: SATA link up 1.5 Gbps (SStatus 113 SControl 300)
[44970.443159] ata3.00: configured for UDMA/100
[44970.456639] ata3: EH complete
Thanks to anyone who spent time looking at my issue!
Have you try to copy the mutex rather than pass by reference in lambda?
int test_pthread_mutex()
{
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
test("pthread_mutex_lock/pthread_mutex_unlock: ", 1000000000,
[=]()
{
pthread_mutex_lock(&m);
pthread_mutex_unlock(&m);
});
pthread_mutex_destroy(&m);
return 0;
}