mirror of https://github.com/KLayout/klayout.git
Integration of Salt with another controller
This commit is contained in:
parent
209b16f3ea
commit
88487a001f
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include "layApplication.h"
|
||||
#include "layMainWindow.h"
|
||||
#include "laySignalHandler.h"
|
||||
#include "gsiDecl.h"
|
||||
#include "gsiQtExternals.h"
|
||||
|
||||
|
|
@ -68,7 +69,7 @@ Class<lay::Application> decl_Application (QT_EXTERNAL_BASE (QApplication) "Appli
|
|||
"method."
|
||||
) +
|
||||
method ("crash_me", &crash_me, "@hide") +
|
||||
method ("symname", &lay::Application::symbol_name_from_address, "@hide") +
|
||||
method ("symname", &lay::get_symbol_name_from_address, "@hide") +
|
||||
method ("is_editable?", &lay::Application::is_editable,
|
||||
"@brief Returns true if the application is in editable mode\n"
|
||||
) +
|
||||
|
|
|
|||
|
|
@ -55,7 +55,9 @@ HEADERS = \
|
|||
laySaltGrainDetailsTextWidget.h \
|
||||
laySaltGrainPropertiesDialog.h \
|
||||
laySaltDownloadManager.h \
|
||||
laySaltModel.h
|
||||
laySaltModel.h \
|
||||
laySaltController.h \
|
||||
laySignalHandler.h
|
||||
|
||||
FORMS = \
|
||||
ClipDialog.ui \
|
||||
|
|
@ -156,7 +158,9 @@ SOURCES = \
|
|||
laySaltGrainDetailsTextWidget.cc \
|
||||
laySaltGrainPropertiesDialog.cc \
|
||||
laySaltDownloadManager.cc \
|
||||
laySaltModel.cc
|
||||
laySaltModel.cc \
|
||||
laySaltController.cc \
|
||||
laySignalHandler.cc
|
||||
|
||||
RESOURCES = layBuildInMacros.qrc \
|
||||
layHelpResources.qrc \
|
||||
|
|
|
|||
|
|
@ -22,19 +22,21 @@
|
|||
|
||||
|
||||
#include "layApplication.h"
|
||||
#include "laySignalHandler.h"
|
||||
#include "laybasicConfig.h"
|
||||
#include "layConfig.h"
|
||||
#include "layMainWindow.h"
|
||||
#include "layMacroEditorDialog.h"
|
||||
#include "layVersion.h"
|
||||
#include "layMacro.h"
|
||||
#include "layCrashMessage.h"
|
||||
#include "laySignalHandler.h"
|
||||
#include "layRuntimeErrorForm.h"
|
||||
#include "layProgress.h"
|
||||
#include "layTextProgress.h"
|
||||
#include "layBackgroundAwareTreeStyle.h"
|
||||
#include "layMacroController.h"
|
||||
#include "layTechnologyController.h"
|
||||
#include "laySaltController.h"
|
||||
#include "gtf.h"
|
||||
#include "gsiDecl.h"
|
||||
#include "gsiInterpreter.h"
|
||||
|
|
@ -61,26 +63,15 @@
|
|||
#include <QAction>
|
||||
#include <QMessageBox>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
# include <DbgHelp.h>
|
||||
# include <Psapi.h>
|
||||
// get rid of these - we have std::min/max ..
|
||||
# ifdef min
|
||||
# undef min
|
||||
# endif
|
||||
# ifdef max
|
||||
# undef max
|
||||
# endif
|
||||
#else
|
||||
# include <dlfcn.h>
|
||||
# include <execinfo.h>
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include <signal.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
|
@ -163,292 +154,6 @@ static void ui_exception_handler_def (QWidget *parent)
|
|||
|
||||
static Application *ms_instance = 0;
|
||||
|
||||
#if defined(WIN32)
|
||||
|
||||
static QString
|
||||
addr2symname (DWORD64 addr)
|
||||
{
|
||||
const int max_symbol_length = 255;
|
||||
|
||||
SYMBOL_INFO *symbol = (SYMBOL_INFO *) calloc (sizeof (SYMBOL_INFO) + (max_symbol_length + 1) * sizeof (char), 1);
|
||||
symbol->MaxNameLen = max_symbol_length;
|
||||
symbol->SizeOfStruct = sizeof (SYMBOL_INFO);
|
||||
|
||||
HANDLE process = GetCurrentProcess ();
|
||||
|
||||
QString sym_name;
|
||||
DWORD64 d;
|
||||
bool has_symbol = false;
|
||||
DWORD64 disp = addr;
|
||||
if (SymFromAddr(process, addr, &d, symbol)) {
|
||||
// Symbols taken from the export table seem to be unreliable - skip these
|
||||
// and report the module name + offset.
|
||||
if (! (symbol->Flags & SYMFLAG_EXPORT)) {
|
||||
sym_name = QString::fromLocal8Bit (symbol->Name);
|
||||
disp = d;
|
||||
has_symbol = true;
|
||||
}
|
||||
}
|
||||
|
||||
// find the module name from the module base address
|
||||
|
||||
HMODULE modules[1024];
|
||||
DWORD modules_size = 0;
|
||||
if (! EnumProcessModules (process, modules, sizeof (modules), &modules_size)) {
|
||||
modules_size = 0;
|
||||
}
|
||||
|
||||
QString mod_name;
|
||||
for (unsigned int i = 0; i < (modules_size / sizeof (HMODULE)); i++) {
|
||||
TCHAR mn[MAX_PATH];
|
||||
if (GetModuleFileName (modules[i], mn, sizeof (mn) / sizeof (TCHAR))) {
|
||||
MODULEINFO mi;
|
||||
if (GetModuleInformation (process, modules[i], &mi, sizeof (mi))) {
|
||||
if ((DWORD64) mi.lpBaseOfDll <= addr && (DWORD64) mi.lpBaseOfDll + mi.SizeOfImage > addr) {
|
||||
mod_name = QFileInfo (QString::fromUtf16 ((unsigned short *) mn)).fileName ();
|
||||
if (! has_symbol) {
|
||||
disp -= (DWORD64) mi.lpBaseOfDll;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! mod_name.isNull ()) {
|
||||
mod_name = QString::fromUtf8 ("(") + mod_name + QString::fromUtf8 (") ");
|
||||
}
|
||||
|
||||
free (symbol);
|
||||
|
||||
return QString::fromUtf8 ("0x%1 - %2%3+%4").
|
||||
arg (addr, 0, 16).
|
||||
arg (mod_name).
|
||||
arg (sym_name).
|
||||
arg (disp);
|
||||
}
|
||||
|
||||
static QString
|
||||
get_symbol_name_from_address (const QString &mod_name, size_t addr)
|
||||
{
|
||||
HANDLE process = GetCurrentProcess ();
|
||||
|
||||
DWORD64 mod_base = 0;
|
||||
if (! mod_name.isEmpty ()) {
|
||||
|
||||
// find the module name from the module base address
|
||||
HMODULE modules[1024];
|
||||
DWORD modules_size = 0;
|
||||
if (! EnumProcessModules (process, modules, sizeof (modules), &modules_size)) {
|
||||
modules_size = 0;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < (modules_size / sizeof (HMODULE)); i++) {
|
||||
TCHAR mn[MAX_PATH];
|
||||
if (GetModuleFileName (modules[i], mn, sizeof (mn) / sizeof (TCHAR))) {
|
||||
if (mod_name == QFileInfo (QString::fromUtf16 ((unsigned short *) mn)).fileName ()) {
|
||||
MODULEINFO mi;
|
||||
if (GetModuleInformation (process, modules[i], &mi, sizeof (mi))) {
|
||||
mod_base = (DWORD64) mi.lpBaseOfDll;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mod_base == 0) {
|
||||
throw tl::Exception (tl::to_string (QObject::tr ("Unknown module name: ") + mod_name));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SymInitialize (process, NULL, TRUE);
|
||||
QString res = addr2symname (mod_base + (DWORD64) addr);
|
||||
SymCleanup (process);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
LONG WINAPI ExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo)
|
||||
{
|
||||
HANDLE process = GetCurrentProcess ();
|
||||
SymInitialize (process, NULL, TRUE);
|
||||
|
||||
QString text;
|
||||
text += QObject::tr ("Exception code: 0x%1\n").arg (pExceptionInfo->ExceptionRecord->ExceptionCode, 0, 16);
|
||||
text += QObject::tr ("Program Version: ") +
|
||||
QString::fromUtf8 (lay::Version::name ()) +
|
||||
QString::fromUtf8 (" ") +
|
||||
QString::fromUtf8 (lay::Version::version ()) +
|
||||
QString::fromUtf8 (" (") +
|
||||
QString::fromUtf8 (lay::Version::subversion ()) +
|
||||
QString::fromUtf8 (")");
|
||||
#if defined(_WIN64)
|
||||
text += QString::fromUtf8 (" AMD64");
|
||||
#else
|
||||
text += QString::fromUtf8 (" x86");
|
||||
#endif
|
||||
text += QString::fromUtf8 ("\n");
|
||||
text += QObject::tr ("\nBacktrace:\n");
|
||||
|
||||
CONTEXT context_record = *pExceptionInfo->ContextRecord;
|
||||
|
||||
// Initialize stack walking.
|
||||
STACKFRAME64 stack_frame;
|
||||
memset(&stack_frame, 0, sizeof(stack_frame));
|
||||
|
||||
#if defined(_WIN64)
|
||||
int machine_type = IMAGE_FILE_MACHINE_AMD64;
|
||||
stack_frame.AddrPC.Offset = context_record.Rip;
|
||||
stack_frame.AddrFrame.Offset = context_record.Rbp;
|
||||
stack_frame.AddrStack.Offset = context_record.Rsp;
|
||||
#else
|
||||
int machine_type = IMAGE_FILE_MACHINE_I386;
|
||||
stack_frame.AddrPC.Offset = context_record.Eip;
|
||||
stack_frame.AddrFrame.Offset = context_record.Ebp;
|
||||
stack_frame.AddrStack.Offset = context_record.Esp;
|
||||
#endif
|
||||
stack_frame.AddrPC.Mode = AddrModeFlat;
|
||||
stack_frame.AddrFrame.Mode = AddrModeFlat;
|
||||
stack_frame.AddrStack.Mode = AddrModeFlat;
|
||||
|
||||
while (StackWalk64 (machine_type,
|
||||
GetCurrentProcess(),
|
||||
GetCurrentThread(),
|
||||
&stack_frame,
|
||||
&context_record,
|
||||
NULL,
|
||||
&SymFunctionTableAccess64,
|
||||
&SymGetModuleBase64,
|
||||
NULL)) {
|
||||
text += addr2symname (stack_frame.AddrPC.Offset);
|
||||
text += QString::fromUtf8 ("\n");
|
||||
}
|
||||
|
||||
SymCleanup (process);
|
||||
|
||||
// YES! I! KNOW!
|
||||
// In a signal handler you shall not do fancy stuff (in particular not
|
||||
// open dialogs) nor shall you throw exceptions! But that scheme appears to
|
||||
// be working since in most cases the signal is raised from our code (hence
|
||||
// from our stack frames) and everything is better than just showing
|
||||
// the "application stopped working" dialog.
|
||||
// Isn't it?
|
||||
|
||||
CrashMessage msg (0, true, text);
|
||||
if (! msg.exec ()) {
|
||||
// terminate unconditionally
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
} else {
|
||||
throw tl::CancelException ();
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_signal (int signo)
|
||||
{
|
||||
signal (signo, handle_signal);
|
||||
int user_base = (1 << 29);
|
||||
RaiseException(signo + user_base, 0, 0, NULL);
|
||||
}
|
||||
|
||||
static void install_signal_handlers ()
|
||||
{
|
||||
// disable any signal handlers that Ruby might have installed.
|
||||
signal (SIGSEGV, SIG_DFL);
|
||||
signal (SIGILL, SIG_DFL);
|
||||
signal (SIGFPE, SIG_DFL);
|
||||
|
||||
signal (SIGABRT, handle_signal);
|
||||
|
||||
#if 0
|
||||
// TODO: not available to MinGW - linking against msvc100 would help
|
||||
// but then the app crashes.
|
||||
_set_abort_behavior( 0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT );
|
||||
#endif
|
||||
|
||||
SetUnhandledExceptionFilter(ExceptionHandler);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
QString get_symbol_name_from_address (const QString &, size_t)
|
||||
{
|
||||
return QString::fromUtf8 ("n/a");
|
||||
}
|
||||
|
||||
void signal_handler (int signo, siginfo_t *si, void *)
|
||||
{
|
||||
void *array [100];
|
||||
|
||||
bool can_resume = (signo != SIGILL);
|
||||
|
||||
size_t nptrs = backtrace (array, sizeof (array) / sizeof (array[0]));
|
||||
|
||||
QString text;
|
||||
text += QObject::tr ("Signal number: %1\n").arg (signo);
|
||||
text += QObject::tr ("Address: 0x%1\n").arg ((size_t) si->si_addr, 0, 16);
|
||||
text += QObject::tr ("Program Version: ") +
|
||||
QString::fromUtf8 (lay::Version::name ()) +
|
||||
QString::fromUtf8 (" ") +
|
||||
QString::fromUtf8 (lay::Version::version ()) +
|
||||
QString::fromUtf8 (" (") +
|
||||
QString::fromUtf8 (lay::Version::subversion ()) +
|
||||
QString::fromUtf8 (")");
|
||||
text += QString::fromUtf8 ("\n");
|
||||
text += QObject::tr ("Backtrace:\n");
|
||||
|
||||
char **symbols = backtrace_symbols (array, nptrs);
|
||||
if (symbols == NULL) {
|
||||
text += QObject::tr ("-- Unable to obtain stack trace --");
|
||||
} else {
|
||||
for (size_t i = 2; i < nptrs; i++) {
|
||||
text += QString::fromUtf8 (symbols [i]) + QString::fromUtf8 ("\n");
|
||||
}
|
||||
}
|
||||
free(symbols);
|
||||
|
||||
// YES! I! KNOW!
|
||||
// In a signal handler you shall not do fancy stuff (in particular not
|
||||
// open dialogs) nor shall you throw exceptions! But that scheme appears to
|
||||
// be working since in most cases the signal is raised from our code (hence
|
||||
// from our stack frames) and everything is better than just core dumping.
|
||||
// Isn't it?
|
||||
|
||||
CrashMessage msg (0, can_resume, text);
|
||||
if (! msg.exec ()) {
|
||||
|
||||
_exit (signo);
|
||||
|
||||
} else {
|
||||
|
||||
sigset_t x;
|
||||
sigemptyset (&x);
|
||||
sigaddset(&x, signo);
|
||||
sigprocmask(SIG_UNBLOCK, &x, NULL);
|
||||
|
||||
throw tl::CancelException ();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void install_signal_handlers ()
|
||||
{
|
||||
struct sigaction act;
|
||||
act.sa_sigaction = signal_handler;
|
||||
sigemptyset (&act.sa_mask);
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
#if !defined(__APPLE__)
|
||||
act.sa_restorer = 0;
|
||||
#endif
|
||||
|
||||
sigaction (SIGSEGV, &act, NULL);
|
||||
sigaction (SIGILL, &act, NULL);
|
||||
sigaction (SIGFPE, &act, NULL);
|
||||
sigaction (SIGABRT, &act, NULL);
|
||||
sigaction (SIGBUS, &act, NULL);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void load_plugin (const std::string &pp)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
|
@ -577,47 +282,6 @@ Application::Application (int &argc, char **argv, bool non_ui_mode)
|
|||
|
||||
}
|
||||
|
||||
// try to locate a global rbainit file and rbm modules
|
||||
std::vector<std::string> global_modules;
|
||||
std::set<std::string> modules;
|
||||
|
||||
// try to locate a global plugins
|
||||
for (std::vector <std::string>::const_iterator p = m_klayout_path.begin (); p != m_klayout_path.end (); ++p) {
|
||||
|
||||
#if 0
|
||||
// deprecated functionality
|
||||
QFileInfo rbainit_file (tl::to_qstring (*p), QString::fromUtf8 ("rbainit"));
|
||||
if (rbainit_file.exists () && rbainit_file.isReadable ()) {
|
||||
std::string m = tl::to_string (rbainit_file.absoluteFilePath ());
|
||||
if (modules.find (m) == modules.end ()) {
|
||||
global_modules.push_back (m);
|
||||
modules.insert (m);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
QDir inst_path_dir (tl::to_qstring (*p));
|
||||
|
||||
QStringList name_filters;
|
||||
name_filters << QString::fromUtf8 ("*.rbm");
|
||||
name_filters << QString::fromUtf8 ("*.pym");
|
||||
|
||||
QStringList inst_modules = inst_path_dir.entryList (name_filters);
|
||||
inst_modules.sort ();
|
||||
|
||||
for (QStringList::const_iterator im = inst_modules.begin (); im != inst_modules.end (); ++im) {
|
||||
QFileInfo rbm_file (tl::to_qstring (*p), *im);
|
||||
if (rbm_file.exists () && rbm_file.isReadable ()) {
|
||||
std::string m = tl::to_string (rbm_file.absoluteFilePath ());
|
||||
if (modules.find (m) == modules.end ()) {
|
||||
global_modules.push_back (m);
|
||||
modules.insert (m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// try to locate the global plugins
|
||||
for (std::vector <std::string>::const_iterator p = m_klayout_path.begin (); p != m_klayout_path.end (); ++p) {
|
||||
|
||||
|
|
@ -927,9 +591,21 @@ Application::Application (int &argc, char **argv, bool non_ui_mode)
|
|||
install_signal_handlers ();
|
||||
}
|
||||
|
||||
lay::SaltController *sc = lay::SaltController::instance ();
|
||||
lay::TechnologyController *tc = lay::TechnologyController::instance ();
|
||||
lay::MacroController *mc = lay::MacroController::instance ();
|
||||
|
||||
lay::TechnologyController *tc = lay::TechnologyController::instance ();
|
||||
if (sc) {
|
||||
|
||||
// auto-import technologies
|
||||
for (std::vector <std::string>::const_iterator p = m_klayout_path.begin (); p != m_klayout_path.end (); ++p) {
|
||||
std::string tp = tl::to_string (QDir (tl::to_qstring (*p)).filePath (QString::fromUtf8 ("salt")));
|
||||
sc->add_path (tp);
|
||||
}
|
||||
|
||||
sc->set_salt_mine_url (tl::salt_mine_url ());
|
||||
|
||||
}
|
||||
|
||||
if (tc) {
|
||||
|
||||
|
|
@ -971,6 +647,8 @@ Application::Application (int &argc, char **argv, bool non_ui_mode)
|
|||
if (! m_no_macros) {
|
||||
|
||||
// Add the global ruby modules as the first ones.
|
||||
// TODO: this is a deprecated feature.
|
||||
std::vector<std::string> global_modules = scan_global_modules ();
|
||||
m_load_macros.insert (m_load_macros.begin (), global_modules.begin (), global_modules.end ());
|
||||
|
||||
for (std::vector <std::string>::const_iterator p = m_klayout_path.begin (); p != m_klayout_path.end (); ++p) {
|
||||
|
|
@ -1085,10 +763,56 @@ Application::~Application ()
|
|||
shutdown ();
|
||||
}
|
||||
|
||||
QString
|
||||
Application::symbol_name_from_address (const QString &mod_name, size_t addr)
|
||||
std::vector<std::string>
|
||||
Application::scan_global_modules ()
|
||||
{
|
||||
return get_symbol_name_from_address (mod_name, addr);
|
||||
// NOTE:
|
||||
// this is deprecated functionality - for backward compatibility, global "*.rbm" and "*.pym" modules
|
||||
// are still considered. The desired solution is autorun macros.
|
||||
|
||||
// try to locate a global rbainit file and rbm modules
|
||||
std::vector<std::string> global_modules;
|
||||
std::set<std::string> modules;
|
||||
|
||||
// try to locate a global plugins
|
||||
for (std::vector <std::string>::const_iterator p = m_klayout_path.begin (); p != m_klayout_path.end (); ++p) {
|
||||
|
||||
#if 0
|
||||
// deprecated functionality
|
||||
QFileInfo rbainit_file (tl::to_qstring (*p), QString::fromUtf8 ("rbainit"));
|
||||
if (rbainit_file.exists () && rbainit_file.isReadable ()) {
|
||||
std::string m = tl::to_string (rbainit_file.absoluteFilePath ());
|
||||
if (modules.find (m) == modules.end ()) {
|
||||
global_modules.push_back (m);
|
||||
modules.insert (m);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
QDir inst_path_dir (tl::to_qstring (*p));
|
||||
|
||||
QStringList name_filters;
|
||||
name_filters << QString::fromUtf8 ("*.rbm");
|
||||
name_filters << QString::fromUtf8 ("*.pym");
|
||||
|
||||
QStringList inst_modules = inst_path_dir.entryList (name_filters);
|
||||
inst_modules.sort ();
|
||||
|
||||
for (QStringList::const_iterator im = inst_modules.begin (); im != inst_modules.end (); ++im) {
|
||||
QFileInfo rbm_file (tl::to_qstring (*p), *im);
|
||||
if (rbm_file.exists () && rbm_file.isReadable ()) {
|
||||
std::string m = tl::to_string (rbm_file.absoluteFilePath ());
|
||||
if (modules.find (m) == modules.end ()) {
|
||||
tl::warn << tl::to_string (tr ("Global modules are deprecated. Turn '%1'' into an autorun macro instead and put it into 'macros' or 'pymacros'.").arg (tl::to_qstring (m)));
|
||||
global_modules.push_back (m);
|
||||
modules.insert (m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return global_modules;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
|||
|
|
@ -257,11 +257,6 @@ public:
|
|||
return ! m_no_gui;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief For debugging purposes: get a symbol name (a description actually) from an address
|
||||
*/
|
||||
static QString symbol_name_from_address (const QString &mod_name, size_t addr);
|
||||
|
||||
/**
|
||||
* @brief Reset config to global configuration
|
||||
*/
|
||||
|
|
@ -301,6 +296,7 @@ public:
|
|||
private:
|
||||
void shutdown ();
|
||||
void finish ();
|
||||
std::vector<std::string> scan_global_modules ();
|
||||
|
||||
enum file_type {
|
||||
layout_file,
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@
|
|||
#include "layLayerToolbox.h"
|
||||
#include "laySettingsForm.h"
|
||||
#include "layTechnologyController.h"
|
||||
#include "laySaltManagerDialog.h"
|
||||
#include "laySaltController.h"
|
||||
#include "layTipDialog.h"
|
||||
#include "laySelectCellViewForm.h"
|
||||
#include "layLayoutPropertiesForm.h"
|
||||
|
|
@ -4513,8 +4513,10 @@ MainWindow::show_progress_bar (bool show)
|
|||
void
|
||||
MainWindow::cm_packages ()
|
||||
{
|
||||
lay::SaltManagerDialog dialog (this);
|
||||
dialog.exec ();
|
||||
lay::SaltController *sc = lay::SaltController::instance ();
|
||||
if (sc) {
|
||||
sc->show_editor ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -0,0 +1,172 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "laySaltController.h"
|
||||
#include "laySaltManagerDialog.h"
|
||||
#include "layConfig.h"
|
||||
#include "layMainWindow.h"
|
||||
#include "layQtTools.h"
|
||||
#include "tlLog.h"
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
static const std::string cfg_salt_manager_window_state ("salt-manager-window-state");
|
||||
|
||||
SaltController::SaltController ()
|
||||
: mp_salt_dialog (0), mp_mw (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void
|
||||
SaltController::initialized (lay::PluginRoot *root)
|
||||
{
|
||||
mp_mw = dynamic_cast <lay::MainWindow *> (root);
|
||||
|
||||
connect (&m_salt, SIGNAL (collections_changed ()), this, SIGNAL (salt_changed ()));
|
||||
}
|
||||
|
||||
void
|
||||
SaltController::uninitialize (lay::PluginRoot * /*root*/)
|
||||
{
|
||||
disconnect (&m_salt, SIGNAL (collections_changed ()), this, SIGNAL (salt_changed ()));
|
||||
|
||||
delete mp_salt_dialog;
|
||||
mp_salt_dialog = 0;
|
||||
mp_mw = 0;
|
||||
}
|
||||
|
||||
void
|
||||
SaltController::get_options (std::vector < std::pair<std::string, std::string> > &options) const
|
||||
{
|
||||
options.push_back (std::pair<std::string, std::string> (cfg_salt_manager_window_state, ""));
|
||||
}
|
||||
|
||||
void
|
||||
SaltController::get_menu_entries (std::vector<lay::MenuEntry> & /*menu_entries*/) const
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
bool
|
||||
SaltController::configure (const std::string & /*name*/, const std::string & /*value*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
SaltController::config_finalize()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
bool
|
||||
SaltController::can_exit (lay::PluginRoot * /*root*/) const
|
||||
{
|
||||
// .. nothing yet ..
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SaltController::accepts_drop (const std::string & /*path_or_url*/) const
|
||||
{
|
||||
// .. nothing yet ..
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
SaltController::drop_url (const std::string & /*path_or_url*/)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void
|
||||
SaltController::show_editor ()
|
||||
{
|
||||
if (mp_mw && !mp_salt_dialog) {
|
||||
|
||||
try {
|
||||
if (! m_salt_mine_url.empty ()) {
|
||||
tl::log << tl::to_string (tr ("Downloading package repository from %1").arg (tl::to_qstring (m_salt_mine_url)));
|
||||
m_salt_mine.load (m_salt_mine_url);
|
||||
}
|
||||
} catch (tl::Exception &ex) {
|
||||
tl::error << ex.msg ();
|
||||
}
|
||||
|
||||
mp_salt_dialog = new lay::SaltManagerDialog (mp_mw, &m_salt, &m_salt_mine);
|
||||
|
||||
}
|
||||
|
||||
if (mp_salt_dialog) {
|
||||
|
||||
if (mp_mw) {
|
||||
std::string s = mp_mw->config_get (cfg_salt_manager_window_state);
|
||||
if (! s.empty ()) {
|
||||
lay::restore_dialog_state (mp_salt_dialog, s);
|
||||
}
|
||||
}
|
||||
|
||||
mp_salt_dialog->exec ();
|
||||
|
||||
if (mp_mw) {
|
||||
mp_mw->config_set (cfg_salt_manager_window_state, lay::save_dialog_state (mp_salt_dialog));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SaltController::add_path (const std::string &path)
|
||||
{
|
||||
try {
|
||||
tl::log << tl::to_string (tr ("Scanning %1 for packages").arg (tl::to_qstring (path)));
|
||||
m_salt.add_location (path);
|
||||
} catch (tl::Exception &ex) {
|
||||
tl::error << ex.msg ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SaltController::set_salt_mine_url (const std::string &url)
|
||||
{
|
||||
m_salt_mine_url = url;
|
||||
}
|
||||
|
||||
SaltController *
|
||||
SaltController::instance ()
|
||||
{
|
||||
for (tl::Registrar<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); cls != tl::Registrar<lay::PluginDeclaration>::end (); ++cls) {
|
||||
SaltController *sc = dynamic_cast <SaltController *> (cls.operator-> ());
|
||||
if (sc) {
|
||||
return sc;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The singleton instance of the salt controller
|
||||
static tl::RegisteredClass<lay::PluginDeclaration> salt_controller_decl (new lay::SaltController (), 100, "SaltController");
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_laySaltController
|
||||
#define HDR_laySaltController
|
||||
|
||||
#include "layCommon.h"
|
||||
#include "layPlugin.h"
|
||||
#include "laySalt.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
class SaltManagerDialog;
|
||||
class MainWindow;
|
||||
|
||||
/**
|
||||
* @brief A controller for the salt package manager
|
||||
*
|
||||
* This object is a singleton that acts as a controller
|
||||
* for the package management. The controller is responsible
|
||||
* to managing the packages and notifying package consumers
|
||||
* of changes.
|
||||
*
|
||||
* It interacts with the SaltManagerDialog which basically
|
||||
* is the view for the packages.
|
||||
*
|
||||
* By making the controller a PluginDeclaration it will receive
|
||||
* initialization and configuration calls.
|
||||
*/
|
||||
class SaltController
|
||||
: public lay::PluginDeclaration, public tl::Object
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
SaltController ();
|
||||
|
||||
/**
|
||||
* @brief Reimplementation of the PluginDeclaration interface
|
||||
*/
|
||||
virtual void initialized (lay::PluginRoot *root);
|
||||
|
||||
/**
|
||||
* @brief Reimplementation of the PluginDeclaration interface
|
||||
*/
|
||||
virtual void uninitialize (lay::PluginRoot *root);
|
||||
|
||||
/**
|
||||
* @brief Reimplementation of the PluginDeclaration interface
|
||||
*/
|
||||
void get_options (std::vector < std::pair<std::string, std::string> > &options) const;
|
||||
|
||||
/**
|
||||
* @brief Reimplementation of the PluginDeclaration interface
|
||||
*/
|
||||
void get_menu_entries (std::vector<lay::MenuEntry> &menu_entries) const;
|
||||
|
||||
/**
|
||||
* @brief Reimplementation of the PluginDeclaration interface
|
||||
*/
|
||||
bool configure (const std::string &key, const std::string &value);
|
||||
|
||||
/**
|
||||
* @brief Reimplementation of the PluginDeclaration interface
|
||||
*/
|
||||
void config_finalize();
|
||||
|
||||
/**
|
||||
* @brief Reimplementation of the PluginDeclaration interface
|
||||
*/
|
||||
bool can_exit (lay::PluginRoot *root) const;
|
||||
|
||||
/**
|
||||
* @brief Reimplementation of the PluginDeclaration interface
|
||||
*/
|
||||
bool accepts_drop (const std::string &path_or_url) const;
|
||||
|
||||
/**
|
||||
* @brief Reimplementation of the PluginDeclaration interface
|
||||
*/
|
||||
void drop_url (const std::string &path_or_url);
|
||||
|
||||
/**
|
||||
* @brief Shows the package editor
|
||||
*/
|
||||
void show_editor ();
|
||||
|
||||
/**
|
||||
* @brief Adds a search path to the package manager
|
||||
*/
|
||||
void add_path (const std::string &path);
|
||||
|
||||
/**
|
||||
* @brief Specifies the salt mine (package repository) URL
|
||||
*/
|
||||
void set_salt_mine_url (const std::string &url);
|
||||
|
||||
/**
|
||||
* @brief Gets the singleton instance for this object
|
||||
*/
|
||||
static SaltController *instance ();
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief This signal is emitted if the salt changed
|
||||
*/
|
||||
void salt_changed ();
|
||||
|
||||
private:
|
||||
lay::SaltManagerDialog *mp_salt_dialog;
|
||||
lay::MainWindow *mp_mw;
|
||||
std::string m_salt_mine_url;
|
||||
lay::Salt m_salt, m_salt_mine;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -110,39 +110,7 @@ private:
|
|||
// --------------------------------------------------------------------------------------
|
||||
// SaltManager implementation
|
||||
|
||||
// @@@
|
||||
lay::Salt salt;
|
||||
static bool salt_initialized = false;
|
||||
void make_salt ()
|
||||
{
|
||||
if (!salt_initialized) {
|
||||
salt_initialized = true;
|
||||
salt.add_location (tl::to_string (QDir::homePath () + QString::fromUtf8("/.klayout/salt")));
|
||||
}
|
||||
}
|
||||
lay::Salt *get_salt ()
|
||||
{
|
||||
salt = lay::Salt (); salt_initialized = false;
|
||||
make_salt ();
|
||||
return &salt;
|
||||
}
|
||||
// @@@
|
||||
|
||||
// @@@
|
||||
lay::Salt salt_mine;
|
||||
void make_salt_mine ()
|
||||
{
|
||||
salt_mine = lay::Salt ();
|
||||
salt_mine.load ("/home/matthias/salt.mine");
|
||||
}
|
||||
lay::Salt *get_salt_mine ()
|
||||
{
|
||||
make_salt_mine();
|
||||
return &salt_mine;
|
||||
}
|
||||
// @@@
|
||||
|
||||
SaltManagerDialog::SaltManagerDialog (QWidget *parent)
|
||||
SaltManagerDialog::SaltManagerDialog (QWidget *parent, lay::Salt *salt, lay::Salt *salt_mine)
|
||||
: QDialog (parent),
|
||||
m_current_changed_enabled (true), dm_update_models (this, &SaltManagerDialog::update_models)
|
||||
{
|
||||
|
|
@ -154,8 +122,8 @@ SaltManagerDialog::SaltManagerDialog (QWidget *parent)
|
|||
connect (delete_button, SIGNAL (clicked ()), this, SLOT (delete_grain ()));
|
||||
connect (apply_button, SIGNAL (clicked ()), this, SLOT (apply ()));
|
||||
|
||||
mp_salt = get_salt ();
|
||||
mp_salt_mine = get_salt_mine ();
|
||||
mp_salt = salt;
|
||||
mp_salt_mine = salt_mine;
|
||||
|
||||
SaltModel *model = new SaltModel (this, mp_salt);
|
||||
salt_view->setModel (model);
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ public:
|
|||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
SaltManagerDialog (QWidget *parent);
|
||||
SaltManagerDialog (QWidget *parent, lay::Salt *salt, lay::Salt *salt_mine);
|
||||
|
||||
private slots:
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,336 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "laySignalHandler.h"
|
||||
#include "layCrashMessage.h"
|
||||
#include "layVersion.h"
|
||||
#include "tlException.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
# include <DbgHelp.h>
|
||||
# include <Psapi.h>
|
||||
// get rid of these - we have std::min/max ..
|
||||
# ifdef min
|
||||
# undef min
|
||||
# endif
|
||||
# ifdef max
|
||||
# undef max
|
||||
# endif
|
||||
#else
|
||||
# include <dlfcn.h>
|
||||
# include <execinfo.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
#if defined(WIN32)
|
||||
|
||||
static QString
|
||||
addr2symname (DWORD64 addr)
|
||||
{
|
||||
const int max_symbol_length = 255;
|
||||
|
||||
SYMBOL_INFO *symbol = (SYMBOL_INFO *) calloc (sizeof (SYMBOL_INFO) + (max_symbol_length + 1) * sizeof (char), 1);
|
||||
symbol->MaxNameLen = max_symbol_length;
|
||||
symbol->SizeOfStruct = sizeof (SYMBOL_INFO);
|
||||
|
||||
HANDLE process = GetCurrentProcess ();
|
||||
|
||||
QString sym_name;
|
||||
DWORD64 d;
|
||||
bool has_symbol = false;
|
||||
DWORD64 disp = addr;
|
||||
if (SymFromAddr(process, addr, &d, symbol)) {
|
||||
// Symbols taken from the export table seem to be unreliable - skip these
|
||||
// and report the module name + offset.
|
||||
if (! (symbol->Flags & SYMFLAG_EXPORT)) {
|
||||
sym_name = QString::fromLocal8Bit (symbol->Name);
|
||||
disp = d;
|
||||
has_symbol = true;
|
||||
}
|
||||
}
|
||||
|
||||
// find the module name from the module base address
|
||||
|
||||
HMODULE modules[1024];
|
||||
DWORD modules_size = 0;
|
||||
if (! EnumProcessModules (process, modules, sizeof (modules), &modules_size)) {
|
||||
modules_size = 0;
|
||||
}
|
||||
|
||||
QString mod_name;
|
||||
for (unsigned int i = 0; i < (modules_size / sizeof (HMODULE)); i++) {
|
||||
TCHAR mn[MAX_PATH];
|
||||
if (GetModuleFileName (modules[i], mn, sizeof (mn) / sizeof (TCHAR))) {
|
||||
MODULEINFO mi;
|
||||
if (GetModuleInformation (process, modules[i], &mi, sizeof (mi))) {
|
||||
if ((DWORD64) mi.lpBaseOfDll <= addr && (DWORD64) mi.lpBaseOfDll + mi.SizeOfImage > addr) {
|
||||
mod_name = QFileInfo (QString::fromUtf16 ((unsigned short *) mn)).fileName ();
|
||||
if (! has_symbol) {
|
||||
disp -= (DWORD64) mi.lpBaseOfDll;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! mod_name.isNull ()) {
|
||||
mod_name = QString::fromUtf8 ("(") + mod_name + QString::fromUtf8 (") ");
|
||||
}
|
||||
|
||||
free (symbol);
|
||||
|
||||
return QString::fromUtf8 ("0x%1 - %2%3+%4").
|
||||
arg (addr, 0, 16).
|
||||
arg (mod_name).
|
||||
arg (sym_name).
|
||||
arg (disp);
|
||||
}
|
||||
|
||||
QString
|
||||
get_symbol_name_from_address (const QString &mod_name, size_t addr)
|
||||
{
|
||||
HANDLE process = GetCurrentProcess ();
|
||||
|
||||
DWORD64 mod_base = 0;
|
||||
if (! mod_name.isEmpty ()) {
|
||||
|
||||
// find the module name from the module base address
|
||||
HMODULE modules[1024];
|
||||
DWORD modules_size = 0;
|
||||
if (! EnumProcessModules (process, modules, sizeof (modules), &modules_size)) {
|
||||
modules_size = 0;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < (modules_size / sizeof (HMODULE)); i++) {
|
||||
TCHAR mn[MAX_PATH];
|
||||
if (GetModuleFileName (modules[i], mn, sizeof (mn) / sizeof (TCHAR))) {
|
||||
if (mod_name == QFileInfo (QString::fromUtf16 ((unsigned short *) mn)).fileName ()) {
|
||||
MODULEINFO mi;
|
||||
if (GetModuleInformation (process, modules[i], &mi, sizeof (mi))) {
|
||||
mod_base = (DWORD64) mi.lpBaseOfDll;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mod_base == 0) {
|
||||
throw tl::Exception (tl::to_string (QObject::tr ("Unknown module name: ") + mod_name));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SymInitialize (process, NULL, TRUE);
|
||||
QString res = addr2symname (mod_base + (DWORD64) addr);
|
||||
SymCleanup (process);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
LONG WINAPI ExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo)
|
||||
{
|
||||
HANDLE process = GetCurrentProcess ();
|
||||
SymInitialize (process, NULL, TRUE);
|
||||
|
||||
QString text;
|
||||
text += QObject::tr ("Exception code: 0x%1\n").arg (pExceptionInfo->ExceptionRecord->ExceptionCode, 0, 16);
|
||||
text += QObject::tr ("Program Version: ") +
|
||||
QString::fromUtf8 (lay::Version::name ()) +
|
||||
QString::fromUtf8 (" ") +
|
||||
QString::fromUtf8 (lay::Version::version ()) +
|
||||
QString::fromUtf8 (" (") +
|
||||
QString::fromUtf8 (lay::Version::subversion ()) +
|
||||
QString::fromUtf8 (")");
|
||||
#if defined(_WIN64)
|
||||
text += QString::fromUtf8 (" AMD64");
|
||||
#else
|
||||
text += QString::fromUtf8 (" x86");
|
||||
#endif
|
||||
text += QString::fromUtf8 ("\n");
|
||||
text += QObject::tr ("\nBacktrace:\n");
|
||||
|
||||
CONTEXT context_record = *pExceptionInfo->ContextRecord;
|
||||
|
||||
// Initialize stack walking.
|
||||
STACKFRAME64 stack_frame;
|
||||
memset(&stack_frame, 0, sizeof(stack_frame));
|
||||
|
||||
#if defined(_WIN64)
|
||||
int machine_type = IMAGE_FILE_MACHINE_AMD64;
|
||||
stack_frame.AddrPC.Offset = context_record.Rip;
|
||||
stack_frame.AddrFrame.Offset = context_record.Rbp;
|
||||
stack_frame.AddrStack.Offset = context_record.Rsp;
|
||||
#else
|
||||
int machine_type = IMAGE_FILE_MACHINE_I386;
|
||||
stack_frame.AddrPC.Offset = context_record.Eip;
|
||||
stack_frame.AddrFrame.Offset = context_record.Ebp;
|
||||
stack_frame.AddrStack.Offset = context_record.Esp;
|
||||
#endif
|
||||
stack_frame.AddrPC.Mode = AddrModeFlat;
|
||||
stack_frame.AddrFrame.Mode = AddrModeFlat;
|
||||
stack_frame.AddrStack.Mode = AddrModeFlat;
|
||||
|
||||
while (StackWalk64 (machine_type,
|
||||
GetCurrentProcess(),
|
||||
GetCurrentThread(),
|
||||
&stack_frame,
|
||||
&context_record,
|
||||
NULL,
|
||||
&SymFunctionTableAccess64,
|
||||
&SymGetModuleBase64,
|
||||
NULL)) {
|
||||
text += addr2symname (stack_frame.AddrPC.Offset);
|
||||
text += QString::fromUtf8 ("\n");
|
||||
}
|
||||
|
||||
SymCleanup (process);
|
||||
|
||||
// YES! I! KNOW!
|
||||
// In a signal handler you shall not do fancy stuff (in particular not
|
||||
// open dialogs) nor shall you throw exceptions! But that scheme appears to
|
||||
// be working since in most cases the signal is raised from our code (hence
|
||||
// from our stack frames) and everything is better than just showing
|
||||
// the "application stopped working" dialog.
|
||||
// Isn't it?
|
||||
|
||||
CrashMessage msg (0, true, text);
|
||||
if (! msg.exec ()) {
|
||||
// terminate unconditionally
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
} else {
|
||||
throw tl::CancelException ();
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_signal (int signo)
|
||||
{
|
||||
signal (signo, handle_signal);
|
||||
int user_base = (1 << 29);
|
||||
RaiseException(signo + user_base, 0, 0, NULL);
|
||||
}
|
||||
|
||||
static void install_signal_handlers ()
|
||||
{
|
||||
// disable any signal handlers that Ruby might have installed.
|
||||
signal (SIGSEGV, SIG_DFL);
|
||||
signal (SIGILL, SIG_DFL);
|
||||
signal (SIGFPE, SIG_DFL);
|
||||
|
||||
signal (SIGABRT, handle_signal);
|
||||
|
||||
#if 0
|
||||
// TODO: not available to MinGW - linking against msvc100 would help
|
||||
// but then the app crashes.
|
||||
_set_abort_behavior( 0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT );
|
||||
#endif
|
||||
|
||||
SetUnhandledExceptionFilter(ExceptionHandler);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
QString get_symbol_name_from_address (const QString &, size_t)
|
||||
{
|
||||
return QString::fromUtf8 ("n/a");
|
||||
}
|
||||
|
||||
void signal_handler (int signo, siginfo_t *si, void *)
|
||||
{
|
||||
void *array [100];
|
||||
|
||||
bool can_resume = (signo != SIGILL);
|
||||
|
||||
size_t nptrs = backtrace (array, sizeof (array) / sizeof (array[0]));
|
||||
|
||||
QString text;
|
||||
text += QObject::tr ("Signal number: %1\n").arg (signo);
|
||||
text += QObject::tr ("Address: 0x%1\n").arg ((size_t) si->si_addr, 0, 16);
|
||||
text += QObject::tr ("Program Version: ") +
|
||||
QString::fromUtf8 (lay::Version::name ()) +
|
||||
QString::fromUtf8 (" ") +
|
||||
QString::fromUtf8 (lay::Version::version ()) +
|
||||
QString::fromUtf8 (" (") +
|
||||
QString::fromUtf8 (lay::Version::subversion ()) +
|
||||
QString::fromUtf8 (")");
|
||||
text += QString::fromUtf8 ("\n");
|
||||
text += QObject::tr ("Backtrace:\n");
|
||||
|
||||
char **symbols = backtrace_symbols (array, nptrs);
|
||||
if (symbols == NULL) {
|
||||
text += QObject::tr ("-- Unable to obtain stack trace --");
|
||||
} else {
|
||||
for (size_t i = 2; i < nptrs; i++) {
|
||||
text += QString::fromUtf8 (symbols [i]) + QString::fromUtf8 ("\n");
|
||||
}
|
||||
}
|
||||
free(symbols);
|
||||
|
||||
// YES! I! KNOW!
|
||||
// In a signal handler you shall not do fancy stuff (in particular not
|
||||
// open dialogs) nor shall you throw exceptions! But that scheme appears to
|
||||
// be working since in most cases the signal is raised from our code (hence
|
||||
// from our stack frames) and everything is better than just core dumping.
|
||||
// Isn't it?
|
||||
|
||||
CrashMessage msg (0, can_resume, text);
|
||||
if (! msg.exec ()) {
|
||||
|
||||
_exit (signo);
|
||||
|
||||
} else {
|
||||
|
||||
sigset_t x;
|
||||
sigemptyset (&x);
|
||||
sigaddset(&x, signo);
|
||||
sigprocmask(SIG_UNBLOCK, &x, NULL);
|
||||
|
||||
throw tl::CancelException ();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void install_signal_handlers ()
|
||||
{
|
||||
struct sigaction act;
|
||||
act.sa_sigaction = signal_handler;
|
||||
sigemptyset (&act.sa_mask);
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
#if !defined(__APPLE__)
|
||||
act.sa_restorer = 0;
|
||||
#endif
|
||||
|
||||
sigaction (SIGSEGV, &act, NULL);
|
||||
sigaction (SIGILL, &act, NULL);
|
||||
sigaction (SIGFPE, &act, NULL);
|
||||
sigaction (SIGABRT, &act, NULL);
|
||||
sigaction (SIGBUS, &act, NULL);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2017 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef HDR_laySignalHandler
|
||||
#define HDR_laySignalHandler
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace lay
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Installs global signal handlers for SIGSEGV and similar
|
||||
*/
|
||||
void install_signal_handlers ();
|
||||
|
||||
/**
|
||||
* @brief For debugging purposes: get the symbol name from a memory address
|
||||
*/
|
||||
QString get_symbol_name_from_address (const QString &mod_name, size_t addr);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -164,5 +164,27 @@ get_klayout_path ()
|
|||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
salt_mine_url ()
|
||||
{
|
||||
const std::string default_url ("https://www.klayout.org/salt.mine");
|
||||
|
||||
#ifdef _WIN32
|
||||
wchar_t *env = _wgetenv (L"KLAYOUT_SALT_MINE");
|
||||
if (env) {
|
||||
return tl::to_string (QString ((const QChar *) env)));
|
||||
} else {
|
||||
return default_url;
|
||||
}
|
||||
#else
|
||||
char *env = getenv ("KLAYOUT_SALT_MINE");
|
||||
if (env) {
|
||||
return (tl::system_to_string (env));
|
||||
} else {
|
||||
return default_url;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,11 @@ TL_PUBLIC void set_klayout_path (const std::vector<std::string> &path);
|
|||
*/
|
||||
TL_PUBLIC void reset_klayout_path ();
|
||||
|
||||
/**
|
||||
* @brief Gets the package manager URL
|
||||
*/
|
||||
TL_PUBLIC std::string salt_mine_url ();
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue