Dump stack trace on crash (Implement #849) (#870)

* Print stack trace on crash

* Printing stack trace is fully functional except for WIN32

* Minor fixes

* Minor fixes
This commit is contained in:
Murat Seker 2020-07-12 18:27:24 +02:00 committed by GitHub
parent 9c5e5cac24
commit 3b48d8c9d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 239 additions and 88 deletions

View File

@ -16,6 +16,7 @@ public:
VideoStandard videoStandard, VideoStandard videoStandard,
PixelFormat pixelFormat, PixelFormat pixelFormat,
int pixelDecimation ); int pixelDecimation );
~V4L2Wrapper() override;
bool getSignalDetectionEnable(); bool getSignalDetectionEnable();

View File

@ -26,6 +26,11 @@ public:
/// ///
X11Wrapper(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, const unsigned updateRate_Hz); X11Wrapper(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, const unsigned updateRate_Hz);
///
/// Destructor of this framebuffer frame grabber. Releases any claimed resources.
///
~X11Wrapper() override;
public slots: public slots:
/// ///
/// Performs a single frame grab and computes the led-colors /// Performs a single frame grab and computes the led-colors

View File

@ -0,0 +1,6 @@
#pragma once
namespace DefaultSignalHandler
{
void install();
}

View File

@ -4,7 +4,7 @@ DispmanxWrapper::DispmanxWrapper(const unsigned grabWidth, const unsigned grabHe
: GrabberWrapper("Dispmanx", &_grabber, grabWidth, grabHeight, updateRate_Hz) : GrabberWrapper("Dispmanx", &_grabber, grabWidth, grabHeight, updateRate_Hz)
, _grabber(grabWidth, grabHeight) , _grabber(grabWidth, grabHeight)
{ {
} }
void DispmanxWrapper::action() void DispmanxWrapper::action()

View File

@ -33,6 +33,11 @@ V4L2Wrapper::V4L2Wrapper(const QString &device,
connect(&_grabber, SIGNAL(readError(const char*)), this, SLOT(readError(const char*)), Qt::DirectConnection); connect(&_grabber, SIGNAL(readError(const char*)), this, SLOT(readError(const char*)), Qt::DirectConnection);
} }
V4L2Wrapper::~V4L2Wrapper()
{
stop();
}
bool V4L2Wrapper::start() bool V4L2Wrapper::start()
{ {
return ( _grabber.start() && GrabberWrapper::start()); return ( _grabber.start() && GrabberWrapper::start());

View File

@ -6,6 +6,14 @@ X11Wrapper::X11Wrapper(int cropLeft, int cropRight, int cropTop, int cropBottom,
, _init(false) , _init(false)
{} {}
X11Wrapper::~X11Wrapper()
{
if ( _init )
{
stop();
}
}
void X11Wrapper::action() void X11Wrapper::action()
{ {
if (! _init ) if (! _init )

View File

@ -39,7 +39,7 @@ GrabberWrapper::GrabberWrapper(QString grabberName, Grabber * ggrabber, unsigned
GrabberWrapper::~GrabberWrapper() GrabberWrapper::~GrabberWrapper()
{ {
GrabberWrapper::stop(); // TODO Is this right???????? stop();
Debug(_log,"Close grabber: %s", QSTRING_CSTR(_grabberName)); Debug(_log,"Close grabber: %s", QSTRING_CSTR(_grabberName));
} }
@ -53,9 +53,12 @@ bool GrabberWrapper::start()
void GrabberWrapper::stop() void GrabberWrapper::stop()
{ {
// Stop the timer, effectivly stopping the process if (_timer->isActive())
Debug(_log,"Grabber stop()"); {
_timer->stop(); // Stop the timer, effectivly stopping the process
Debug(_log,"Grabber stop()");
_timer->stop();
}
} }
QStringList GrabberWrapper::availableGrabbers() QStringList GrabberWrapper::availableGrabbers()
@ -216,7 +219,7 @@ void GrabberWrapper::handleSourceRequest(const hyperion::Components& component,
void GrabberWrapper::tryStart() void GrabberWrapper::tryStart()
{ {
// verify start condition // verify start condition
if((_grabberName.startsWith("V4L") && !GRABBER_V4L_CLIENTS.empty()) || (!_grabberName.startsWith("V4L") && !GRABBER_SYS_CLIENTS.empty())) if((_grabberName.startsWith("V4L") && !GRABBER_V4L_CLIENTS.empty()) || (!_grabberName.startsWith("V4L") && !GRABBER_SYS_CLIENTS.empty()))
{ {
start(); start();

View File

@ -141,9 +141,8 @@ void Hyperion::start()
_boblightServer = new BoblightServer(this, getSetting(settings::BOBLSERVER)); _boblightServer = new BoblightServer(this, getSetting(settings::BOBLSERVER));
connect(this, &Hyperion::settingsChanged, _boblightServer, &BoblightServer::handleSettingsUpdate); connect(this, &Hyperion::settingsChanged, _boblightServer, &BoblightServer::handleSettingsUpdate);
// instance inited // instance inited, enter thread event loop
emit started(); emit started();
// enter thread event loop
} }
void Hyperion::stop() void Hyperion::stop()
@ -380,18 +379,8 @@ void Hyperion::setColor(const int priority, const std::vector<ColorRgb> &ledColo
_effectEngine->channelCleared(priority); _effectEngine->channelCleared(priority);
// create full led vector from single/multiple colors // create full led vector from single/multiple colors
size_t size = _ledString.leds().size();
std::vector<ColorRgb> newLedColors; std::vector<ColorRgb> newLedColors;
while (true) std::copy_n(ledColors.begin(), _ledString.leds().size(), std::back_inserter(newLedColors));
{
for (const auto &entry : ledColors)
{
newLedColors.emplace_back(entry);
if (newLedColors.size() == size)
goto end;
}
}
end:
if (getPriorityInfo(priority).componentId != hyperion::COMP_COLOR) if (getPriorityInfo(priority).componentId != hyperion::COMP_COLOR)
clear(priority); clear(priority);

View File

@ -36,7 +36,7 @@ LedDevice::LedDevice(const QJsonObject& config, QObject* parent)
LedDevice::~LedDevice() LedDevice::~LedDevice()
{ {
_refresh_timer->deleteLater(); delete _refresh_timer;
} }
int LedDevice::open() int LedDevice::open()

View File

@ -39,7 +39,7 @@ bool ProviderSpi::init(const QJsonObject &deviceConfig)
_baudRate_Hz = deviceConfig["rate"].toInt(_baudRate_Hz); _baudRate_Hz = deviceConfig["rate"].toInt(_baudRate_Hz);
_spiMode = deviceConfig["spimode"].toInt(_spiMode); _spiMode = deviceConfig["spimode"].toInt(_spiMode);
_spiDataInvert = deviceConfig["invert"].toBool(_spiDataInvert); _spiDataInvert = deviceConfig["invert"].toBool(_spiDataInvert);
return isInitOK; return isInitOK;
} }

View File

@ -116,20 +116,17 @@ void SSDPHandler::handleWebServerStateChange(const bool newState)
void SSDPHandler::handleNetworkConfigurationChanged(const QNetworkConfiguration &config) void SSDPHandler::handleNetworkConfigurationChanged(const QNetworkConfiguration &config)
{ {
// get localAddress from interface // get localAddress from interface
if(!getLocalAddress().isEmpty()) QString localAddress = getLocalAddress();
if(!localAddress.isEmpty() && _localAddress != localAddress)
{ {
QString localAddress = getLocalAddress(); // revoke old ip
if(_localAddress != localAddress) sendAnnounceList(false);
{
// revoke old ip
sendAnnounceList(false);
// update desc & notify new ip // update desc & notify new ip
_localAddress = localAddress; _localAddress = localAddress;
_webserver->setSSDPDescription(buildDesc()); _webserver->setSSDPDescription(buildDesc());
setDescriptionAddress(getDescAddress()); setDescriptionAddress(getDescAddress());
sendAnnounceList(true); sendAnnounceList(true);
}
} }
} }

View File

@ -0,0 +1,132 @@
#ifndef _WIN32
#include <utils/DefaultSignalHandler.h>
#include <utils/Logger.h>
#include <ctype.h>
#include <cxxabi.h>
#include <execinfo.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <QCoreApplication>
namespace DefaultSignalHandler
{
std::string decipher_trace(const std::string &trace)
{
std::string result;
if(trace.empty())
{
result += "??\n";
return result;
}
auto* begin = strchr(trace.c_str(), '(') + 1;
auto* end = strchr(begin, '+');
if(!end)
end = strchr(begin, ')');
std::string mangled_name(begin, end);
int status;
char * realname = abi::__cxa_demangle(mangled_name.c_str(), 0, 0, &status);
result.insert(result.end(), trace.c_str(), begin);
if(realname)
result += realname;
else
result.insert(result.end(), begin, end);
free(realname);
result.insert(result.size(), end);
return result;
}
void print_trace()
{
const int MAX_SIZE = 50;
void * addresses[MAX_SIZE];
int size = backtrace(addresses, MAX_SIZE);
if (!size)
return;
Logger* log = Logger::getInstance("CORE");
char ** symbols = backtrace_symbols(addresses, size);
for (int i = 0; i < size; ++i)
{
std::string line = "\t" + decipher_trace(symbols[i]);
Error(log, line.c_str());
}
free(symbols);
}
/* Note that this signal handler is not async signal safe !
* Ideally a signal handler should only flip a bit and defer
* heavy work to some kind of bottom-half processing. */
void signal_handler(int signum, siginfo_t * /*info*/, void * /*context*/)
{
Logger* log = Logger::getInstance("SIGNAL");
char *name = strsignal(signum);
if (name)
{
Info(log, "Signal received : %s", name);
}
switch(signum)
{
case SIGSEGV:
case SIGABRT:
case SIGFPE :
print_trace();
exit(1);
case SIGINT :
case SIGTERM:
case SIGPIPE:
default:
/* If the signal_handler is hit before the event loop is started,
* following call will do nothing. So we queue the call. */
// QCoreApplication::quit();
QMetaObject::invokeMethod(qApp, "quit", Qt::QueuedConnection);
// Reset signal handler to default (in case this handler is not capable of stopping)
struct sigaction action{};
action.sa_handler = SIG_DFL;
sigaction(signum, &action, nullptr);
}
}
} // namespace DefaultSignalHandler
#endif // _WIN32
namespace DefaultSignalHandler
{
void install()
{
#ifndef _WIN32
struct sigaction action{};
action.sa_sigaction = signal_handler;
action.sa_flags = SA_RESTART | SA_SIGINFO;
sigaction(SIGHUP , &action, nullptr);
sigaction(SIGFPE , &action, nullptr);
sigaction(SIGINT , &action, nullptr);
sigaction(SIGTERM, &action, nullptr);
sigaction(SIGABRT, &action, nullptr);
sigaction(SIGSEGV, &action, nullptr);
sigaction(SIGPIPE, &action, nullptr);
#endif // _WIN32
}
} // namespace DefaultSignalHandler

View File

@ -12,6 +12,8 @@
// ssdp discover // ssdp discover
#include <ssdp/SSDPDiscover.h> #include <ssdp/SSDPDiscover.h>
#include <utils/DefaultSignalHandler.h>
using namespace commandline; using namespace commandline;
// save the image as screenshot // save the image as screenshot
@ -29,6 +31,8 @@ int main(int argc, char ** argv)
<< "\tVersion : " << HYPERION_VERSION << " (" << HYPERION_BUILD_ID << ")" << std::endl << "\tVersion : " << HYPERION_VERSION << " (" << HYPERION_BUILD_ID << ")" << std::endl
<< "\tbuild time: " << __DATE__ << " " << __TIME__ << std::endl; << "\tbuild time: " << __DATE__ << " " << __TIME__ << std::endl;
DefaultSignalHandler::install();
QCoreApplication app(argc, argv); QCoreApplication app(argc, argv);
try try

View File

@ -12,6 +12,8 @@
// ssdp discover // ssdp discover
#include <ssdp/SSDPDiscover.h> #include <ssdp/SSDPDiscover.h>
#include <utils/DefaultSignalHandler.h>
using namespace commandline; using namespace commandline;
// save the image as screenshot // save the image as screenshot
@ -29,6 +31,8 @@ int main(int argc, char ** argv)
<< "\tVersion : " << HYPERION_VERSION << " (" << HYPERION_BUILD_ID << ")" << std::endl << "\tVersion : " << HYPERION_VERSION << " (" << HYPERION_BUILD_ID << ")" << std::endl
<< "\tbuild time: " << __DATE__ << " " << __TIME__ << std::endl; << "\tbuild time: " << __DATE__ << " " << __TIME__ << std::endl;
DefaultSignalHandler::install();
QCoreApplication app(argc, argv); QCoreApplication app(argc, argv);
try try

View File

@ -9,6 +9,8 @@
// ssdp discover // ssdp discover
#include <ssdp/SSDPDiscover.h> #include <ssdp/SSDPDiscover.h>
#include <utils/DefaultSignalHandler.h>
using namespace commandline; using namespace commandline;
// save the image as screenshot // save the image as screenshot
@ -21,6 +23,8 @@ void saveScreenshot(QString filename, const Image<ColorRgb> & image)
int main(int argc, char ** argv) int main(int argc, char ** argv)
{ {
DefaultSignalHandler::install();
QCoreApplication app(argc, argv); QCoreApplication app(argc, argv);
try try

View File

@ -11,6 +11,8 @@
// ssdp discover // ssdp discover
#include <ssdp/SSDPDiscover.h> #include <ssdp/SSDPDiscover.h>
#include <utils/DefaultSignalHandler.h>
using namespace commandline; using namespace commandline;
// save the image as screenshot // save the image as screenshot
@ -23,6 +25,8 @@ void saveScreenshot(QString filename, const Image<ColorRgb> & image)
int main(int argc, char ** argv) int main(int argc, char ** argv)
{ {
DefaultSignalHandler::install();
QCoreApplication app(argc, argv); QCoreApplication app(argc, argv);
try try

View File

@ -17,6 +17,7 @@
#include "HyperionConfig.h" #include "HyperionConfig.h"
#include <commandline/Parser.h> #include <commandline/Parser.h>
#include <utils/DefaultSignalHandler.h>
using namespace commandline; using namespace commandline;
@ -66,6 +67,8 @@ int main(int argc, char * argv[])
<< "\tVersion : " << HYPERION_VERSION << " (" << HYPERION_BUILD_ID << ")" << std::endl << "\tVersion : " << HYPERION_VERSION << " (" << HYPERION_BUILD_ID << ")" << std::endl
<< "\tbuild time: " << __DATE__ << " " << __TIME__ << std::endl; << "\tbuild time: " << __DATE__ << " " << __TIME__ << std::endl;
DefaultSignalHandler::install();
QCoreApplication app(argc, argv); QCoreApplication app(argc, argv);
// force the locale // force the locale

View File

@ -24,6 +24,8 @@
// ssdp discover // ssdp discover
#include <ssdp/SSDPDiscover.h> #include <ssdp/SSDPDiscover.h>
#include <utils/DefaultSignalHandler.h>
using namespace commandline; using namespace commandline;
int main(int argc, char** argv) int main(int argc, char** argv)

View File

@ -5,6 +5,7 @@
#include <commandline/Parser.h> #include <commandline/Parser.h>
#include <flatbufserver/FlatBufferConnection.h> #include <flatbufserver/FlatBufferConnection.h>
#include <utils/DefaultSignalHandler.h>
#include "X11Wrapper.h" #include "X11Wrapper.h"
#include "HyperionConfig.h" #include "HyperionConfig.h"
@ -28,6 +29,8 @@ int main(int argc, char ** argv)
<< "\tVersion : " << HYPERION_VERSION << " (" << HYPERION_BUILD_ID << ")" << std::endl << "\tVersion : " << HYPERION_VERSION << " (" << HYPERION_BUILD_ID << ")" << std::endl
<< "\tbuild time: " << __DATE__ << " " << __TIME__ << std::endl; << "\tbuild time: " << __DATE__ << " " << __TIME__ << std::endl;
DefaultSignalHandler::install();
QApplication app(argc, argv); QApplication app(argc, argv);
try try
{ {

View File

@ -11,54 +11,42 @@
// psapi.h requires windows.h to be included // psapi.h requires windows.h to be included
#include <Windows.h> #include <Windows.h>
#include <Psapi.h> #include <Psapi.h>
#include <tlhelp32.h>
#endif #endif
unsigned int getProcessIdsByProcessName(const char *processName, QStringList &listOfPids) QStringList getProcessIdsByProcessName(const char *processName)
{ {
// Clear content of returned list of PIDS QStringList listOfPids;
listOfPids.clear();
#if defined(WIN32) #if defined(WIN32)
// Get the list of process identifiers. // https://docs.microsoft.com/en-us/windows/win32/toolhelp/taking-a-snapshot-and-viewing-processes
DWORD aProcesses[1024], cbNeeded, cProcesses; /* Take a snapshot of all processes in the system */
unsigned int i; HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(hProcessSnap == INVALID_HANDLE_VALUE)
if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
return 0;
// Calculate how many process identifiers were returned.
cProcesses = cbNeeded / sizeof(DWORD);
// Search for a matching name for each process
for (i = 0; i < cProcesses; i++)
{ {
if (aProcesses[i] != 0) return {};
{
char szProcessName[MAX_PATH] = {0};
DWORD processID = aProcesses[i];
// Get a handle to the process.
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
// Get the process name
if (NULL != hProcess)
{
HMODULE hMod;
DWORD cbNeeded;
if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded))
GetModuleBaseNameA(hProcess, hMod, szProcessName, sizeof(szProcessName) / sizeof(char));
// Release the handle to the process.
CloseHandle(hProcess);
if (*szProcessName != 0 && strcmp(processName, szProcessName) == 0)
listOfPids.append(QString::number(processID));
}
}
} }
PROCESSENTRY32 pe32{};
pe32.dwSize = sizeof(PROCESSENTRY32);
/* Retrieve information about the first process */
if(!Process32First(hProcessSnap, &pe32))
{
CloseHandle(hProcessSnap);
return {};
}
/* Walk through the snapshot of processes */
do
{
if (strcmp(processName, pe32.szExeFile) == 0)
listOfPids.append(QString::number(pe32.th32ProcessID));
} while(Process32Next(hProcessSnap, &pe32));
CloseHandle(hProcessSnap);
#else #else
QDir dir("/proc"); QDir dir("/proc");
@ -90,5 +78,5 @@ unsigned int getProcessIdsByProcessName(const char *processName, QStringList &li
#endif #endif
return listOfPids.count(); return listOfPids;
} }

View File

@ -33,6 +33,7 @@
#include <utils/FileUtils.h> #include <utils/FileUtils.h>
#include <commandline/Parser.h> #include <commandline/Parser.h>
#include <commandline/IntOption.h> #include <commandline/IntOption.h>
#include <utils/DefaultSignalHandler.h>
#include <../../include/db/AuthTable.h> #include <../../include/db/AuthTable.h>
#include "detectProcess.h" #include "detectProcess.h"
@ -76,11 +77,6 @@ void signal_handler(const int signum)
} }
return; return;
} }
QCoreApplication::quit();
// reset signal handler to default (in case this handler is not capable of stopping)
signal(signum, SIG_DFL);
} }
#endif #endif
@ -150,14 +146,13 @@ int main(int argc, char** argv)
// check if we are running already an instance // check if we are running already an instance
// TODO Allow one session per user // TODO Allow one session per user
// http://www.qtcentre.org/threads/44489-Get-Process-ID-for-a-running-application
QStringList listOfPids;
#ifdef _WIN32 #ifdef _WIN32
const char* processName = "hyperiond.exe"; const char* processName = "hyperiond.exe";
#else #else
const char* processName = "hyperiond"; const char* processName = "hyperiond";
#endif #endif
if (getProcessIdsByProcessName(processName, listOfPids) > 1) const QStringList listOfPids = getProcessIdsByProcessName(processName);
if (listOfPids.size() > 1)
{ {
Error(log, "The Hyperion Daemon is already running, abort start"); Error(log, "The Hyperion Daemon is already running, abort start");
return 0; return 0;
@ -168,12 +163,10 @@ int main(int argc, char** argv)
bool isGuiApp = (qobject_cast<QApplication *>(app.data()) != 0 && QSystemTrayIcon::isSystemTrayAvailable()); bool isGuiApp = (qobject_cast<QApplication *>(app.data()) != 0 && QSystemTrayIcon::isSystemTrayAvailable());
DefaultSignalHandler::install();
#ifndef _WIN32 #ifndef _WIN32
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGABRT, signal_handler);
signal(SIGCHLD, signal_handler); signal(SIGCHLD, signal_handler);
signal(SIGPIPE, signal_handler);
signal(SIGUSR1, signal_handler); signal(SIGUSR1, signal_handler);
signal(SIGUSR2, signal_handler); signal(SIGUSR2, signal_handler);
#endif #endif
@ -309,7 +302,7 @@ int main(int argc, char** argv)
// delete database before start // delete database before start
if(parser.isSet(deleteDB)) if(parser.isSet(deleteDB))
{ {
QString dbFile = mDir.absolutePath() + "/db/hyperion.db"; const QString dbFile = mDir.absolutePath() + "/db/hyperion.db";
if (QFile::exists(dbFile)) if (QFile::exists(dbFile))
{ {
if (!QFile::remove(dbFile)) if (!QFile::remove(dbFile))

View File

@ -127,7 +127,7 @@ void SysTray::closeEvent(QCloseEvent *event)
void SysTray::settings() void SysTray::settings()
{ {
#ifndef _WIN32 #ifndef _WIN32
// Hide error messages when opening webbrowser // Hide error messages when opening webbrowser
int out_pipe[2]; int out_pipe[2];