Integration of Salt with another controller

This commit is contained in:
Matthias Koefferlein 2017-04-16 23:03:33 +02:00
parent 209b16f3ea
commit 88487a001f
13 changed files with 817 additions and 396 deletions

View File

@ -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"
) +

View File

@ -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 \

View File

@ -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

View File

@ -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,

View 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

View File

@ -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");
}

148
src/lay/laySaltController.h Normal file
View File

@ -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

View File

@ -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);

View File

@ -48,7 +48,7 @@ public:
/**
* @brief Constructor
*/
SaltManagerDialog (QWidget *parent);
SaltManagerDialog (QWidget *parent, lay::Salt *salt, lay::Salt *salt_mine);
private slots:
/**

336
src/lay/laySignalHandler.cc Normal file
View File

@ -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
}

View File

@ -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

View File

@ -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
}
}

View File

@ -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