From c32bcd02f2d392044d57d1ced9abbabad8469075 Mon Sep 17 00:00:00 2001 From: David Nichols Date: Mon, 10 Dec 2018 22:19:23 +0100 Subject: [PATCH 01/17] reffs #3169 initial new stack handling --- include/qore/ExceptionSink.h | 67 ++++- include/qore/intern/AbstractStatement.h | 20 ++ include/qore/intern/Function.h | 57 +++-- include/qore/intern/QoreException.h | 121 ++++----- include/qore/intern/QoreLibIntern.h | 67 ++--- include/qore/intern/QoreThreadList.h | 288 ++++++++++----------- include/qore/intern/qore_thread_intern.h | 127 ++++++++-- lib/AbstractStatement.cpp | 47 ++-- lib/ExceptionSink.cpp | 300 ++++++++++++++++++++-- lib/Function.cpp | 34 ++- lib/QoreClass.cpp | 96 +++---- lib/QoreException.cpp | 303 +++-------------------- lib/QoreProgram.cpp | 2 +- lib/thread.cpp | 116 ++++++--- 14 files changed, 982 insertions(+), 663 deletions(-) diff --git a/include/qore/ExceptionSink.h b/include/qore/ExceptionSink.h index 60a08379ce..0f3ca2806b 100644 --- a/include/qore/ExceptionSink.h +++ b/include/qore/ExceptionSink.h @@ -239,7 +239,7 @@ class ExceptionSink { }; //! call stack element type -enum qore_call_t { +enum qore_call_t : signed char { CT_UNUSED = -1, CT_USER = 0, CT_BUILTIN = 1, @@ -309,19 +309,24 @@ static inline void makeAccessDeletedObjectException(ExceptionSink *xsink, const xsink->raiseException("OBJECT-ALREADY-DELETED", "attempt to access an already-deleted object of class '%s'", cname); } -// returns a custom Qore program location for external modules to generate runtime exceptions with the source location +//! returns a custom Qore program location for external modules to generate runtime exceptions with the source location class QoreExternalProgramLocationWrapper { public: + //! empty constructor; use set() to set the location DLLEXPORT QoreExternalProgramLocationWrapper(); + //! constructor setting the source location DLLEXPORT QoreExternalProgramLocationWrapper(const char* file, int start_line, int end_line, const char* source = nullptr, int offset = 0); + //! destructor; frees memory DLLEXPORT ~QoreExternalProgramLocationWrapper(); + //! sets the program source location DLLEXPORT void set(const char* file, int start_line, int end_line, const char* source = nullptr, int offset = 0); + //! returns the source location DLLEXPORT const QoreProgramLocation& get() const { return *loc; } @@ -335,4 +340,62 @@ class QoreExternalProgramLocationWrapper { QoreProgramLocation* loc; }; +//! Stack location element abstract class +/** @since %Qore 0.9 +*/ +class QoreStackLocation { +public: + //! virtual destructor + DLLLOCAL virtual ~QoreStackLocation() = default; + + //! called when pushed on the stack to set the next location + DLLLOCAL void setNext(const QoreStackLocation* next) { + stack_next = next; + } + + //! returns a pointer to the current Qore statement; internal use only + DLLLOCAL virtual const AbstractStatement* getStatement() const { + return nullptr; + } + + //! returns the next location in the stack or nullptr if there is none + DLLLOCAL virtual const QoreStackLocation* getNext() const { + return stack_next; + } + + //! returns the name of the function or method call + DLLLOCAL virtual const char* getCallName() const { + return stack_next ? stack_next->getCallName() : ""; + } + + //! returns the call type + DLLLOCAL virtual qore_call_t getCallType() const { + return stack_next ? stack_next->getCallType() : qore_call_t::CT_BUILTIN; + } + + //! returns the source location of the element + DLLLOCAL virtual const QoreProgramLocation& getLocation() const = 0; + +protected: + const QoreStackLocation* stack_next = nullptr; +}; + +/* +//! Sets the current runtime location and restores the old location on exit +class QoreExternalRuntimeLocationHelper { +public: + //! Sets the current runtime location + DLLEXPORT QoreExternalRuntimeLocationHelper(const QoreProgramLocation& loc); + + //! Sets the current runtime location + DLLEXPORT QoreExternalRuntimeLocationHelper(const QoreExternalProgramLocationWrapper& loc); + + //! Restores the old runtime location + DLLEXPORT ~QoreExternalRuntimeLocationHelper(); + +private: + const QoreProgramLocation* loc; +}; +*/ + #endif diff --git a/include/qore/intern/AbstractStatement.h b/include/qore/intern/AbstractStatement.h index ac0c3e726a..07bfdaddb9 100644 --- a/include/qore/intern/AbstractStatement.h +++ b/include/qore/intern/AbstractStatement.h @@ -116,6 +116,26 @@ class AbstractStatement { DLLLOCAL virtual void parseCommit(QoreProgram* pgm); }; +class QoreInternalStatementLocationHelper : public QoreStackLocation, public QoreProgramStackLocationHelper { +public: + DLLLOCAL QoreInternalStatementLocationHelper(const AbstractStatement* stmt) : + QoreProgramStackLocationHelper(this), stmt(stmt) { + } + + //! returns a pointer to the current Qore statement; internal use only + DLLLOCAL virtual const AbstractStatement* getStatement() const { + return stmt; + } + + //! returns the source location of the element + DLLLOCAL virtual const QoreProgramLocation& getLocation() const { + return *stmt->loc; + } + +protected: + const AbstractStatement* stmt; +}; + DLLLOCAL void push_cvar(const char* name); DLLLOCAL void pop_cvar(); DLLLOCAL LocalVar* pop_local_var(bool set_unassigned = false); diff --git a/include/qore/intern/Function.h b/include/qore/intern/Function.h index b05c434c92..4805cd7fd1 100644 --- a/include/qore/intern/Function.h +++ b/include/qore/intern/Function.h @@ -258,22 +258,7 @@ class UserSignature : public AbstractFunctionSignature { class AbstractQoreFunctionVariant; -class CodeEvaluationHelper { -protected: - qore_call_t ct; - const char* name; - ExceptionSink* xsink; - // method class - const qore_class_private* qc; - const QoreProgramLocation* loc; - QoreListNodeEvalOptionalRefHolder tmp; - const QoreTypeInfo* returnTypeInfo; // saved return type info - QoreProgram* pgm; // program used when evaluated (to find stacks for references) - q_rt_flags_t rtflags; // runtime flags - - DLLLOCAL void init(const QoreFunction* func, const AbstractQoreFunctionVariant*& variant, bool is_copy, - const qore_class_private* cctx); - +class CodeEvaluationHelper : public QoreStackLocation { public: // saves current program location in case there's an exception DLLLOCAL CodeEvaluationHelper(ExceptionSink* n_xsink, const QoreFunction* func, const AbstractQoreFunctionVariant*& variant, const char* n_name, const QoreListNode* args = nullptr, QoreObject* self = nullptr, const qore_class_private* n_qc = nullptr, qore_call_t n_ct = CT_UNUSED, bool is_copy = false, const qore_class_private* cctx = nullptr); @@ -313,9 +298,9 @@ class CodeEvaluationHelper { return pgm; } - DLLLOCAL void restorePosition() const { - update_runtime_location(loc); - } + //DLLLOCAL void restorePosition() const { + // update_runtime_location(loc); + //} DLLLOCAL q_rt_flags_t getRuntimeFlags() const { return rtflags; @@ -324,6 +309,40 @@ class CodeEvaluationHelper { DLLLOCAL const qore_class_private* getClass() const { return qc; } + + //! returns the source location of the element + DLLLOCAL virtual const QoreProgramLocation& getLocation() const { + return *loc; + } + + //! returns the name of the function or method call + DLLLOCAL virtual const char* getCallName() const { + return callName.c_str(); + } + + DLLLOCAL virtual qore_call_t getCallType() const { + return ct; + } + +protected: + qore_call_t ct; + const char* name; + ExceptionSink* xsink; + // method class + const qore_class_private* qc; + const QoreProgramLocation* loc; + QoreListNodeEvalOptionalRefHolder tmp; + const QoreTypeInfo* returnTypeInfo; // saved return type info + QoreProgram* pgm = getProgram(); // program used when evaluated (to find stacks for references) + q_rt_flags_t rtflags = 0; // runtime flags + QoreString callName; + const QoreStackLocation* stack_loc = nullptr; + bool restore_stack = false; + + DLLLOCAL void init(const QoreFunction* func, const AbstractQoreFunctionVariant*& variant, bool is_copy, + const qore_class_private* cctx); + + DLLLOCAL void setCallName(const QoreFunction* func); }; class UserVariantBase; diff --git a/include/qore/intern/QoreException.h b/include/qore/intern/QoreException.h index 6f6a021cde..a8ab51281e 100644 --- a/include/qore/intern/QoreException.h +++ b/include/qore/intern/QoreException.h @@ -36,23 +36,18 @@ #include #include -// exception/callstack entry types -#define ET_SYSTEM 0 -#define ET_USER 1 - struct QoreExceptionBase { - int type; + qore_call_t type; QoreListNode* callStack = new QoreListNode(autoTypeInfo); QoreValue err, desc, arg; - DLLLOCAL QoreExceptionBase(QoreValue n_err, QoreValue n_desc, QoreValue n_arg = QoreValue(), int n_type = ET_SYSTEM) - : type(n_type), err(n_err), desc(n_desc), arg(n_arg) { - } + DLLLOCAL QoreExceptionBase(QoreValue n_err, QoreValue n_desc, QoreValue n_arg = QoreValue(), + qore_call_t n_type = CT_BUILTIN); DLLLOCAL QoreExceptionBase(const QoreExceptionBase& old) : - type(old.type), callStack(old.callStack->copy()), - err(old.err.refSelf()), desc(old.desc.refSelf()), - arg(old.arg.refSelf()) { + type(old.type), callStack(old.callStack->copy()), + err(old.err.refSelf()), desc(old.desc.refSelf()), + arg(old.arg.refSelf()) { } DLLLOCAL ~QoreExceptionBase() { @@ -61,50 +56,32 @@ struct QoreExceptionBase { }; struct QoreExceptionLocation : QoreProgramLineLocation { - std::string file; - std::string source; - int offset; - - DLLLOCAL QoreExceptionLocation(const QoreProgramLocation& loc) : QoreProgramLineLocation(loc), - file(loc.getFileValue()), source(loc.getSourceValue()), offset(loc.offset) { - } - - DLLLOCAL QoreExceptionLocation(const QoreExceptionLocation& old) : QoreProgramLineLocation(old), - file(old.file), source(old.source), offset(old.offset) { - } - - DLLLOCAL void set(const QoreProgramLocation& loc) { - start_line = loc.start_line; - end_line = loc.end_line; - file = loc.getFileValue(); - source = loc.getSourceValue(); - offset = loc.offset; - } -}; + std::string file; + std::string source; + std::string lang; + int offset; -class QoreException : public QoreExceptionBase, public QoreExceptionLocation { - friend class ExceptionSink; - friend struct qore_es_private; - -private: - //! this function is not implemented; it is here as a private function in order to prohibit it from being used - DLLLOCAL QoreException& operator=(const QoreException&); - -protected: - DLLLOCAL ~QoreException() { - assert(!callStack); - assert(!err.hasNode()); - assert(!desc.hasNode()); - assert(!arg.hasNode()); + DLLLOCAL QoreExceptionLocation(const QoreProgramLocation& loc) : QoreProgramLineLocation(loc), + file(loc.getFileValue()), source(loc.getSourceValue()), lang(loc.getLanguageValue()), offset(loc.offset) { } - DLLLOCAL void addStackInfo(QoreHashNode* n); - - DLLLOCAL static const char* getType(qore_call_t type); + DLLLOCAL QoreExceptionLocation(const QoreExceptionLocation& old) : QoreProgramLineLocation(old), + file(old.file), source(old.source), lang(old.lang), offset(old.offset) { + } - DLLLOCAL static QoreHashNode* getStackHash(int type, const char *class_name, const char *code, const QoreProgramLocation& loc); + DLLLOCAL void set(const QoreProgramLocation& loc) { + start_line = loc.start_line; + end_line = loc.end_line; + file = loc.getFileValue(); + source = loc.getSourceValue(); + lang = loc.getLanguageValue(); + offset = loc.offset; + } +}; - DLLLOCAL static QoreHashNode* getStackHash(const QoreCallStackElement& cse); +class QoreException : public QoreExceptionBase, public QoreExceptionLocation { + friend class ExceptionSink; + friend struct qore_es_private; public: QoreException* next = nullptr; @@ -114,17 +91,23 @@ class QoreException : public QoreExceptionBase, public QoreExceptionLocation { DLLLOCAL QoreHashNode* makeExceptionObject(); // called for runtime exceptions - DLLLOCAL QoreException(const char *n_err, QoreValue n_desc, QoreValue n_arg = QoreValue()) : QoreExceptionBase(new QoreStringNode(n_err), n_desc, n_arg), QoreExceptionLocation(*get_runtime_location()) { + DLLLOCAL QoreException(const char *n_err, QoreValue n_desc, QoreValue n_arg = QoreValue()) + : QoreExceptionBase(new QoreStringNode(n_err), n_desc, n_arg), + QoreExceptionLocation(*get_runtime_location()) { } - DLLLOCAL QoreException(QoreStringNode *n_err, QoreValue n_desc, QoreValue n_arg = QoreValue()) : QoreExceptionBase(n_err, n_desc, n_arg), QoreExceptionLocation(*get_runtime_location()) { + DLLLOCAL QoreException(QoreStringNode *n_err, QoreValue n_desc, QoreValue n_arg = QoreValue()) + : QoreExceptionBase(n_err, n_desc, n_arg), + QoreExceptionLocation(*get_runtime_location()) { } - DLLLOCAL QoreException(const QoreException& old) : QoreExceptionBase(old), QoreExceptionLocation(old), next(old.next ? new QoreException(*old.next) : nullptr) { + DLLLOCAL QoreException(const QoreException& old) : QoreExceptionBase(old), + QoreExceptionLocation(old), next(old.next ? new QoreException(*old.next) : nullptr) { } // called for user exceptions - DLLLOCAL QoreException(const QoreListNode* n) : QoreExceptionBase(0, 0, 0, ET_USER), QoreExceptionLocation(*get_runtime_location()) { + DLLLOCAL QoreException(const QoreListNode* n) : QoreExceptionBase(0, 0, 0, CT_USER), + QoreExceptionLocation(*get_runtime_location()) { if (n) { err = n->getReferencedEntry(0); desc = n->getReferencedEntry(1); @@ -132,7 +115,9 @@ class QoreException : public QoreExceptionBase, public QoreExceptionLocation { } } - DLLLOCAL QoreException(const QoreProgramLocation& n_loc, const char *n_err, QoreValue n_desc, QoreValue n_arg = QoreValue(), int n_type = ET_SYSTEM) : QoreExceptionBase(new QoreStringNode(n_err), n_desc, n_arg, n_type), QoreExceptionLocation(n_loc) { + DLLLOCAL QoreException(const QoreProgramLocation& n_loc, const char *n_err, QoreValue n_desc, + QoreValue n_arg = QoreValue(), qore_call_t n_type = CT_BUILTIN) : + QoreExceptionBase(new QoreStringNode(n_err), n_desc, n_arg, n_type), QoreExceptionLocation(n_loc) { } DLLLOCAL void del(ExceptionSink *xsink); @@ -152,6 +137,26 @@ class QoreException : public QoreExceptionBase, public QoreExceptionLocation { return e; } + + DLLLOCAL static QoreHashNode* getStackHash(qore_call_t type, const char *class_name, const char *code, + const QoreProgramLocation& loc); + +protected: + DLLLOCAL ~QoreException() { + assert(!callStack); + assert(!err.hasNode()); + assert(!desc.hasNode()); + assert(!arg.hasNode()); + } + + DLLLOCAL void addStackInfo(QoreHashNode* n); + + DLLLOCAL static const char* getType(qore_call_t type); + + DLLLOCAL static QoreHashNode* getStackHash(const QoreCallStackElement& cse); + +private: + DLLLOCAL QoreException& operator=(const QoreException&) = delete; }; class ParseException : public QoreException { @@ -213,7 +218,8 @@ struct qore_es_private { } // creates a stack trace node and adds it to all exceptions in this sink - DLLLOCAL void addStackInfo(int type, const char *class_name, const char *code, const QoreProgramLocation& loc) { + DLLLOCAL void addStackInfo(qore_call_t type, const char *class_name, const char *code, + const QoreProgramLocation& loc) { assert(head); QoreHashNode* n = QoreException::getStackHash(type, class_name, code, loc); @@ -246,7 +252,8 @@ struct qore_es_private { addStackInfo(i); } - DLLLOCAL static void addStackInfo(ExceptionSink& xsink, int type, const char* class_name, const char* code, const QoreProgramLocation& loc) { + DLLLOCAL static void addStackInfo(ExceptionSink& xsink, qore_call_t type, const char* class_name, + const char* code, const QoreProgramLocation& loc) { xsink.priv->addStackInfo(type, class_name, code, loc); } diff --git a/include/qore/intern/QoreLibIntern.h b/include/qore/intern/QoreLibIntern.h index ce8d987ebe..70cf75ed9f 100644 --- a/include/qore/intern/QoreLibIntern.h +++ b/include/qore/intern/QoreLibIntern.h @@ -218,6 +218,14 @@ struct QoreProgramLocation : public QoreProgramLineLocation { return source ? source : ""; } + DLLLOCAL const char* getLanguage() const { + return lang; + } + + DLLLOCAL const char* getLanguageValue() const { + return lang ? lang : ""; + } + DLLLOCAL void setFile(const char* f) { file = f; } @@ -231,7 +239,8 @@ struct QoreProgramLocation : public QoreProgramLineLocation { || end_line < loc.end_line || file < loc.file || source < loc.source - || offset < loc.offset; + || offset < loc.offset + || lang < loc.lang; } DLLLOCAL bool operator==(const QoreProgramLocation& loc) const { @@ -239,7 +248,8 @@ struct QoreProgramLocation : public QoreProgramLineLocation { && end_line == loc.end_line && file == loc.file && source == loc.source - && offset == loc.offset; + && offset == loc.offset + && lang == loc.lang; } DLLLOCAL bool operator!=(const QoreProgramLocation& loc) const { @@ -249,6 +259,7 @@ struct QoreProgramLocation : public QoreProgramLineLocation { protected: const char* file = nullptr; const char* source = nullptr; + const char* lang = "Qore"; public: int16_t offset = 0; @@ -275,7 +286,7 @@ struct QoreCommandLineLocation : public QoreProgramLocation { // parse location for objects parsed on the command-line DLLLOCAL extern QoreCommandLineLocation qoreCommandLineLocation; -// the following functions are implemented in support.cc +// the following functions are implemented in support.cpp DLLLOCAL void parse_error(const QoreProgramLocation& loc, const char* fmt, ...); DLLLOCAL void parseException(const QoreProgramLocation& loc, const char* err, const char* fmt, ...); DLLLOCAL void parseException(const QoreProgramLocation& loc, const char* err, QoreStringNode* desc); @@ -397,31 +408,31 @@ DLLLOCAL QoreStringNode* q_fix_decimal(QoreStringNode* str, size_t offset = 0); #define Q_SVF_BSIZE 4096 #define Q_HAVE_STATVFS struct statvfs { - unsigned long f_bsize; /* File system block size */ - unsigned long f_frsize; /* Fundamental file system block size */ - unsigned int f_blocks; /* Blocks on FS in units of f_frsize */ - unsigned int f_bfree; /* Free blocks */ - unsigned int f_bavail; /* Blocks available to non-root */ - unsigned int f_files; /* Total inodes */ - unsigned int f_ffree; /* Free inodes */ - unsigned int f_favail; /* Free inodes for non-root */ - unsigned long f_fsid; /* Filesystem ID */ - unsigned long f_flag; /* Bit mask of values */ - unsigned long f_namemax; /* Max file name length */ - - DLLLOCAL void set(int64 avail, int64 total, int64 free) { - f_frsize = f_bsize = Q_SVF_BSIZE; - f_blocks = total / Q_SVF_BSIZE; - f_bfree = free / Q_SVF_BSIZE; - f_bavail = avail / Q_SVF_BSIZE; - // simulate inodes - f_files = f_blocks / 8; - f_ffree = f_bfree / 8; - f_favail = f_bavail / 8; - f_fsid = 0; - f_flag = 0; - f_namemax = 256; - } + unsigned long f_bsize; /* File system block size */ + unsigned long f_frsize; /* Fundamental file system block size */ + unsigned int f_blocks; /* Blocks on FS in units of f_frsize */ + unsigned int f_bfree; /* Free blocks */ + unsigned int f_bavail; /* Blocks available to non-root */ + unsigned int f_files; /* Total inodes */ + unsigned int f_ffree; /* Free inodes */ + unsigned int f_favail; /* Free inodes for non-root */ + unsigned long f_fsid; /* Filesystem ID */ + unsigned long f_flag; /* Bit mask of values */ + unsigned long f_namemax; /* Max file name length */ + + DLLLOCAL void set(int64 avail, int64 total, int64 free) { + f_frsize = f_bsize = Q_SVF_BSIZE; + f_blocks = total / Q_SVF_BSIZE; + f_bfree = free / Q_SVF_BSIZE; + f_bavail = avail / Q_SVF_BSIZE; + // simulate inodes + f_files = f_blocks / 8; + f_ffree = f_bfree / 8; + f_favail = f_bavail / 8; + f_fsid = 0; + f_flag = 0; + f_namemax = 256; + } }; DLLLOCAL int statvfs(const char* path, struct statvfs* buf); DLLLOCAL int q_fstatvfs(const char* filepath, struct statvfs* buf); diff --git a/include/qore/intern/QoreThreadList.h b/include/qore/intern/QoreThreadList.h index f3f5514388..695d08efc1 100644 --- a/include/qore/intern/QoreThreadList.h +++ b/include/qore/intern/QoreThreadList.h @@ -1,32 +1,32 @@ /* -*- mode: c++; indent-tabs-mode: nil -*- */ /* - QoreThreadList.h + QoreThreadList.h - Qore Programming Language + Qore Programming Language - Copyright (C) 2003 - 2015 David Nichols + Copyright (C) 2003 - 2018 Qore Technologies, s.r.o. - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. - Note that the Qore library is released under a choice of three open-source - licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more - information. + Note that the Qore library is released under a choice of three open-source + licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more + information. */ #ifndef _QORE_QORETHREADLIST_H @@ -40,8 +40,10 @@ #endif class ThreadData; +#ifdef QORE_RUNTIME_THREAD_STACK_TRACE class CallStack; class CallNode; +#endif #define QTS_AVAIL 0 #define QTS_NA 1 @@ -71,175 +73,175 @@ class tid_node { // this structure holds all thread data that can be addressed with the qore tid class ThreadEntry { public: - pthread_t ptid; - tid_node* tidnode; + pthread_t ptid; + tid_node* tidnode; #ifdef QORE_RUNTIME_THREAD_STACK_TRACE - CallStack* callStack; + CallStack* callStack; #endif - ThreadData* thread_data; - unsigned char status; - bool joined; // if set to true then pthread_detach should not be called on exit + ThreadData* thread_data; + unsigned char status; + bool joined; // if set to true then pthread_detach should not be called on exit - DLLLOCAL void cleanup(); + DLLLOCAL void cleanup(); - DLLLOCAL void allocate(tid_node* tn, int stat = QTS_NA); + DLLLOCAL void allocate(tid_node* tn, int stat = QTS_NA); - DLLLOCAL void activate(int tid, pthread_t n_ptid, QoreProgram* p, bool foreign = false); + DLLLOCAL void activate(int tid, pthread_t n_ptid, QoreProgram* p, bool foreign = false); - DLLLOCAL bool active() const { - return status == QTS_ACTIVE; - } + DLLLOCAL bool active() const { + return status == QTS_ACTIVE; + } - DLLLOCAL bool available() const { - return status == QTS_AVAIL; - } + DLLLOCAL bool available() const { + return status == QTS_AVAIL; + } }; class QoreThreadList { friend class QoreThreadListIterator; friend class tid_node; -protected: - mutable QoreThreadLock l; - unsigned num_threads; - ThreadEntry entry[MAX_QORE_THREADS]; - - tid_node* tid_head, * tid_tail; - - // current TID to be issued next - int current_tid; - - bool exiting; - - DLLLOCAL void releaseIntern(int tid) { - // NOTE: cannot safely call printd here, because normally the thread_data has been deleted - //printf("DEBUG: ThreadList.releaseIntern() TID %d terminated\n", tid); - entry[tid].cleanup(); - if (tid) - --num_threads; - } - public: - DLLLOCAL QoreThreadList() : num_threads(0), tid_head(0), tid_tail(0), current_tid(1), exiting(false) { - } - - DLLLOCAL int get(int status = QTS_NA) { - int tid = -1; - AutoLocker al(l); - - if (current_tid == MAX_QORE_THREADS) { - int i; - // scan thread_list for free entry - for (i = 1; i < MAX_QORE_THREADS; i++) { - if (entry[i].available()) { - tid = i; - goto finish; + DLLLOCAL QoreThreadList() { + } + + DLLLOCAL int get(int status = QTS_NA) { + int tid = -1; + AutoLocker al(l); + + if (current_tid == MAX_QORE_THREADS) { + int i; + // scan thread_list for free entry + for (i = 1; i < MAX_QORE_THREADS; i++) { + if (entry[i].available()) { + tid = i; + goto finish; + } } - } - if (i == MAX_QORE_THREADS) + if (i == MAX_QORE_THREADS) + return -1; + } + else + tid = current_tid++; + +finish: + entry[tid].allocate(new tid_node(tid), status); + ++num_threads; + //printf("t%d cs=0\n", tid); + + return tid; + } + + DLLLOCAL int getSignalThreadEntry() { + AutoLocker al(l); + entry[0].allocate(0); + return 0; + } + + DLLLOCAL void release(int tid) { + AutoLocker al(l); + releaseIntern(tid); + } + + DLLLOCAL int releaseReserved(int tid) { + AutoLocker al(l); + if (entry[tid].status != QTS_RESERVED) return -1; - } - else - tid = current_tid++; - finish: - entry[tid].allocate(new tid_node(tid), status); - ++num_threads; - //printf("t%d cs=0\n", tid); + releaseIntern(tid); + return 0; + } - return tid; - } + DLLLOCAL void activate(int tid, pthread_t ptid = pthread_self(), QoreProgram* p = 0, bool foreign = false) { + AutoLocker al(l); + entry[tid].activate(tid, ptid, p, foreign); + } - DLLLOCAL int getSignalThreadEntry() { - AutoLocker al(l); - entry[0].allocate(0); - return 0; - } + DLLLOCAL void setStatus(int tid, int status) { + AutoLocker al(l); + assert(entry[tid].status != status); + entry[tid].status = status; + } - DLLLOCAL void release(int tid) { - AutoLocker al(l); - releaseIntern(tid); - } + DLLLOCAL void deleteData(int tid); - DLLLOCAL int releaseReserved(int tid) { - AutoLocker al(l); - if (entry[tid].status != QTS_RESERVED) - return -1; + DLLLOCAL void deleteDataRelease(int tid); - releaseIntern(tid); - return 0; - } + DLLLOCAL void deleteDataReleaseSignalThread(); - DLLLOCAL void activate(int tid, pthread_t ptid = pthread_self(), QoreProgram* p = 0, bool foreign = false) { - AutoLocker al(l); - entry[tid].activate(tid, ptid, p, foreign); - } + DLLLOCAL int activateReserved(int tid) { + AutoLocker al(l); - DLLLOCAL void setStatus(int tid, int status) { - AutoLocker al(l); - assert(entry[tid].status != status); - entry[tid].status = status; - } - - DLLLOCAL void deleteData(int tid); + if (entry[tid].status != QTS_RESERVED) + return -1; - DLLLOCAL void deleteDataRelease(int tid); + entry[tid].activate(tid, pthread_self(), 0, true); + return 0; + } - DLLLOCAL void deleteDataReleaseSignalThread(); + DLLLOCAL unsigned getNumThreads() const { + return num_threads; + } - DLLLOCAL int activateReserved(int tid) { - AutoLocker al(l); + DLLLOCAL unsigned cancelAllActiveThreads(); - if (entry[tid].status != QTS_RESERVED) - return -1; +#ifdef QORE_RUNTIME_THREAD_STACK_TRACE + DLLLOCAL QoreHashNode* getAllCallStacks(); - entry[tid].activate(tid, pthread_self(), 0, true); - return 0; - } + DLLLOCAL void pushCall(CallNode* cn); - DLLLOCAL unsigned getNumThreads() const { - return num_threads; - } + DLLLOCAL void popCall(ExceptionSink* xsink); - DLLLOCAL unsigned cancelAllActiveThreads(); + DLLLOCAL QoreListNode* getCallStackList(); -#ifdef QORE_RUNTIME_THREAD_STACK_TRACE - DLLLOCAL QoreHashNode* getAllCallStacks(); + DLLLOCAL CallStack* getCallStack() { + return entry[gettid()].callStack; + } +#endif - DLLLOCAL void pushCall(CallNode* cn); +protected: + mutable QoreThreadLock l; + unsigned num_threads = 0; + ThreadEntry entry[MAX_QORE_THREADS]; - DLLLOCAL void popCall(ExceptionSink* xsink); + tid_node* tid_head = nullptr, + * tid_tail = nullptr; - DLLLOCAL QoreListNode* getCallStackList(); + // current TID to be issued next + int current_tid = 1; - DLLLOCAL CallStack* getCallStack() { - return entry[gettid()].callStack; - } -#endif + bool exiting = false; + DLLLOCAL void releaseIntern(int tid) { + // NOTE: cannot safely call printd here, because normally the thread_data has been deleted + //printf("DEBUG: ThreadList.releaseIntern() TID %d terminated\n", tid); + entry[tid].cleanup(); + if (tid) + --num_threads; + } }; DLLLOCAL extern QoreThreadList thread_list; class QoreThreadListIterator : public AutoLocker { protected: - tid_node* w; + tid_node* w; public: - DLLLOCAL QoreThreadListIterator() : AutoLocker(thread_list.l), w(0) { - } + DLLLOCAL QoreThreadListIterator() : AutoLocker(thread_list.l), w(0) { + } - DLLLOCAL bool next() { - do { - w = w ? w->next : thread_list.tid_head; - } while (w && (!w->tid || (thread_list.entry[w->tid].status != QTS_ACTIVE))); + DLLLOCAL bool next() { + do { + w = w ? w->next : thread_list.tid_head; + } while (w && (!w->tid || (thread_list.entry[w->tid].status != QTS_ACTIVE))); - return (bool)w; - } + return (bool)w; + } - DLLLOCAL unsigned operator*() const { - assert(w); - return w->tid; - } + DLLLOCAL unsigned operator*() const { + assert(w); + return w->tid; + } }; #endif diff --git a/include/qore/intern/qore_thread_intern.h b/include/qore/intern/qore_thread_intern.h index 8ab38ba15e..7f7917d991 100644 --- a/include/qore/intern/qore_thread_intern.h +++ b/include/qore/intern/qore_thread_intern.h @@ -243,15 +243,19 @@ DLLLOCAL void* endParsing(); DLLLOCAL Context* get_context_stack(); DLLLOCAL void update_context_stack(Context* cstack); +DLLLOCAL const QoreStackLocation* get_runtime_stack_location(); +DLLLOCAL const QoreStackLocation* update_get_runtime_stack_location(QoreStackLocation* stack_loc); +DLLLOCAL void update_runtime_stack_location(const QoreStackLocation* stack_loc); + DLLLOCAL const QoreProgramLocation* get_runtime_location(); -DLLLOCAL const QoreProgramLocation* update_get_runtime_location(const QoreProgramLocation* loc); -DLLLOCAL void update_runtime_location(const QoreProgramLocation* loc); +//DLLLOCAL const QoreProgramLocation* update_get_runtime_location(const QoreProgramLocation* loc); +//DLLLOCAL void update_runtime_location(const QoreProgramLocation* loc); DLLLOCAL void set_parse_file_info(QoreProgramLocation& loc); DLLLOCAL const char* get_parse_code(); DLLLOCAL const AbstractStatement* get_runtime_statement(); -DLLLOCAL const AbstractStatement* update_get_runtime_statement(const AbstractStatement* s); +//DLLLOCAL const AbstractStatement* update_get_runtime_statement(const AbstractStatement* s); DLLLOCAL const QoreTypeInfo* parse_set_implicit_arg_type_info(const QoreTypeInfo* ti); DLLLOCAL const QoreTypeInfo* parse_get_implicit_arg_type_info(); @@ -418,26 +422,112 @@ class QoreParseClassHelper { DLLLOCAL ~QoreParseClassHelper(); }; -class QoreProgramLocationHelper { +class QoreProgramStackLocationHelper { +public: + DLLLOCAL QoreProgramStackLocationHelper(QoreStackLocation* stack_loc) : + stack_loc(update_get_runtime_stack_location(stack_loc)) { + } + + DLLLOCAL ~QoreProgramStackLocationHelper() { + update_runtime_stack_location(stack_loc); + } + protected: - const QoreProgramLocation* loc; - const AbstractStatement* statement; + const QoreStackLocation* stack_loc; +}; + +class QoreProgramStackOptionalLocationHelper { public: - DLLLOCAL QoreProgramLocationHelper(const QoreProgramLocation* n_loc, const AbstractStatement* n_stat = nullptr) : loc(update_get_runtime_location(n_loc)), statement(update_get_runtime_statement(n_stat)) { - } + DLLLOCAL QoreProgramStackOptionalLocationHelper(QoreStackLocation* stack_loc) : + stack_loc(stack_loc ? update_get_runtime_stack_location(stack_loc) : nullptr), + restore(stack_loc ? true : false) { + } - DLLLOCAL ~QoreProgramLocationHelper() { - update_runtime_location(loc); - update_get_runtime_statement(statement); - } + DLLLOCAL ~QoreProgramStackOptionalLocationHelper() { + if (restore) { + update_runtime_stack_location(stack_loc); + } + } + +protected: + const QoreStackLocation* stack_loc; + bool restore; }; -class QoreProgramOptionalLocationHelper { +class QoreInternalStackLocationHelper : public QoreStackLocation, public QoreProgramStackLocationHelper { +public: + DLLLOCAL QoreInternalStackLocationHelper(const QoreProgramLocation& loc) : QoreProgramStackLocationHelper(this), + loc(loc) { + } + + //! returns the source location of the element + DLLLOCAL virtual const QoreProgramLocation& getLocation() const { + return loc; + } + +protected: + const QoreProgramLocation& loc; +}; + +class QoreInternalStackOptionalLocationHelper : public QoreStackLocation, public QoreProgramStackOptionalLocationHelper { +public: + DLLLOCAL QoreInternalStackOptionalLocationHelper(const QoreProgramLocation* loc) : QoreProgramStackOptionalLocationHelper(loc ? this : nullptr), + loc(loc) { + } + + //! returns the source location of the element + DLLLOCAL virtual const QoreProgramLocation& getLocation() const { + assert(loc); + return *loc; + } + +protected: + const QoreProgramLocation* loc; +}; + +class QoreInternalCallStackLocationHelper : public QoreStackLocation, public QoreProgramStackLocationHelper { +public: + DLLLOCAL QoreInternalCallStackLocationHelper(const QoreProgramLocation& loc, const char* call, qore_call_t call_type) : + QoreProgramStackLocationHelper(this), loc(loc), call(call), call_type(call_type) { + } + + //! returns the source location of the element + DLLLOCAL virtual const QoreProgramLocation& getLocation() const { + return loc; + } + + //! returns the name of the function or method call + DLLLOCAL virtual const char* getCallName() const { + return call; + } + + DLLLOCAL virtual qore_call_t getCallType() const { + return call_type; + } + +protected: + const QoreProgramLocation& loc; + const char* call; + qore_call_t call_type; +}; + +/* +class QoreProgramLocationHelper { +public: + DLLLOCAL QoreProgramLocationHelper(const QoreProgramLocation* n_loc, const AbstractStatement* n_stat = nullptr) : loc(update_get_runtime_location(n_loc)), statement(update_get_runtime_statement(n_stat)) { + } + + DLLLOCAL ~QoreProgramLocationHelper() { + update_runtime_location(loc); + update_get_runtime_statement(statement); + } + protected: const QoreProgramLocation* loc; const AbstractStatement* statement; - bool restore; +}; +class QoreProgramOptionalLocationHelper { public: DLLLOCAL QoreProgramOptionalLocationHelper(const QoreProgramLocation* n_loc, const AbstractStatement* n_stat = nullptr) : restore((bool)n_loc) { if (n_loc) { @@ -452,7 +542,13 @@ class QoreProgramOptionalLocationHelper { update_get_runtime_statement(statement); } } + +protected: + const QoreProgramLocation* loc; + const AbstractStatement* statement; + bool restore; }; +*/ // allows for the parse lock for the current program to be acquired by binary modules class CurrentProgramRuntimeParseContextHelper { @@ -827,7 +923,8 @@ class CallStackHelper : public CallNode { DLLLOCAL void* operator new(size_t); public: - DLLLOCAL CallStackHelper(const char* f, int t, QoreObject* o, const qore_class_private* c, ExceptionSink* n_xsink) : CallNode(f, t, o, c, getProgram(), get_runtime_statement()), xsink(n_xsink) { + DLLLOCAL CallStackHelper(const char* f, int t, QoreObject* o, const qore_class_private* c, ExceptionSink* n_xsink) + : CallNode(f, t, o, c, getProgram(), get_runtime_statement()), xsink(n_xsink) { pushCall(this); } DLLLOCAL ~CallStackHelper() { diff --git a/lib/AbstractStatement.cpp b/lib/AbstractStatement.cpp index 0c6e5fc011..f78eab38b6 100644 --- a/lib/AbstractStatement.cpp +++ b/lib/AbstractStatement.cpp @@ -84,7 +84,8 @@ void AbstractStatement::finalizeBlock(int sline, int eline) { int AbstractStatement::exec(QoreValue& return_value, ExceptionSink *xsink) { printd(1, "AbstractStatement::exec() this: %p file: %s line: %d\n", this, loc->getFile(), loc->start_line); - QoreProgramLocationHelper l(loc, this); + QoreInternalStatementLocationHelper stack_lock(this); + //QoreProgramLocationHelper l(loc, this); #ifdef QORE_MANAGE_STACK if (check_stack(xsink)) @@ -105,35 +106,35 @@ int AbstractStatement::parseInit(LocalVar *oflag, int pflag) { } QoreBreakpoint* AbstractStatement::getBreakpoint() const { - if (breakpointFlag) { - for (std::list::iterator it = breakpoints->begin(); it != breakpoints->end(); ++it) { - if ((*it)->checkBreak()) { - return *it; - } - } - } - return 0; + if (breakpointFlag) { + for (std::list::iterator it = breakpoints->begin(); it != breakpoints->end(); ++it) { + if ((*it)->checkBreak()) { + return *it; + } + } + } + return 0; } void AbstractStatement::assignBreakpoint(QoreBreakpoint *bkpt) { - if (!breakpoints) { - breakpoints = new QoreBreakpointList_t(); - breakpoints->push_front(bkpt); - } else { - if (std::find(breakpoints->begin(), breakpoints->end(), bkpt) == breakpoints->end()) { - breakpoints->push_front(bkpt); - } - } - breakpointFlag = !breakpoints->empty(); + if (!breakpoints) { + breakpoints = new QoreBreakpointList_t(); + breakpoints->push_front(bkpt); + } else { + if (std::find(breakpoints->begin(), breakpoints->end(), bkpt) == breakpoints->end()) { + breakpoints->push_front(bkpt); + } + } + breakpointFlag = !breakpoints->empty(); } void AbstractStatement::unassignBreakpoint(QoreBreakpoint *bkpt) { - if (breakpoints) { - breakpoints->remove(bkpt); - breakpointFlag = !breakpoints->empty(); - } + if (breakpoints) { + breakpoints->remove(bkpt); + breakpointFlag = !breakpoints->empty(); + } } void AbstractStatement::parseCommit(QoreProgram* pgm) { - qore_program_private::registerStatement(pgm, this, true); + qore_program_private::registerStatement(pgm, this, true); } diff --git a/lib/ExceptionSink.cpp b/lib/ExceptionSink.cpp index 8254550127..66adee1cf9 100644 --- a/lib/ExceptionSink.cpp +++ b/lib/ExceptionSink.cpp @@ -155,8 +155,9 @@ AbstractQoreNode* ExceptionSink::raiseException(const char *err, const char *fmt va_start(args, fmt); int rc = desc->vsprintf(fmt, args); va_end(args); - if (!rc) - break; + if (!rc) { + break; + } } printd(5, "ExceptionSink::raiseException(%s, %s)\n", err, desc->getBuffer()); priv->insert(new QoreException(err, desc)); @@ -182,8 +183,9 @@ AbstractQoreNode* ExceptionSink::raiseErrnoException(const char *err, int en, co va_start(args, fmt); int rc = desc->vsprintf(fmt, args); va_end(args); - if (!rc) - break; + if (!rc) { + break; + } } return raiseErrnoException(err, en, desc); @@ -211,7 +213,8 @@ AbstractQoreNode* ExceptionSink::raiseExceptionArg(const char* err, QoreValue ar return nullptr; } -AbstractQoreNode* ExceptionSink::raiseExceptionArg(const char* err, QoreValue arg, QoreStringNode *desc, const QoreCallStack& stack) { +AbstractQoreNode* ExceptionSink::raiseExceptionArg(const char* err, QoreValue arg, QoreStringNode *desc, + const QoreCallStack& stack) { printd(5, "ExceptionSink::raiseExceptionArg(%s, %s, %p)\n", err, desc->getBuffer(), &stack); QoreException* exc = new QoreException(err, desc); exc->arg = arg; @@ -220,7 +223,8 @@ AbstractQoreNode* ExceptionSink::raiseExceptionArg(const char* err, QoreValue ar return nullptr; } -AbstractQoreNode* ExceptionSink::raiseExceptionArg(const QoreProgramLocation& loc, const char* err, QoreValue arg, QoreStringNode *desc, const QoreCallStack& stack) { +AbstractQoreNode* ExceptionSink::raiseExceptionArg(const QoreProgramLocation& loc, const char* err, QoreValue arg, + QoreStringNode *desc, const QoreCallStack& stack) { printd(5, "ExceptionSink::raiseExceptionArg(loc, %s, %s, %p)\n", err, desc->getBuffer(), &stack); priv->insert(new QoreException(loc, err, desc, arg)); priv->addStackInfo(stack); @@ -236,8 +240,9 @@ AbstractQoreNode* ExceptionSink::raiseExceptionArg(const char* err, QoreValue ar va_start(args, fmt); int rc = desc->vsprintf(fmt, args); va_end(args); - if (!rc) + if (!rc) { break; + } } printd(5, "ExceptionSink::raiseExceptionArg(%s, %s)\n", err, desc->getBuffer()); QoreException* exc = new QoreException(err, desc); @@ -255,11 +260,13 @@ void ExceptionSink::raiseException(const QoreListNode* n) { } void ExceptionSink::raiseException(const QoreProgramLocation& loc, const char* err, QoreValue arg, QoreValue desc) { - printd(5, "ExceptionSink::raiseExceptionArg(%s, %s)\n", err, desc.getType() == NT_STRING ? desc.get()->c_str() : desc.getTypeName()); + printd(5, "ExceptionSink::raiseExceptionArg(%s, %s)\n", err, + desc.getType() == NT_STRING ? desc.get()->c_str() : desc.getTypeName()); priv->insert(new QoreException(loc, err, desc, arg)); } -void ExceptionSink::raiseException(const QoreProgramLocation &loc, const char *err, QoreValue arg, const char *fmt, ...) { +void ExceptionSink::raiseException(const QoreProgramLocation &loc, const char *err, QoreValue arg, + const char *fmt, ...) { QoreStringNode *desc = new QoreStringNode; va_list args; @@ -268,8 +275,9 @@ void ExceptionSink::raiseException(const QoreProgramLocation &loc, const char *e va_start(args, fmt); int rc = desc->vsprintf(fmt, args); va_end(args); - if (!rc) + if (!rc) { break; + } } raiseException(loc, err, arg, desc); @@ -290,10 +298,11 @@ void ExceptionSink::assimilate(ExceptionSink& xs) { xs.priv->thread_exit = false; } if (xs.priv->tail) { - if (priv->tail) - priv->tail->next = xs.priv->head; - else - priv->head = xs.priv->head; + if (priv->tail) { + priv->tail->next = xs.priv->head; + } else { + priv->head = xs.priv->head; + } priv->tail = xs.priv->tail; } xs.priv->head = xs.priv->tail = nullptr; @@ -316,6 +325,269 @@ void ExceptionSink::outOfMemory() { #endif } +// static member function +void ExceptionSink::defaultExceptionHandler(QoreException* e) { + ExceptionSink xsink; + + QoreString nstr; + { + DateTime now; + now.setNow(); + now.format(nstr, "YYYY-MM-DD HH:mm:SS.xx Dy Z (z)"); + } + + unsigned ecnt = 0; + + while (e) { + //printd(5, "ExceptionSink::defaultExceptionHandler() cs size=%d\n", cs->size()); + printe("unhandled QORE %s exception thrown in TID %d at %s", + e->type == CT_USER ? "User" : "System", gettid(), nstr.getBuffer()); + + QoreListNode* cs = e->callStack; + bool found = false; + if (cs->size()) { + // find first non-rethrow element + unsigned i = 0; + + QoreHashNode *h; + while (true) { + h = cs->retrieveEntry(i).get(); + assert(h); + if (h->getKeyValue("typecode").getAsBigInt() != CT_RETHROW) + break; + i++; + if (i == cs->size()) + break; + } + + if (i < cs->size()) { + found = true; + QoreStringNode *func = h->getKeyValue("function").get(); + QoreStringNode *type = h->getKeyValue("type").get(); + + printe(" in %s() (%s:%d", func->getBuffer(), e->file.c_str(), e->start_line); + + if (e->start_line == e->end_line) { + if (!e->source.empty()) + printe(", source %s:%d", e->source.c_str(), e->start_line + e->offset); + } + else { + printe("-%d", e->end_line); + if (!e->source.empty()) + printe(", source %s:%d-%d", e->source.c_str(), e->start_line + e->offset, + e->end_line + e->offset); + } + printe(", %s code)\n", type->getBuffer()); + } + } + + if (!found) { + if (!e->file.empty()) { + printe(" at %s:", e->file.c_str()); + if (e->start_line == e->end_line) { + if (!e->start_line) { + printe(""); + if (!e->source.empty()) + printe(" (source %s)", e->source.c_str()); + } + else { + printe("%d", e->start_line); + if (!e->source.empty()) + printe(" (source %s:%d)", e->source.c_str(), e->start_line + e->offset); + } + } + else { + printe("%d-%d", e->start_line, e->end_line); + if (!e->source.empty()) + printe(" (source %s:%d-%d)", e->source.c_str(), e->start_line + e->offset, + e->end_line + e->offset); + } + } + else if (e->start_line) { + if (e->start_line == e->end_line) { + if (!e->start_line) + printe(" at "); + else + printe(" on line %d", e->start_line); + } + else + printe(" on lines %d through %d", e->start_line, e->end_line); + } + printe("\n"); + } + + if (e->type == CT_BUILTIN) { + QoreStringNode* err = e->err.get(); + QoreStringNode* desc = e->desc.get(); + printe("%s: %s\n", err->c_str(), desc->c_str()); + } else { + bool hdr = false; + if (!e->err.isNothing()) { + if (e->err.getType() == NT_STRING) { + QoreStringNode *err = e->err.get(); + printe("%s", err->c_str()); + } + else { + QoreNodeAsStringHelper str(e->err, FMT_NORMAL, &xsink); + printe("EXCEPTION: %s", str->c_str()); + hdr = true; + } + } + else + printe("EXCEPTION"); + + if (!e->desc.isNothing()) { + if (e->desc.getType() == NT_STRING) { + QoreStringNode *desc = e->desc.get(); + printe("%s%s", hdr ? ", desc: " : ": ", desc->c_str()); + } + else { + QoreNodeAsStringHelper str(e->desc, FMT_NORMAL, &xsink); + printe(", desc: %s", str->c_str()); + if (!hdr) + hdr = true; + } + } + + if (!e->arg.isNothing()) { + if (e->arg.getType() == NT_STRING) { + QoreStringNode *arg = e->arg.get(); + printe("%s%s", hdr ? ", arg: " : ": ", arg->c_str()); + } + else { + QoreNodeAsStringHelper str(e->arg, FMT_NORMAL, &xsink); + printe(", arg: %s", str->c_str()); + } + } + printe("\n"); + } + + if (cs->size()) { + printe("call stack:\n"); + for (unsigned i = 0; i < cs->size(); i++) { + int pos = cs->size() - i; + QoreHashNode* h = cs->retrieveEntry(i).get(); + QoreStringNode* strtype = h->getKeyValue("type").get(); + const char* type = strtype->getBuffer(); + int typecode = (int)h->getKeyValue("typecode").getAsBigInt(); + if (!strcmp(type, "new-thread")) + printe(" %2d: *thread start*\n", pos); + else { + QoreStringNode* fn = h->getKeyValue("file").get(); + const char* fns = fn && !fn->empty() ? fn->getBuffer() : 0; + int start_line = (int)h->getKeyValue("line").getAsBigInt(); + int end_line = (int)h->getKeyValue("endline").getAsBigInt(); + + QoreStringNode* src = h->getKeyValue("source").get(); + const char* srcs = src && !src->empty() ? src->getBuffer() : 0; + int offset = (int)h->getKeyValue("offset").getAsBigInt(); + + printe(" %2d: ", pos); + + if (typecode == CT_RETHROW) { + printe("RETHROW at "); + if (fn) { + printe("%s:", fn->getBuffer()); + } + else + printe("line"); + printe("%d", start_line); + if (srcs) + printe(" (source %s:%d)", srcs, offset + start_line); + } + else { + QoreStringNode* fs = h->getKeyValue("function").get(); + printe("%s() (", fs->getBuffer()); + if (fns) { + if (start_line == end_line) { + if (!start_line) + printe("%s:", fns); + else { + printe("%s:%d", fns, start_line); + if (srcs) + printe(" (source %s:%d)", srcs, start_line + offset); + } + } + else { + printe("%s:%d-%d", fns, start_line, end_line); + if (srcs) + printe(" (source %s:%d-%d)", srcs, start_line + offset, end_line + offset); + } + } + else { + if (start_line == end_line) { + if (!start_line) + printe(""); + else + printe("line %d", start_line); + } + else + printe("line %d - %d", start_line, end_line); + } + printe(", %s code)", type); + } + printe("\n"); + } + } + } + e = e->next; + if (e) { + ++ecnt; + if (ecnt == Q_MAX_EXCEPTIONS) { + printe("*** maximum exception count reached (%d); suppressing further output\n", ecnt); + break; + } + printe("chained exception:\n"); + } + } +} + +// static member function +void ExceptionSink::defaultWarningHandler(QoreException *e) { + ExceptionSink xsink; + + while (e) { + printe("warning encountered "); + + if (!e->file.empty()) { + printe("at %s:", e->file.c_str()); + if (e->start_line == e->end_line) { + if (!e->start_line) { + printe(""); + if (!e->source.empty()) + printe(" (source %s)", e->source.c_str()); + } else { + printe("%d", e->start_line); + if (!e->source.empty()) + printe(" (source %s:%d)", e->source.c_str(), e->start_line + e->offset); + } + } else { + printe("%d-%d", e->start_line, e->end_line); + if (!e->source.empty()) + printe(" (source %s:%d-%d)", e->source.c_str(), e->start_line + e->offset, e->end_line + e->offset); + } + } else if (e->start_line) { + if (e->start_line == e->end_line) { + if (!e->start_line) + printe("at "); + else + printe("on line %d", e->start_line); + } else + printe("on line %d-%d", e->start_line, e->end_line); + } + printe("\n"); + + QoreStringNode* err = e->err.get(); + QoreStringNode* desc = e->desc.get(); + + printe("%s: %s\n", err->c_str(), desc->c_str()); + + e = e->next; + if (e) + printe("next warning:\n"); + } +} + QoreExternalProgramLocationWrapper::QoreExternalProgramLocationWrapper() : loc(new QoreProgramLocation) { } diff --git a/lib/Function.cpp b/lib/Function.cpp index 0738363654..f5dc859887 100644 --- a/lib/Function.cpp +++ b/lib/Function.cpp @@ -136,15 +136,19 @@ static void add_args(QoreStringNode &desc, const QoreListNode* args) { } } -CodeEvaluationHelper::CodeEvaluationHelper(ExceptionSink* n_xsink, const QoreFunction* func, const AbstractQoreFunctionVariant*& variant, const char* n_name, const QoreListNode* args, QoreObject* self, const qore_class_private* n_qc, qore_call_t n_ct, bool is_copy, const qore_class_private* cctx) +CodeEvaluationHelper::CodeEvaluationHelper(ExceptionSink* n_xsink, const QoreFunction* func, + const AbstractQoreFunctionVariant*& variant, const char* n_name, const QoreListNode* args, QoreObject* self, + const qore_class_private* n_qc, qore_call_t n_ct, bool is_copy, const qore_class_private* cctx) : ct(n_ct), name(n_name), xsink(n_xsink), qc(n_qc), - loc(get_runtime_location()), tmp(n_xsink), returnTypeInfo((const QoreTypeInfo*)-1), pgm(getProgram()), rtflags(0) { + loc(get_runtime_location()), + tmp(n_xsink), returnTypeInfo((const QoreTypeInfo*)-1) { if (self && !self->isValid()) { assert(n_qc); xsink->raiseException("OBJECT-ALREADY-DELETED", "cannot call %s::%s() on an object that has already been deleted", qc->name.c_str(), func->getName()); return; } + setCallName(func); tmp.assignEval(args); if (*xsink) { return; @@ -153,16 +157,19 @@ CodeEvaluationHelper::CodeEvaluationHelper(ExceptionSink* n_xsink, const QoreFun init(func, variant, is_copy, cctx); } -CodeEvaluationHelper::CodeEvaluationHelper(ExceptionSink* n_xsink, const QoreFunction* func, const AbstractQoreFunctionVariant*& variant, const char* n_name, QoreListNode* args, QoreObject* self, const qore_class_private* n_qc, qore_call_t n_ct, bool is_copy, const qore_class_private* cctx) +CodeEvaluationHelper::CodeEvaluationHelper(ExceptionSink* n_xsink, const QoreFunction* func, + const AbstractQoreFunctionVariant*& variant, const char* n_name, QoreListNode* args, QoreObject* self, + const qore_class_private* n_qc, qore_call_t n_ct, bool is_copy, const qore_class_private* cctx) : ct(n_ct), name(n_name), xsink(n_xsink), qc(n_qc), loc(get_runtime_location()), - tmp(n_xsink), returnTypeInfo((const QoreTypeInfo*)-1), pgm(getProgram()), rtflags(0) { + tmp(n_xsink), returnTypeInfo((const QoreTypeInfo*)-1) { if (self && !self->isValid()) { assert(n_qc); xsink->raiseException("OBJECT-ALREADY-DELETED", "cannot call %s::%s() on an object that has already been deleted", qc->name.c_str(), func->getName()); return; } + setCallName(func); tmp.assignEval(args); if (*xsink) { return; @@ -172,10 +179,19 @@ CodeEvaluationHelper::CodeEvaluationHelper(ExceptionSink* n_xsink, const QoreFun } CodeEvaluationHelper::~CodeEvaluationHelper() { - if (returnTypeInfo != (const QoreTypeInfo*)-1) + if (restore_stack) { + update_runtime_stack_location(stack_loc); + } + if (returnTypeInfo != (const QoreTypeInfo*)-1) { saveReturnTypeInfo(returnTypeInfo); - if (ct != CT_UNUSED && xsink->isException()) - qore_es_private::addStackInfo(*xsink, ct, qc ? qc->name.c_str() : nullptr, name, *loc); + } +} + +void CodeEvaluationHelper::setCallName(const QoreFunction* func) { + if (qc) { + callName.sprintf("%s::", qc->name.c_str()); + } + callName.concat(func->getName()); } void CodeEvaluationHelper::init(const QoreFunction* func, const AbstractQoreFunctionVariant*& variant, bool is_copy, @@ -211,6 +227,10 @@ void CodeEvaluationHelper::init(const QoreFunction* func, const AbstractQoreFunc setCallType(variant->getCallType()); setReturnTypeInfo(variant->getReturnTypeInfo()); + + // add call to call stack + stack_loc = update_get_runtime_stack_location(this); + restore_stack = true; } int CodeEvaluationHelper::processDefaultArgs(const QoreFunction* func, const AbstractQoreFunctionVariant* variant, bool check_args, bool is_copy) { diff --git a/lib/QoreClass.cpp b/lib/QoreClass.cpp index 1e661005cf..0a5730d27e 100644 --- a/lib/QoreClass.cpp +++ b/lib/QoreClass.cpp @@ -973,7 +973,8 @@ int qore_class_private::initMember(QoreObject& o, bool& need_scan, const char* m assert(v.isNothing()); if (!info.exp.isNothing()) { // set runtime location - QoreProgramLocationHelper l(info.loc); + QoreInternalStackLocationHelper l(*info.loc); + //QoreProgramLocationHelper l(info.loc); ValueEvalRefHolder val(info.exp, xsink); if (*xsink) { return -1; @@ -1004,23 +1005,25 @@ int qore_class_private::initMember(QoreObject& o, bool& need_scan, const char* m } void qore_class_private::execBaseClassConstructor(QoreObject* self, BCEAList* bceal, ExceptionSink* xsink) const { - //printd(5, "qore_class_private::execBaseClassConstructor() '%s' constructor: %p\n", name.c_str(), constructor); - // if there is no constructor, execute the superclass constructors directly - if (!constructor){ - if (scl) // execute base class constructors if any - scl->execConstructors(self, bceal, xsink); + //printd(5, "qore_class_private::execBaseClassConstructor() '%s' constructor: %p\n", name.c_str(), constructor); + // if there is no constructor, execute the superclass constructors directly + if (!constructor){ + if (scl) { // execute base class constructors if any + scl->execConstructors(self, bceal, xsink); + } - return; - } - // no lock is sent with constructor, because no variable has been assigned yet - bool already_executed; - const AbstractQoreFunctionVariant* variant; - const QoreProgramLocation* aloc = nullptr; - QoreListNode* args = bceal->findArgs(cls->getID(), &already_executed, variant, aloc); - if (!already_executed) { - QoreProgramOptionalLocationHelper plh(aloc); - constructor->priv->evalConstructor(variant, self, args, bceal, xsink); - } + return; + } + // no lock is sent with constructor, because no variable has been assigned yet + bool already_executed; + const AbstractQoreFunctionVariant* variant; + const QoreProgramLocation* aloc = nullptr; + QoreListNode* args = bceal->findArgs(cls->getID(), &already_executed, variant, aloc); + if (!already_executed) { + QoreInternalStackOptionalLocationHelper plh(aloc); + //QoreProgramOptionalLocationHelper plh(aloc); + constructor->priv->evalConstructor(variant, self, args, bceal, xsink); + } } QoreObject* qore_class_private::execConstructor(const AbstractQoreFunctionVariant* variant, const QoreListNode* args, ExceptionSink* xsink) const { @@ -4405,15 +4408,17 @@ int ConstructorMethodVariant::constructorPrelude(const QoreClass& thisclass, Cod const BCAList* bcal = getBaseClassArgumentList(); if (bcal) { bcal->execBaseClassConstructorArgs(bceal, xsink); - if (*xsink) + if (*xsink) { return -1; + } } bcl->execConstructors(self, bceal, xsink); - if (*xsink) + if (*xsink) { return -1; + } } - ceh.restorePosition(); + //ceh.restorePosition(); return 0; } @@ -4497,27 +4502,29 @@ void BuiltinExternalDestructorVariant::evalDestructor(const QoreClass &thisclass } void UserCopyVariant::evalCopy(const QoreClass& thisclass, QoreObject* self, QoreObject* old, CodeEvaluationHelper& ceh, BCList* scl, ExceptionSink* xsink) const { - // there can only be max 1 param - assert(signature.numParams() <= 1); + // there can only be max 1 param + assert(signature.numParams() <= 1); - QoreListNode* args = new QoreListNode; - args->push(self->refSelf(), nullptr); - ceh.setArgs(args); + QoreListNode* args = new QoreListNode; + args->push(self->refSelf(), nullptr); + ceh.setArgs(args); - UserVariantExecHelper uveh(this, &ceh, xsink); - if (!uveh) - return; + UserVariantExecHelper uveh(this, &ceh, xsink); + if (!uveh) { + return; + } - CodeContextHelper cch(xsink, CT_USER, "copy", self, qore_class_private::get(thisclass)); + CodeContextHelper cch(xsink, CT_USER, "copy", self, qore_class_private::get(thisclass)); - if (scl) { - scl->sml.execCopyMethods(self, old, xsink); - if (*xsink) - return; - ceh.restorePosition(); - } + if (scl) { + scl->sml.execCopyMethods(self, old, xsink); + if (*xsink) { + return; + } + //ceh.restorePosition(); + } - evalIntern(uveh.getArgv(), self, xsink).discard(xsink); + evalIntern(uveh.getArgv(), self, xsink).discard(xsink); } void UserCopyVariant::parseInit(QoreFunction* f) { @@ -4561,16 +4568,17 @@ void UserCopyVariant::parseInit(QoreFunction* f) { } void BuiltinCopyVariantBase::evalCopy(const QoreClass& thisclass, QoreObject* self, QoreObject* old, CodeEvaluationHelper& ceh, BCList* scl, ExceptionSink* xsink) const { - CodeContextHelper cch(xsink, CT_BUILTIN, "copy", self, qore_class_private::get(thisclass)); + CodeContextHelper cch(xsink, CT_BUILTIN, "copy", self, qore_class_private::get(thisclass)); - if (scl) { - scl->sml.execCopyMethods(self, old, xsink); - if (*xsink) - return; - ceh.restorePosition(); - } + if (scl) { + scl->sml.execCopyMethods(self, old, xsink); + if (*xsink) { + return; + } + //ceh.restorePosition(); + } - old->evalCopyMethodWithPrivateData(thisclass, this, self, xsink); + old->evalCopyMethodWithPrivateData(thisclass, this, self, xsink); } void ConstructorMethodFunction::evalConstructor(const AbstractQoreFunctionVariant* variant, const QoreClass& thisclass, QoreObject* self, const QoreListNode* args, BCList* bcl, BCEAList* bceal, ExceptionSink* xsink) const { diff --git a/lib/QoreException.cpp b/lib/QoreException.cpp index 1c17e9585a..70a9db9435 100644 --- a/lib/QoreException.cpp +++ b/lib/QoreException.cpp @@ -38,6 +38,31 @@ #define Q_MAX_EXCEPTIONS 10 +QoreExceptionBase::QoreExceptionBase(QoreValue n_err, QoreValue n_desc, QoreValue n_arg, qore_call_t n_type) + : type(n_type), err(n_err), desc(n_desc), arg(n_arg) { + // populate call stack + const QoreStackLocation* w = get_runtime_stack_location(); + qore_call_t last_call_type; + const char* last_call_name = nullptr; + const QoreProgramLocation* last_loc = nullptr; + while (w) { + qore_call_t call_type = w->getCallType(); + const char* call_name = w->getCallName(); + const QoreProgramLocation& loc = w->getLocation(); + // only push if the location is not equal to the last one + if (!last_loc || call_type != last_call_type || *last_loc != loc || strcmp(call_name, last_call_name)) { + callStack->push( + QoreException::getStackHash(call_type, nullptr, call_name, loc), + nullptr + ); + } + last_call_type = call_type; + last_call_name = call_name; + last_loc = &loc; + w = w->getNext(); + } +} + void QoreException::del(ExceptionSink* xsink) { if (callStack) { //printd(5, "QoreException::del() this: %p callStack: %p (r: %d)\n", this, callStack, callStack->reference_count()); @@ -49,8 +74,9 @@ void QoreException::del(ExceptionSink* xsink) { err.discard(xsink); desc.discard(xsink); arg.discard(xsink); - if (next) + if (next) { next->del(xsink); + } delete this; } @@ -61,7 +87,7 @@ QoreHashNode* QoreException::makeExceptionObject() { QoreHashNode* h = new QoreHashNode(hashdeclExceptionInfo, nullptr); auto ph = qore_hash_private::get(*h); - ph->setKeyValueIntern("type", new QoreStringNode(type == ET_USER ? "User" : "System")); + ph->setKeyValueIntern("type", new QoreStringNode(type == CT_USER ? "User" : "System")); ph->setKeyValueIntern("file", new QoreStringNode(file)); ph->setKeyValueIntern("line", start_line); ph->setKeyValueIntern("endline", end_line); @@ -98,271 +124,6 @@ void QoreException::addStackInfo(QoreHashNode* n) { callStack->push(n, nullptr); } -// static member function -void ExceptionSink::defaultExceptionHandler(QoreException* e) { - ExceptionSink xsink; - - QoreString nstr; - { - DateTime now; - now.setNow(); - now.format(nstr, "YYYY-MM-DD HH:mm:SS.xx Dy Z (z)"); - } - - unsigned ecnt = 0; - - while (e) { - //printd(5, "ExceptionSink::defaultExceptionHandler() cs size=%d\n", cs->size()); - printe("unhandled QORE %s exception thrown in TID %d at %s", e->type == ET_USER ? "User" : "System", gettid(), nstr.getBuffer()); - - QoreListNode* cs = e->callStack; - bool found = false; - if (cs->size()) { - // find first non-rethrow element - unsigned i = 0; - - QoreHashNode *h; - while (true) { - h = cs->retrieveEntry(i).get(); - assert(h); - if (h->getKeyValue("typecode").getAsBigInt() != CT_RETHROW) - break; - i++; - if (i == cs->size()) - break; - } - - if (i < cs->size()) { - found = true; - QoreStringNode *func = h->getKeyValue("function").get(); - QoreStringNode *type = h->getKeyValue("type").get(); - - printe(" in %s() (%s:%d", func->getBuffer(), e->file.c_str(), e->start_line); - - if (e->start_line == e->end_line) { - if (!e->source.empty()) - printe(", source %s:%d", e->source.c_str(), e->start_line + e->offset); - } - else { - printe("-%d", e->end_line); - if (!e->source.empty()) - printe(", source %s:%d-%d", e->source.c_str(), e->start_line + e->offset, e->end_line + e->offset); - } - printe(", %s code)\n", type->getBuffer()); - } - } - - if (!found) { - if (!e->file.empty()) { - printe(" at %s:", e->file.c_str()); - if (e->start_line == e->end_line) { - if (!e->start_line) { - printe(""); - if (!e->source.empty()) - printe(" (source %s)", e->source.c_str()); - } - else { - printe("%d", e->start_line); - if (!e->source.empty()) - printe(" (source %s:%d)", e->source.c_str(), e->start_line + e->offset); - } - } - else { - printe("%d-%d", e->start_line, e->end_line); - if (!e->source.empty()) - printe(" (source %s:%d-%d)", e->source.c_str(), e->start_line + e->offset, e->end_line + e->offset); - } - } - else if (e->start_line) { - if (e->start_line == e->end_line) { - if (!e->start_line) - printe(" at "); - else - printe(" on line %d", e->start_line); - } - else - printe(" on lines %d through %d", e->start_line, e->end_line); - } - printe("\n"); - } - - if (e->type == ET_SYSTEM) { - QoreStringNode* err = e->err.get(); - QoreStringNode* desc = e->desc.get(); - printe("%s: %s\n", err->c_str(), desc->c_str()); - } - else { - bool hdr = false; - if (!e->err.isNothing()) { - if (e->err.getType() == NT_STRING) { - QoreStringNode *err = e->err.get(); - printe("%s", err->c_str()); - } - else { - QoreNodeAsStringHelper str(e->err, FMT_NORMAL, &xsink); - printe("EXCEPTION: %s", str->c_str()); - hdr = true; - } - } - else - printe("EXCEPTION"); - - if (!e->desc.isNothing()) { - if (e->desc.getType() == NT_STRING) { - QoreStringNode *desc = e->desc.get(); - printe("%s%s", hdr ? ", desc: " : ": ", desc->c_str()); - } - else { - QoreNodeAsStringHelper str(e->desc, FMT_NORMAL, &xsink); - printe(", desc: %s", str->c_str()); - if (!hdr) - hdr = true; - } - } - - if (!e->arg.isNothing()) { - if (e->arg.getType() == NT_STRING) { - QoreStringNode *arg = e->arg.get(); - printe("%s%s", hdr ? ", arg: " : ": ", arg->c_str()); - } - else { - QoreNodeAsStringHelper str(e->arg, FMT_NORMAL, &xsink); - printe(", arg: %s", str->c_str()); - } - } - printe("\n"); - } - - if (cs->size()) { - printe("call stack:\n"); - for (unsigned i = 0; i < cs->size(); i++) { - int pos = cs->size() - i; - QoreHashNode* h = cs->retrieveEntry(i).get(); - QoreStringNode* strtype = h->getKeyValue("type").get(); - const char* type = strtype->getBuffer(); - int typecode = (int)h->getKeyValue("typecode").getAsBigInt(); - if (!strcmp(type, "new-thread")) - printe(" %2d: *thread start*\n", pos); - else { - QoreStringNode* fn = h->getKeyValue("file").get(); - const char* fns = fn && !fn->empty() ? fn->getBuffer() : 0; - int start_line = (int)h->getKeyValue("line").getAsBigInt(); - int end_line = (int)h->getKeyValue("endline").getAsBigInt(); - - QoreStringNode* src = h->getKeyValue("source").get(); - const char* srcs = src && !src->empty() ? src->getBuffer() : 0; - int offset = (int)h->getKeyValue("offset").getAsBigInt(); - - printe(" %2d: ", pos); - - if (typecode == CT_RETHROW) { - printe("RETHROW at "); - if (fn) { - printe("%s:", fn->getBuffer()); - } - else - printe("line"); - printe("%d", start_line); - if (srcs) - printe(" (source %s:%d)", srcs, offset + start_line); - } - else { - QoreStringNode* fs = h->getKeyValue("function").get(); - printe("%s() (", fs->getBuffer()); - if (fns) { - if (start_line == end_line) { - if (!start_line) - printe("%s:", fns); - else { - printe("%s:%d", fns, start_line); - if (srcs) - printe(" (source %s:%d)", srcs, start_line + offset); - } - } - else { - printe("%s:%d-%d", fns, start_line, end_line); - if (srcs) - printe(" (source %s:%d-%d)", srcs, start_line + offset, end_line + offset); - } - } - else { - if (start_line == end_line) { - if (!start_line) - printe(""); - else - printe("line %d", start_line); - } - else - printe("line %d - %d", start_line, end_line); - } - printe(", %s code)", type); - } - printe("\n"); - } - } - } - e = e->next; - if (e) { - ++ecnt; - if (ecnt == Q_MAX_EXCEPTIONS) { - printe("*** maximum exception count reached (%d); suppressing further output\n", ecnt); - break; - } - printe("chained exception:\n"); - } - } -} - -// static member function -void ExceptionSink::defaultWarningHandler(QoreException *e) { - ExceptionSink xsink; - - while (e) { - printe("warning encountered "); - - if (!e->file.empty()) { - printe("at %s:", e->file.c_str()); - if (e->start_line == e->end_line) { - if (!e->start_line) { - printe(""); - if (!e->source.empty()) - printe(" (source %s)", e->source.c_str()); - } - else { - printe("%d", e->start_line); - if (!e->source.empty()) - printe(" (source %s:%d)", e->source.c_str(), e->start_line + e->offset); - } - } - else { - printe("%d-%d", e->start_line, e->end_line); - if (!e->source.empty()) - printe(" (source %s:%d-%d)", e->source.c_str(), e->start_line + e->offset, e->end_line + e->offset); - } - } - else if (e->start_line) { - if (e->start_line == e->end_line) { - if (!e->start_line) - printe("at "); - else - printe("on line %d", e->start_line); - } - else - printe("on line %d-%d", e->start_line, e->end_line); - } - printe("\n"); - - QoreStringNode* err = e->err.get(); - QoreStringNode* desc = e->desc.get(); - - printe("%s: %s\n", err->c_str(), desc->c_str()); - - e = e->next; - if (e) - printe("next warning:\n"); - } -} - const char* QoreException::getType(qore_call_t type) { switch (type) { case CT_USER: @@ -398,7 +159,8 @@ QoreHashNode* QoreException::getStackHash(const QoreCallStackElement& cse) { } // static function -QoreHashNode* QoreException::getStackHash(int type, const char* class_name, const char* code, const QoreProgramLocation& loc) { +QoreHashNode* QoreException::getStackHash(qore_call_t type, const char* class_name, const char* code, + const QoreProgramLocation& loc) { QoreHashNode* h = new QoreHashNode; qore_hash_private* ph = qore_hash_private::get(*h); @@ -423,6 +185,7 @@ QoreHashNode* QoreException::getStackHash(int type, const char* class_name, cons } DLLLOCAL ParseExceptionSink::~ParseExceptionSink() { - if (xsink) - qore_program_private::addParseException(getProgram(), xsink); + if (xsink) { + qore_program_private::addParseException(getProgram(), xsink); + } } diff --git a/lib/QoreProgram.cpp b/lib/QoreProgram.cpp index 68775133ef..6c3de3361f 100644 --- a/lib/QoreProgram.cpp +++ b/lib/QoreProgram.cpp @@ -652,7 +652,7 @@ void qore_program_private::waitForTerminationAndClear(ExceptionSink* xsink) { del(xsink); // clear program location - update_runtime_location(&loc_builtin); + //update_runtime_location(&loc_builtin); } } diff --git a/lib/thread.cpp b/lib/thread.cpp index a3e041c15e..8af4c86302 100644 --- a/lib/thread.cpp +++ b/lib/thread.cpp @@ -267,6 +267,9 @@ class ThreadData { QoreClass* parseClass = nullptr; // current class being parsed QoreException* catchException = nullptr; + // current runtime stack location + const QoreStackLocation* current_stack_location = nullptr; + std::list on_block_exit_list; ThreadResourceList* trlist = new ThreadResourceList; @@ -686,7 +689,7 @@ class BGThreadParams { // create thread-local data in the program object qore_program_private::startThread(*pgm, xsink); // set program counter for new thread - update_runtime_location(loc); + //update_runtime_location(loc); started = true; //printd(5, "BGThreadParams::startThread() this: %p pgm: %p\n", this, pgm); pgm->depRef(); @@ -1198,10 +1201,39 @@ Context* get_context_stack() { } void update_context_stack(Context* cstack) { - ThreadData* td = thread_data.get(); - td->context_stack = cstack; + ThreadData* td = thread_data.get(); + td->context_stack = cstack; +} + +const QoreStackLocation* get_runtime_stack_location() { + return thread_data.get()->current_stack_location; +} + +// called when pushing a new location on the stack +const QoreStackLocation* update_get_runtime_stack_location(QoreStackLocation* stack_loc) { + ThreadData* td = thread_data.get(); + const QoreStackLocation* rv = td->current_stack_location; + td->current_stack_location = stack_loc; + stack_loc->setNext(rv); + return rv; } +// called when restoring the previous location +void update_runtime_stack_location(const QoreStackLocation* stack_loc) { + thread_data.get()->current_stack_location = stack_loc; +} + +const AbstractStatement* get_runtime_statement() { + const QoreStackLocation* stack_loc = thread_data.get()->current_stack_location; + return stack_loc ? stack_loc->getStatement() : nullptr; +} + +const QoreProgramLocation* get_runtime_location() { + const QoreStackLocation* stack_loc = get_runtime_stack_location(); + return stack_loc ? &stack_loc->getLocation() : &loc_builtin; +} + +/* const QoreProgramLocation* get_runtime_location() { return thread_data.get()->runtime_loc; } @@ -1215,7 +1247,9 @@ const QoreProgramLocation* update_get_runtime_location(const QoreProgramLocation void update_runtime_location(const QoreProgramLocation* loc) { thread_data.get()->runtime_loc = loc; } +*/ +/* const AbstractStatement* get_runtime_statement() { return thread_data.get()->runtime_statement; } @@ -1225,6 +1259,7 @@ const AbstractStatement* update_get_runtime_statement(const AbstractStatement* s thread_data.get()->runtime_statement = s; return rv; } +*/ void set_parse_file_info(QoreProgramLocation& loc) { ThreadData* td = thread_data.get(); @@ -2214,6 +2249,7 @@ namespace { QoreValue rv; { CodeContextHelper cch(&xsink, CT_NEWTHREAD, "background operator", btp->getContextObject(), btp->class_ctx); + QoreInternalCallStackLocationHelper stack_loc(*btp->loc, "", CT_NEWTHREAD); // dereference call object if present btp->derefCallObj(); @@ -2574,63 +2610,63 @@ extern QoreRWLock thread_stack_lock; #endif QoreHashNode* getAllCallStacks() { - return thread_list.getAllCallStacks(); + return thread_list.getAllCallStacks(); } QoreHashNode* QoreThreadList::getAllCallStacks() { - QoreHashNode* h = new QoreHashNode(qore_get_complex_list_type(hashdeclCallStackInfo->getTypeInfo())); - QoreString str; + QoreHashNode* h = new QoreHashNode(qore_get_complex_list_type(hashdeclCallStackInfo->getTypeInfo())); + QoreString str; - // grab the call stack write lock - QoreAutoRWWriteLocker wl(thread_stack_lock); + // grab the call stack write lock + QoreAutoRWWriteLocker wl(thread_stack_lock); - QoreThreadListIterator i; - if (exiting) - return h; + QoreThreadListIterator i; + if (exiting) + return h; - auto ph = qore_hash_private::get(*h); + auto ph = qore_hash_private::get(*h); - while (i.next()) { - // get call stack - if (entry[*i].callStack) { - QoreListNode* l = entry[*i].callStack->getCallStack(); - if (!l->empty()) { - // make hash entry - str.clear(); - str.sprintf("%d", *i); - ph->setKeyValueIntern(str.getBuffer(), l); - } - else - l->deref(nullptr); - } - } + while (i.next()) { + // get call stack + if (entry[*i].callStack) { + QoreListNode* l = entry[*i].callStack->getCallStack(); + if (!l->empty()) { + // make hash entry + str.clear(); + str.sprintf("%d", *i); + ph->setKeyValueIntern(str.getBuffer(), l); + } + else + l->deref(nullptr); + } + } - return h; + return h; } #endif void ThreadEntry::cleanup() { - //printf("ThreadEntry::cleanup() TID %d\n", tidnode ? tidnode->tid : 0); - assert(status != QTS_AVAIL); - // delete tidnode from tid_list - delete tidnode; + //printf("ThreadEntry::cleanup() TID %d\n", tidnode ? tidnode->tid : 0); + assert(status != QTS_AVAIL); + // delete tidnode from tid_list + delete tidnode; #ifdef QORE_RUNTIME_THREAD_STACK_TRACE - // delete call stack - delete callStack; + // delete call stack + delete callStack; #ifdef DEBUG - callStack = 0; + callStack = 0; #endif #endif #ifdef DEBUG - assert(!thread_data); + assert(!thread_data); #endif - if (status != QTS_NA && status != QTS_RESERVED) { - if (!joined) - pthread_detach(ptid); - } - status = QTS_AVAIL; + if (status != QTS_NA && status != QTS_RESERVED) { + if (!joined) + pthread_detach(ptid); + } + status = QTS_AVAIL; } void QoreThreadList::deleteData(int tid) { From 4cb631c223910288664001771441a38d50421daf Mon Sep 17 00:00:00 2001 From: David Nichols Date: Tue, 11 Dec 2018 18:01:59 +0100 Subject: [PATCH 02/17] refs #3169 progress on unified thread and exception stack trace support --- examples/test/qore/misc/has_effect.qtest | 2 +- include/qore/ExceptionSink.h | 13 +- include/qore/intern/AbstractStatement.h | 20 - include/qore/intern/Function.h | 21 +- include/qore/intern/QoreException.h | 33 +- include/qore/intern/QoreThreadList.h | 66 ++- include/qore/intern/qore_thread_intern.h | 70 +-- lib/AbstractStatement.cpp | 14 +- lib/Function.cpp | 8 +- lib/QoreClass.cpp | 6 +- lib/QoreException.cpp | 106 ++-- lib/thread.cpp | 659 +++++++++++++---------- 12 files changed, 547 insertions(+), 471 deletions(-) diff --git a/examples/test/qore/misc/has_effect.qtest b/examples/test/qore/misc/has_effect.qtest index 7f345cf625..569a6b27b5 100755 --- a/examples/test/qore/misc/has_effect.qtest +++ b/examples/test/qore/misc/has_effect.qtest @@ -33,7 +33,7 @@ public class HasEffectTest inherits QUnit::Test { assertEq(NOTHING, parse_result); if (testBackground) { Program p3(PO_NEW_STYLE|PO_ALLOW_STATEMENT_NO_EFFECT); - *hash parse_result = p3.parse("background " + statements, ""); + parse_result = p3.parse("background " + statements, ""); assertEq(NOTHING, parse_result); } } diff --git a/include/qore/ExceptionSink.h b/include/qore/ExceptionSink.h index 0f3ca2806b..785a86c7ef 100644 --- a/include/qore/ExceptionSink.h +++ b/include/qore/ExceptionSink.h @@ -353,11 +353,6 @@ class QoreStackLocation { stack_next = next; } - //! returns a pointer to the current Qore statement; internal use only - DLLLOCAL virtual const AbstractStatement* getStatement() const { - return nullptr; - } - //! returns the next location in the stack or nullptr if there is none DLLLOCAL virtual const QoreStackLocation* getNext() const { return stack_next; @@ -373,6 +368,14 @@ class QoreStackLocation { return stack_next ? stack_next->getCallType() : qore_call_t::CT_BUILTIN; } + //! returns the statement for internal Qore code + DLLLOCAL virtual const AbstractStatement* getStatement() const { + return nullptr; + } + + //! returns the QoreProgram container + DLLLOCAL virtual QoreProgram* getProgram() const = 0; + //! returns the source location of the element DLLLOCAL virtual const QoreProgramLocation& getLocation() const = 0; diff --git a/include/qore/intern/AbstractStatement.h b/include/qore/intern/AbstractStatement.h index 07bfdaddb9..ac0c3e726a 100644 --- a/include/qore/intern/AbstractStatement.h +++ b/include/qore/intern/AbstractStatement.h @@ -116,26 +116,6 @@ class AbstractStatement { DLLLOCAL virtual void parseCommit(QoreProgram* pgm); }; -class QoreInternalStatementLocationHelper : public QoreStackLocation, public QoreProgramStackLocationHelper { -public: - DLLLOCAL QoreInternalStatementLocationHelper(const AbstractStatement* stmt) : - QoreProgramStackLocationHelper(this), stmt(stmt) { - } - - //! returns a pointer to the current Qore statement; internal use only - DLLLOCAL virtual const AbstractStatement* getStatement() const { - return stmt; - } - - //! returns the source location of the element - DLLLOCAL virtual const QoreProgramLocation& getLocation() const { - return *stmt->loc; - } - -protected: - const AbstractStatement* stmt; -}; - DLLLOCAL void push_cvar(const char* name); DLLLOCAL void pop_cvar(); DLLLOCAL LocalVar* pop_local_var(bool set_unassigned = false); diff --git a/include/qore/intern/Function.h b/include/qore/intern/Function.h index 4805cd7fd1..e4b4bed646 100644 --- a/include/qore/intern/Function.h +++ b/include/qore/intern/Function.h @@ -261,11 +261,17 @@ class AbstractQoreFunctionVariant; class CodeEvaluationHelper : public QoreStackLocation { public: // saves current program location in case there's an exception - DLLLOCAL CodeEvaluationHelper(ExceptionSink* n_xsink, const QoreFunction* func, const AbstractQoreFunctionVariant*& variant, const char* n_name, const QoreListNode* args = nullptr, QoreObject* self = nullptr, const qore_class_private* n_qc = nullptr, qore_call_t n_ct = CT_UNUSED, bool is_copy = false, const qore_class_private* cctx = nullptr); + DLLLOCAL CodeEvaluationHelper(ExceptionSink* n_xsink, const QoreFunction* func, + const AbstractQoreFunctionVariant*& variant, const char* n_name, const QoreListNode* args = nullptr, + QoreObject* self = nullptr, const qore_class_private* n_qc = nullptr, qore_call_t n_ct = CT_UNUSED, + bool is_copy = false, const qore_class_private* cctx = nullptr); // saves current program location in case there's an exception // performs destructive evaluation of "args" - DLLLOCAL CodeEvaluationHelper(ExceptionSink* n_xsink, const QoreFunction* func, const AbstractQoreFunctionVariant*& variant, const char* n_name, QoreListNode* args, QoreObject* self = nullptr, const qore_class_private* n_qc = nullptr, qore_call_t n_ct = CT_UNUSED, bool is_copy = false, const qore_class_private* cctx = nullptr); + DLLLOCAL CodeEvaluationHelper(ExceptionSink* n_xsink, const QoreFunction* func, + const AbstractQoreFunctionVariant*& variant, const char* n_name, QoreListNode* args, + QoreObject* self = nullptr, const qore_class_private* n_qc = nullptr, qore_call_t n_ct = CT_UNUSED, + bool is_copy = false, const qore_class_private* cctx = nullptr); DLLLOCAL ~CodeEvaluationHelper(); @@ -278,7 +284,8 @@ class CodeEvaluationHelper : public QoreStackLocation { ct = n_ct; } - DLLLOCAL int processDefaultArgs(const QoreFunction* func, const AbstractQoreFunctionVariant* variant, bool check_args, bool is_copy = false); + DLLLOCAL int processDefaultArgs(const QoreFunction* func, const AbstractQoreFunctionVariant* variant, + bool check_args, bool is_copy = false); DLLLOCAL void setArgs(QoreListNode* n_args) { assert(!*tmp); @@ -324,6 +331,11 @@ class CodeEvaluationHelper : public QoreStackLocation { return ct; } + //! returns the QoreProgram container + DLLLOCAL virtual QoreProgram* getProgram() const { + return pgm; + } + protected: qore_call_t ct; const char* name; @@ -333,7 +345,8 @@ class CodeEvaluationHelper : public QoreStackLocation { const QoreProgramLocation* loc; QoreListNodeEvalOptionalRefHolder tmp; const QoreTypeInfo* returnTypeInfo; // saved return type info - QoreProgram* pgm = getProgram(); // program used when evaluated (to find stacks for references) + QoreProgram* pgm = nullptr; // program used when evaluated (to find stacks for references) + const AbstractStatement* stmt = nullptr; // the current statement for the call stack entry q_rt_flags_t rtflags = 0; // runtime flags QoreString callName; const QoreStackLocation* stack_loc = nullptr; diff --git a/include/qore/intern/QoreException.h b/include/qore/intern/QoreException.h index a8ab51281e..dfd620cae6 100644 --- a/include/qore/intern/QoreException.h +++ b/include/qore/intern/QoreException.h @@ -122,24 +122,7 @@ class QoreException : public QoreExceptionBase, public QoreExceptionLocation { DLLLOCAL void del(ExceptionSink *xsink); - DLLLOCAL QoreException* rethrow() { - QoreException *e = new QoreException(*this); - - // insert current position as a rethrow entry in the new callstack - QoreListNode* l = e->callStack; - const char *fn = nullptr; - QoreHashNode* n = l->retrieveEntry(0).get(); - // get function name - fn = !n ? "" : n->getKeyValue("function").get()->c_str(); - - QoreHashNode* h = getStackHash(CT_RETHROW, 0, fn, *get_runtime_location()); - l->insert(h, nullptr); - - return e; - } - - DLLLOCAL static QoreHashNode* getStackHash(qore_call_t type, const char *class_name, const char *code, - const QoreProgramLocation& loc); + DLLLOCAL QoreException* rethrow(); protected: DLLLOCAL ~QoreException() { @@ -219,19 +202,7 @@ struct qore_es_private { // creates a stack trace node and adds it to all exceptions in this sink DLLLOCAL void addStackInfo(qore_call_t type, const char *class_name, const char *code, - const QoreProgramLocation& loc) { - assert(head); - QoreHashNode* n = QoreException::getStackHash(type, class_name, code, loc); - - assert(head); - QoreException* w = head; - while (w) { - w->addStackInfo(n); - w = w->next; - if (w) - n->ref(); - } - } + const QoreProgramLocation& loc); DLLLOCAL void addStackInfo(const QoreCallStackElement& cse) { assert(head); diff --git a/include/qore/intern/QoreThreadList.h b/include/qore/intern/QoreThreadList.h index 695d08efc1..261ebd0c76 100644 --- a/include/qore/intern/QoreThreadList.h +++ b/include/qore/intern/QoreThreadList.h @@ -33,6 +33,8 @@ #define _QORE_QORETHREADLIST_H +#include + // FIXME: move to config.h or something like that // not more than this number of threads can be running at the same time #ifndef MAX_QORE_THREADS @@ -101,27 +103,34 @@ class QoreThreadList { friend class QoreThreadListIterator; friend class tid_node; public: + // lock for reading / writing call stacks externally + /** if both lck and stack_lck are grabbed concurrently (for example, when all threads stacks are read externally), + then first lck must be acquired, and then stack_lck + */ + mutable QoreRWLock stack_lck; + DLLLOCAL QoreThreadList() { } DLLLOCAL int get(int status = QTS_NA) { int tid = -1; - AutoLocker al(l); + AutoLocker al(lck); if (current_tid == MAX_QORE_THREADS) { int i; // scan thread_list for free entry for (i = 1; i < MAX_QORE_THREADS; i++) { if (entry[i].available()) { - tid = i; - goto finish; + tid = i; + goto finish; } } - if (i == MAX_QORE_THREADS) + if (i == MAX_QORE_THREADS) { return -1; - } - else + } + } else { tid = current_tid++; + } finish: entry[tid].allocate(new tid_node(tid), status); @@ -132,32 +141,33 @@ friend class tid_node; } DLLLOCAL int getSignalThreadEntry() { - AutoLocker al(l); + AutoLocker al(lck); entry[0].allocate(0); return 0; } DLLLOCAL void release(int tid) { - AutoLocker al(l); + AutoLocker al(lck); releaseIntern(tid); } DLLLOCAL int releaseReserved(int tid) { - AutoLocker al(l); - if (entry[tid].status != QTS_RESERVED) + AutoLocker al(lck); + if (entry[tid].status != QTS_RESERVED) { return -1; + } releaseIntern(tid); return 0; } DLLLOCAL void activate(int tid, pthread_t ptid = pthread_self(), QoreProgram* p = 0, bool foreign = false) { - AutoLocker al(l); + AutoLocker al(lck); entry[tid].activate(tid, ptid, p, foreign); } DLLLOCAL void setStatus(int tid, int status) { - AutoLocker al(l); + AutoLocker al(lck); assert(entry[tid].status != status); entry[tid].status = status; } @@ -169,10 +179,11 @@ friend class tid_node; DLLLOCAL void deleteDataReleaseSignalThread(); DLLLOCAL int activateReserved(int tid) { - AutoLocker al(l); + AutoLocker al(lck); - if (entry[tid].status != QTS_RESERVED) + if (entry[tid].status != QTS_RESERVED) { return -1; + } entry[tid].activate(tid, pthread_self(), 0, true); return 0; @@ -184,22 +195,28 @@ friend class tid_node; DLLLOCAL unsigned cancelAllActiveThreads(); -#ifdef QORE_RUNTIME_THREAD_STACK_TRACE DLLLOCAL QoreHashNode* getAllCallStacks(); - DLLLOCAL void pushCall(CallNode* cn); - - DLLLOCAL void popCall(ExceptionSink* xsink); - DLLLOCAL QoreListNode* getCallStackList(); DLLLOCAL CallStack* getCallStack() { return entry[gettid()].callStack; } + +#ifdef QORE_RUNTIME_THREAD_STACK_TRACE + DLLLOCAL void pushCall(CallNode* cn); + + DLLLOCAL void popCall(ExceptionSink* xsink); #endif + DLLLOCAL static QoreHashNode* getCallStackHash(const QoreStackLocation& loc); + + DLLLOCAL static QoreHashNode* getCallStackHash(qore_call_t type, const char *code, + const QoreProgramLocation& loc); + protected: - mutable QoreThreadLock l; + // lock for reading the thread list + mutable QoreThreadLock lck; unsigned num_threads = 0; ThreadEntry entry[MAX_QORE_THREADS]; @@ -215,19 +232,22 @@ friend class tid_node; // NOTE: cannot safely call printd here, because normally the thread_data has been deleted //printf("DEBUG: ThreadList.releaseIntern() TID %d terminated\n", tid); entry[tid].cleanup(); - if (tid) + if (tid) { --num_threads; + } } + + DLLLOCAL QoreListNode* getCallStack(const QoreStackLocation* stack_location) const; }; DLLLOCAL extern QoreThreadList thread_list; class QoreThreadListIterator : public AutoLocker { protected: - tid_node* w; + tid_node* w = nullptr; public: - DLLLOCAL QoreThreadListIterator() : AutoLocker(thread_list.l), w(0) { + DLLLOCAL QoreThreadListIterator() : AutoLocker(thread_list.lck) { } DLLLOCAL bool next() { diff --git a/include/qore/intern/qore_thread_intern.h b/include/qore/intern/qore_thread_intern.h index 7f7917d991..bd004347c1 100644 --- a/include/qore/intern/qore_thread_intern.h +++ b/include/qore/intern/qore_thread_intern.h @@ -244,12 +244,14 @@ DLLLOCAL Context* get_context_stack(); DLLLOCAL void update_context_stack(Context* cstack); DLLLOCAL const QoreStackLocation* get_runtime_stack_location(); -DLLLOCAL const QoreStackLocation* update_get_runtime_stack_location(QoreStackLocation* stack_loc); +DLLLOCAL const QoreStackLocation* update_get_runtime_stack_location(QoreStackLocation* stack_loc, + const AbstractStatement*& current_stmt, QoreProgram*& current_pgm); DLLLOCAL void update_runtime_stack_location(const QoreStackLocation* stack_loc); DLLLOCAL const QoreProgramLocation* get_runtime_location(); -//DLLLOCAL const QoreProgramLocation* update_get_runtime_location(const QoreProgramLocation* loc); -//DLLLOCAL void update_runtime_location(const QoreProgramLocation* loc); +DLLLOCAL void update_get_runtime_statement_location(const AbstractStatement* stmt, + const QoreProgramLocation* loc, const AbstractStatement*& old_stmt, const QoreProgramLocation*& old_loc); +DLLLOCAL void update_runtime_statement_location(const AbstractStatement* stmt, const QoreProgramLocation* loc); DLLLOCAL void set_parse_file_info(QoreProgramLocation& loc); DLLLOCAL const char* get_parse_code(); @@ -424,8 +426,9 @@ class QoreParseClassHelper { class QoreProgramStackLocationHelper { public: - DLLLOCAL QoreProgramStackLocationHelper(QoreStackLocation* stack_loc) : - stack_loc(update_get_runtime_stack_location(stack_loc)) { + DLLLOCAL QoreProgramStackLocationHelper(QoreStackLocation* stack_loc, const AbstractStatement*& current_stmt, + QoreProgram*& current_pgm) : + stack_loc(update_get_runtime_stack_location(stack_loc, current_stmt, current_pgm)) { } DLLLOCAL ~QoreProgramStackLocationHelper() { @@ -436,39 +439,7 @@ class QoreProgramStackLocationHelper { const QoreStackLocation* stack_loc; }; -class QoreProgramStackOptionalLocationHelper { -public: - DLLLOCAL QoreProgramStackOptionalLocationHelper(QoreStackLocation* stack_loc) : - stack_loc(stack_loc ? update_get_runtime_stack_location(stack_loc) : nullptr), - restore(stack_loc ? true : false) { - } - - DLLLOCAL ~QoreProgramStackOptionalLocationHelper() { - if (restore) { - update_runtime_stack_location(stack_loc); - } - } - -protected: - const QoreStackLocation* stack_loc; - bool restore; -}; - -class QoreInternalStackLocationHelper : public QoreStackLocation, public QoreProgramStackLocationHelper { -public: - DLLLOCAL QoreInternalStackLocationHelper(const QoreProgramLocation& loc) : QoreProgramStackLocationHelper(this), - loc(loc) { - } - - //! returns the source location of the element - DLLLOCAL virtual const QoreProgramLocation& getLocation() const { - return loc; - } - -protected: - const QoreProgramLocation& loc; -}; - +/* class QoreInternalStackOptionalLocationHelper : public QoreStackLocation, public QoreProgramStackOptionalLocationHelper { public: DLLLOCAL QoreInternalStackOptionalLocationHelper(const QoreProgramLocation* loc) : QoreProgramStackOptionalLocationHelper(loc ? this : nullptr), @@ -484,11 +455,12 @@ class QoreInternalStackOptionalLocationHelper : public QoreStackLocation, public protected: const QoreProgramLocation* loc; }; +*/ class QoreInternalCallStackLocationHelper : public QoreStackLocation, public QoreProgramStackLocationHelper { public: DLLLOCAL QoreInternalCallStackLocationHelper(const QoreProgramLocation& loc, const char* call, qore_call_t call_type) : - QoreProgramStackLocationHelper(this), loc(loc), call(call), call_type(call_type) { + QoreProgramStackLocationHelper(this, stmt, pgm), loc(loc), call(call), call_type(call_type) { } //! returns the source location of the element @@ -505,21 +477,26 @@ class QoreInternalCallStackLocationHelper : public QoreStackLocation, public Qor return call_type; } + DLLLOCAL virtual QoreProgram* getProgram() const { + return pgm; + } + protected: const QoreProgramLocation& loc; const char* call; qore_call_t call_type; + const AbstractStatement* stmt; + QoreProgram* pgm; }; -/* class QoreProgramLocationHelper { public: - DLLLOCAL QoreProgramLocationHelper(const QoreProgramLocation* n_loc, const AbstractStatement* n_stat = nullptr) : loc(update_get_runtime_location(n_loc)), statement(update_get_runtime_statement(n_stat)) { + DLLLOCAL QoreProgramLocationHelper(const QoreProgramLocation* n_loc, const AbstractStatement* n_stat = nullptr) { + update_get_runtime_statement_location(n_stat, n_loc, statement, loc); } DLLLOCAL ~QoreProgramLocationHelper() { - update_runtime_location(loc); - update_get_runtime_statement(statement); + update_runtime_statement_location(statement, loc); } protected: @@ -531,15 +508,13 @@ class QoreProgramOptionalLocationHelper { public: DLLLOCAL QoreProgramOptionalLocationHelper(const QoreProgramLocation* n_loc, const AbstractStatement* n_stat = nullptr) : restore((bool)n_loc) { if (n_loc) { - loc = update_get_runtime_location(n_loc); - statement = update_get_runtime_statement(n_stat); + update_get_runtime_statement_location(n_stat, n_loc, statement, loc); } } DLLLOCAL ~QoreProgramOptionalLocationHelper() { if (restore) { - update_runtime_location(loc); - update_get_runtime_statement(statement); + update_runtime_statement_location(statement, loc); } } @@ -548,7 +523,6 @@ class QoreProgramOptionalLocationHelper { const AbstractStatement* statement; bool restore; }; -*/ // allows for the parse lock for the current program to be acquired by binary modules class CurrentProgramRuntimeParseContextHelper { diff --git a/lib/AbstractStatement.cpp b/lib/AbstractStatement.cpp index f78eab38b6..8ee4cd2cfb 100644 --- a/lib/AbstractStatement.cpp +++ b/lib/AbstractStatement.cpp @@ -84,12 +84,13 @@ void AbstractStatement::finalizeBlock(int sline, int eline) { int AbstractStatement::exec(QoreValue& return_value, ExceptionSink *xsink) { printd(1, "AbstractStatement::exec() this: %p file: %s line: %d\n", this, loc->getFile(), loc->start_line); - QoreInternalStatementLocationHelper stack_lock(this); + QoreProgramLocationHelper stack_loc(loc, this); //QoreProgramLocationHelper l(loc, this); #ifdef QORE_MANAGE_STACK - if (check_stack(xsink)) + if (check_stack(xsink)) { return 0; + } #endif pthread_testcancel(); @@ -98,11 +99,12 @@ int AbstractStatement::exec(QoreValue& return_value, ExceptionSink *xsink) { } int AbstractStatement::parseInit(LocalVar *oflag, int pflag) { - printd(2, "AbstractStatement::parseInit() this: %p type: %s file: %s line: %d\n", this, typeid(this).name(), loc->getFile(), loc->start_line); - // set parse options and warning mask for this statement - ParseWarnHelper pwh(pwo); + printd(2, "AbstractStatement::parseInit() this: %p type: %s file: %s line: %d\n", this, typeid(this).name(), + loc->getFile(), loc->start_line); + // set parse options and warning mask for this statement + ParseWarnHelper pwh(pwo); - return parseInitImpl(oflag, pflag); + return parseInitImpl(oflag, pflag); } QoreBreakpoint* AbstractStatement::getBreakpoint() const { diff --git a/lib/Function.cpp b/lib/Function.cpp index f5dc859887..5806396035 100644 --- a/lib/Function.cpp +++ b/lib/Function.cpp @@ -196,6 +196,9 @@ void CodeEvaluationHelper::setCallName(const QoreFunction* func) { void CodeEvaluationHelper::init(const QoreFunction* func, const AbstractQoreFunctionVariant*& variant, bool is_copy, const qore_class_private* cctx) { + //printd(5, "CodeEvaluationHelper::init() this: %p '%s()' file: %s line: %d\n", this, func->getName(), + // loc->getFile(), loc->start_line); + // issue #2145: set the call reference class context only after arguments are evaluated OptionalClassOnlySubstitutionHelper cosh(cctx); @@ -222,14 +225,15 @@ void CodeEvaluationHelper::init(const QoreFunction* func, const AbstractQoreFunc } } - if (processDefaultArgs(func, variant, true, is_copy)) + if (processDefaultArgs(func, variant, true, is_copy)) { return; + } setCallType(variant->getCallType()); setReturnTypeInfo(variant->getReturnTypeInfo()); // add call to call stack - stack_loc = update_get_runtime_stack_location(this); + stack_loc = update_get_runtime_stack_location(this, stmt, pgm); restore_stack = true; } diff --git a/lib/QoreClass.cpp b/lib/QoreClass.cpp index 0a5730d27e..c1e648a106 100644 --- a/lib/QoreClass.cpp +++ b/lib/QoreClass.cpp @@ -973,8 +973,7 @@ int qore_class_private::initMember(QoreObject& o, bool& need_scan, const char* m assert(v.isNothing()); if (!info.exp.isNothing()) { // set runtime location - QoreInternalStackLocationHelper l(*info.loc); - //QoreProgramLocationHelper l(info.loc); + QoreProgramLocationHelper l(info.loc); ValueEvalRefHolder val(info.exp, xsink); if (*xsink) { return -1; @@ -1020,8 +1019,7 @@ void qore_class_private::execBaseClassConstructor(QoreObject* self, BCEAList* bc const QoreProgramLocation* aloc = nullptr; QoreListNode* args = bceal->findArgs(cls->getID(), &already_executed, variant, aloc); if (!already_executed) { - QoreInternalStackOptionalLocationHelper plh(aloc); - //QoreProgramOptionalLocationHelper plh(aloc); + QoreProgramOptionalLocationHelper plh(aloc); constructor->priv->evalConstructor(variant, self, args, bceal, xsink); } } diff --git a/lib/QoreException.cpp b/lib/QoreException.cpp index 70a9db9435..d8717be416 100644 --- a/lib/QoreException.cpp +++ b/lib/QoreException.cpp @@ -31,6 +31,7 @@ #include #include "qore/intern/qore_program_private.h" #include "qore/intern/QoreHashNodeIntern.h" +#include "qore/intern/QoreThreadList.h" #include @@ -42,23 +43,14 @@ QoreExceptionBase::QoreExceptionBase(QoreValue n_err, QoreValue n_desc, QoreValu : type(n_type), err(n_err), desc(n_desc), arg(n_arg) { // populate call stack const QoreStackLocation* w = get_runtime_stack_location(); - qore_call_t last_call_type; - const char* last_call_name = nullptr; - const QoreProgramLocation* last_loc = nullptr; while (w) { + /* qore_call_t call_type = w->getCallType(); const char* call_name = w->getCallName(); const QoreProgramLocation& loc = w->getLocation(); - // only push if the location is not equal to the last one - if (!last_loc || call_type != last_call_type || *last_loc != loc || strcmp(call_name, last_call_name)) { - callStack->push( - QoreException::getStackHash(call_type, nullptr, call_name, loc), - nullptr - ); - } - last_call_type = call_type; - last_call_name = call_name; - last_loc = &loc; + callStack->push(QoreException::getStackHash(call_type, nullptr, call_name, loc), nullptr); + */ + callStack->push(QoreThreadList::getCallStackHash(*w), nullptr); w = w->getNext(); } } @@ -81,6 +73,45 @@ void QoreException::del(ExceptionSink* xsink) { delete this; } +class QoreExceptionHolder { +public: + DLLLOCAL QoreExceptionHolder(QoreException* e) : e(e) { + } + + DLLLOCAL ~QoreExceptionHolder() { + if (e) { + e->del(nullptr); + } + } + + DLLLOCAL QoreException* release() { + QoreException* rv = e; + e = nullptr; + return rv; + } + + DLLLOCAL QoreException* operator->() { + return e; + } + +private: + QoreException * e; +}; + +QoreException* QoreException::rethrow() { + QoreExceptionHolder e(new QoreException(*this)); + + // insert current position as a rethrow entry in the new callstack + QoreListNode* l = e->callStack; + const char *fn = nullptr; + QoreHashNode* n = l->retrieveEntry(0).get(); + // get function name + fn = !n ? "" : n->getKeyValue("function").get()->c_str(); + + l->insert(QoreThreadList::getCallStackHash(CT_RETHROW, fn, *get_runtime_location()), nullptr); + return e.release(); +} + QoreHashNode* QoreException::makeExceptionObject() { QORE_TRACE("makeExceptionObject()"); @@ -132,6 +163,8 @@ const char* QoreException::getType(qore_call_t type) { return "builtin"; case CT_RETHROW: return "rethrow"; + case CT_NEWTHREAD: + return "new-thread"; default: break; } @@ -158,34 +191,29 @@ QoreHashNode* QoreException::getStackHash(const QoreCallStackElement& cse) { return h; } -// static function -QoreHashNode* QoreException::getStackHash(qore_call_t type, const char* class_name, const char* code, - const QoreProgramLocation& loc) { - QoreHashNode* h = new QoreHashNode; - - qore_hash_private* ph = qore_hash_private::get(*h); - - QoreStringNode *str = new QoreStringNode; - if (class_name) - str->sprintf("%s::", class_name); - str->concat(code); - - //printd(5, "QoreException::getStackHash() %s at %s:%d-%d src: %s+%d\n", str->getBuffer(), loc.getFile() ? loc.getFile() : "n/a", loc.start_line, loc.end_line, loc.getSource() ? loc.getSource() : "n/a", loc.offset); - - ph->setKeyValueIntern("function", str); - ph->setKeyValueIntern("line", loc.start_line); - ph->setKeyValueIntern("endline", loc.end_line); - ph->setKeyValueIntern("file", loc.getFile() ? new QoreStringNode(loc.getFile()) : QoreValue()); - ph->setKeyValueIntern("source", loc.getSource() ? new QoreStringNode(loc.getSource()) : QoreValue()); - ph->setKeyValueIntern("offset", loc.offset); - ph->setKeyValueIntern("typecode", type); - ph->setKeyValueIntern("type", new QoreStringNode(getType((qore_call_t)type))); - - return h; -} - DLLLOCAL ParseExceptionSink::~ParseExceptionSink() { if (xsink) { qore_program_private::addParseException(getProgram(), xsink); } } + +// creates a stack trace node and adds it to all exceptions in this sink +void qore_es_private::addStackInfo(qore_call_t type, const char *class_name, const char *code, + const QoreProgramLocation& loc) { + assert(head); + QoreString str; + if (class_name) { + str.sprintf("%s::", class_name); + } + str.concat(code); + QoreHashNode* n = QoreThreadList::getCallStackHash(type, str.c_str(), loc); + + assert(head); + QoreException* w = head; + while (w) { + w->addStackInfo(n); + w = w->next; + if (w) + n->ref(); + } +} diff --git a/lib/thread.cpp b/lib/thread.cpp index 8af4c86302..f7df69c9ca 100644 --- a/lib/thread.cpp +++ b/lib/thread.cpp @@ -86,6 +86,7 @@ DLLLOCAL QoreRWLock lck_debug_program; #ifdef QORE_MANAGE_STACK QoreThreadLock stack_lck; +// 512 KB default thread stack size #define MAX_STACK_SIZE 512*1024 // default size and limit for qore threads; to be set in init_qore_threads() @@ -255,7 +256,11 @@ class ThreadData { Context* context_stack = nullptr; ProgramParseContext* plStack = nullptr; + // current runtime stack location + const QoreStackLocation* current_stack_location = nullptr; + // current dynamic runtime location const QoreProgramLocation* runtime_loc = &loc_builtin; + // current dynamic runtime statement const AbstractStatement* runtime_statement = nullptr; const char* parse_code = nullptr; // the current function, method, or closure being parsed const char* parse_file = nullptr; // the current file or label being parsed @@ -267,9 +272,6 @@ class ThreadData { QoreClass* parseClass = nullptr; // current class being parsed QoreException* catchException = nullptr; - // current runtime stack location - const QoreStackLocation* current_stack_location = nullptr; - std::list on_block_exit_list; ThreadResourceList* trlist = new ThreadResourceList; @@ -454,46 +456,73 @@ class ThreadData { static QoreThreadLocalStorage thread_data; void ThreadEntry::allocate(tid_node* tn, int stat) { - assert(status == QTS_AVAIL); - status = stat; - tidnode = tn; + assert(status == QTS_AVAIL); + status = stat; + tidnode = tn; #ifdef QORE_RUNTIME_THREAD_STACK_TRACE - assert(!callStack); - callStack = new CallStack; + assert(!callStack); + callStack = new CallStack; #endif - joined = false; - assert(!thread_data); + joined = false; + assert(!thread_data); } void ThreadEntry::activate(int tid, pthread_t n_ptid, QoreProgram* p, bool foreign) { - assert(status == QTS_NA || status == QTS_RESERVED); - ptid = n_ptid; + assert(status == QTS_NA || status == QTS_RESERVED); + ptid = n_ptid; +#ifdef QORE_RUNTIME_THREAD_STACK_TRACE + assert(callStack); +#endif + assert(!thread_data); + thread_data = new ThreadData(tid, p, foreign); + ::thread_data.set(thread_data); + status = QTS_ACTIVE; + // set lvstack if QoreProgram set + if (p) { + thread_data->tpd->saveProgram(true, 0); + } +} + +void ThreadEntry::cleanup() { + //printf("ThreadEntry::cleanup() TID %d\n", tidnode ? tidnode->tid : 0); + assert(status != QTS_AVAIL); + // delete tidnode from tid_list + delete tidnode; + #ifdef QORE_RUNTIME_THREAD_STACK_TRACE - assert(callStack); +// XXX DELETEME + // delete call stack + delete callStack; +#ifdef DEBUG + callStack = nullptr; +#endif +#endif +#ifdef DEBUG + assert(!thread_data); #endif - assert(!thread_data); - thread_data = new ThreadData(tid, p, foreign); - ::thread_data.set(thread_data); - status = QTS_ACTIVE; - // set lvstack if QoreProgram set - if (p) - thread_data->tpd->saveProgram(true, 0); + + if (status != QTS_NA && status != QTS_RESERVED) { + if (!joined) + pthread_detach(ptid); + } + status = QTS_AVAIL; } void ThreadProgramData::delProgram(QoreProgram* pgm) { - //printd(5, "ThreadProgramData::delProgram() this: %p pgm: %p\n", this, pgm); - { - AutoLocker al(pslock); - pgm_set_t::iterator i = pgm_set.find(pgm); - if (i == pgm_set.end()) - return; - pgm_set.erase(i); - } - //printd(5, "ThreadProgramData::delProgram() this: %p deref pgm: %p\n", this, pgm); - // this can never cause the program to go out of scope because it's always called - // when the reference count > 1, therefore *xsink = 0 is OK - pgm->depDeref(); - deref(); + //printd(5, "ThreadProgramData::delProgram() this: %p pgm: %p\n", this, pgm); + { + AutoLocker al(pslock); + pgm_set_t::iterator i = pgm_set.find(pgm); + if (i == pgm_set.end()) { + return; + } + pgm_set.erase(i); + } + //printd(5, "ThreadProgramData::delProgram() this: %p deref pgm: %p\n", this, pgm); + // this can never cause the program to go out of scope because it's always called + // when the reference count > 1, therefore *xsink = 0 is OK + pgm->depDeref(); + deref(); } bool ThreadProgramData::saveProgram(bool runtime, ExceptionSink* xsink) { @@ -580,26 +609,29 @@ class ThreadParams { // this constructor must only be called with the QoreThreadList lock held tid_node::tid_node(int ntid) { - tid = ntid; - next = 0; - prev = thread_list.tid_tail; - if (!thread_list.tid_head) - thread_list.tid_head = this; - else - thread_list.tid_tail->next = this; - thread_list.tid_tail = this; + tid = ntid; + next = nullptr; + prev = thread_list.tid_tail; + if (!thread_list.tid_head) { + thread_list.tid_head = this; + } else { + thread_list.tid_tail->next = this; + } + thread_list.tid_tail = this; } // this destructor must only be called with the QoreThreadList lock held tid_node::~tid_node() { - if (prev) - prev->next = next; - else - thread_list.tid_head = next; - if (next) - next->prev = prev; - else - thread_list.tid_tail = prev; + if (prev) { + prev->next = next; + } else { + thread_list.tid_head = next; + } + if (next) { + next->prev = prev; + } else { + thread_list.tid_tail = prev; + } } class BGThreadParams { @@ -1205,14 +1237,24 @@ void update_context_stack(Context* cstack) { td->context_stack = cstack; } +// only called from the current thread, no locking needed const QoreStackLocation* get_runtime_stack_location() { return thread_data.get()->current_stack_location; } // called when pushing a new location on the stack -const QoreStackLocation* update_get_runtime_stack_location(QoreStackLocation* stack_loc) { +const QoreStackLocation* update_get_runtime_stack_location(QoreStackLocation* stack_loc, + const AbstractStatement*& current_stmt, QoreProgram*& current_pgm) { ThreadData* td = thread_data.get(); + + current_pgm = td->current_pgm; + current_stmt = td->runtime_statement; + const QoreStackLocation* rv = td->current_stack_location; + + // get read access to the stack lock to write to the local thread stack location + // locking is necessary due to the fact that thread stacks can be read from other threads + QoreAutoRWReadLocker l(thread_list.stack_lck); td->current_stack_location = stack_loc; stack_loc->setNext(rv); return rv; @@ -1220,22 +1262,35 @@ const QoreStackLocation* update_get_runtime_stack_location(QoreStackLocation* st // called when restoring the previous location void update_runtime_stack_location(const QoreStackLocation* stack_loc) { - thread_data.get()->current_stack_location = stack_loc; + ThreadData* td = thread_data.get(); + + // get read access to the stack lock to write to the local thread stack location + // locking is necessary due to the fact that thread stacks can be read from other threads + QoreAutoRWReadLocker l(thread_list.stack_lck); + td->current_stack_location = stack_loc; } const AbstractStatement* get_runtime_statement() { - const QoreStackLocation* stack_loc = thread_data.get()->current_stack_location; - return stack_loc ? stack_loc->getStatement() : nullptr; + return thread_data.get()->runtime_statement; } const QoreProgramLocation* get_runtime_location() { - const QoreStackLocation* stack_loc = get_runtime_stack_location(); - return stack_loc ? &stack_loc->getLocation() : &loc_builtin; + return thread_data.get()->runtime_loc; } -/* -const QoreProgramLocation* get_runtime_location() { - return thread_data.get()->runtime_loc; +void update_get_runtime_statement_location(const AbstractStatement* stmt, + const QoreProgramLocation* loc, const AbstractStatement*& old_stmt, const QoreProgramLocation*& old_loc) { + ThreadData* td = thread_data.get(); + old_stmt = td->runtime_statement; + old_loc = td->runtime_loc; + td->runtime_statement = stmt; + td->runtime_loc = loc; +} + +void update_runtime_statement_location(const AbstractStatement* stmt, const QoreProgramLocation* loc) { + ThreadData* td = thread_data.get(); + td->runtime_statement = stmt; + td->runtime_loc = loc; } const QoreProgramLocation* update_get_runtime_location(const QoreProgramLocation* loc) { @@ -1247,19 +1302,6 @@ const QoreProgramLocation* update_get_runtime_location(const QoreProgramLocation void update_runtime_location(const QoreProgramLocation* loc) { thread_data.get()->runtime_loc = loc; } -*/ - -/* -const AbstractStatement* get_runtime_statement() { - return thread_data.get()->runtime_statement; -} - -const AbstractStatement* update_get_runtime_statement(const AbstractStatement* s) { - const AbstractStatement* rv = thread_data.get()->runtime_statement; - thread_data.get()->runtime_statement = s; - return rv; -} -*/ void set_parse_file_info(QoreProgramLocation& loc) { ThreadData* td = thread_data.get(); @@ -1609,14 +1651,6 @@ void pushCall(CallNode* cn) { void popCall(ExceptionSink* xsink) { thread_list.popCall(xsink); } - -QoreListNode* getCallStackList() { - return thread_list.getCallStackList(); -} - -CallStack* getCallStack() { - return thread_list.getCallStack(); -} #endif bool runtime_in_object_method(const char* name, const QoreObject* o) { @@ -1989,148 +2023,155 @@ QoreException* catchGetException() { } void qore_exit_process(int rc) { - // call exit() in a single-threaded process; flushes file buffers, etc - if (thread_list.getNumThreads() <= 1) - exit(rc); - // do not call exit here since it will try to execute cleanup, which will cause crashes - // in multithreaded programs; call quick_exit() instead (issue - _Exit(rc); + // call exit() in a single-threaded process; flushes file buffers, etc + if (thread_list.getNumThreads() <= 1) + exit(rc); + // do not call exit here since it will try to execute cleanup, which will cause crashes + // in multithreaded programs; call quick_exit() instead (issue + _Exit(rc); } // sets up the signal thread entry in the thread list int get_signal_thread_entry() { - return thread_list.getSignalThreadEntry(); + return thread_list.getSignalThreadEntry(); } // returns tid allocated for thread int get_thread_entry() { - return thread_list.get(); + return thread_list.get(); } void deregister_thread(int tid) { - thread_list.release(tid); + thread_list.release(tid); } void deregister_signal_thread() { - thread_list.release(0); + thread_list.release(0); } void delete_signal_thread() { - thread_list.deleteDataReleaseSignalThread(); + thread_list.deleteDataReleaseSignalThread(); } // should only be called from the new thread void register_thread(int tid, pthread_t ptid, QoreProgram* p, bool foreign) { - thread_list.activate(tid, ptid, p, foreign); + thread_list.activate(tid, ptid, p, foreign); } static void qore_thread_cleanup(void* n = 0) { #ifdef HAVE_MPFR_BUILDOPT_TLS_T - // only call mpfr_free_cache if MPFR uses TLS - if (mpfr_buildopt_tls_p()) - mpfr_free_cache(); + // only call mpfr_free_cache if MPFR uses TLS + if (mpfr_buildopt_tls_p()) { + mpfr_free_cache(); + } #endif #ifndef HAVE_OPENSSL_INIT_CRYPTO - // issue #2135: ERR_remove_state() is deprecated and a noop in openssl 1.0.0+ - ERR_remove_state(0); + // issue #2135: ERR_remove_state() is deprecated and a noop in openssl 1.0.0+ + ERR_remove_state(0); #endif } int q_register_foreign_thread() { - // see if the current thread has already been registered - ThreadData* td = thread_data.get(); - if (td) - return QFT_REGISTERED; + // see if the current thread has already been registered + ThreadData* td = thread_data.get(); + if (td) { + return QFT_REGISTERED; + } - // get a TID for the new thread - int tid = get_thread_entry(); + // get a TID for the new thread + int tid = get_thread_entry(); - if (tid == -1) - return QFT_ERROR; + if (tid == -1) { + return QFT_ERROR; + } - thread_list.activate(tid, pthread_self(), 0, true); + thread_list.activate(tid, pthread_self(), 0, true); - return QFT_OK; + return QFT_OK; } int q_deregister_foreign_thread() { - ThreadData* td = thread_data.get(); - if (!td || !td->foreign) - return -1; + ThreadData* td = thread_data.get(); + if (!td || !td->foreign) { + return -1; + } - // set thread entry as not available while it's being deleted - thread_list.setStatus(td->tid, QTS_NA); + // set thread entry as not available while it's being deleted + thread_list.setStatus(td->tid, QTS_NA); - ExceptionSink xsink; + ExceptionSink xsink; - // delete any thread data - td->del(&xsink); + // delete any thread data + td->del(&xsink); - // cleanup thread resources - purge_thread_resources(&xsink); + // cleanup thread resources + purge_thread_resources(&xsink); - xsink.handleExceptions(); + xsink.handleExceptions(); - // save tid for freeing the thread entry later - int tid = td->tid; + // save tid for freeing the thread entry later + int tid = td->tid; - // run any thread cleanup functions - tclist.exec(); + // run any thread cleanup functions + tclist.exec(); - // delete internal thread data structure and release TID entry - thread_list.deleteDataRelease(tid); + // delete internal thread data structure and release TID entry + thread_list.deleteDataRelease(tid); - qore_thread_cleanup(); + qore_thread_cleanup(); - return 0; + return 0; } int q_reserve_foreign_thread_id() { - return thread_list.get(QTS_RESERVED); + return thread_list.get(QTS_RESERVED); } int q_release_reserved_foreign_thread_id(int tid) { - if (tid < 0 || tid >= MAX_QORE_THREADS) - return -1; + if (tid < 0 || tid >= MAX_QORE_THREADS) { + return -1; + } - // release the thread entry - return thread_list.releaseReserved(tid); + // release the thread entry + return thread_list.releaseReserved(tid); } int q_register_reserved_foreign_thread(int tid) { - if (tid < 0 || tid >= MAX_QORE_THREADS) - return -1; + if (tid < 0 || tid >= MAX_QORE_THREADS) { + return -1; + } - return thread_list.activateReserved(tid); + return thread_list.activateReserved(tid); } int q_deregister_reserved_foreign_thread() { - ThreadData* td = thread_data.get(); - if (!td || !td->foreign) - return -1; + ThreadData* td = thread_data.get(); + if (!td || !td->foreign) { + return -1; + } - // set thread entry as RESERVED immediately - thread_list.setStatus(td->tid, QTS_RESERVED); + // set thread entry as RESERVED immediately + thread_list.setStatus(td->tid, QTS_RESERVED); - ExceptionSink xsink; + ExceptionSink xsink; - // delete any thread data - td->del(&xsink); + // delete any thread data + td->del(&xsink); - // cleanup thread resources - purge_thread_resources(&xsink); + // cleanup thread resources + purge_thread_resources(&xsink); - xsink.handleExceptions(); + xsink.handleExceptions(); - // run any thread cleanup functions - tclist.exec(); + // run any thread cleanup functions + tclist.exec(); - // delete internal thread data structure (do not release TID entry) - thread_list.deleteData(td->tid); + // delete internal thread data structure (do not release TID entry) + thread_list.deleteData(td->tid); - qore_thread_cleanup(); + qore_thread_cleanup(); - return 0; + return 0; } class qore_foreign_thread_priv {}; @@ -2247,14 +2288,16 @@ namespace { { QoreValue rv; + ThreadData* td = thread_data.get(); + { CodeContextHelper cch(&xsink, CT_NEWTHREAD, "background operator", btp->getContextObject(), btp->class_ctx); QoreInternalCallStackLocationHelper stack_loc(*btp->loc, "", CT_NEWTHREAD); + td->runtime_loc = btp->loc; // dereference call object if present btp->derefCallObj(); - ThreadLocalProgramData* tlpd = get_thread_local_program_data(); if (tlpd) { tlpd->dbgAttach(&xsink); @@ -2282,7 +2325,7 @@ namespace { btp->del(); // delete any thread data - thread_data.get()->del(&xsink); + td->del(&xsink); xsink.handleExceptions(); @@ -2459,70 +2502,56 @@ void q_get_thread_name(QoreString& str) { #endif #endif -#ifdef QORE_RUNTIME_THREAD_STACK_TRACE -#include - -#ifdef _Q_WINDOWS -extern QoreRWLock* thread_stack_lock; -#else -extern QoreRWLock thread_stack_lock; -#endif -#endif - static int initial_thread; void init_qore_threads() { - QORE_TRACE("qore_init_threads()"); - -#if defined(QORE_RUNTIME_THREAD_STACK_TRACE) && defined(_Q_WINDOWS) - thread_stack_lock = new QoreRWLock; -#endif + QORE_TRACE("qore_init_threads()"); #ifdef QORE_MANAGE_STACK - // get default stack size + // get default stack size #ifdef SOLARIS #if TARGET_BITS == 32 - // pthread_attr_getstacksize() on default attributes returns 0 on Solaris - qore_thread_stack_size = MAX_STACK_SIZE; + // pthread_attr_getstacksize() on default attributes returns 0 on Solaris + qore_thread_stack_size = MAX_STACK_SIZE; #else - qore_thread_stack_size = MAX_STACK_SIZE; + qore_thread_stack_size = MAX_STACK_SIZE; #endif // #if TARGET_BITS == 32 #else #ifdef _Q_WINDOWS - // windows stacks are extended automatically; here we set a limit of 1 MB per thread - qore_thread_stack_size = MAX_STACK_SIZE; + // windows stacks are extended automatically; here we set a limit of 1 MB per thread + qore_thread_stack_size = MAX_STACK_SIZE; #else // !_Q_WINDOWS && !SOLARIS - qore_thread_stack_size = ta_default.getstacksize(); - assert(qore_thread_stack_size); - //printd(5, "getstacksize() returned: %ld\n", qore_thread_stack_size); - if (qore_thread_stack_size > MAX_STACK_SIZE) { - //printd(5, "setting stack size from %ld to %ld\n", qore_thread_stack_size, (size_t)MAX_STACK_SIZE); - ta_default.setstacksize(MAX_STACK_SIZE); - qore_thread_stack_size = MAX_STACK_SIZE; - } + qore_thread_stack_size = ta_default.getstacksize(); + assert(qore_thread_stack_size); + //printd(5, "getstacksize() returned: %ld\n", qore_thread_stack_size); + if (qore_thread_stack_size > MAX_STACK_SIZE) { + //printd(5, "setting stack size from %ld to %ld\n", qore_thread_stack_size, (size_t)MAX_STACK_SIZE); + ta_default.setstacksize(MAX_STACK_SIZE); + qore_thread_stack_size = MAX_STACK_SIZE; + } #endif // #ifdef _Q_WINDOWS #endif // #ifdef SOLARIS #ifdef IA64_64 - // the top half of the stack is for the normal stack, the bottom half is for the register stack - qore_thread_stack_size /= 2; + // the top half of the stack is for the normal stack, the bottom half is for the register stack + qore_thread_stack_size /= 2; #endif // #ifdef IA64_64 - qore_thread_stack_limit = qore_thread_stack_size - QORE_STACK_GUARD; - //printd(8, "default stack size %ld, limit %ld\n", qore_thread_stack_size, qore_thread_stack_limit); + qore_thread_stack_limit = qore_thread_stack_size - QORE_STACK_GUARD; + //printd(8, "default stack size %ld, limit %ld\n", qore_thread_stack_size, qore_thread_stack_limit); #endif // #ifdef QORE_MANAGE_STACK - // setup parent thread data - thread_list.activate(initial_thread = get_thread_entry()); + // setup parent thread data + thread_list.activate(initial_thread = get_thread_entry()); - // initialize recursive mutex attribute - pthread_mutexattr_init(&ma_recursive); - pthread_mutexattr_settype(&ma_recursive, PTHREAD_MUTEX_RECURSIVE); + // initialize recursive mutex attribute + pthread_mutexattr_init(&ma_recursive); + pthread_mutexattr_settype(&ma_recursive, PTHREAD_MUTEX_RECURSIVE); - // set default thread name for initial thread - set_tid_thread_name(gettid()); + // set default thread name for initial thread + set_tid_thread_name(gettid()); - // mark threading as active - threads_initialized = true; + // mark threading as active + threads_initialized = true; } QoreNamespace* get_thread_ns(QoreNamespace &qorens) { @@ -2565,26 +2594,20 @@ void delete_thread_local_data() { } void delete_qore_threads() { - QORE_TRACE("delete_qore_threads()"); - - // mark threading as inactive - threads_initialized = false; + QORE_TRACE("delete_qore_threads()"); - pthread_mutexattr_destroy(&ma_recursive); + // mark threading as inactive + threads_initialized = false; - assert(initial_thread); - thread_list.deleteDataRelease(initial_thread); + pthread_mutexattr_destroy(&ma_recursive); -#ifdef QORE_RUNTIME_THREAD_STACK_TRACE -#ifdef _Q_WINDOWS - delete thread_stack_lock; -#endif -#endif + assert(initial_thread); + thread_list.deleteDataRelease(initial_thread); #ifdef HAVE_MPFR_BUILDOPT_TLS_T - // only call mpfr_free_cache if MPFR uses TLS - if (mpfr_buildopt_tls_p()) - mpfr_free_cache(); + // only call mpfr_free_cache if MPFR uses TLS + if (mpfr_buildopt_tls_p()) + mpfr_free_cache(); #endif } @@ -2600,127 +2623,190 @@ QoreListNode* get_thread_list() { return l; } -#ifdef QORE_RUNTIME_THREAD_STACK_TRACE -#include - -#ifdef _Q_WINDOWS -extern QoreRWLock* thread_stack_lock; -#else -extern QoreRWLock thread_stack_lock; -#endif - QoreHashNode* getAllCallStacks() { return thread_list.getAllCallStacks(); } +QoreListNode* getCallStackList() { + return thread_list.getCallStackList(); +} + +CallStack* getCallStack() { + return thread_list.getCallStack(); +} + QoreHashNode* QoreThreadList::getAllCallStacks() { - QoreHashNode* h = new QoreHashNode(qore_get_complex_list_type(hashdeclCallStackInfo->getTypeInfo())); - QoreString str; + ReferenceHolder h(new QoreHashNode( + qore_get_complex_list_type(hashdeclCallStackInfo->getTypeInfo())), nullptr); - // grab the call stack write lock - QoreAutoRWWriteLocker wl(thread_stack_lock); + // grab the thread lock to ensure that threads do not get released while running this call + AutoLocker al(lck); - QoreThreadListIterator i; - if (exiting) - return h; + // grab the call stack write lock to get exclusive access to all thread stacks + QoreAutoRWWriteLocker wl(stack_lck); - auto ph = qore_hash_private::get(*h); + if (exiting) { + return h.release(); + } + + auto ph = qore_hash_private::get(**h); + + QoreString str; + QoreThreadListIterator i; while (i.next()) { // get call stack - if (entry[*i].callStack) { - QoreListNode* l = entry[*i].callStack->getCallStack(); - if (!l->empty()) { + ThreadData* td = entry[*i].thread_data; + if (td && td->current_stack_location) { + ReferenceHolder stack(getCallStack(td->current_stack_location), nullptr); + if (!stack->empty()) { // make hash entry str.clear(); str.sprintf("%d", *i); - ph->setKeyValueIntern(str.getBuffer(), l); + ph->setKeyValueIntern(str.c_str(), stack.release()); } - else - l->deref(nullptr); } } - return h; + return h.release(); } -#endif -void ThreadEntry::cleanup() { - //printf("ThreadEntry::cleanup() TID %d\n", tidnode ? tidnode->tid : 0); - assert(status != QTS_AVAIL); - // delete tidnode from tid_list - delete tidnode; +QoreListNode* QoreThreadList::getCallStack(const QoreStackLocation* stack_location) const { + ReferenceHolder stack(new QoreListNode(hashdeclCallStackInfo->getTypeInfo()), nullptr); -#ifdef QORE_RUNTIME_THREAD_STACK_TRACE - // delete call stack - delete callStack; -#ifdef DEBUG - callStack = 0; -#endif -#endif -#ifdef DEBUG - assert(!thread_data); -#endif + const QoreStackLocation* w = stack_location; + while (w) { + stack->push(getCallStackHash(*w), nullptr); + /* + qore_call_t call_type = w->getCallType(); + const char* call_name = w->getCallName(); + const QoreProgramLocation& loc = w->getLocation(); - if (status != QTS_NA && status != QTS_RESERVED) { - if (!joined) - pthread_detach(ptid); + stack->push(QoreException::getStackHash(call_type, nullptr, call_name, loc), nullptr); + */ + w = w->getNext(); } - status = QTS_AVAIL; + + return stack.release(); +} + +// static +QoreHashNode* QoreThreadList::getCallStackHash(qore_call_t call_type, const char* code, const QoreProgramLocation& loc) { + ReferenceHolder h(new QoreHashNode(hashdeclCallStackInfo, nullptr), nullptr); + + qore_hash_private* ph = qore_hash_private::get(**h); + + ph->setKeyValueIntern("function", new QoreStringNode(code)); + ph->setKeyValueIntern("line", loc.start_line); + ph->setKeyValueIntern("endline", loc.end_line); + ph->setKeyValueIntern("file", new QoreStringNode(loc.getFile())); + // do not set "source" to NOTHING as it must be set to a value according to the hashdecl + { + const char* src = loc.getSource(); + if (src) { + ph->setKeyValueIntern("source", new QoreStringNode(src)); + } + } + ph->setKeyValueIntern("offset", loc.offset); + ph->setKeyValueIntern("typecode", call_type); + // CT_RETHROW is only aded manually + switch (call_type) { + case CT_USER: + ph->setKeyValueIntern("type", new QoreStringNode("user")); + break; + case CT_BUILTIN: + ph->setKeyValueIntern("type", new QoreStringNode("builtin")); + break; + case CT_NEWTHREAD: + ph->setKeyValueIntern("type", new QoreStringNode("new-thread")); + break; + case CT_RETHROW: + ph->setKeyValueIntern("type", new QoreStringNode("rethrow")); + break; + case CT_UNUSED: + default: + assert(false); + } + + return h.release(); +} + +// static +QoreHashNode* QoreThreadList::getCallStackHash(const QoreStackLocation& stack_loc) { + ReferenceHolder h(getCallStackHash(stack_loc.getCallType(), stack_loc.getCallName(), + stack_loc.getLocation()), nullptr); + + QoreProgram* pgm = stack_loc.getProgram(); + if (pgm) { + qore_hash_private* ph = qore_hash_private::get(**h); + + ph->setKeyValueIntern("programid", pgm->getProgramId()); + const AbstractStatement* statement = stack_loc.getStatement(); + if (statement) { + unsigned long sid = pgm->getStatementId(statement); + if (sid) { + ph->setKeyValueIntern("statementid", sid); + } + } + } + + return h.release(); } void QoreThreadList::deleteData(int tid) { - delete thread_data.get(); - thread_data.set(0); + delete thread_data.get(); + thread_data.set(0); -#ifdef DEBUG - AutoLocker al(l); - entry[tid].thread_data = 0; -#endif + AutoLocker al(lck); + entry[tid].thread_data = nullptr; } void QoreThreadList::deleteDataRelease(int tid) { - delete thread_data.get(); - thread_data.set(0); + delete thread_data.get(); + thread_data.set(0); - AutoLocker al(l); -#ifdef DEBUG - entry[tid].thread_data = 0; -#endif + AutoLocker al(lck); + entry[tid].thread_data = nullptr; - releaseIntern(tid); + releaseIntern(tid); } void QoreThreadList::deleteDataReleaseSignalThread() { - thread_data.get()->del(0); - deleteDataRelease(0); + thread_data.get()->del(0); + deleteDataRelease(0); } unsigned QoreThreadList::cancelAllActiveThreads() { - int tid = gettid(); + int tid = gettid(); - // thread cancel count - unsigned tcc = 0; + // thread cancel count + unsigned tcc = 0; - QoreThreadListIterator i; + QoreThreadListIterator i; - assert(!exiting); - exiting = true; + assert(!exiting); + exiting = true; - while (i.next()) { - if (*i != (unsigned)tid) { - //printf("QoreThreadList::cancelAllActiveThreads() canceling TID %d ptid: %p (this TID: %d)\n", *i, entry[*i].ptid, tid); - int trc = pthread_cancel(entry[*i].ptid); - if (!trc) - ++tcc; + while (i.next()) { + if (*i != (unsigned)tid) { + //printf("QoreThreadList::cancelAllActiveThreads() canceling TID %d ptid: %p (this TID: %d)\n", *i, entry[*i].ptid, tid); + int trc = pthread_cancel(entry[*i].ptid); + if (!trc) { + ++tcc; + } #ifdef DEBUG - else - printd(0, "pthread_cancel() returned %d (%s) on tid %d (%p)\n", trc, strerror(trc), tid, entry[*i].ptid); + else { + printd(0, "pthread_cancel() returned %d (%s) on tid %d (%p)\n", trc, strerror(trc), tid, entry[*i].ptid); + } #endif - } - } + } + } + + return tcc; +} - return tcc; +QoreListNode* QoreThreadList::getCallStackList() { + return entry[gettid()].callStack->getCallStack(); } #ifdef QORE_RUNTIME_THREAD_STACK_TRACE @@ -2732,7 +2818,4 @@ void QoreThreadList::popCall(ExceptionSink* xsink) { entry[gettid()].callStack->pop(xsink); } -QoreListNode* QoreThreadList::getCallStackList() { - return entry[gettid()].callStack->getCallStack(); -} #endif From 59a60ee578a3e26156fe105f7b5d0b4ac677e178 Mon Sep 17 00:00:00 2001 From: David Nichols Date: Tue, 11 Dec 2018 21:38:43 +0100 Subject: [PATCH 03/17] reffs #3169 removed lots of old code related to the old optional call stack implementation; the HAVE_RUNTIME_THREAD_STACK_TRACE constant is always True, removed associated build options and internal logic --- CMakeLists.txt | 9 -- configure.ac | 15 --- doxygen/lang/900_release_notes.dox.tmpl | 4 + include/qore/intern/QoreThreadList.h | 23 +--- include/qore/intern/qore_thread_intern.h | 72 +---------- lib/CallStack.cpp | 156 ----------------------- lib/Makefile.am | 3 - lib/QoreLib.cpp | 4 - lib/qc_option.qpp | 12 +- lib/ql_thread.qpp | 42 ++---- lib/single-compilation-unit.cpp | 3 - lib/thread.cpp | 49 +------ 12 files changed, 26 insertions(+), 366 deletions(-) delete mode 100644 lib/CallStack.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d979061a8f..5b9023b103 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,10 +6,6 @@ option(ICONV_TRANSLIT "Force //Translit to iconv encoding to do transliteration, if OFF it is tested for" OFF) -option(QORE_RUNTIME_THREAD_STACK_TRACE - "enable runtime thread stack trace (turning off breaks compatibility)" - ON) - set(VERSION_MAJOR 0) set(VERSION_MINOR 9) set(VERSION_SUB 0) @@ -37,10 +33,6 @@ else () add_definitions(-DNDEBUG) endif () -#if ((NOT ${QORE_BUILD_TYPE_LWR} MATCHES "debug") AND QORE_RUNTIME_THREAD_STACK_TRACE) -# message(FATAL_ERROR "QORE_RUNTIME_THREAD_STACK_TRACE only available on debug builds") -#endif() - # simulate QoreConfig set(QORE_QDX_EXECUTABLE "${CMAKE_SOURCE_DIR}/doxygen/qdx") # simulate QoreConfig @@ -491,7 +483,6 @@ set(LIBQORE_CPP_SRC lib/QoreSQLStatement.cpp lib/ExecArgList.cpp lib/CallReferenceNode.cpp - lib/CallStack.cpp lib/NamedScope.cpp lib/RWLock.cpp lib/QoreSSLBase.cpp diff --git a/configure.ac b/configure.ac index 7664211a98..7efb849af7 100644 --- a/configure.ac +++ b/configure.ac @@ -1308,20 +1308,6 @@ if test "$enable_optimization" = "auto"; then fi fi -AC_ARG_ENABLE([runtime-thread-stack-trace], - [AS_HELP_STRING([--enable-runtime-thread-stack-trace], - [enable runtime thread stack trace (performance penalty, default: off)])], - [case "${enable_runtime_thread_stack_trace}" in - yes|no) ;; - *) AC_MSG_ERROR(bad value ${enable_runtime_thread_stack_trace} for --enable-runtime-thread-stack-trace) ;; - esac], - [enable_runtime_thread_stack_trace=yes]) - -if test "${enable_runtime_thread_stack_trace}" = "yes" -o "${enable_debug}" = yes; then - AC_DEFINE(QORE_RUNTIME_THREAD_STACK_TRACE, 1, [to enable runtime thread stack tracing, needed for getAllThreadCallStacks()]) - enable_runtime_thread_stack_trace=yes -fi - # check for gcc visibility support AC_MSG_CHECKING([for gcc visibility support]) if test "$GXX" = "yes"; then @@ -1878,7 +1864,6 @@ AM_CONDITIONAL([COND_PROFILE], [test "$enable_profile" = yes]) AM_CONDITIONAL([COND_SINGLE_COMPILATION_UNIT], [test "$enable_single_compilation_unit" = yes]) AM_CONDITIONAL([COND_MACOSX], [test "$darwin" = yes]) AM_CONDITIONAL([COND_DOXYGEN], [test -n "$with_doxygen"]) -AM_CONDITIONAL([COND_STACK_TRACE], [test "$enable_runtime_thread_stack_trace" = yes]) AM_CONDITIONAL([COND_SOLARIS_CC_X86_64], [test "$solaris_cc_x86_64" = yes]) AM_CONDITIONAL([COND_SOLARIS_CC_I386], [test "$solaris_cc_i386" = yes]) AM_CONDITIONAL([COND_SOLARIS_CC_SPARC32], [test "$solaris_cc_sparc32" = yes]) diff --git a/doxygen/lang/900_release_notes.dox.tmpl b/doxygen/lang/900_release_notes.dox.tmpl index ad2a6a4372..9592382c64 100644 --- a/doxygen/lang/900_release_notes.dox.tmpl +++ b/doxygen/lang/900_release_notes.dox.tmpl @@ -183,6 +183,10 @@ - DebugHandler reimplemented to support multiple websocket handlers - Programs are not interrupted in bootstrap code - Command line utils display source line code when interrupted + - Runtime thread stack traces are available in all builds and the + @ref Qore::Option::HAVE_RUNTIME_THREAD_STACK_TRACE "HAVE_RUNTIME_THREAD_STACK_TRACE" constant is always + @ref True "True". Furthermore, the %Qore library has been extended to support stack tracing when embeddingg + or integrating code in other programming languages @subsection qore_09_bug_fixes Bug Fixes in Qore - fixed a bug with simple additional and subtraction with mixed @ref timeout_type "timeout" and diff --git a/include/qore/intern/QoreThreadList.h b/include/qore/intern/QoreThreadList.h index 261ebd0c76..4b6a81465a 100644 --- a/include/qore/intern/QoreThreadList.h +++ b/include/qore/intern/QoreThreadList.h @@ -42,10 +42,6 @@ #endif class ThreadData; -#ifdef QORE_RUNTIME_THREAD_STACK_TRACE -class CallStack; -class CallNode; -#endif #define QTS_AVAIL 0 #define QTS_NA 1 @@ -77,9 +73,6 @@ class ThreadEntry { public: pthread_t ptid; tid_node* tidnode; -#ifdef QORE_RUNTIME_THREAD_STACK_TRACE - CallStack* callStack; -#endif ThreadData* thread_data; unsigned char status; bool joined; // if set to true then pthread_detach should not be called on exit @@ -197,23 +190,13 @@ friend class tid_node; DLLLOCAL QoreHashNode* getAllCallStacks(); - DLLLOCAL QoreListNode* getCallStackList(); - - DLLLOCAL CallStack* getCallStack() { - return entry[gettid()].callStack; - } - -#ifdef QORE_RUNTIME_THREAD_STACK_TRACE - DLLLOCAL void pushCall(CallNode* cn); - - DLLLOCAL void popCall(ExceptionSink* xsink); -#endif - DLLLOCAL static QoreHashNode* getCallStackHash(const QoreStackLocation& loc); DLLLOCAL static QoreHashNode* getCallStackHash(qore_call_t type, const char *code, const QoreProgramLocation& loc); + DLLLOCAL QoreListNode* getCallStack(const QoreStackLocation* stack_location) const; + protected: // lock for reading the thread list mutable QoreThreadLock lck; @@ -236,8 +219,6 @@ friend class tid_node; --num_threads; } } - - DLLLOCAL QoreListNode* getCallStack(const QoreStackLocation* stack_location) const; }; DLLLOCAL extern QoreThreadList thread_list; diff --git a/include/qore/intern/qore_thread_intern.h b/include/qore/intern/qore_thread_intern.h index bd004347c1..08433c682f 100644 --- a/include/qore/intern/qore_thread_intern.h +++ b/include/qore/intern/qore_thread_intern.h @@ -358,20 +358,6 @@ DLLLOCAL const QoreTypeInfo* parse_get_return_type_info(); DLLLOCAL QoreProgram* get_set_program_call_context(QoreProgram* new_pgm); DLLLOCAL void set_program_call_context(QoreProgram* new_pgm); -#ifdef QORE_RUNTIME_THREAD_STACK_TRACE -DLLLOCAL void pushCall(CallNode* cn); -DLLLOCAL void popCall(ExceptionSink* xsink); -DLLLOCAL CallStack* getCallStack(); -DLLLOCAL QoreListNode* getCallStackList(); -#else -#ifdef __GNUC__ -#define pushCall(args...) -#else -#define pushCall(args, ...) -#endif -#define popCall(x) -#endif - class ProgramCallContextHelper { public: DLLLOCAL ProgramCallContextHelper(QoreProgram* new_pgm) @@ -859,75 +845,19 @@ class ImplicitElementHelper { } }; -#ifdef QORE_RUNTIME_THREAD_STACK_TRACE -class CallNode { -public: - const char* func; - const QoreProgramLocation* loc; - int type; - - QoreObject* obj; - const qore_class_private* cls; - const QoreProgram* pgm; - const AbstractStatement* statement; - CallNode* next, *prev; - - DLLLOCAL CallNode(const char* f, int t, QoreObject* o, const qore_class_private* c, const QoreProgram* p=nullptr, const AbstractStatement* s=nullptr); - DLLLOCAL QoreHashNode* getInfo() const; -}; - -class CallStack { -private: - CallNode* tail; - -public: - DLLLOCAL CallStack(); - DLLLOCAL ~CallStack(); - DLLLOCAL QoreListNode* getCallStack() const; - DLLLOCAL void push(CallNode* cn); - DLLLOCAL void pop(ExceptionSink* xsink); -}; - -class CallStackHelper : public CallNode { - ExceptionSink* xsink; - - // not implemented - DLLLOCAL CallStackHelper(const CallStackHelper&); - DLLLOCAL CallStackHelper& operator=(const CallStackHelper&); - DLLLOCAL void* operator new(size_t); - -public: - DLLLOCAL CallStackHelper(const char* f, int t, QoreObject* o, const qore_class_private* c, ExceptionSink* n_xsink) - : CallNode(f, t, o, c, getProgram(), get_runtime_statement()), xsink(n_xsink) { - pushCall(this); - } - DLLLOCAL ~CallStackHelper() { - popCall(xsink); - } -}; - -class CodeContextHelper : public CodeContextHelperBase, public CallStackHelper { -public: - DLLLOCAL CodeContextHelper(ExceptionSink* xs, int t, const char* c, QoreObject* obj = nullptr, const qore_class_private* cls = nullptr, bool ref_obj = true) : - CodeContextHelperBase(c, obj, cls, xs, ref_obj), - CallStackHelper(c, t, obj, cls, xs) { - } -}; - -#else class CodeContextHelper : public CodeContextHelperBase { public: DLLLOCAL CodeContextHelper(ExceptionSink* xs, int t, const char* c, QoreObject* obj = nullptr, const qore_class_private* cls = nullptr, bool ref_obj = true) : CodeContextHelperBase(c, obj, cls, xs, ref_obj) { } }; -#endif DLLLOCAL void init_qore_threads(); DLLLOCAL QoreNamespace* get_thread_ns(QoreNamespace& qorens); DLLLOCAL void delete_qore_threads(); DLLLOCAL QoreListNode* get_thread_list(); DLLLOCAL QoreHashNode* getAllCallStacks(); +DLLLOCAL QoreListNode* qore_get_thread_call_stack(); #if defined(QORE_HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACKSIZE) #define QORE_HAVE_GET_STACK_SIZE diff --git a/lib/CallStack.cpp b/lib/CallStack.cpp deleted file mode 100644 index 5b059a28c9..0000000000 --- a/lib/CallStack.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - CallStack.cpp - - Qore Programming Language - - Copyright (C) 2003 - 2018 Qore Technologies, s.r.o. - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. - - Note that the Qore library is released under a choice of three open-source - licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more - information. -*/ - -#include -#include "qore/intern/QoreHashNodeIntern.h" - -// a read-write lock is used in an inverted fashion to provide thread-safe -// access to call stacks: writing to each call stack is performed within -// the read lock, reading all threads' stacks is performed in the write lock -#include - -#ifdef _Q_WINDOWS -QoreRWLock* thread_stack_lock; -#else -QoreRWLock thread_stack_lock; -#endif - -CallNode::CallNode(const char *f, int t, QoreObject* o, const qore_class_private* c, const QoreProgram* p, const AbstractStatement* s) : func(f), loc(get_runtime_location()), type(t), obj(o), cls(c), pgm(p), statement(s) { -} - -QoreHashNode* CallNode::getInfo() const { - QoreHashNode* h = new QoreHashNode(hashdeclCallStackInfo, nullptr); - QoreStringNode* str = new QoreStringNode; - if (cls) { - str->concat(cls->name.c_str()); - str->concat("::"); - } - str->concat(func); - - qore_hash_private* ph = qore_hash_private::get(*h); - - ph->setKeyValueIntern("function", str); - ph->setKeyValueIntern("line", loc->start_line); - ph->setKeyValueIntern("endline", loc->end_line); - ph->setKeyValueIntern("file", new QoreStringNode(loc->getFile())); - // do not set "source" to NOTHING as it must be set to a value according to the hashdecl - { - const char* src = loc->getSource(); - if (src) { - ph->setKeyValueIntern("source", new QoreStringNode(src)); - } - } - ph->setKeyValueIntern("offset", loc->offset); - ph->setKeyValueIntern("typecode", type); - // CT_RETHROW is only aded manually - switch (type) { - case CT_USER: - ph->setKeyValueIntern("type", new QoreStringNode("user")); - break; - case CT_BUILTIN: - ph->setKeyValueIntern("type", new QoreStringNode("builtin")); - break; - case CT_NEWTHREAD: - ph->setKeyValueIntern("type", new QoreStringNode("new-thread")); - break; - } - if (pgm) { - ph->setKeyValueIntern("programid", pgm->getProgramId()); - if (statement) { - unsigned long sid = pgm->getStatementId(statement); - if (sid) { - ph->setKeyValueIntern("statementid", sid); - } - } - } - - return h; -} - -CallStack::CallStack() { - tail = 0; -} - -CallStack::~CallStack() { - while (tail) { - CallNode *c = tail->prev; - delete tail; - tail = c; - } -} - -void CallStack::push(CallNode *c) { - QORE_TRACE("CallStack::push()"); - c->next = 0; - c->prev = tail; - QoreAutoRWReadLocker l(thread_stack_lock); - if (tail) - tail->next = c; - tail = c; -} - -void CallStack::pop(ExceptionSink *xsink) { - QORE_TRACE("CallStack::pop()"); - QoreAutoRWReadLocker l(thread_stack_lock); - tail = tail->prev; - if (tail) - tail->next = 0; -} - -QoreListNode* CallStack::getCallStack() const { - QoreListNode* l = new QoreListNode(hashdeclCallStackInfo->getTypeInfo()); - CallNode* c = tail; - while (c) { - l->push(c->getInfo(), nullptr); - c = c->prev; - } - return l; -} - -/* -void CallStack::substituteObjectIfEqual(QoreObject *o) { - if (!tail->obj && tail->prev && tail->prev->obj == o) { - tail->obj = o; - o->ref(); - } -} - -QoreObject *CallStack::getStackObject() const { - if (!tail) - return 0; - return tail->obj; -} - -QoreObject *CallStack::substituteObject(QoreObject *o) { - QoreObject *ro = tail->obj); - tail->obj = o; - return ro; -} -*/ diff --git a/lib/Makefile.am b/lib/Makefile.am index e2b6a6c705..f5b8bea507 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -339,9 +339,6 @@ endif if COND_DEBUG libqore_la_SOURCES += ql_debug.cpp endif -if COND_STACK_TRACE -libqore_la_SOURCES += CallStack.cpp -endif if COND_NEED_GLOB libqore_la_SOURCES += glob.cpp endif diff --git a/lib/QoreLib.cpp b/lib/QoreLib.cpp index 603b1762ab..c0cb60d337 100644 --- a/lib/QoreLib.cpp +++ b/lib/QoreLib.cpp @@ -229,11 +229,7 @@ const qore_option_s qore_option_list_l[] = { { QORE_OPT_RUNTIME_STACK_TRACE, "HAVE_RUNTIME_THREAD_STACK_TRACE", QO_OPTION, -#ifdef QORE_RUNTIME_THREAD_STACK_TRACE true -#else - false -#endif }, { QORE_OPT_TERMIOS, "HAVE_TERMIOS", diff --git a/lib/qc_option.qpp b/lib/qc_option.qpp index e434e2bd7b..bcd5ad5d31 100644 --- a/lib/qc_option.qpp +++ b/lib/qc_option.qpp @@ -55,12 +55,6 @@ #define QORE_CONST_DEBUG 0 #endif -#ifdef QORE_RUNTIME_THREAD_STACK_TRACE -#define QORE_CONST_QORE_RUNTIME_THREAD_STACK_TRACE 1 -#else -#define QORE_CONST_QORE_RUNTIME_THREAD_STACK_TRACE 0 -#endif - #ifdef HAVE_ROUND #define QORE_CONST_HAVE_ROUND 1 #else @@ -248,8 +242,10 @@ const HAVE_SIGNAL_HANDLING = qore((bool)(QORE_CONST_HAVE_SIGNAL_HANDLING && !(qo //! Indicates if the %Qore library has been built with debugging enabled const HAVE_LIBRARY_DEBUGGING = bool(QORE_CONST_DEBUG); -//! Indicates if active thread stack tracing has been enabled as a debugging option and if the getAllThreadCallStacks() function is available -const HAVE_RUNTIME_THREAD_STACK_TRACE = bool(QORE_CONST_QORE_RUNTIME_THREAD_STACK_TRACE); +//! Indicates if active thread stack tracing has been enabled as a debugging option and if the get_alT_thread_call_stacks() function is available +/** @since %Qore 0.9 always @ref True +*/ +const HAVE_RUNTIME_THREAD_STACK_TRACE = bool(true); //! Indicates if the round() function is available; the availability of this function depends on the presence of the C-library's %round() function const HAVE_ROUND = bool(QORE_CONST_HAVE_ROUND); diff --git a/lib/ql_thread.qpp b/lib/ql_thread.qpp index 3e462f048c..c6f032c2c2 100644 --- a/lib/ql_thread.qpp +++ b/lib/ql_thread.qpp @@ -336,26 +336,16 @@ hash get_all_thread_data() [flags=RET_VALUE_ONLY;dom=THREAD_CONTROL,THREAD_INFO] //! Returns a hash of lists of @ref CallStackInfo hashes keyed by TID (thread ID) /** - @par Platform Availability: - @ref Qore::Option::HAVE_RUNTIME_THREAD_STACK_TRACE - @return a hash of lists of @ref CallStackInfo hashes keyed by TID (thread ID) @deprecated use get_all_thread_call_stacks(); camel-case function names were deprecated in %Qore 0.8.12 */ hash>> getAllThreadCallStacks() [dom=THREAD_CONTROL,THREAD_INFO;flags=DEPRECATED] { -#ifdef QORE_RUNTIME_THREAD_STACK_TRACE return getAllCallStacks(); -#else - return xsink->raiseException("MISSING-FEATURE-ERROR", "this version of the Qore library was built without support for runtime thread stack tracing; check Qore::Option::HAVE_RUNTIME_THREAD_STACK_TRACE before calling"); -#endif } //! Returns a hash of lists of @ref CallStackInfo hashes keyed by TID (thread ID) /** - @par Platform Availability: - @ref Qore::Option::HAVE_RUNTIME_THREAD_STACK_TRACE - @return a hash of lists of @ref CallStackInfo hashes keyed by TID (thread ID) @par Example: @@ -364,50 +354,42 @@ hash>> cs = get_all_thread_call_stacks(); foreach string tid in (cs.keyIterator()) { printf("TID %d\n", tid); int i; - foreach hash l in (cs{tid}) - if (l.type != "new-thread") + foreach hash l in (cs{tid}) { + if (l.type != "new-thread") { printf(" %d: %s() called at %s:%d (%s function)\n", ++i, l.function, l.file, l.line, l.type); - else + } else { printf(" %d: *** thread started by background operator ***\n", ++i); + } + } } @endcode @since %Qore 0.8.12 as a replacement for deprecated camel-case getAllThreadCallStacks() */ hash>> get_all_thread_call_stacks() [dom=THREAD_CONTROL,THREAD_INFO] { -#ifdef QORE_RUNTIME_THREAD_STACK_TRACE return getAllCallStacks(); -#else - return xsink->raiseException("MISSING-FEATURE-ERROR", "this version of the Qore library was built without support for runtime thread stack tracing; check Qore::Option::HAVE_RUNTIME_THREAD_STACK_TRACE before calling"); -#endif } //! Returns a list of @ref CallStackInfo hashes for the current TID (thread ID); because it is always from the same thread being read, no locking is applied as in @ref get_all_thread_call_stacks() /** - @par Platform Availability: - @ref Qore::Option::HAVE_RUNTIME_THREAD_STACK_TRACE - @return a list of @ref CallStackInfo hashes for the current TID (thread ID) @par Example: @code{.py} list> cs = get_thread_call_stack(); - foreach hash l in (cs) - if (l.type != "new-thread") - printf(" %d: %s() called at %s:%d (%s function)\n", ++i, l.function, l.file, l.line, l.type); - else - printf(" %d: *** thread started by background operator ***\n", ++i); +foreach hash l in (cs) { + if (l.type != "new-thread") { + printf(" %d: %s() called at %s:%d (%s function)\n", ++i, l.function, l.file, l.line, l.type); + } else { + printf(" %d: *** thread started by background operator ***\n", ++i); + } } @endcode @since %Qore 0.8.13 */ list> get_thread_call_stack() [dom=THREAD_CONTROL,THREAD_INFO] { -#ifdef QORE_RUNTIME_THREAD_STACK_TRACE - return getCallStack()->getCallStack(); -#else - return xsink->raiseException("MISSING-FEATURE-ERROR", "this version of the Qore library was built without support for runtime thread stack tracing; check Qore::Option::HAVE_RUNTIME_THREAD_STACK_TRACE before calling"); -#endif + return qore_get_thread_call_stack(); } //! Immediately runs all thread resource cleanup routines for the current thread and throws all associated exceptions diff --git a/lib/single-compilation-unit.cpp b/lib/single-compilation-unit.cpp index 97e7c743b9..2d8e27e9bd 100644 --- a/lib/single-compilation-unit.cpp +++ b/lib/single-compilation-unit.cpp @@ -127,9 +127,6 @@ #include "QoreRWLock.cpp" #include "AbstractSmartLock.cpp" #include "SmartMutex.cpp" -#ifdef QORE_RUNTIME_THREAD_STACK_TRACE -#include "CallStack.cpp" -#endif #include "Datasource.cpp" #include "DatasourcePool.cpp" #include "ManagedDatasource.cpp" diff --git a/lib/thread.cpp b/lib/thread.cpp index f7df69c9ca..fdcc62d036 100644 --- a/lib/thread.cpp +++ b/lib/thread.cpp @@ -459,10 +459,6 @@ void ThreadEntry::allocate(tid_node* tn, int stat) { assert(status == QTS_AVAIL); status = stat; tidnode = tn; -#ifdef QORE_RUNTIME_THREAD_STACK_TRACE - assert(!callStack); - callStack = new CallStack; -#endif joined = false; assert(!thread_data); } @@ -470,9 +466,6 @@ void ThreadEntry::allocate(tid_node* tn, int stat) { void ThreadEntry::activate(int tid, pthread_t n_ptid, QoreProgram* p, bool foreign) { assert(status == QTS_NA || status == QTS_RESERVED); ptid = n_ptid; -#ifdef QORE_RUNTIME_THREAD_STACK_TRACE - assert(callStack); -#endif assert(!thread_data); thread_data = new ThreadData(tid, p, foreign); ::thread_data.set(thread_data); @@ -489,14 +482,6 @@ void ThreadEntry::cleanup() { // delete tidnode from tid_list delete tidnode; -#ifdef QORE_RUNTIME_THREAD_STACK_TRACE -// XXX DELETEME - // delete call stack - delete callStack; -#ifdef DEBUG - callStack = nullptr; -#endif -#endif #ifdef DEBUG assert(!thread_data); #endif @@ -1643,16 +1628,6 @@ const QoreListNode* thread_get_implicit_args() { return thread_data.get()->current_implicit_arg; } -#ifdef QORE_RUNTIME_THREAD_STACK_TRACE -void pushCall(CallNode* cn) { - thread_list.pushCall(cn); -} - -void popCall(ExceptionSink* xsink) { - thread_list.popCall(xsink); -} -#endif - bool runtime_in_object_method(const char* name, const QoreObject* o) { ThreadData* td = thread_data.get(); return (td->current_obj == o && td->current_code == name) ? true : false; @@ -2627,12 +2602,9 @@ QoreHashNode* getAllCallStacks() { return thread_list.getAllCallStacks(); } -QoreListNode* getCallStackList() { - return thread_list.getCallStackList(); -} - -CallStack* getCallStack() { - return thread_list.getCallStack(); +QoreListNode* qore_get_thread_call_stack() { + ThreadData* td = thread_data.get(); + return thread_list.getCallStack(td->current_stack_location); } QoreHashNode* QoreThreadList::getAllCallStacks() { @@ -2804,18 +2776,3 @@ unsigned QoreThreadList::cancelAllActiveThreads() { return tcc; } - -QoreListNode* QoreThreadList::getCallStackList() { - return entry[gettid()].callStack->getCallStack(); -} - -#ifdef QORE_RUNTIME_THREAD_STACK_TRACE -void QoreThreadList::pushCall(CallNode* cn) { - entry[gettid()].callStack->push(cn); -} - -void QoreThreadList::popCall(ExceptionSink* xsink) { - entry[gettid()].callStack->pop(xsink); -} - -#endif From 9988ea3b6ade2375300aa9585e6900245d44f43a Mon Sep 17 00:00:00 2001 From: David Nichols Date: Wed, 12 Dec 2018 07:21:59 +0100 Subject: [PATCH 04/17] refs #3169 fixed deadlock in thread stack retrieval --- examples/test/qore/stack/get-call-stack.qtest | 8 ++++--- include/qore/intern/QoreThreadList.h | 21 +++++++++++++++---- lib/thread.cpp | 8 +------ 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/examples/test/qore/stack/get-call-stack.qtest b/examples/test/qore/stack/get-call-stack.qtest index 8be2262701..8aa9ffa57d 100755 --- a/examples/test/qore/stack/get-call-stack.qtest +++ b/examples/test/qore/stack/get-call-stack.qtest @@ -16,14 +16,16 @@ class GetCallThreadTest inherits QUnit::Test { set_return_value(main()); } - any f2(bool all) { + auto f2(bool all) { return all ? get_all_thread_call_stacks() : get_thread_call_stack(); } - any f1(bool all) { + + auto f1(bool all) { return f2(all); } + testGetCallThread() { - hash h = f1(True); + hash h = f1(True); testAssertionValue("get_all_thread_call_stacks-0", h{gettid()}[0].function, "get_all_thread_call_stacks"); testAssertionValue("get_all_thread_call_stacks-1", h{gettid()}[1].function, "GetCallThreadTest::f2"); testAssertionValue("get_all_thread_call_stacks-2", h{gettid()}[2].function, "GetCallThreadTest::f1"); diff --git a/include/qore/intern/QoreThreadList.h b/include/qore/intern/QoreThreadList.h index 4b6a81465a..15cc17bbd3 100644 --- a/include/qore/intern/QoreThreadList.h +++ b/include/qore/intern/QoreThreadList.h @@ -224,11 +224,20 @@ friend class tid_node; DLLLOCAL extern QoreThreadList thread_list; class QoreThreadListIterator : public AutoLocker { -protected: - tid_node* w = nullptr; - public: - DLLLOCAL QoreThreadListIterator() : AutoLocker(thread_list.lck) { + DLLLOCAL QoreThreadListIterator(bool access_stack = false) : AutoLocker(thread_list.lck), + access_stack(access_stack) { + if (access_stack) { + // grab the call stack write lock to get exclusive access to all thread stacks + thread_list.stack_lck.wrlock(); + } + } + + DLLLOCAL ~QoreThreadListIterator() { + if (access_stack) { + // release the call stack write lock + thread_list.stack_lck.unlock(); + } } DLLLOCAL bool next() { @@ -243,6 +252,10 @@ class QoreThreadListIterator : public AutoLocker { assert(w); return w->tid; } + +protected: + tid_node* w = nullptr; + bool access_stack; }; #endif diff --git a/lib/thread.cpp b/lib/thread.cpp index fdcc62d036..d7c414dea1 100644 --- a/lib/thread.cpp +++ b/lib/thread.cpp @@ -2611,12 +2611,6 @@ QoreHashNode* QoreThreadList::getAllCallStacks() { ReferenceHolder h(new QoreHashNode( qore_get_complex_list_type(hashdeclCallStackInfo->getTypeInfo())), nullptr); - // grab the thread lock to ensure that threads do not get released while running this call - AutoLocker al(lck); - - // grab the call stack write lock to get exclusive access to all thread stacks - QoreAutoRWWriteLocker wl(stack_lck); - if (exiting) { return h.release(); } @@ -2625,7 +2619,7 @@ QoreHashNode* QoreThreadList::getAllCallStacks() { QoreString str; - QoreThreadListIterator i; + QoreThreadListIterator i(true); while (i.next()) { // get call stack ThreadData* td = entry[*i].thread_data; From 7e546d79d7e60c0ca2456425836acf6079fc3fae Mon Sep 17 00:00:00 2001 From: David Nichols Date: Thu, 13 Dec 2018 08:50:02 +0100 Subject: [PATCH 05/17] refs #3169 new stack trace API updates --- doxygen/lang/900_release_notes.dox.tmpl | 4 +- include/qore/ExceptionSink.h | 59 +++++++++++++++--------- include/qore/intern/Function.h | 8 ++-- include/qore/intern/qore_thread_intern.h | 33 ++++++------- lib/AbstractStatement.cpp | 1 - lib/ExceptionSink.cpp | 38 +++++++++++++++ lib/QoreClass.cpp | 3 -- lib/QoreException.cpp | 2 - lib/thread.cpp | 3 +- 9 files changed, 94 insertions(+), 57 deletions(-) diff --git a/doxygen/lang/900_release_notes.dox.tmpl b/doxygen/lang/900_release_notes.dox.tmpl index 395c0d9e9c..1951729f3c 100644 --- a/doxygen/lang/900_release_notes.dox.tmpl +++ b/doxygen/lang/900_release_notes.dox.tmpl @@ -189,8 +189,8 @@ - Command line utils display source line code when interrupted - Runtime thread stack traces are available in all builds and the @ref Qore::Option::HAVE_RUNTIME_THREAD_STACK_TRACE "HAVE_RUNTIME_THREAD_STACK_TRACE" constant is always - @ref True "True". Furthermore, the %Qore library has been extended to support stack tracing when embeddingg - or integrating code in other programming languages + @ref True "True". Furthermore, the %Qore library has been extended to support stack tracing when embedding + or integrating code in other programming languages at runtime @subsection qore_09_bug_fixes Bug Fixes in Qore - fixed a bug with simple additional and subtraction with mixed @ref timeout_type "timeout" and diff --git a/include/qore/ExceptionSink.h b/include/qore/ExceptionSink.h index 785a86c7ef..e13bed56e1 100644 --- a/include/qore/ExceptionSink.h +++ b/include/qore/ExceptionSink.h @@ -358,23 +358,17 @@ class QoreStackLocation { return stack_next; } - //! returns the name of the function or method call - DLLLOCAL virtual const char* getCallName() const { - return stack_next ? stack_next->getCallName() : ""; - } + //! returns the QoreProgram container + DLLLOCAL virtual QoreProgram* getProgram() const = 0; - //! returns the call type - DLLLOCAL virtual qore_call_t getCallType() const { - return stack_next ? stack_next->getCallType() : qore_call_t::CT_BUILTIN; - } + //! returns the statement for the call for internal Qore code + DLLLOCAL virtual const AbstractStatement* getStatement() const = 0; - //! returns the statement for internal Qore code - DLLLOCAL virtual const AbstractStatement* getStatement() const { - return nullptr; - } + //! returns the name of the function or method call + DLLLOCAL virtual const char* getCallName() const = 0; - //! returns the QoreProgram container - DLLLOCAL virtual QoreProgram* getProgram() const = 0; + //! returns the call type + DLLLOCAL virtual qore_call_t getCallType() const = 0; //! returns the source location of the element DLLLOCAL virtual const QoreProgramLocation& getLocation() const = 0; @@ -383,22 +377,41 @@ class QoreStackLocation { const QoreStackLocation* stack_next = nullptr; }; -/* -//! Sets the current runtime location and restores the old location on exit -class QoreExternalRuntimeLocationHelper { +//! Stack location element abstract class for external binary modules +/** @since %Qore 0.9 +*/ +class QoreExternalStackLocation : public QoreStackLocation { + friend class qore_external_runtime_stack_location_helper_priv; public: - //! Sets the current runtime location - DLLEXPORT QoreExternalRuntimeLocationHelper(const QoreProgramLocation& loc); + //! create the object + DLLEXPORT QoreExternalStackLocation(); + + //! destroys the object + DLLEXPORT virtual ~QoreExternalStackLocation(); + + //! returns the QoreProgram container + DLLLOCAL virtual QoreProgram* getProgram() const; + + //! returns the statement for the call for internal Qore code + DLLLOCAL virtual const AbstractStatement* getStatement() const; + +private: + class qore_external_stack_location_priv* priv; +}; +//! Sets the current runtime location and restores the old location on exit +/** @since %Qore 0.9 +*/ +class QoreExternalRuntimeStackLocationHelper { +public: //! Sets the current runtime location - DLLEXPORT QoreExternalRuntimeLocationHelper(const QoreExternalProgramLocationWrapper& loc); + DLLEXPORT QoreExternalRuntimeStackLocationHelper(QoreExternalStackLocation& stack_loc); //! Restores the old runtime location - DLLEXPORT ~QoreExternalRuntimeLocationHelper(); + DLLEXPORT ~QoreExternalRuntimeStackLocationHelper(); private: - const QoreProgramLocation* loc; + class qore_external_runtime_stack_location_helper_priv* priv; }; -*/ #endif diff --git a/include/qore/intern/Function.h b/include/qore/intern/Function.h index e4b4bed646..ff69c9413f 100644 --- a/include/qore/intern/Function.h +++ b/include/qore/intern/Function.h @@ -305,10 +305,6 @@ class CodeEvaluationHelper : public QoreStackLocation { return pgm; } - //DLLLOCAL void restorePosition() const { - // update_runtime_location(loc); - //} - DLLLOCAL q_rt_flags_t getRuntimeFlags() const { return rtflags; } @@ -336,6 +332,10 @@ class CodeEvaluationHelper : public QoreStackLocation { return pgm; } + DLLLOCAL virtual const AbstractStatement* getStatement() const { + return stmt; + } + protected: qore_call_t ct; const char* name; diff --git a/include/qore/intern/qore_thread_intern.h b/include/qore/intern/qore_thread_intern.h index 08433c682f..591dbea0ba 100644 --- a/include/qore/intern/qore_thread_intern.h +++ b/include/qore/intern/qore_thread_intern.h @@ -257,7 +257,6 @@ DLLLOCAL void set_parse_file_info(QoreProgramLocation& loc); DLLLOCAL const char* get_parse_code(); DLLLOCAL const AbstractStatement* get_runtime_statement(); -//DLLLOCAL const AbstractStatement* update_get_runtime_statement(const AbstractStatement* s); DLLLOCAL const QoreTypeInfo* parse_set_implicit_arg_type_info(const QoreTypeInfo* ti); DLLLOCAL const QoreTypeInfo* parse_get_implicit_arg_type_info(); @@ -425,28 +424,28 @@ class QoreProgramStackLocationHelper { const QoreStackLocation* stack_loc; }; -/* -class QoreInternalStackOptionalLocationHelper : public QoreStackLocation, public QoreProgramStackOptionalLocationHelper { +class QoreInternalCallStackLocationHelperBase : public QoreStackLocation, public QoreProgramStackLocationHelper { public: - DLLLOCAL QoreInternalStackOptionalLocationHelper(const QoreProgramLocation* loc) : QoreProgramStackOptionalLocationHelper(loc ? this : nullptr), - loc(loc) { + DLLLOCAL QoreInternalCallStackLocationHelperBase() : QoreProgramStackLocationHelper(this, stmt, pgm) { } - //! returns the source location of the element - DLLLOCAL virtual const QoreProgramLocation& getLocation() const { - assert(loc); - return *loc; + DLLLOCAL virtual QoreProgram* getProgram() const { + return pgm; + } + + DLLLOCAL virtual const AbstractStatement* getStatement() const { + return stmt; } protected: - const QoreProgramLocation* loc; + const AbstractStatement* stmt; + QoreProgram* pgm; }; -*/ -class QoreInternalCallStackLocationHelper : public QoreStackLocation, public QoreProgramStackLocationHelper { +class QoreInternalCallStackLocationHelper : public QoreInternalCallStackLocationHelperBase { public: - DLLLOCAL QoreInternalCallStackLocationHelper(const QoreProgramLocation& loc, const char* call, qore_call_t call_type) : - QoreProgramStackLocationHelper(this, stmt, pgm), loc(loc), call(call), call_type(call_type) { + DLLLOCAL QoreInternalCallStackLocationHelper(const QoreProgramLocation& loc, const char* call, + qore_call_t call_type) : loc(loc), call(call), call_type(call_type) { } //! returns the source location of the element @@ -463,16 +462,10 @@ class QoreInternalCallStackLocationHelper : public QoreStackLocation, public Qor return call_type; } - DLLLOCAL virtual QoreProgram* getProgram() const { - return pgm; - } - protected: const QoreProgramLocation& loc; const char* call; qore_call_t call_type; - const AbstractStatement* stmt; - QoreProgram* pgm; }; class QoreProgramLocationHelper { diff --git a/lib/AbstractStatement.cpp b/lib/AbstractStatement.cpp index 8ee4cd2cfb..c601fc62fd 100644 --- a/lib/AbstractStatement.cpp +++ b/lib/AbstractStatement.cpp @@ -85,7 +85,6 @@ void AbstractStatement::finalizeBlock(int sline, int eline) { int AbstractStatement::exec(QoreValue& return_value, ExceptionSink *xsink) { printd(1, "AbstractStatement::exec() this: %p file: %s line: %d\n", this, loc->getFile(), loc->start_line); QoreProgramLocationHelper stack_loc(loc, this); - //QoreProgramLocationHelper l(loc, this); #ifdef QORE_MANAGE_STACK if (check_stack(xsink)) { diff --git a/lib/ExceptionSink.cpp b/lib/ExceptionSink.cpp index 66adee1cf9..c25b215b8e 100644 --- a/lib/ExceptionSink.cpp +++ b/lib/ExceptionSink.cpp @@ -32,6 +32,8 @@ #include +#define Q_MAX_EXCEPTIONS 10 + // check if "this" is valid in class member functions (cannot check "this" directly in g++ 4.9+ for example with optimization enabled) static bool qore_check_this(const void* p) { assert(p); @@ -626,3 +628,39 @@ void QoreExternalProgramLocationWrapper::set(const char* file, int start_line, i assert(offset <= 0xffff); loc->offset = offset; } + +class qore_external_stack_location_priv { +public: + const AbstractStatement* stmt = nullptr; + QoreProgram* pgm = nullptr; +}; + +QoreExternalStackLocation::QoreExternalStackLocation() : priv(new qore_external_stack_location_priv) { +} + +QoreExternalStackLocation::~QoreExternalStackLocation() { + delete priv; +} + +QoreProgram* QoreExternalStackLocation::getProgram() const { + return priv->pgm; +} + +const AbstractStatement* QoreExternalStackLocation::getStatement() const { + return priv->stmt; +} + +class qore_external_runtime_stack_location_helper_priv : public QoreProgramStackLocationHelper { +public: + DLLLOCAL qore_external_runtime_stack_location_helper_priv(QoreExternalStackLocation& stack_loc) + : QoreProgramStackLocationHelper(&stack_loc, stack_loc.priv->stmt, stack_loc.priv->pgm) { + } +}; + +QoreExternalRuntimeStackLocationHelper::QoreExternalRuntimeStackLocationHelper(QoreExternalStackLocation& stack_loc) + : priv(new qore_external_runtime_stack_location_helper_priv(stack_loc)) { +} + +QoreExternalRuntimeStackLocationHelper::~QoreExternalRuntimeStackLocationHelper() { + delete priv; +} \ No newline at end of file diff --git a/lib/QoreClass.cpp b/lib/QoreClass.cpp index c1e648a106..cac7eba191 100644 --- a/lib/QoreClass.cpp +++ b/lib/QoreClass.cpp @@ -4416,7 +4416,6 @@ int ConstructorMethodVariant::constructorPrelude(const QoreClass& thisclass, Cod } } - //ceh.restorePosition(); return 0; } @@ -4519,7 +4518,6 @@ void UserCopyVariant::evalCopy(const QoreClass& thisclass, QoreObject* self, Qor if (*xsink) { return; } - //ceh.restorePosition(); } evalIntern(uveh.getArgv(), self, xsink).discard(xsink); @@ -4573,7 +4571,6 @@ void BuiltinCopyVariantBase::evalCopy(const QoreClass& thisclass, QoreObject* se if (*xsink) { return; } - //ceh.restorePosition(); } old->evalCopyMethodWithPrivateData(thisclass, this, self, xsink); diff --git a/lib/QoreException.cpp b/lib/QoreException.cpp index d8717be416..2c026061e8 100644 --- a/lib/QoreException.cpp +++ b/lib/QoreException.cpp @@ -37,8 +37,6 @@ #include -#define Q_MAX_EXCEPTIONS 10 - QoreExceptionBase::QoreExceptionBase(QoreValue n_err, QoreValue n_desc, QoreValue n_arg, qore_call_t n_type) : type(n_type), err(n_err), desc(n_desc), arg(n_arg) { // populate call stack diff --git a/lib/thread.cpp b/lib/thread.cpp index d7c414dea1..6e79bbd280 100644 --- a/lib/thread.cpp +++ b/lib/thread.cpp @@ -705,8 +705,6 @@ class BGThreadParams { qore_program_private::registerNewThread(*pgm, tid); // create thread-local data in the program object qore_program_private::startThread(*pgm, xsink); - // set program counter for new thread - //update_runtime_location(loc); started = true; //printd(5, "BGThreadParams::startThread() this: %p pgm: %p\n", this, pgm); pgm->depRef(); @@ -2268,6 +2266,7 @@ namespace { { CodeContextHelper cch(&xsink, CT_NEWTHREAD, "background operator", btp->getContextObject(), btp->class_ctx); QoreInternalCallStackLocationHelper stack_loc(*btp->loc, "", CT_NEWTHREAD); + // save runtime location of thread creation call td->runtime_loc = btp->loc; // dereference call object if present From 1871a808e138b0613d5634faefe836dbab8a307f Mon Sep 17 00:00:00 2001 From: David Nichols Date: Wed, 12 Dec 2018 16:33:17 +0100 Subject: [PATCH 06/17] refs #3177 fixed binary module installation with autotools --- modules/astparser/src/Makefile.am | 2 +- modules/reflection/src/Makefile.am | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/astparser/src/Makefile.am b/modules/astparser/src/Makefile.am index 021d20791e..a1dd369db7 100644 --- a/modules/astparser/src/Makefile.am +++ b/modules/astparser/src/Makefile.am @@ -1,5 +1,5 @@ # issue #3069: make sure to install correctly when cross compiling -modexeclibdir=${pkglibdir}/${VERSION} +modexeclibdir=${libdir}/${mypkglibdir}/${VERSION} MNAME=astparser diff --git a/modules/reflection/src/Makefile.am b/modules/reflection/src/Makefile.am index f23e425343..51a2963672 100644 --- a/modules/reflection/src/Makefile.am +++ b/modules/reflection/src/Makefile.am @@ -1,5 +1,5 @@ # issue #3069: make sure to install correctly when cross compiling -modexeclibdir=${pkglibdir}/${VERSION} +modexeclibdir=${libdir}/${mypkglibdir}/${VERSION} MNAME=reflection From fbf1e2c78d4988bf4e7729b77bd3350f2f72d001 Mon Sep 17 00:00:00 2001 From: David Nichols Date: Thu, 13 Dec 2018 10:06:04 +0100 Subject: [PATCH 07/17] refs #3179 std::string COW issue workaround (#3180) --- doxygen/lang/900_release_notes.dox.tmpl | 2 + .../test/qore/misc/issue3179/issue3179.qm | 18 +++++++++ .../test/qore/misc/issue3179/issue3179.qtest | 40 +++++++++++++++++++ lib/QoreClass.cpp | 3 +- 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 examples/test/qore/misc/issue3179/issue3179.qm create mode 100755 examples/test/qore/misc/issue3179/issue3179.qtest diff --git a/doxygen/lang/900_release_notes.dox.tmpl b/doxygen/lang/900_release_notes.dox.tmpl index 1951729f3c..8f4f4c88be 100644 --- a/doxygen/lang/900_release_notes.dox.tmpl +++ b/doxygen/lang/900_release_notes.dox.tmpl @@ -193,6 +193,8 @@ or integrating code in other programming languages at runtime @subsection qore_09_bug_fixes Bug Fixes in Qore + - worked around a potential COW bug in \c std::string in GNU libdstdc++ 6+ + (issue 3179) - fixed a bug with simple additional and subtraction with mixed @ref timeout_type "timeout" and @ref date_type "date" values; updated docs that arithmetic operations with timeout values are not recommended and can return unexpected values in some situations diff --git a/examples/test/qore/misc/issue3179/issue3179.qm b/examples/test/qore/misc/issue3179/issue3179.qm new file mode 100644 index 0000000000..2a2d0defd8 --- /dev/null +++ b/examples/test/qore/misc/issue3179/issue3179.qm @@ -0,0 +1,18 @@ +%new-style +%require-types +%strict-args +%enable-all-warnings + +module issue3179 { + version = "1.0"; + desc = "issue 3179 test"; + author = "Qore Technologies, s.r.o."; + url = "https://qore.org"; + license = "MIT"; +} + +public class Disappear { + static string get() { + return "str"; + } +} diff --git a/examples/test/qore/misc/issue3179/issue3179.qtest b/examples/test/qore/misc/issue3179/issue3179.qtest new file mode 100755 index 0000000000..bf05e9ac10 --- /dev/null +++ b/examples/test/qore/misc/issue3179/issue3179.qtest @@ -0,0 +1,40 @@ +#!/usr/bin/env qore + +%new-style +%require-types +%strict-args +%enable-all-warnings + +%requires ../../../../../qlib/Util.qm +%requires ../../../../../qlib/QUnit.qm +%requires ./issue3179.qm + +%requires reflection + +%exec-class Issue3179Test + +class Issue3179Test inherits QUnit::Test { + constructor() : QUnit::Test("Issue3179Test", "1.0") { + addTestCase("issue 3179", \issue3179()); + + set_return_value(main()); + } + + issue3179() { + for (int i = 0; i < 3; ++i) { + doTest(); + } + } + + private doTest() { + Program p(PO_NO_INHERIT_USER_CLASSES); + p.importClass("Disappear"); + p.parse("string sub get() { return Disappear::get(); }", ""); + assertEq("str", p.callFunction("get")); + assertEq("str", Disappear::get()); + + Class cls = Class::forName("Disappear"); + AbstractMethod m = cls.findMethod("get").method; + assertEq("Disappear", m.getClass().getName()); + } +} diff --git a/lib/QoreClass.cpp b/lib/QoreClass.cpp index cac7eba191..8d085fc980 100644 --- a/lib/QoreClass.cpp +++ b/lib/QoreClass.cpp @@ -481,7 +481,8 @@ qore_class_private::qore_class_private(QoreClass* n_cls, std::string&& nme, int6 // only called while the parse lock for the QoreProgram owning "old" is held qore_class_private::qore_class_private(const qore_class_private& old, qore_ns_private* ns, QoreProgram* spgm, const char* new_name, bool inject, const qore_class_private* injectedClass) - : name(new_name ? new_name : old.name), + // issue #3179: we force a deep copy of "name" to work around COW issues with std::string with GNU libstdc++ 6+ + : name(new_name ? new_name : old.name.c_str()), ns(ns), ahm(old.ahm), constlist(old.constlist, 0, this), // committed constants From 4f565d30c3cb48fb3a9390502867817eb097dbd0 Mon Sep 17 00:00:00 2001 From: David Nichols Date: Tue, 11 Dec 2018 21:38:43 +0100 Subject: [PATCH 08/17] reffs #3169 removed lots of old code related to the old optional call stack implementation; the HAVE_RUNTIME_THREAD_STACK_TRACE constant is always True, removed associated build options and internal logic --- doxygen/lang/900_release_notes.dox.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doxygen/lang/900_release_notes.dox.tmpl b/doxygen/lang/900_release_notes.dox.tmpl index 8f4f4c88be..330fcc2fa9 100644 --- a/doxygen/lang/900_release_notes.dox.tmpl +++ b/doxygen/lang/900_release_notes.dox.tmpl @@ -189,8 +189,8 @@ - Command line utils display source line code when interrupted - Runtime thread stack traces are available in all builds and the @ref Qore::Option::HAVE_RUNTIME_THREAD_STACK_TRACE "HAVE_RUNTIME_THREAD_STACK_TRACE" constant is always - @ref True "True". Furthermore, the %Qore library has been extended to support stack tracing when embedding - or integrating code in other programming languages at runtime + @ref True "True". Furthermore, the %Qore library has been extended to support stack tracing when embeddingg + or integrating code in other programming languages @subsection qore_09_bug_fixes Bug Fixes in Qore - worked around a potential COW bug in \c std::string in GNU libdstdc++ 6+ From a7358a3d51c9c9a6a8e3994e9af16b499714afab Mon Sep 17 00:00:00 2001 From: David Nichols Date: Thu, 13 Dec 2018 08:50:02 +0100 Subject: [PATCH 09/17] refs #3169 new stack trace API updates --- doxygen/lang/900_release_notes.dox.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doxygen/lang/900_release_notes.dox.tmpl b/doxygen/lang/900_release_notes.dox.tmpl index 330fcc2fa9..8f4f4c88be 100644 --- a/doxygen/lang/900_release_notes.dox.tmpl +++ b/doxygen/lang/900_release_notes.dox.tmpl @@ -189,8 +189,8 @@ - Command line utils display source line code when interrupted - Runtime thread stack traces are available in all builds and the @ref Qore::Option::HAVE_RUNTIME_THREAD_STACK_TRACE "HAVE_RUNTIME_THREAD_STACK_TRACE" constant is always - @ref True "True". Furthermore, the %Qore library has been extended to support stack tracing when embeddingg - or integrating code in other programming languages + @ref True "True". Furthermore, the %Qore library has been extended to support stack tracing when embedding + or integrating code in other programming languages at runtime @subsection qore_09_bug_fixes Bug Fixes in Qore - worked around a potential COW bug in \c std::string in GNU libdstdc++ 6+ From 330a512e82380c3e53f30f1eedfc49b1f6bb7f1b Mon Sep 17 00:00:00 2001 From: David Nichols Date: Thu, 13 Dec 2018 22:09:44 +0100 Subject: [PATCH 10/17] refs #3169 finalized the API and fixed C++ issues --- doxygen/lang/900_release_notes.dox.tmpl | 5 +- .../test/qlib/Util/get_exception_string.qtest | 31 +++ .../qore/classes/Program/program-vars.qtest | 8 +- include/qore/ExceptionSink.h | 81 +++++++- include/qore/intern/QoreLibIntern.h | 26 ++- lib/ExceptionSink.cpp | 176 ++++++++++++++---- lib/QoreException.cpp | 6 - lib/ql_thread.qpp | 3 + lib/thread.cpp | 10 +- qlib/Util.qm | 9 +- 10 files changed, 282 insertions(+), 73 deletions(-) create mode 100755 examples/test/qlib/Util/get_exception_string.qtest diff --git a/doxygen/lang/900_release_notes.dox.tmpl b/doxygen/lang/900_release_notes.dox.tmpl index 8f4f4c88be..25d81f16f0 100644 --- a/doxygen/lang/900_release_notes.dox.tmpl +++ b/doxygen/lang/900_release_notes.dox.tmpl @@ -95,7 +95,10 @@ - reflection - New and updated hashdecls: - @ref Qore::NetIfInfo "NetIfInfo": new @ref hashdecl "hashdecl" for the @ref Qore::get_netif_list() "get_netif_list()" function - - @ref Qore::CallStackInfo "CallStackInfo": updated with new members: \c programid and \c statementid + - @ref Qore::CallStackInfo "CallStackInfo": updated with new members: + - \c lang + - \c programid + - \c statementid - New constants: - @ref Qore::DomainCodeMap "DomainCodeMap" - @ref Qore::DomainStringMap "DomainStringMap" diff --git a/examples/test/qlib/Util/get_exception_string.qtest b/examples/test/qlib/Util/get_exception_string.qtest new file mode 100755 index 0000000000..0de422fcee --- /dev/null +++ b/examples/test/qlib/Util/get_exception_string.qtest @@ -0,0 +1,31 @@ +#!/usr/bin/env qore +# -*- mode: qore; indent-tabs-mode: nil -*- + +%new-style +%enable-all-warnings +%require-types +%strict-args + +%requires ../../../../qlib/Util.qm +%requires ../../../../qlib/QUnit.qm + +%exec-class GetExceptionString + +public class GetExceptionString inherits QUnit::Test { + constructor() : Test("GetExceptionString", "1.0") { + addTestCase("get_exception_string()", \testGetExceptionString()); + + set_return_value(main()); + } + + testGetExceptionString() { + hash ex; + try { + throw "ERR", "err"; + } catch (hash ex1) { + ex = ex1; + } + assertEq("Qore", ex.callstack[0].lang); + assertEq("GetExceptionString::constructor", ex.callstack.last().function); + } +} diff --git a/examples/test/qore/classes/Program/program-vars.qtest b/examples/test/qore/classes/Program/program-vars.qtest index dac3e7b560..219d2e2529 100755 --- a/examples/test/qore/classes/Program/program-vars.qtest +++ b/examples/test/qore/classes/Program/program-vars.qtest @@ -6,7 +6,7 @@ %require-types %strict-args %no-child-restrictions -%no-debugging +%no-debugging %requires QUnit @@ -58,7 +58,6 @@ class ProgramVarsTest inherits QUnit::Test { "endline": 1, "source": "", "offset": 0, - "callstack": new list>(), "err": 1, )), ), @@ -67,7 +66,10 @@ class ProgramVarsTest inherits QUnit::Test { "value": NOTHING, ), ); - assertEq(vh, p.callFunction("test3")); + auto v = p.callFunction("test3"); + # remove the call stack + v.ex.value -= "callstack"; + assertEq(vh, v); } } } diff --git a/include/qore/ExceptionSink.h b/include/qore/ExceptionSink.h index e13bed56e1..c3e68fc5e1 100644 --- a/include/qore/ExceptionSink.h +++ b/include/qore/ExceptionSink.h @@ -315,26 +315,54 @@ class QoreExternalProgramLocationWrapper { //! empty constructor; use set() to set the location DLLEXPORT QoreExternalProgramLocationWrapper(); + //! copy ctor + DLLEXPORT QoreExternalProgramLocationWrapper(const QoreExternalProgramLocationWrapper&); + + //! move ctor + DLLEXPORT QoreExternalProgramLocationWrapper(QoreExternalProgramLocationWrapper&&); + //! constructor setting the source location DLLEXPORT QoreExternalProgramLocationWrapper(const char* file, int start_line, int end_line, - const char* source = nullptr, int offset = 0); + const char* source = nullptr, int offset = 0, const char* lang = nullptr); //! destructor; frees memory DLLEXPORT ~QoreExternalProgramLocationWrapper(); //! sets the program source location DLLEXPORT void set(const char* file, int start_line, int end_line, - const char* source = nullptr, int offset = 0); + const char* source = nullptr, int offset = 0, const char* lang = nullptr); //! returns the source location DLLEXPORT const QoreProgramLocation& get() const { return *loc; } + //! returns the file name + DLLEXPORT const char* getFile() const { + return file_str.c_str(); + } + + //! returns the source + DLLEXPORT const char* getSource() const { + return source_str.c_str(); + } + + //! returns the language + DLLEXPORT const char* getLanguage() const { + return lang_str.c_str(); + } + + //! returns the start line + DLLEXPORT int getStartLine() const; + + //! returns the start line + DLLEXPORT int getEndLine() const; + private: // save strings for exceptions in case they are epheremal when this object is created std::string file_str; std::string source_str; + std::string lang_str; // actual exception location QoreProgramLocation* loc; @@ -345,9 +373,24 @@ class QoreExternalProgramLocationWrapper { */ class QoreStackLocation { public: + //! constructor + DLLLOCAL QoreStackLocation(); + + //! copy ctor + DLLLOCAL QoreStackLocation(const QoreStackLocation&) = default; + + //! move ctor + DLLLOCAL QoreStackLocation(QoreStackLocation&&) = default; + //! virtual destructor DLLLOCAL virtual ~QoreStackLocation() = default; + //! no assignment operator + DLLLOCAL QoreStackLocation& operator=(const QoreStackLocation&) = delete; + + //! no move assignment operator + DLLLOCAL QoreStackLocation& operator=(QoreStackLocation&&) = delete; + //! called when pushed on the stack to set the next location DLLLOCAL void setNext(const QoreStackLocation* next) { stack_next = next; @@ -386,30 +429,54 @@ class QoreExternalStackLocation : public QoreStackLocation { //! create the object DLLEXPORT QoreExternalStackLocation(); + //! copy ctor + DLLEXPORT QoreExternalStackLocation(const QoreExternalStackLocation&); + + //! move ctor + DLLEXPORT QoreExternalStackLocation(QoreExternalStackLocation&&); + //! destroys the object DLLEXPORT virtual ~QoreExternalStackLocation(); + //! no assignment operator + DLLLOCAL QoreExternalStackLocation& operator=(const QoreExternalStackLocation&) = delete; + + //! no move assignment operator + DLLLOCAL QoreExternalStackLocation& operator=(QoreExternalStackLocation&&) = delete; + //! returns the QoreProgram container - DLLLOCAL virtual QoreProgram* getProgram() const; + DLLEXPORT virtual QoreProgram* getProgram() const; //! returns the statement for the call for internal Qore code - DLLLOCAL virtual const AbstractStatement* getStatement() const; + DLLEXPORT virtual const AbstractStatement* getStatement() const; private: class qore_external_stack_location_priv* priv; }; -//! Sets the current runtime location and restores the old location on exit +//! Sets the stack location for external modules providing language support /** @since %Qore 0.9 */ -class QoreExternalRuntimeStackLocationHelper { +class QoreExternalRuntimeStackLocationHelper : public QoreExternalStackLocation { public: //! Sets the current runtime location - DLLEXPORT QoreExternalRuntimeStackLocationHelper(QoreExternalStackLocation& stack_loc); + DLLEXPORT QoreExternalRuntimeStackLocationHelper(); + + //! copy ctor + DLLEXPORT QoreExternalRuntimeStackLocationHelper(const QoreExternalRuntimeStackLocationHelper&); + + //! move ctor + DLLEXPORT QoreExternalRuntimeStackLocationHelper(QoreExternalRuntimeStackLocationHelper&&); //! Restores the old runtime location DLLEXPORT ~QoreExternalRuntimeStackLocationHelper(); + //! no assignment operator + DLLLOCAL QoreExternalRuntimeStackLocationHelper& operator=(const QoreExternalRuntimeStackLocationHelper&) = delete; + + //! no move assignment operator + DLLLOCAL QoreExternalRuntimeStackLocationHelper& operator=(QoreExternalRuntimeStackLocationHelper&&) = delete; + private: class qore_external_runtime_stack_location_helper_priv* priv; }; diff --git a/include/qore/intern/QoreLibIntern.h b/include/qore/intern/QoreLibIntern.h index 70cf75ed9f..0e3e3936bc 100644 --- a/include/qore/intern/QoreLibIntern.h +++ b/include/qore/intern/QoreLibIntern.h @@ -177,8 +177,9 @@ struct QoreProgramLineLocation { DLLLOCAL QoreProgramLineLocation() : start_line(-1), end_line(-1) { } - DLLLOCAL QoreProgramLineLocation(const QoreProgramLineLocation& old) : start_line(old.start_line), end_line(old.end_line) { - } + DLLLOCAL QoreProgramLineLocation(const QoreProgramLineLocation& old) = default; + + DLLLOCAL QoreProgramLineLocation(QoreProgramLineLocation&& old) = default; }; struct QoreProgramLocation : public QoreProgramLineLocation { @@ -187,11 +188,18 @@ struct QoreProgramLocation : public QoreProgramLineLocation { DLLLOCAL QoreProgramLocation() { } + DLLLOCAL explicit QoreProgramLocation(const char* f, int sline, int eline, const char* source, int offset, + const char* lang = "Qore") : + QoreProgramLineLocation(sline, eline), file(f), source(source), offset(offset), lang(lang) { + assert(offset <= 0xffff); + } + // sets file position info from thread-local parse information DLLLOCAL QoreProgramLocation(int sline, int eline); - DLLLOCAL QoreProgramLocation(const QoreProgramLocation& old) : QoreProgramLineLocation(old), file(old.file), source(old.source), offset(old.offset) { - } + DLLLOCAL QoreProgramLocation(const QoreProgramLocation& old) = default; + + DLLLOCAL QoreProgramLocation(QoreProgramLocation&& old) = default; DLLLOCAL void clear() { start_line = end_line = -1; @@ -234,6 +242,10 @@ struct QoreProgramLocation : public QoreProgramLineLocation { source = s; } + DLLLOCAL void setLanguage(const char* l) { + lang = l; + } + DLLLOCAL bool operator<(const QoreProgramLocation& loc) const { return start_line < loc.start_line || end_line < loc.end_line @@ -268,12 +280,6 @@ struct QoreProgramLocation : public QoreProgramLineLocation { DLLLOCAL explicit QoreProgramLocation(const char* f, int sline = 0, int eline = 0) : QoreProgramLineLocation(sline, eline), file(f) { } - -public: - DLLLOCAL explicit QoreProgramLocation(const char* f, int sline, int eline, const char* source, int offset) : - QoreProgramLineLocation(sline, eline), file(f), source(source), offset(offset) { - assert(offset <= 0xffff); - } }; DLLLOCAL extern const QoreProgramLocation loc_builtin; diff --git a/lib/ExceptionSink.cpp b/lib/ExceptionSink.cpp index c25b215b8e..01638ee0f8 100644 --- a/lib/ExceptionSink.cpp +++ b/lib/ExceptionSink.cpp @@ -386,23 +386,42 @@ void ExceptionSink::defaultExceptionHandler(QoreException* e) { if (!found) { if (!e->file.empty()) { printe(" at %s:", e->file.c_str()); + bool openparen = false; if (e->start_line == e->end_line) { if (!e->start_line) { printe(""); - if (!e->source.empty()) - printe(" (source %s)", e->source.c_str()); + if (!e->source.empty()) { + printe(" (source %s", e->source.c_str()); + openparen = true; + } } else { printe("%d", e->start_line); - if (!e->source.empty()) + if (!e->source.empty()) { printe(" (source %s:%d)", e->source.c_str(), e->start_line + e->offset); + openparen = true; + } } } else { printe("%d-%d", e->start_line, e->end_line); - if (!e->source.empty()) + if (!e->source.empty()) { printe(" (source %s:%d-%d)", e->source.c_str(), e->start_line + e->offset, e->end_line + e->offset); + openparen = true; + } + } + if (e->lang != "Qore") { + if (!openparen) { + printe("(lang "); + openparen = true; + } else { + printe(" lang "); + } + printe(e->lang.c_str()); + } + if (openparen) { + printe(")"); } } else if (e->start_line) { @@ -501,30 +520,47 @@ void ExceptionSink::defaultExceptionHandler(QoreException* e) { QoreStringNode* fs = h->getKeyValue("function").get(); printe("%s() (", fs->getBuffer()); if (fns) { + QoreStringNode* lang = h->getKeyValue("lang").get(); + bool openparen = false; if (start_line == end_line) { - if (!start_line) + if (!start_line) { printe("%s:", fns); - else { + } else { printe("%s:%d", fns, start_line); - if (srcs) - printe(" (source %s:%d)", srcs, start_line + offset); + if (srcs) { + printe(" (source %s:%d", srcs, start_line + offset); + openparen = true; + } } - } - else { + } else { printe("%s:%d-%d", fns, start_line, end_line); - if (srcs) - printe(" (source %s:%d-%d)", srcs, start_line + offset, end_line + offset); + if (srcs) { + printe(" (source %s:%d-%d", srcs, start_line + offset, end_line + offset); + openparen = true; + } } - } - else { + if (lang && *lang != "Qore") { + if (!openparen) { + printe("(lang "); + openparen = true; + } else { + printe(" lang "); + } + printe(lang->c_str()); + } + if (openparen) { + printe(")"); + } + } else { if (start_line == end_line) { - if (!start_line) + if (!start_line) { printe(""); - else + } else { printe("line %d", start_line); - } - else + } + } else { printe("line %d - %d", start_line, end_line); + } } printe(", %s code)", type); } @@ -553,20 +589,39 @@ void ExceptionSink::defaultWarningHandler(QoreException *e) { if (!e->file.empty()) { printe("at %s:", e->file.c_str()); + bool openparen = false; if (e->start_line == e->end_line) { if (!e->start_line) { printe(""); - if (!e->source.empty()) - printe(" (source %s)", e->source.c_str()); + if (!e->source.empty()) { + openparen = true; + printe(" (source %s", e->source.c_str()); + } } else { printe("%d", e->start_line); - if (!e->source.empty()) - printe(" (source %s:%d)", e->source.c_str(), e->start_line + e->offset); + if (!e->source.empty()) { + openparen = true; + printe(" (source %s:%d", e->source.c_str(), e->start_line + e->offset); + } } } else { printe("%d-%d", e->start_line, e->end_line); - if (!e->source.empty()) - printe(" (source %s:%d-%d)", e->source.c_str(), e->start_line + e->offset, e->end_line + e->offset); + if (!e->source.empty()) { + openparen = true; + printe(" (source %s:%d-%d", e->source.c_str(), e->start_line + e->offset, e->end_line + e->offset); + } + } + if (e->lang != "Qore") { + if (!openparen) { + printe("(lang "); + openparen = true; + } else { + printe(" lang "); + } + printe(e->lang.c_str()); + } + if (openparen) { + printe(")"); } } else if (e->start_line) { if (e->start_line == e->end_line) { @@ -590,14 +645,29 @@ void ExceptionSink::defaultWarningHandler(QoreException *e) { } } -QoreExternalProgramLocationWrapper::QoreExternalProgramLocationWrapper() : - loc(new QoreProgramLocation) { +QoreStackLocation::QoreStackLocation() { +} + +QoreExternalProgramLocationWrapper::QoreExternalProgramLocationWrapper() : loc(new QoreProgramLocation) { +} + +QoreExternalProgramLocationWrapper::QoreExternalProgramLocationWrapper(const QoreExternalProgramLocationWrapper& old) + : file_str(old.file_str), source_str(old.source_str), lang_str(old.lang_str), + loc(new QoreProgramLocation(file_str.c_str(), old.loc->start_line, old.loc->end_line, + source_str.empty() ? nullptr : source_str.c_str(), old.loc->offset, + lang_str.empty() ? nullptr : lang_str.c_str())) { +} + +QoreExternalProgramLocationWrapper::QoreExternalProgramLocationWrapper(QoreExternalProgramLocationWrapper&& old) + : loc(old.loc) { + old.loc = nullptr; } QoreExternalProgramLocationWrapper::QoreExternalProgramLocationWrapper(const char* file, int start_line, int end_line, - const char* source, int offset) : file_str(file ? file : ""), source_str(source ? source : ""), - loc(new QoreProgramLocation(file_str.c_str(), start_line, end_line, - source_str.empty() ? nullptr : source_str.c_str(), offset)) { + const char* source, int offset, const char* lang) : file_str(file ? file : ""), source_str(source ? source : ""), + lang_str(lang ? lang : ""), loc(new QoreProgramLocation(file_str.c_str(), start_line, end_line, + source_str.empty() ? nullptr : source_str.c_str(), offset, lang_str.empty() ? nullptr : lang_str.c_str())) { + assert(file); } QoreExternalProgramLocationWrapper::~QoreExternalProgramLocationWrapper() { @@ -605,39 +675,67 @@ QoreExternalProgramLocationWrapper::~QoreExternalProgramLocationWrapper() { } void QoreExternalProgramLocationWrapper::set(const char* file, int start_line, int end_line, - const char* source, int offset) { + const char* source, int offset, const char* lang) { // we need to save strings in case they are epheremal when this object is created if (!file) { file_str.clear(); } else { file_str = file; } + loc->setFile(file_str.c_str()); if (!source) { source_str.clear(); } else { source_str = source; } - loc->setFile(file_str.c_str()); + loc->setSource(source_str.c_str()); + if (!lang) { + lang_str.clear(); + } else { + lang_str = lang; + } + loc->setLanguage(lang_str.c_str()); // the internal storage for start_line is currently 16 bits assert(start_line <= 0xffff); loc->start_line = start_line; // the internal storage for end_line is currently 16 bits assert(end_line <= 0xffff); loc->end_line = end_line; - loc->setSource(source_str.c_str()); assert(offset <= 0xffff); loc->offset = offset; } +int QoreExternalProgramLocationWrapper::getStartLine() const { + return loc->start_line; +} + +int QoreExternalProgramLocationWrapper::getEndLine() const { + return loc->end_line; +} + class qore_external_stack_location_priv { public: const AbstractStatement* stmt = nullptr; QoreProgram* pgm = nullptr; + + DLLLOCAL qore_external_stack_location_priv() = default; + + DLLLOCAL qore_external_stack_location_priv(const qore_external_stack_location_priv& old) = default; + + DLLLOCAL qore_external_stack_location_priv(qore_external_stack_location_priv&& old) = default; }; QoreExternalStackLocation::QoreExternalStackLocation() : priv(new qore_external_stack_location_priv) { } +QoreExternalStackLocation::QoreExternalStackLocation(const QoreExternalStackLocation& old) + : priv(new qore_external_stack_location_priv(*old.priv)) { +} + +QoreExternalStackLocation::QoreExternalStackLocation(QoreExternalStackLocation&& old) : priv(old.priv) { + old.priv = nullptr; +} + QoreExternalStackLocation::~QoreExternalStackLocation() { delete priv; } @@ -655,10 +753,20 @@ class qore_external_runtime_stack_location_helper_priv : public QoreProgramStack DLLLOCAL qore_external_runtime_stack_location_helper_priv(QoreExternalStackLocation& stack_loc) : QoreProgramStackLocationHelper(&stack_loc, stack_loc.priv->stmt, stack_loc.priv->pgm) { } + + DLLLOCAL qore_external_runtime_stack_location_helper_priv(const qore_external_runtime_stack_location_helper_priv& old) = default; }; -QoreExternalRuntimeStackLocationHelper::QoreExternalRuntimeStackLocationHelper(QoreExternalStackLocation& stack_loc) - : priv(new qore_external_runtime_stack_location_helper_priv(stack_loc)) { +QoreExternalRuntimeStackLocationHelper::QoreExternalRuntimeStackLocationHelper() + : priv(new qore_external_runtime_stack_location_helper_priv(*this)) { +} + +QoreExternalRuntimeStackLocationHelper::QoreExternalRuntimeStackLocationHelper(const QoreExternalRuntimeStackLocationHelper& old) + : priv(new qore_external_runtime_stack_location_helper_priv(*old.priv)) { +} + +QoreExternalRuntimeStackLocationHelper::QoreExternalRuntimeStackLocationHelper(QoreExternalRuntimeStackLocationHelper&& old) : priv(old.priv) { + old.priv = nullptr; } QoreExternalRuntimeStackLocationHelper::~QoreExternalRuntimeStackLocationHelper() { diff --git a/lib/QoreException.cpp b/lib/QoreException.cpp index 2c026061e8..8edc708c7e 100644 --- a/lib/QoreException.cpp +++ b/lib/QoreException.cpp @@ -42,12 +42,6 @@ QoreExceptionBase::QoreExceptionBase(QoreValue n_err, QoreValue n_desc, QoreValu // populate call stack const QoreStackLocation* w = get_runtime_stack_location(); while (w) { - /* - qore_call_t call_type = w->getCallType(); - const char* call_name = w->getCallName(); - const QoreProgramLocation& loc = w->getLocation(); - callStack->push(QoreException::getStackHash(call_type, nullptr, call_name, loc), nullptr); - */ callStack->push(QoreThreadList::getCallStackHash(*w), nullptr); w = w->getNext(); } diff --git a/lib/ql_thread.qpp b/lib/ql_thread.qpp index c6f032c2c2..4d016e2b19 100644 --- a/lib/ql_thread.qpp +++ b/lib/ql_thread.qpp @@ -119,6 +119,9 @@ hashdecl CallStackInfo { //! a type string; either \c "user", \c "builtin", \c "new-thread" or \c "rethrow" (for rethrown exceptions) string type; + //! the language of the source code; normally \c "Qore" + string lang; + //! related program id (since 0.8.13.7) *int programid; diff --git a/lib/thread.cpp b/lib/thread.cpp index 6e79bbd280..4ff3f0aab5 100644 --- a/lib/thread.cpp +++ b/lib/thread.cpp @@ -2642,13 +2642,6 @@ QoreListNode* QoreThreadList::getCallStack(const QoreStackLocation* stack_locati const QoreStackLocation* w = stack_location; while (w) { stack->push(getCallStackHash(*w), nullptr); - /* - qore_call_t call_type = w->getCallType(); - const char* call_name = w->getCallName(); - const QoreProgramLocation& loc = w->getLocation(); - - stack->push(QoreException::getStackHash(call_type, nullptr, call_name, loc), nullptr); - */ w = w->getNext(); } @@ -2664,7 +2657,7 @@ QoreHashNode* QoreThreadList::getCallStackHash(qore_call_t call_type, const char ph->setKeyValueIntern("function", new QoreStringNode(code)); ph->setKeyValueIntern("line", loc.start_line); ph->setKeyValueIntern("endline", loc.end_line); - ph->setKeyValueIntern("file", new QoreStringNode(loc.getFile())); + ph->setKeyValueIntern("file", new QoreStringNode(loc.getFileValue())); // do not set "source" to NOTHING as it must be set to a value according to the hashdecl { const char* src = loc.getSource(); @@ -2673,6 +2666,7 @@ QoreHashNode* QoreThreadList::getCallStackHash(qore_call_t call_type, const char } } ph->setKeyValueIntern("offset", loc.offset); + ph->setKeyValueIntern("lang", new QoreStringNode(loc.getLanguageValue())); ph->setKeyValueIntern("typecode", call_type); // CT_RETHROW is only aded manually switch (call_type) { diff --git a/qlib/Util.qm b/qlib/Util.qm index 94f5488632..7357eb2f02 100644 --- a/qlib/Util.qm +++ b/qlib/Util.qm @@ -896,12 +896,13 @@ string pass = get_random_string(); if (ex.callstack) { foreach hash l in (ex.callstack) { - if (l.type == "new-thread") + if (l.type == "new-thread") { str += sprintf("\n *** thread started by background operator ***"); - else if (l.type == "rethrow") + } else if (l.type == "rethrow") { str += sprintf("\n *** RETHROW at %s", get_ex_pos(l)); - else - str += sprintf("\n %s() called at %s (%s function)", l.function, get_ex_pos(l), l.type); + } else { + str += sprintf("\n %s() called at %s (%s %s code)", l.function, get_ex_pos(l), l.lang, l.type); + } } } From d7a9902c7aca82049df71b696174adf2b3a27936 Mon Sep 17 00:00:00 2001 From: David Nichols Date: Fri, 14 Dec 2018 09:10:07 +0100 Subject: [PATCH 11/17] refs #3169 refactored default exception handling & warning output, added language info to exceptions, updates tests and docs --- doxygen/lang/900_release_notes.dox.tmpl | 6 + examples/test/qore/functions/get_ex_pos.qtest | 32 ++ examples/test/qore/misc/exception.qtest | 95 +----- include/qore/ExceptionSink.h | 3 + include/qore/intern/QoreLibIntern.h | 2 +- lib/ExceptionSink.cpp | 282 +++++++----------- lib/QoreException.cpp | 1 + lib/TypedHashDecl.cpp | 5 +- lib/ql_lib.qpp | 3 + lib/ql_misc.qpp | 23 +- qlib/Util.qm | 8 +- 11 files changed, 192 insertions(+), 268 deletions(-) create mode 100755 examples/test/qore/functions/get_ex_pos.qtest diff --git a/doxygen/lang/900_release_notes.dox.tmpl b/doxygen/lang/900_release_notes.dox.tmpl index 25d81f16f0..8d0a985343 100644 --- a/doxygen/lang/900_release_notes.dox.tmpl +++ b/doxygen/lang/900_release_notes.dox.tmpl @@ -89,6 +89,8 @@ - @ref Qore::set_default_thread_stack_size() "set_default_thread_stack_size()" - @ref Qore::set_module_option() "set_module_option()" - @ref Qore::set_thread_name() "set_thread_name()" + - Updated functions: + - @ref Qore::get_ex_pos() "get_ex_pos()": \c lang info was added to the result string if available - New modules: - FsUtil - Logger @@ -99,6 +101,8 @@ - \c lang - \c programid - \c statementid + - @ref Qore::ExceptionInfo "ExceptionInfo": updated with a new member: + - \c lang - New constants: - @ref Qore::DomainCodeMap "DomainCodeMap" - @ref Qore::DomainStringMap "DomainStringMap" @@ -174,6 +178,8 @@ - Updated \c parse_to_qore_value() to support single-element lists and hashes with curly brackets including empty hashes (issue 3138) + - updated @ref Util::get_exception_string() "get_exception_string()" to show the \c lang value + (issue 3182) - CsvUtil module updates: - Added public methods \c AbstractCsvIterator::getRawLine() and \c AbstractCsvIterator::getRawLineValues() (issue 2739) - Swagger: diff --git a/examples/test/qore/functions/get_ex_pos.qtest b/examples/test/qore/functions/get_ex_pos.qtest new file mode 100755 index 0000000000..2795e14528 --- /dev/null +++ b/examples/test/qore/functions/get_ex_pos.qtest @@ -0,0 +1,32 @@ +#!/usr/bin/env qore +# -*- mode: qore; indent-tabs-mode: nil -*- + +%new-style +%enable-all-warnings +%require-types +%strict-args + +%requires ../../../../qlib/QUnit.qm + +%exec-class GetExPosTest + +public class GetExPosTest inherits QUnit::Test { + constructor() : Test("GetExPosTest", "1.0") { + addTestCase("get_ex_pos() test", \getExPosTest()); + + # Return for compatibility with test harness that checks return value. + set_return_value(main()); + } + + getExPosTest() { + try { + if (True) { + throw "ERR", "desc"; + } + assertTrue(False); + } catch (hash ex) { + string str = get_ex_pos(ex); + assertRegex("get_ex_pos.qtest:24 \\(Qore\\)$", str); + } + } +} diff --git a/examples/test/qore/misc/exception.qtest b/examples/test/qore/misc/exception.qtest index 7f37b39b28..1fb7448859 100755 --- a/examples/test/qore/misc/exception.qtest +++ b/examples/test/qore/misc/exception.qtest @@ -12,62 +12,42 @@ class ExceptionTest inherits QUnit::Test { private { - string err; - string err2; - string err3; - string desc; - string arg; - string type; + hash ex; } constructor() : QUnit::Test("Exception test", "1.0") { addTestCase("Test simple try/catch block", \testSimpleTryCatch()); addTestCase("Test rethrow", \testRethrow()); addTestCase("misc tests", \miscTests()); - #addTestCase("Complex try/catch hierarchy", \testComplexHierarchy()); set_return_value(main()); } - setUp() { - err = ''; - err2 = ''; - err3 = ''; - desc = ''; - arg = ''; - type = ''; - } - testSimpleTryCatch() { try { throw "testing", "123", "test"; + } catch (hash ex1) { + ex = ex1; } - catch (ex) { - err = ex.err; - desc = ex.desc; - arg = ex.arg; - } - assertEq("testing", err); - assertEq("123", desc); - assertEq("test", arg); + assertEq("testing", ex.err); + assertEq("123", ex.desc); + assertEq("test", ex.arg); + assertEq("Qore", ex.lang); } testRethrow() { try { try { throw "TEST-ERROR", "this is a test"; - } - catch () { + } catch () { rethrow; } + } catch (hash ex1) { + ex = ex1; } - catch (ex) { - err = ex.err; - desc = ex.desc; - type = ex.type; - } - assertEq("TEST-ERROR", err); - assertEq("this is a test", desc); - assertEq("User", type); + assertEq("TEST-ERROR", ex.err); + assertEq("this is a test", ex.desc); + assertEq("User", ex.type); + assertEq("Qore", ex.lang); } miscTests() { @@ -77,53 +57,8 @@ class ExceptionTest inherits QUnit::Test { int a = 1; try { throw a; - } - catch (hash ex) { + } catch (hash ex) { assertEq(1, ex.err); } } - - /*testComplexHierarchy() { - try { - try { - try { - printf("%s\n", snope.refresh()); - } - catch (ex) { - err = ex.err; - - try { - try { - context gee (gee) where (%foo == "gee") - printf("%s\n", sdfdas); - } - catch (ex1) { - desc = shift argv; - printf("QORE %s Exception in line %d of file %s: %s: %s\n", - ex1.type, ex1.line, ex1.file, ex1.err, ex1.desc); - throw snope.blah(); - } - throw snope.sdfds(); - } - catch (ex2) { - err2 = ex2.err; - throw "TEST"; - } - } - } - catch (ex) { - err3 = ex.err; - } - } - catch (ex) { - printf("QORE %s Exception in line %d of file %s: %s: %s\n", - ex.type, ex.line, ex.file, ex.err, ex.desc); - context (gee) where (%whiz == "wdsf") - printf("%s\n", %dsfdf); - } - - assertEq("PSEUDO-METHOD-DOES-NOT-EXIST", err); - assertEq("PSEUDO-METHOD-DOES-NOT-EXIST", err2); - assertEq("TEST", err3); - }*/ } diff --git a/include/qore/ExceptionSink.h b/include/qore/ExceptionSink.h index c3e68fc5e1..842bbf2211 100644 --- a/include/qore/ExceptionSink.h +++ b/include/qore/ExceptionSink.h @@ -236,6 +236,9 @@ class ExceptionSink { DLLLOCAL static void defaultExceptionHandler(QoreException* e); DLLLOCAL static void defaultWarningHandler(QoreException* e); + + DLLLOCAL static void outputExceptionLocation(const char* fns, int start_line, int end_line, const char* srcs, + int offset, const char* langs, const char* types); }; //! call stack element type diff --git a/include/qore/intern/QoreLibIntern.h b/include/qore/intern/QoreLibIntern.h index 0e3e3936bc..72d97dcc8e 100644 --- a/include/qore/intern/QoreLibIntern.h +++ b/include/qore/intern/QoreLibIntern.h @@ -190,7 +190,7 @@ struct QoreProgramLocation : public QoreProgramLineLocation { DLLLOCAL explicit QoreProgramLocation(const char* f, int sline, int eline, const char* source, int offset, const char* lang = "Qore") : - QoreProgramLineLocation(sline, eline), file(f), source(source), offset(offset), lang(lang) { + QoreProgramLineLocation(sline, eline), file(f), source(source), lang(lang), offset(offset) { assert(offset <= 0xffff); } diff --git a/lib/ExceptionSink.cpp b/lib/ExceptionSink.cpp index 01638ee0f8..6bddfb5fe9 100644 --- a/lib/ExceptionSink.cpp +++ b/lib/ExceptionSink.cpp @@ -355,91 +355,45 @@ void ExceptionSink::defaultExceptionHandler(QoreException* e) { while (true) { h = cs->retrieveEntry(i).get(); assert(h); - if (h->getKeyValue("typecode").getAsBigInt() != CT_RETHROW) + if (h->getKeyValue("typecode").getAsBigInt() != CT_RETHROW) { break; + } i++; - if (i == cs->size()) + if (i == cs->size()) { break; + } } if (i < cs->size()) { found = true; - QoreStringNode *func = h->getKeyValue("function").get(); - QoreStringNode *type = h->getKeyValue("type").get(); - - printe(" in %s() (%s:%d", func->getBuffer(), e->file.c_str(), e->start_line); - - if (e->start_line == e->end_line) { - if (!e->source.empty()) - printe(", source %s:%d", e->source.c_str(), e->start_line + e->offset); - } - else { - printe("-%d", e->end_line); - if (!e->source.empty()) - printe(", source %s:%d-%d", e->source.c_str(), e->start_line + e->offset, - e->end_line + e->offset); - } - printe(", %s code)\n", type->getBuffer()); + QoreStringNode* func = h->getKeyValue("function").get(); + QoreStringNode* type = h->getKeyValue("type").get(); + + printe(" in %s() (", func->c_str()); + const char* fns = !e->file.empty() ? e->file.c_str() : nullptr; + const char* srcs = !e->source.empty() ? e->source.c_str() : nullptr; + const char* langs = !e->lang.empty() ? e->lang.c_str() : nullptr; + outputExceptionLocation(fns, e->start_line, e->end_line, srcs, e->offset, langs, type->c_str()); + printe(")\n"); } } if (!found) { - if (!e->file.empty()) { - printe(" at %s:", e->file.c_str()); - bool openparen = false; - if (e->start_line == e->end_line) { - if (!e->start_line) { - printe(""); - if (!e->source.empty()) { - printe(" (source %s", e->source.c_str()); - openparen = true; - } - } - else { - printe("%d", e->start_line); - if (!e->source.empty()) { - printe(" (source %s:%d)", e->source.c_str(), e->start_line + e->offset); - openparen = true; - } - } - } - else { - printe("%d-%d", e->start_line, e->end_line); - if (!e->source.empty()) { - printe(" (source %s:%d-%d)", e->source.c_str(), e->start_line + e->offset, - e->end_line + e->offset); - openparen = true; - } - } - if (e->lang != "Qore") { - if (!openparen) { - printe("(lang "); - openparen = true; - } else { - printe(" lang "); - } - printe(e->lang.c_str()); - } - if (openparen) { - printe(")"); - } - } - else if (e->start_line) { - if (e->start_line == e->end_line) { - if (!e->start_line) - printe(" at "); - else - printe(" on line %d", e->start_line); - } - else - printe(" on lines %d through %d", e->start_line, e->end_line); - } + printe(" at "); + + const char* fns = !e->file.empty() ? e->file.c_str() : nullptr; + const char* srcs = !e->source.empty() ? e->source.c_str() : nullptr; + const char* langs = !e->lang.empty() ? e->lang.c_str() : nullptr; + outputExceptionLocation(fns, e->start_line, e->end_line, srcs, e->offset, langs, + e->type == CT_USER ? "user" : "builtin"); printe("\n"); } if (e->type == CT_BUILTIN) { QoreStringNode* err = e->err.get(); + assert(!err->empty()); QoreStringNode* desc = e->desc.get(); + assert(!desc->empty()); printe("%s: %s\n", err->c_str(), desc->c_str()); } else { bool hdr = false; @@ -447,26 +401,25 @@ void ExceptionSink::defaultExceptionHandler(QoreException* e) { if (e->err.getType() == NT_STRING) { QoreStringNode *err = e->err.get(); printe("%s", err->c_str()); - } - else { + } else { QoreNodeAsStringHelper str(e->err, FMT_NORMAL, &xsink); printe("EXCEPTION: %s", str->c_str()); hdr = true; } - } - else + } else { printe("EXCEPTION"); + } if (!e->desc.isNothing()) { if (e->desc.getType() == NT_STRING) { QoreStringNode *desc = e->desc.get(); printe("%s%s", hdr ? ", desc: " : ": ", desc->c_str()); - } - else { + } else { QoreNodeAsStringHelper str(e->desc, FMT_NORMAL, &xsink); printe(", desc: %s", str->c_str()); - if (!hdr) + if (!hdr) { hdr = true; + } } } @@ -474,8 +427,7 @@ void ExceptionSink::defaultExceptionHandler(QoreException* e) { if (e->arg.getType() == NT_STRING) { QoreStringNode *arg = e->arg.get(); printe("%s%s", hdr ? ", arg: " : ": ", arg->c_str()); - } - else { + } else { QoreNodeAsStringHelper str(e->arg, FMT_NORMAL, &xsink); printe(", arg: %s", str->c_str()); } @@ -489,82 +441,42 @@ void ExceptionSink::defaultExceptionHandler(QoreException* e) { int pos = cs->size() - i; QoreHashNode* h = cs->retrieveEntry(i).get(); QoreStringNode* strtype = h->getKeyValue("type").get(); - const char* type = strtype->getBuffer(); + const char* type = strtype->c_str(); int typecode = (int)h->getKeyValue("typecode").getAsBigInt(); if (!strcmp(type, "new-thread")) printe(" %2d: *thread start*\n", pos); else { QoreStringNode* fn = h->getKeyValue("file").get(); - const char* fns = fn && !fn->empty() ? fn->getBuffer() : 0; + const char* fns = fn && !fn->empty() ? fn->c_str() : nullptr; int start_line = (int)h->getKeyValue("line").getAsBigInt(); int end_line = (int)h->getKeyValue("endline").getAsBigInt(); QoreStringNode* src = h->getKeyValue("source").get(); - const char* srcs = src && !src->empty() ? src->getBuffer() : 0; + const char* srcs = src && !src->empty() ? src->c_str() : nullptr; int offset = (int)h->getKeyValue("offset").getAsBigInt(); + QoreStringNode* lang = h->getKeyValue("lang").get(); + const char* langs = lang && !lang->empty() ? lang->c_str() : nullptr; + printe(" %2d: ", pos); if (typecode == CT_RETHROW) { printe("RETHROW at "); if (fn) { printe("%s:", fn->getBuffer()); - } - else + } else { printe("line"); + } printe("%d", start_line); - if (srcs) + if (srcs) { printe(" (source %s:%d)", srcs, offset + start_line); - } - else { + } + } else { QoreStringNode* fs = h->getKeyValue("function").get(); printe("%s() (", fs->getBuffer()); - if (fns) { - QoreStringNode* lang = h->getKeyValue("lang").get(); - bool openparen = false; - if (start_line == end_line) { - if (!start_line) { - printe("%s:", fns); - } else { - printe("%s:%d", fns, start_line); - if (srcs) { - printe(" (source %s:%d", srcs, start_line + offset); - openparen = true; - } - } - } else { - printe("%s:%d-%d", fns, start_line, end_line); - if (srcs) { - printe(" (source %s:%d-%d", srcs, start_line + offset, end_line + offset); - openparen = true; - } - } - if (lang && *lang != "Qore") { - if (!openparen) { - printe("(lang "); - openparen = true; - } else { - printe(" lang "); - } - printe(lang->c_str()); - } - if (openparen) { - printe(")"); - } - } else { - if (start_line == end_line) { - if (!start_line) { - printe(""); - } else { - printe("line %d", start_line); - } - } else { - printe("line %d - %d", start_line, end_line); - } - } - printe(", %s code)", type); + outputExceptionLocation(fns, start_line, end_line, srcs, offset, langs, type); } - printe("\n"); + printe(")\n"); } } } @@ -580,58 +492,71 @@ void ExceptionSink::defaultExceptionHandler(QoreException* e) { } } +// static +void ExceptionSink::outputExceptionLocation(const char* fns, int start_line, int end_line, const char* srcs, int offset, + const char* langs, const char* types) { + if (fns) { + printe("%s:", fns); + if (!start_line) { + printe(""); + } else if (start_line == end_line) { + printe("%d", start_line); + } else { + printe("%d-%d", start_line, end_line); + } + } else { + if (!start_line) { + printe(""); + } else if (start_line == end_line) { + printe("line %d", start_line); + } else { + printe("line %d - %d", start_line, end_line); + } + } + + bool openparen = false; + if (langs) { + printe(" (%s", langs); + openparen = true; + } + if (srcs) { + printe(" "); + if (!openparen) { + printe("("); + openparen = true; + } + if (!start_line) { + printe("source %s", srcs); + } else if (start_line == end_line) { + printe("source %s:%d", srcs, start_line + offset); + } else { + printe("source %s:%d-%d", srcs, start_line + offset, end_line + offset); + } + } + if (types) { + printe(" "); + if (!openparen) { + printe("("); + openparen = true; + } + printe("%s code", types); + } + if (openparen) { + printe(")"); + } +} + // static member function void ExceptionSink::defaultWarningHandler(QoreException *e) { ExceptionSink xsink; while (e) { - printe("warning encountered "); - - if (!e->file.empty()) { - printe("at %s:", e->file.c_str()); - bool openparen = false; - if (e->start_line == e->end_line) { - if (!e->start_line) { - printe(""); - if (!e->source.empty()) { - openparen = true; - printe(" (source %s", e->source.c_str()); - } - } else { - printe("%d", e->start_line); - if (!e->source.empty()) { - openparen = true; - printe(" (source %s:%d", e->source.c_str(), e->start_line + e->offset); - } - } - } else { - printe("%d-%d", e->start_line, e->end_line); - if (!e->source.empty()) { - openparen = true; - printe(" (source %s:%d-%d", e->source.c_str(), e->start_line + e->offset, e->end_line + e->offset); - } - } - if (e->lang != "Qore") { - if (!openparen) { - printe("(lang "); - openparen = true; - } else { - printe(" lang "); - } - printe(e->lang.c_str()); - } - if (openparen) { - printe(")"); - } - } else if (e->start_line) { - if (e->start_line == e->end_line) { - if (!e->start_line) - printe("at "); - else - printe("on line %d", e->start_line); - } else - printe("on line %d-%d", e->start_line, e->end_line); - } + printe("warning encountered at "); + const char* fns = !e->file.empty() ? e->file.c_str() : nullptr; + const char* srcs = !e->source.empty() ? e->source.c_str() : nullptr; + const char* langs = !e->lang.empty() ? e->lang.c_str() : nullptr; + outputExceptionLocation(fns, e->start_line, e->end_line, srcs, e->offset, langs, + e->type == CT_USER ? "user" : "builtin"); printe("\n"); QoreStringNode* err = e->err.get(); @@ -640,8 +565,9 @@ void ExceptionSink::defaultWarningHandler(QoreException *e) { printe("%s: %s\n", err->c_str(), desc->c_str()); e = e->next; - if (e) + if (e) { printe("next warning:\n"); + } } } diff --git a/lib/QoreException.cpp b/lib/QoreException.cpp index 8edc708c7e..c029c29fb5 100644 --- a/lib/QoreException.cpp +++ b/lib/QoreException.cpp @@ -116,6 +116,7 @@ QoreHashNode* QoreException::makeExceptionObject() { ph->setKeyValueIntern("endline", end_line); ph->setKeyValueIntern("source", new QoreStringNode(source)); ph->setKeyValueIntern("offset", offset); + ph->setKeyValueIntern("lang", new QoreStringNode(lang)); ph->setKeyValueIntern("callstack", callStack->refSelf()); if (err) { ph->setKeyValueIntern("err", err.refSelf()); diff --git a/lib/TypedHashDecl.cpp b/lib/TypedHashDecl.cpp index f8bc3911d3..2806cf78b1 100644 --- a/lib/TypedHashDecl.cpp +++ b/lib/TypedHashDecl.cpp @@ -264,8 +264,9 @@ int typed_hash_decl_private::initHash(QoreHashNode* h, const QoreHashNode* init, int typed_hash_decl_private::initHashIntern(QoreHashNode* h, const QoreHashNode* init, ExceptionSink* xsink) const { #ifdef QORE_MANAGE_STACK - if (check_stack(xsink)) + if (xsink && check_stack(xsink)) { return -1; + } #endif for (auto& i : members.member_list) { @@ -417,4 +418,4 @@ const QoreExternalMemberBase& TypedHashDeclMemberIterator::getMember() const { const char* TypedHashDeclMemberIterator::getName() const { return priv->getName(); -} \ No newline at end of file +} diff --git a/lib/ql_lib.qpp b/lib/ql_lib.qpp index ed733e7213..8d5edb66a1 100644 --- a/lib/ql_lib.qpp +++ b/lib/ql_lib.qpp @@ -181,6 +181,9 @@ hashdecl ExceptionInfo { //! this key is populated with the value of the third expression of the @ref throw "throw statement" (if a list was thrown). For system exceptions, this is populated for some exceptions where additional information is provided. auto arg; + //! the language of the source code location; normally \c "Qore" + string lang; + //! chained exception info hash next; } diff --git a/lib/ql_misc.qpp b/lib/ql_misc.qpp index ab9550dd8a..969807c410 100644 --- a/lib/ql_misc.qpp +++ b/lib/ql_misc.qpp @@ -2628,8 +2628,7 @@ set_return_value(auto val) [dom=PROCESS] { @code{.py} try { throw "oops", sprintf("arg %y is invalid", arg); -} -catch (hash ex) { +} catch (hash ex) { log("%s: %s: %s", get_ex_pos(ex), ex.err, ex.desc); } @endcode @@ -2642,18 +2641,34 @@ catch (hash ex) { */ string get_ex_pos(hash ex) [flags=CONSTANT] { QoreValue n = ex->getKeyValue("file"); - QoreStringNode* str = new QoreStringNode(n.getType() == NT_STRING ? n.get()->c_str() : ""); + QoreStringNode* str = new QoreStringNode(n.getType() == NT_STRING + ? n.get()->c_str() + : ""); int ln = (int)ex->getKeyValue("line").getAsBigInt(); str->sprintf(":%d", ln); + bool openparen = false; + n = ex->getKeyValue("lang"); + if (n.getType() == NT_STRING) { + str->sprintf(" (%s", n.get()->c_str()); + openparen = true; + } n = ex->getKeyValue("source"); if (n.getType() == NT_STRING) { const QoreStringNode* sstr = n.get(); if (!sstr->empty()) { int offset = (int)ex->getKeyValue("offset").getAsBigInt(); - str->sprintf(" (source \"%s\":%d)", sstr->c_str(), ln + offset); + str->concat(" "); + if (!openparen) { + str->concat('('); + openparen = true; + } + str->sprintf("source \"%s\":%d", sstr->c_str(), ln + offset); } } + if (openparen) { + str->concat(')'); + } return str; } diff --git a/qlib/Util.qm b/qlib/Util.qm index 7357eb2f02..52f37f62b8 100644 --- a/qlib/Util.qm +++ b/qlib/Util.qm @@ -93,6 +93,8 @@ module Util { - updated @ref Util::parse_to_qore_value() "parse_to_qore_value()" to support single-element lists and hashes with curly brackets including empty hashes (issue 3138) + - updated @ref Util::get_exception_string() "get_exception_string()" to show the \c lang value + (issue 3182) @subsection util_1_3 Util 1.3 - updated for complex types @@ -886,7 +888,7 @@ string pass = get_random_string(); @return a multi-line string from the exception hash argument suitable for logging or output on the console */ - public string sub get_exception_string(hash ex) { + public string sub get_exception_string(hash ex) { string str; while (True) { @@ -895,13 +897,13 @@ string pass = get_random_string(); ex.arg ? sprintf(" (arg: %y)", ex.arg) : ""); if (ex.callstack) { - foreach hash l in (ex.callstack) { + foreach hash l in (ex.callstack) { if (l.type == "new-thread") { str += sprintf("\n *** thread started by background operator ***"); } else if (l.type == "rethrow") { str += sprintf("\n *** RETHROW at %s", get_ex_pos(l)); } else { - str += sprintf("\n %s() called at %s (%s %s code)", l.function, get_ex_pos(l), l.lang, l.type); + str += sprintf("\n %s() called at %s (%s code)", l.function, get_ex_pos(l), l.type); } } } From 5172049d3b805b5e32d61e04483647f058d74438 Mon Sep 17 00:00:00 2001 From: David Nichols Date: Fri, 14 Dec 2018 09:14:14 +0100 Subject: [PATCH 12/17] refs #3169 fixed test --- examples/test/qore/classes/Program/program-vars.qtest | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/test/qore/classes/Program/program-vars.qtest b/examples/test/qore/classes/Program/program-vars.qtest index 219d2e2529..c2eb9fb85a 100755 --- a/examples/test/qore/classes/Program/program-vars.qtest +++ b/examples/test/qore/classes/Program/program-vars.qtest @@ -58,6 +58,7 @@ class ProgramVarsTest inherits QUnit::Test { "endline": 1, "source": "", "offset": 0, + "lang": "Qore", "err": 1, )), ), From c0af588c9c6526c57574f348363ec35c8cc4ed97 Mon Sep 17 00:00:00 2001 From: David Nichols Date: Fri, 14 Dec 2018 11:09:11 +0100 Subject: [PATCH 13/17] refs #3169 more API updates --- include/qore/ExceptionSink.h | 45 +++++++++++++++++++++++++----------- lib/ExceptionSink.cpp | 7 ++++++ lib/QoreException.cpp | 16 ++++++++----- lib/ql_misc.qpp | 1 - qlib/Util.qm | 14 ++++++++--- 5 files changed, 60 insertions(+), 23 deletions(-) diff --git a/include/qore/ExceptionSink.h b/include/qore/ExceptionSink.h index 842bbf2211..cdd49610e6 100644 --- a/include/qore/ExceptionSink.h +++ b/include/qore/ExceptionSink.h @@ -158,6 +158,19 @@ class ExceptionSink { */ DLLEXPORT AbstractQoreNode* raiseExceptionArg(const QoreProgramLocation& loc, const char* err, QoreValue arg, QoreStringNode* desc, const QoreCallStack& stack); + //! appends a Qore-language exception to the list, and sets the 'arg' member (this object takes over the reference counts of 'arg' and 'desc') + /** The AbstractQoreNode pointer returned is always 0; used to simplify error handling code. + @param loc the source location for the exception + @param err the exception code string + @param arg the 'arg' member of the Qore-language exception object; will be dereferenced when the QoreException object is destroyed + @param desc the description string for the exception; the ExceptionSink object takes ownership of the reference count + + @return always returns nullptr + + @since %Qore 0.9 + */ + DLLEXPORT AbstractQoreNode* raiseExceptionArg(const QoreProgramLocation& loc, const char* err, QoreValue arg, QoreStringNode* desc); + //! appends a Qore-language exception to the list; takes owenership of the "desc" argument reference /** The AbstractQoreNode pointer returned is always 0; used to simplify error handling code. @param err the exception code string @@ -260,28 +273,32 @@ struct QoreSourceLocation { std::string source; //!< optional additional source file unsigned offset = 0; //!< offset in source file (only used if source is not empty) std::string code; //!< the function or method call name; method calls in format class::name + std::string lang; //!< the source language - DLLLOCAL QoreSourceLocation(const char* n_label, int start, int end, const char* n_code) : - label(n_label), start_line(start), end_line(end), code(n_code) { + DLLLOCAL QoreSourceLocation(const char* label, int start, int end, const char* code, const char* lang = "Qore") : + label(label), start_line(start), end_line(end), code(code), lang(lang) { } - DLLLOCAL QoreSourceLocation(const char* n_label, int start, int end, const char* n_source, unsigned n_offset, const char* n_code) : - label(n_label), start_line(start), end_line(end), source(n_source), offset(n_offset), code(n_code) { + DLLLOCAL QoreSourceLocation(const char* label, int start, int end, const char* source, unsigned offset, + const char* code, const char* lang = "Qore") : + label(label), start_line(start), end_line(end), source(source), offset(offset), code(code), lang(lang) { } }; //! call stack element; strings must be in the default encoding for the Qore process /** @since %Qore 0.8.13 */ -struct QoreCallStackElement : QoreSourceLocation { +struct QoreCallStackElement : public QoreSourceLocation { qore_call_t type; //!< the call stack element type - DLLLOCAL QoreCallStackElement(qore_call_t n_type, const char* n_label, int start, int end, const char* n_code) : - QoreSourceLocation(n_label, start, end, n_code), type(n_type) { + DLLLOCAL QoreCallStackElement(qore_call_t type, const char* label, int start, int end, const char* code, + const char* lang = "Qore") : + QoreSourceLocation(label, start, end, code, lang), type(type) { } - DLLLOCAL QoreCallStackElement(qore_call_t n_type, const char* n_label, int start, int end, const char* n_source, unsigned n_offset, const char* n_code) : - QoreSourceLocation(n_label, start, end, n_source, n_offset, n_code), type(n_type) { + DLLLOCAL QoreCallStackElement(qore_call_t type, const char* label, int start, int end, const char* source, + unsigned offset, const char* code, const char* lang = "Qore") : + QoreSourceLocation(label, start, end, source, offset, code, lang), type(type) { } }; @@ -291,12 +308,14 @@ typedef std::vector callstack_vec_t; /** @since %Qore 0.8.13 */ struct QoreCallStack : public callstack_vec_t { - DLLLOCAL void add(qore_call_t n_type, const char* n_label, int start, int end, const char* n_code) { - push_back(QoreCallStackElement(n_type, n_label, start, end, n_code)); + DLLLOCAL void add(qore_call_t type, const char* label, int start, int end, const char* code, + const char* lang = "Qore") { + push_back(QoreCallStackElement(type, label, start, end, code, lang)); } - DLLLOCAL void add(qore_call_t n_type, const char* n_label, int start, int end, const char* n_source, unsigned n_offset, const char* n_code) { - push_back(QoreCallStackElement(n_type, n_label, start, end, n_source, n_offset, n_code)); + DLLLOCAL void add(qore_call_t type, const char* label, int start, int end, const char* source, + unsigned offset, const char* code, const char* lang = "Qore") { + push_back(QoreCallStackElement(type, label, start, end, source, offset, code, lang)); } }; diff --git a/lib/ExceptionSink.cpp b/lib/ExceptionSink.cpp index 6bddfb5fe9..91a5e05e27 100644 --- a/lib/ExceptionSink.cpp +++ b/lib/ExceptionSink.cpp @@ -225,6 +225,13 @@ AbstractQoreNode* ExceptionSink::raiseExceptionArg(const char* err, QoreValue ar return nullptr; } +AbstractQoreNode* ExceptionSink::raiseExceptionArg(const QoreProgramLocation& loc, const char* err, QoreValue arg, + QoreStringNode *desc) { + printd(5, "ExceptionSink::raiseExceptionArg(loc, %s, %s, %p)\n", err, desc->getBuffer(), &stack); + priv->insert(new QoreException(loc, err, desc, arg)); + return nullptr; +} + AbstractQoreNode* ExceptionSink::raiseExceptionArg(const QoreProgramLocation& loc, const char* err, QoreValue arg, QoreStringNode *desc, const QoreCallStack& stack) { printd(5, "ExceptionSink::raiseExceptionArg(loc, %s, %s, %p)\n", err, desc->getBuffer(), &stack); diff --git a/lib/QoreException.cpp b/lib/QoreException.cpp index c029c29fb5..767548bfab 100644 --- a/lib/QoreException.cpp +++ b/lib/QoreException.cpp @@ -145,7 +145,7 @@ QoreHashNode *QoreException::makeExceptionObjectAndDelete(ExceptionSink *xsink) void QoreException::addStackInfo(QoreHashNode* n) { //printd(5, "QoreException::addStackInfo() this: %p callStack: %p (r: %d) n: %p (nr: %d)\n", this, callStack, callStack->reference_count(), n, n->reference_count()); - callStack->push(n, nullptr); + callStack->insert(n, nullptr); } const char* QoreException::getType(qore_call_t type) { @@ -167,21 +167,25 @@ const char* QoreException::getType(qore_call_t type) { // static function QoreHashNode* QoreException::getStackHash(const QoreCallStackElement& cse) { - QoreHashNode* h = new QoreHashNode; + ReferenceHolder h(new QoreHashNode(hashdeclCallStackInfo, nullptr), nullptr); - qore_hash_private* ph = qore_hash_private::get(*h); + qore_hash_private* ph = qore_hash_private::get(**h); assert(!cse.code.empty()); ph->setKeyValueIntern("function", new QoreStringNode(cse.code)); ph->setKeyValueIntern("line", cse.start_line); ph->setKeyValueIntern("endline", cse.end_line); - ph->setKeyValueIntern("file", !cse.label.empty() ? new QoreStringNode(cse.label) : QoreValue()); - ph->setKeyValueIntern("source", !cse.source.empty() ? new QoreStringNode(cse.source) : QoreValue()); + ph->setKeyValueIntern("file", new QoreStringNode(cse.label)); + // do not set "source" to NOTHING, as it must have a value according to the CallStackInfo hashdecl + if (!cse.source.empty()) { + ph->setKeyValueIntern("source", new QoreStringNode(cse.source)); + } ph->setKeyValueIntern("offset", cse.offset); + ph->setKeyValueIntern("lang", new QoreStringNode(cse.lang)); ph->setKeyValueIntern("typecode", cse.type); ph->setKeyValueIntern("type", new QoreStringNode(getType(cse.type))); - return h; + return h.release(); } DLLLOCAL ParseExceptionSink::~ParseExceptionSink() { diff --git a/lib/ql_misc.qpp b/lib/ql_misc.qpp index 969807c410..5a9f3d65d4 100644 --- a/lib/ql_misc.qpp +++ b/lib/ql_misc.qpp @@ -2665,7 +2665,6 @@ string get_ex_pos(hash ex) [flags=CONSTANT] { str->sprintf("source \"%s\":%d", sstr->c_str(), ln + offset); } } - if (openparen) { str->concat(')'); } diff --git a/qlib/Util.qm b/qlib/Util.qm index 52f37f62b8..b1b30eded6 100644 --- a/qlib/Util.qm +++ b/qlib/Util.qm @@ -900,10 +900,18 @@ string pass = get_random_string(); foreach hash l in (ex.callstack) { if (l.type == "new-thread") { str += sprintf("\n *** thread started by background operator ***"); - } else if (l.type == "rethrow") { - str += sprintf("\n *** RETHROW at %s", get_ex_pos(l)); } else { - str += sprintf("\n %s() called at %s (%s code)", l.function, get_ex_pos(l), l.type); + string pos = get_ex_pos(l); + if (l.type == "rethrow") { + str += sprintf("\n *** RETHROW at %s", pos); + } else { + if (pos =~ /\)$/) { + splice pos, -1, 1, sprintf(" %s code)", l.type); + } else { + pos += sprintf(" (%s code)", l.type); + } + str += sprintf("\n %s() called at %s", l.function, pos); + } } } } From 5f2c10b083209840388a36f88752c16a0160e350 Mon Sep 17 00:00:00 2001 From: David Nichols Date: Fri, 14 Dec 2018 11:11:03 +0100 Subject: [PATCH 14/17] refs #3169 fixed dbg stmt --- lib/ExceptionSink.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ExceptionSink.cpp b/lib/ExceptionSink.cpp index 91a5e05e27..966c058d30 100644 --- a/lib/ExceptionSink.cpp +++ b/lib/ExceptionSink.cpp @@ -227,7 +227,7 @@ AbstractQoreNode* ExceptionSink::raiseExceptionArg(const char* err, QoreValue ar AbstractQoreNode* ExceptionSink::raiseExceptionArg(const QoreProgramLocation& loc, const char* err, QoreValue arg, QoreStringNode *desc) { - printd(5, "ExceptionSink::raiseExceptionArg(loc, %s, %s, %p)\n", err, desc->getBuffer(), &stack); + printd(5, "ExceptionSink::raiseExceptionArg(loc, %s, %s)\n", err, desc->getBuffer()); priv->insert(new QoreException(loc, err, desc, arg)); return nullptr; } From a74d9ac5f77174b3f5829ea4cb9ecc214770559e Mon Sep 17 00:00:00 2001 From: David Nichols Date: Fri, 14 Dec 2018 13:41:10 +0100 Subject: [PATCH 15/17] refs #3169 show builtin code location properly in call & exception stacks --- include/qore/intern/Function.h | 2 ++ include/qore/intern/QoreException.h | 2 ++ include/qore/intern/QoreLibIntern.h | 14 ++++++------ include/qore/intern/qore_thread_intern.h | 3 +++ lib/Function.cpp | 14 +++++++++--- lib/QoreLib.cpp | 3 ++- lib/thread.cpp | 29 ++++++++++++++++++++++++ 7 files changed, 56 insertions(+), 11 deletions(-) diff --git a/include/qore/intern/Function.h b/include/qore/intern/Function.h index ff69c9413f..f6dae93281 100644 --- a/include/qore/intern/Function.h +++ b/include/qore/intern/Function.h @@ -315,6 +315,7 @@ class CodeEvaluationHelper : public QoreStackLocation { //! returns the source location of the element DLLLOCAL virtual const QoreProgramLocation& getLocation() const { + // return loc_builtin for Qore builtin calls return *loc; } @@ -350,6 +351,7 @@ class CodeEvaluationHelper : public QoreStackLocation { q_rt_flags_t rtflags = 0; // runtime flags QoreString callName; const QoreStackLocation* stack_loc = nullptr; + const QoreProgramLocation* old_runtime_loc = nullptr; bool restore_stack = false; DLLLOCAL void init(const QoreFunction* func, const AbstractQoreFunctionVariant*& variant, bool is_copy, diff --git a/include/qore/intern/QoreException.h b/include/qore/intern/QoreException.h index dfd620cae6..2357bc4dac 100644 --- a/include/qore/intern/QoreException.h +++ b/include/qore/intern/QoreException.h @@ -69,6 +69,8 @@ struct QoreExceptionLocation : QoreProgramLineLocation { file(old.file), source(old.source), lang(old.lang), offset(old.offset) { } + DLLLOCAL QoreExceptionLocation(QoreExceptionLocation&& old) = default; + DLLLOCAL void set(const QoreProgramLocation& loc) { start_line = loc.start_line; end_line = loc.end_line; diff --git a/include/qore/intern/QoreLibIntern.h b/include/qore/intern/QoreLibIntern.h index 72d97dcc8e..c4bab1be7f 100644 --- a/include/qore/intern/QoreLibIntern.h +++ b/include/qore/intern/QoreLibIntern.h @@ -166,7 +166,8 @@ struct ParseWarnOptions { }; struct QoreProgramLineLocation { - int16_t start_line, end_line; + int16_t start_line = -1, + end_line = -1; // if sline is 0 and eline is > 0 then set sline to 1 DLLLOCAL QoreProgramLineLocation(int sline, int eline) : start_line(sline ? sline : (eline ? 1 : 0)), end_line(eline) { @@ -174,7 +175,7 @@ struct QoreProgramLineLocation { assert(eline <= 0xffff); } - DLLLOCAL QoreProgramLineLocation() : start_line(-1), end_line(-1) { + DLLLOCAL QoreProgramLineLocation() { } DLLLOCAL QoreProgramLineLocation(const QoreProgramLineLocation& old) = default; @@ -194,6 +195,10 @@ struct QoreProgramLocation : public QoreProgramLineLocation { assert(offset <= 0xffff); } + DLLLOCAL explicit QoreProgramLocation(const char* f, int sline = 0, int eline = 0) : + QoreProgramLineLocation(sline, eline), file(f) { + } + // sets file position info from thread-local parse information DLLLOCAL QoreProgramLocation(int sline, int eline); @@ -275,11 +280,6 @@ struct QoreProgramLocation : public QoreProgramLineLocation { public: int16_t offset = 0; - -protected: - DLLLOCAL explicit QoreProgramLocation(const char* f, int sline = 0, int eline = 0) : - QoreProgramLineLocation(sline, eline), file(f) { - } }; DLLLOCAL extern const QoreProgramLocation loc_builtin; diff --git a/include/qore/intern/qore_thread_intern.h b/include/qore/intern/qore_thread_intern.h index 591dbea0ba..cffc1c4df1 100644 --- a/include/qore/intern/qore_thread_intern.h +++ b/include/qore/intern/qore_thread_intern.h @@ -246,7 +246,10 @@ DLLLOCAL void update_context_stack(Context* cstack); DLLLOCAL const QoreStackLocation* get_runtime_stack_location(); DLLLOCAL const QoreStackLocation* update_get_runtime_stack_location(QoreStackLocation* stack_loc, const AbstractStatement*& current_stmt, QoreProgram*& current_pgm); +DLLLOCAL const QoreStackLocation* update_get_runtime_stack_builtin_location(QoreStackLocation* stack_loc, + const AbstractStatement*& current_stmt, QoreProgram*& current_pgm, const QoreProgramLocation*& old_runtime_loc); DLLLOCAL void update_runtime_stack_location(const QoreStackLocation* stack_loc); +DLLLOCAL void update_runtime_stack_location(const QoreStackLocation* stack_loc, const QoreProgramLocation* runtime_loc); DLLLOCAL const QoreProgramLocation* get_runtime_location(); DLLLOCAL void update_get_runtime_statement_location(const AbstractStatement* stmt, diff --git a/lib/Function.cpp b/lib/Function.cpp index 5806396035..194768d506 100644 --- a/lib/Function.cpp +++ b/lib/Function.cpp @@ -180,7 +180,11 @@ CodeEvaluationHelper::CodeEvaluationHelper(ExceptionSink* n_xsink, const QoreFun CodeEvaluationHelper::~CodeEvaluationHelper() { if (restore_stack) { - update_runtime_stack_location(stack_loc); + if (ct == CT_BUILTIN) { + update_runtime_stack_location(stack_loc, old_runtime_loc); + } else { + update_runtime_stack_location(stack_loc); + } } if (returnTypeInfo != (const QoreTypeInfo*)-1) { saveReturnTypeInfo(returnTypeInfo); @@ -232,8 +236,12 @@ void CodeEvaluationHelper::init(const QoreFunction* func, const AbstractQoreFunc setCallType(variant->getCallType()); setReturnTypeInfo(variant->getReturnTypeInfo()); - // add call to call stack - stack_loc = update_get_runtime_stack_location(this, stmt, pgm); + // add call to call stack; push builtin location on the stack if executing builtin c++ code + if (ct == CT_BUILTIN) { + stack_loc = update_get_runtime_stack_builtin_location(this, stmt, pgm, old_runtime_loc); + } else { + stack_loc = update_get_runtime_stack_location(this, stmt, pgm); + } restore_stack = true; } diff --git a/lib/QoreLib.cpp b/lib/QoreLib.cpp index c0cb60d337..57fcf88e44 100644 --- a/lib/QoreLib.cpp +++ b/lib/QoreLib.cpp @@ -97,7 +97,8 @@ int qore_min_mod_api_minor = QORE_MODULE_COMPAT_API_MINOR; DLLLOCAL QoreListNode* ARGV = nullptr; DLLLOCAL QoreListNode* QORE_ARGV = nullptr; -const QoreProgramLocation loc_builtin; +// default location for Qore builtin C++ code +const QoreProgramLocation loc_builtin("", -1, -1); QoreString random_salt; diff --git a/lib/thread.cpp b/lib/thread.cpp index 4ff3f0aab5..73738851a6 100644 --- a/lib/thread.cpp +++ b/lib/thread.cpp @@ -1243,6 +1243,25 @@ const QoreStackLocation* update_get_runtime_stack_location(QoreStackLocation* st return rv; } + const QoreStackLocation* update_get_runtime_stack_builtin_location(QoreStackLocation* stack_loc, + const AbstractStatement*& current_stmt, QoreProgram*& current_pgm, const QoreProgramLocation*& old_runtime_loc) { + ThreadData* td = thread_data.get(); + + current_pgm = td->current_pgm; + current_stmt = td->runtime_statement; + + const QoreStackLocation* rv = td->current_stack_location; + + // get read access to the stack lock to write to the local thread stack location + // locking is necessary due to the fact that thread stacks can be read from other threads + QoreAutoRWReadLocker l(thread_list.stack_lck); + td->current_stack_location = stack_loc; + stack_loc->setNext(rv); + old_runtime_loc = td->runtime_loc; + td->runtime_loc = &loc_builtin; + return rv; +} + // called when restoring the previous location void update_runtime_stack_location(const QoreStackLocation* stack_loc) { ThreadData* td = thread_data.get(); @@ -1253,6 +1272,16 @@ void update_runtime_stack_location(const QoreStackLocation* stack_loc) { td->current_stack_location = stack_loc; } +void update_runtime_stack_location(const QoreStackLocation* stack_loc, const QoreProgramLocation* runtime_loc) { + ThreadData* td = thread_data.get(); + + // get read access to the stack lock to write to the local thread stack location + // locking is necessary due to the fact that thread stacks can be read from other threads + QoreAutoRWReadLocker l(thread_list.stack_lck); + td->current_stack_location = stack_loc; + td->runtime_loc = runtime_loc; +} + const AbstractStatement* get_runtime_statement() { return thread_data.get()->runtime_statement; } From bed7b1109363cee8e8df8dfd25f7805ab829c344 Mon Sep 17 00:00:00 2001 From: David Nichols Date: Fri, 14 Dec 2018 16:01:42 +0100 Subject: [PATCH 16/17] reffs #3169 updated from review comments --- include/qore/ExceptionSink.h | 22 +++++++++++----------- include/qore/intern/Function.h | 6 +++--- include/qore/intern/QoreThreadList.h | 2 +- include/qore/intern/qore_thread_intern.h | 6 +++--- lib/Function.cpp | 5 +++-- lib/thread.cpp | 2 +- 6 files changed, 22 insertions(+), 21 deletions(-) diff --git a/include/qore/ExceptionSink.h b/include/qore/ExceptionSink.h index cdd49610e6..6602127218 100644 --- a/include/qore/ExceptionSink.h +++ b/include/qore/ExceptionSink.h @@ -360,18 +360,18 @@ class QoreExternalProgramLocationWrapper { } //! returns the file name - DLLEXPORT const char* getFile() const { - return file_str.c_str(); + DLLEXPORT const std::string& getFile() const { + return file_str; } //! returns the source - DLLEXPORT const char* getSource() const { - return source_str.c_str(); + DLLEXPORT const std::string& getSource() const { + return source_str; } //! returns the language - DLLEXPORT const char* getLanguage() const { - return lang_str.c_str(); + DLLEXPORT const std::string& getLanguage() const { + return lang_str; } //! returns the start line @@ -407,11 +407,11 @@ class QoreStackLocation { //! virtual destructor DLLLOCAL virtual ~QoreStackLocation() = default; - //! no assignment operator - DLLLOCAL QoreStackLocation& operator=(const QoreStackLocation&) = delete; + //! default assignment operator + DLLLOCAL QoreStackLocation& operator=(const QoreStackLocation&) = default; - //! no move assignment operator - DLLLOCAL QoreStackLocation& operator=(QoreStackLocation&&) = delete; + //! default move assignment operator + DLLLOCAL QoreStackLocation& operator=(QoreStackLocation&&) = default; //! called when pushed on the stack to set the next location DLLLOCAL void setNext(const QoreStackLocation* next) { @@ -430,7 +430,7 @@ class QoreStackLocation { DLLLOCAL virtual const AbstractStatement* getStatement() const = 0; //! returns the name of the function or method call - DLLLOCAL virtual const char* getCallName() const = 0; + DLLLOCAL virtual const std::string& getCallName() const = 0; //! returns the call type DLLLOCAL virtual qore_call_t getCallType() const = 0; diff --git a/include/qore/intern/Function.h b/include/qore/intern/Function.h index f6dae93281..0490217ffc 100644 --- a/include/qore/intern/Function.h +++ b/include/qore/intern/Function.h @@ -320,8 +320,8 @@ class CodeEvaluationHelper : public QoreStackLocation { } //! returns the name of the function or method call - DLLLOCAL virtual const char* getCallName() const { - return callName.c_str(); + DLLLOCAL virtual const std::string& getCallName() const { + return callName; } DLLLOCAL virtual qore_call_t getCallType() const { @@ -349,7 +349,7 @@ class CodeEvaluationHelper : public QoreStackLocation { QoreProgram* pgm = nullptr; // program used when evaluated (to find stacks for references) const AbstractStatement* stmt = nullptr; // the current statement for the call stack entry q_rt_flags_t rtflags = 0; // runtime flags - QoreString callName; + std::string callName; const QoreStackLocation* stack_loc = nullptr; const QoreProgramLocation* old_runtime_loc = nullptr; bool restore_stack = false; diff --git a/include/qore/intern/QoreThreadList.h b/include/qore/intern/QoreThreadList.h index 15cc17bbd3..7d4e0be8e8 100644 --- a/include/qore/intern/QoreThreadList.h +++ b/include/qore/intern/QoreThreadList.h @@ -192,7 +192,7 @@ friend class tid_node; DLLLOCAL static QoreHashNode* getCallStackHash(const QoreStackLocation& loc); - DLLLOCAL static QoreHashNode* getCallStackHash(qore_call_t type, const char *code, + DLLLOCAL static QoreHashNode* getCallStackHash(qore_call_t type, const std::string& code, const QoreProgramLocation& loc); DLLLOCAL QoreListNode* getCallStack(const QoreStackLocation* stack_location) const; diff --git a/include/qore/intern/qore_thread_intern.h b/include/qore/intern/qore_thread_intern.h index cffc1c4df1..2cd477b6fb 100644 --- a/include/qore/intern/qore_thread_intern.h +++ b/include/qore/intern/qore_thread_intern.h @@ -447,7 +447,7 @@ class QoreInternalCallStackLocationHelperBase : public QoreStackLocation, public class QoreInternalCallStackLocationHelper : public QoreInternalCallStackLocationHelperBase { public: - DLLLOCAL QoreInternalCallStackLocationHelper(const QoreProgramLocation& loc, const char* call, + DLLLOCAL QoreInternalCallStackLocationHelper(const QoreProgramLocation& loc, const std::string call, qore_call_t call_type) : loc(loc), call(call), call_type(call_type) { } @@ -457,7 +457,7 @@ class QoreInternalCallStackLocationHelper : public QoreInternalCallStackLocation } //! returns the name of the function or method call - DLLLOCAL virtual const char* getCallName() const { + DLLLOCAL virtual const std::string& getCallName() const { return call; } @@ -467,7 +467,7 @@ class QoreInternalCallStackLocationHelper : public QoreInternalCallStackLocation protected: const QoreProgramLocation& loc; - const char* call; + const std::string call; qore_call_t call_type; }; diff --git a/lib/Function.cpp b/lib/Function.cpp index 194768d506..8194526f10 100644 --- a/lib/Function.cpp +++ b/lib/Function.cpp @@ -193,9 +193,10 @@ CodeEvaluationHelper::~CodeEvaluationHelper() { void CodeEvaluationHelper::setCallName(const QoreFunction* func) { if (qc) { - callName.sprintf("%s::", qc->name.c_str()); + callName = qc->name.c_str(); + callName += "::"; } - callName.concat(func->getName()); + callName += func->getName(); } void CodeEvaluationHelper::init(const QoreFunction* func, const AbstractQoreFunctionVariant*& variant, bool is_copy, diff --git a/lib/thread.cpp b/lib/thread.cpp index 73738851a6..3e93fe0dfc 100644 --- a/lib/thread.cpp +++ b/lib/thread.cpp @@ -2678,7 +2678,7 @@ QoreListNode* QoreThreadList::getCallStack(const QoreStackLocation* stack_locati } // static -QoreHashNode* QoreThreadList::getCallStackHash(qore_call_t call_type, const char* code, const QoreProgramLocation& loc) { +QoreHashNode* QoreThreadList::getCallStackHash(qore_call_t call_type, const std::string& code, const QoreProgramLocation& loc) { ReferenceHolder h(new QoreHashNode(hashdeclCallStackInfo, nullptr), nullptr); qore_hash_private* ph = qore_hash_private::get(**h); From 5cd8d36e350dd86bd3fb721129784940f8989d0a Mon Sep 17 00:00:00 2001 From: David Nichols Date: Fri, 14 Dec 2018 16:57:35 +0100 Subject: [PATCH 17/17] reffs #3169 updated from review feedback --- include/qore/ExceptionSink.h | 7 +++++++ include/qore/intern/qore_thread_intern.h | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/qore/ExceptionSink.h b/include/qore/ExceptionSink.h index 6602127218..466556dadf 100644 --- a/include/qore/ExceptionSink.h +++ b/include/qore/ExceptionSink.h @@ -414,6 +414,13 @@ class QoreStackLocation { DLLLOCAL QoreStackLocation& operator=(QoreStackLocation&&) = default; //! called when pushed on the stack to set the next location + /** @param next a pointer to the existing next stack element that must exist and must stay on the stack while + this object exists or nullptr in case this is the first element on the stack + + @note it is an error and will cause a segfault if the next object is destroyed before this object is + destroyed. Under no circumstances, and under the very real threat of at least 700 lashings with a wet noodle, + should the next object be deleted before this object. + */ DLLLOCAL void setNext(const QoreStackLocation* next) { stack_next = next; } diff --git a/include/qore/intern/qore_thread_intern.h b/include/qore/intern/qore_thread_intern.h index 2cd477b6fb..5052369312 100644 --- a/include/qore/intern/qore_thread_intern.h +++ b/include/qore/intern/qore_thread_intern.h @@ -447,7 +447,7 @@ class QoreInternalCallStackLocationHelperBase : public QoreStackLocation, public class QoreInternalCallStackLocationHelper : public QoreInternalCallStackLocationHelperBase { public: - DLLLOCAL QoreInternalCallStackLocationHelper(const QoreProgramLocation& loc, const std::string call, + DLLLOCAL QoreInternalCallStackLocationHelper(const QoreProgramLocation& loc, const std::string& call, qore_call_t call_type) : loc(loc), call(call), call_type(call_type) { }