diff --git a/src/lay/gsiDeclLayApplication.cc b/src/lay/gsiDeclLayApplication.cc index 6c4321d24..e5c89415d 100644 --- a/src/lay/gsiDeclLayApplication.cc +++ b/src/lay/gsiDeclLayApplication.cc @@ -23,6 +23,7 @@ #include "layApplication.h" #include "layMainWindow.h" +#include "laySignalHandler.h" #include "gsiDecl.h" #include "gsiQtExternals.h" @@ -68,7 +69,7 @@ Class 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" ) + diff --git a/src/lay/lay.pro b/src/lay/lay.pro index d6b1a048a..f076212b6 100644 --- a/src/lay/lay.pro +++ b/src/lay/lay.pro @@ -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 \ diff --git a/src/lay/layApplication.cc b/src/lay/layApplication.cc index d53df08fb..ccd1906c2 100644 --- a/src/lay/layApplication.cc +++ b/src/lay/layApplication.cc @@ -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 #include -#ifdef _WIN32 -# include -# include -# include -// get rid of these - we have std::min/max .. -# ifdef min -# undef min -# endif -# ifdef max -# undef max -# endif -#else -# include -# include -#endif - #include #include #include -#include + +#ifdef _WIN32 +# include +#else +# include +#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 global_modules; - std::set modules; - - // try to locate a global plugins - for (std::vector ::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 ::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 ::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 global_modules = scan_global_modules (); m_load_macros.insert (m_load_macros.begin (), global_modules.begin (), global_modules.end ()); for (std::vector ::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 +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 global_modules; + std::set modules; + + // try to locate a global plugins + for (std::vector ::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 diff --git a/src/lay/layApplication.h b/src/lay/layApplication.h index 3cfdb8dd3..3186c7c56 100644 --- a/src/lay/layApplication.h +++ b/src/lay/layApplication.h @@ -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 scan_global_modules (); enum file_type { layout_file, diff --git a/src/lay/layMainWindow.cc b/src/lay/layMainWindow.cc index 465821e34..f94f059a1 100644 --- a/src/lay/layMainWindow.cc +++ b/src/lay/layMainWindow.cc @@ -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 diff --git a/src/lay/laySaltController.cc b/src/lay/laySaltController.cc new file mode 100644 index 000000000..6b799f835 --- /dev/null +++ b/src/lay/laySaltController.cc @@ -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 (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 > &options) const +{ + options.push_back (std::pair (cfg_salt_manager_window_state, "")); +} + +void +SaltController::get_menu_entries (std::vector & /*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::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { + SaltController *sc = dynamic_cast (cls.operator-> ()); + if (sc) { + return sc; + } + } + return 0; +} + +// The singleton instance of the salt controller +static tl::RegisteredClass salt_controller_decl (new lay::SaltController (), 100, "SaltController"); + +} + diff --git a/src/lay/laySaltController.h b/src/lay/laySaltController.h new file mode 100644 index 000000000..2e310f31a --- /dev/null +++ b/src/lay/laySaltController.h @@ -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 +#include + +#include + +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 > &options) const; + + /** + * @brief Reimplementation of the PluginDeclaration interface + */ + void get_menu_entries (std::vector &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 + diff --git a/src/lay/laySaltManagerDialog.cc b/src/lay/laySaltManagerDialog.cc index aa3ecd745..b12e8b11d 100644 --- a/src/lay/laySaltManagerDialog.cc +++ b/src/lay/laySaltManagerDialog.cc @@ -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); diff --git a/src/lay/laySaltManagerDialog.h b/src/lay/laySaltManagerDialog.h index 6e1f40cf4..feee212b9 100644 --- a/src/lay/laySaltManagerDialog.h +++ b/src/lay/laySaltManagerDialog.h @@ -48,7 +48,7 @@ public: /** * @brief Constructor */ - SaltManagerDialog (QWidget *parent); + SaltManagerDialog (QWidget *parent, lay::Salt *salt, lay::Salt *salt_mine); private slots: /** diff --git a/src/lay/laySignalHandler.cc b/src/lay/laySignalHandler.cc new file mode 100644 index 000000000..9b65614c2 --- /dev/null +++ b/src/lay/laySignalHandler.cc @@ -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 +# include +# include +// get rid of these - we have std::min/max .. +# ifdef min +# undef min +# endif +# ifdef max +# undef max +# endif +#else +# include +# include +# include +#endif + +#include + +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 + +} diff --git a/src/lay/laySignalHandler.h b/src/lay/laySignalHandler.h new file mode 100644 index 000000000..9b9c528f9 --- /dev/null +++ b/src/lay/laySignalHandler.h @@ -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 + +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 diff --git a/src/tl/tlSystemPaths.cc b/src/tl/tlSystemPaths.cc index d1847b853..53a9c5615 100644 --- a/src/tl/tlSystemPaths.cc +++ b/src/tl/tlSystemPaths.cc @@ -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 +} + } diff --git a/src/tl/tlSystemPaths.h b/src/tl/tlSystemPaths.h index 16187c8a3..84b83bf86 100644 --- a/src/tl/tlSystemPaths.h +++ b/src/tl/tlSystemPaths.h @@ -66,6 +66,11 @@ TL_PUBLIC void set_klayout_path (const std::vector &path); */ TL_PUBLIC void reset_klayout_path (); +/** + * @brief Gets the package manager URL + */ +TL_PUBLIC std::string salt_mine_url (); + } #endif