From 700b3b1036db937068d807fc26842d0d5f7d60a0 Mon Sep 17 00:00:00 2001 From: oana Date: Wed, 1 Apr 2020 11:47:55 +0000 Subject: [PATCH] Add Engine methods for retrieving current line of source code being executed, current source file name and the concatenation of both. Add Message.trace() method for tracking of message handling in JS code. git-svn-id: http://voip.null.ro/svn/yate@6397 acf43c95-373e-0410-b603-e72c3f656dc1 --- libs/yscript/javascript.cpp | 65 ++++++++++++++--- libs/yscript/yatescript.h | 15 ++++ modules/javascript.cpp | 142 ++++++++++++++++++++++++++++++++++-- 3 files changed, 207 insertions(+), 15 deletions(-) diff --git a/libs/yscript/javascript.cpp b/libs/yscript/javascript.cpp index 823a5a0b..06515aa8 100644 --- a/libs/yscript/javascript.cpp +++ b/libs/yscript/javascript.cpp @@ -83,11 +83,11 @@ class JsCodeFile : public String { public: inline JsCodeFile(const String& file) - : String(file), m_fileTime(0) - { File::getFileTime(file,m_fileTime); } + : String(file), m_fileTime(0), m_shortName(file) + { File::getFileTime(file,m_fileTime); setShortName(); } inline JsCodeFile(const String& file, unsigned int fTime) - : String(file), m_fileTime(fTime) - { } + : String(file), m_fileTime(fTime), m_shortName(file) + { setShortName(); } inline unsigned int fileTime() const { return m_fileTime; } inline bool fileChanged() const @@ -98,8 +98,21 @@ public: File::getFileTime(c_str(),t); return t != m_fileTime; } + inline const String& shortName() const + { return m_shortName; } private: + inline void setShortName() + { + int pos = rfind('/'); +#ifdef _WINDOWS + int backPos = rfind('\\'); + pos = pos > backPos ? pos : backPos; +#endif + if (pos >= 0 && (pos < (int)length() - 1)) + m_shortName = substr(pos + 1); + } unsigned int m_fileTime; + String m_shortName; }; struct JsEntry @@ -191,9 +204,9 @@ public: { return (line >> 24) & 0xff; } inline unsigned int getFileCount() const { return m_included.length(); } - const String& getFileAt(unsigned int index) const; - inline const String& getFileName(unsigned int line) const - { return getFileAt(getFileNo(line)); } + const String& getFileAt(unsigned int index, bool wholePath = true) const; + inline const String& getFileName(unsigned int line, bool wholePath = true) const + { return getFileAt(getFileNo(line),wholePath); } bool scriptChanged() const; protected: inline void trace(bool allowed) @@ -381,6 +394,9 @@ public: void tracePost(const ExpOperation& oper); void traceCall(const ExpOperation& oper, const JsFunction& func); void traceReturn(); + const ExpOperation* getCurrentOpCode() const; + virtual unsigned int currentLineNo() const; + virtual const String& currentFileName(bool wholePath = false) const; protected: virtual Status resume(); void traceDump(); @@ -1048,12 +1064,12 @@ bool JsCode::link() return true; } -const String& JsCode::getFileAt(unsigned int index) const +const String& JsCode::getFileAt(unsigned int index, bool wholePath) const { if (!index) return s_noFile; - const GenObject* file = m_included[index - 1]; - return file ? file->toString() : s_noFile; + const JsCodeFile* file = static_cast(m_included[index - 1]); + return file ? (wholePath ? file->toString() : file->shortName()) : s_noFile; } void JsCode::formatLineNo(String& buf, unsigned int line) const @@ -3305,7 +3321,36 @@ void JsFuncStats::updateCall(const char* name, unsigned int caller, unsigned int l->insert(new JsCallStats(name,caller,called,instr,usec)); } +const ExpOperation* JsRunner::getCurrentOpCode() const +{ + const ExpOperation* o = 0; + if (m_opcode) + o = static_cast(m_opcode->get()); + if (!o) + o = static_cast(static_cast(code())->m_linked[m_index]); + if (!o) { + String str; + static_cast(code())->formatLineNo(str,m_lastLine); + Debug(DebugWarn,"Current operation unavailable in %s [%p]",str.c_str(),this); + return 0; + } + return o; +} +unsigned int JsRunner::currentLineNo() const +{ + const ExpOperation* o = getCurrentOpCode(); + return o ? JsCode::getLineNo(o->lineNumber()) : 0; +} + +const String& JsRunner::currentFileName(bool wholePath) const +{ + const ExpOperation* o = getCurrentOpCode(); + if (!(o && code())) + return YSTRING("???"); + return (static_cast(code()))->getFileName(o->lineNumber(),wholePath); +} + JsCodeStats::JsCodeStats(JsCode* code, const char* file) : Mutex(false,"JsCodeStats"), m_fileName(file) diff --git a/libs/yscript/yatescript.h b/libs/yscript/yatescript.h index 8f61d3c4..b18be854 100644 --- a/libs/yscript/yatescript.h +++ b/libs/yscript/yatescript.h @@ -1782,6 +1782,21 @@ public: */ virtual bool appendAsync(ScriptAsync* oper); + /** + * Retrieve current file line being executed + * @return The file line being evaluated + */ + virtual unsigned int currentLineNo() const + { return 0; } + + /** + * Retrieve the name of the source file from which code is being executed + * @parma wholePath Retrieve name including path + * @return The file name + */ + virtual const String& currentFileName(bool wholePath = false) const + { return String::empty(); } + /** * Try to assign a value to a single field in the script context * @param oper Field to assign to, contains the field name and new value diff --git a/modules/javascript.cpp b/modules/javascript.cpp index 1b5c3e66..fb1237b3 100644 --- a/modules/javascript.cpp +++ b/modules/javascript.cpp @@ -41,6 +41,21 @@ using namespace TelEngine; namespace { // anonymous +static inline void dumpTraceToMsg(Message* msg, ObjList* lst) +{ + if (!(msg && lst)) + return; + unsigned int count = msg->getIntValue(YSTRING("trace_msg_count"),0); + static String s_tracePref = "trace_msg_"; + for (ObjList* o = lst->skipNull(); o; o = o->skipNext()) { + String* s = static_cast(o->get()); + if (TelEngine::null(s)) + continue; + msg->setParam(s_tracePref + String(count++),*s); + } + msg->setParam(YSTRING("trace_msg_count"),String(count)); +} + class JsEngineWorker; class JsEngine; @@ -375,6 +390,9 @@ public: params().addParam(new ExpFunction("output")); params().addParam(new ExpFunction("debug")); params().addParam(new ExpFunction("alarm")); + params().addParam(new ExpFunction("lineNo")); + params().addParam(new ExpFunction("fileName")); + params().addParam(new ExpFunction("fileNo")); params().addParam(new ExpFunction("sleep")); params().addParam(new ExpFunction("usleep")); params().addParam(new ExpFunction("yield")); @@ -435,7 +453,8 @@ public: inline JsMessage(Mutex* mtx) : JsObject("Message",mtx,true), - m_message(0), m_dispatch(false), m_owned(false), m_trackPrio(true) + m_message(0), m_dispatch(false), m_owned(false), m_trackPrio(true), + m_traceLvl(DebugInfo), m_traceLst(0) { XDebug(&__plugin,DebugAll,"JsMessage::JsMessage() [%p]",this); params().addParam(new ExpFunction("enqueue")); @@ -452,12 +471,15 @@ public: params().addParam(new ExpFunction("getResult")); params().addParam(new ExpFunction("copyParams")); params().addParam(new ExpFunction("clearParam")); + params().addParam(new ExpFunction("trace")); } inline JsMessage(Message* message, Mutex* mtx, bool disp, bool owned = false) : JsObject(mtx,"[object Message]"), - m_message(message), m_dispatch(disp), m_owned(owned), m_trackPrio(true) + m_message(message), m_dispatch(disp), m_owned(owned), m_trackPrio(true), + m_traceLvl(DebugInfo), m_traceLst(0) { XDebug(&__plugin,DebugAll,"JsMessage::JsMessage(%p) [%p]",message,this); + setTrace(); } virtual ~JsMessage() { @@ -471,6 +493,7 @@ public: MessageHook* hook = static_cast(o->get()); Engine::uninstallHook(hook); } + TelEngine::destruct(m_traceLst); } virtual void* getObject(const String& name) const; virtual NamedList* nativeParams() const @@ -489,9 +512,15 @@ public: construct->params().addParam(new ExpFunction("trackName")); } inline void clearMsg() - { m_message = 0; m_owned = false; m_dispatch = false; } + { + dumpTraceToMsg(m_message,m_traceLst); + m_message = 0; + m_owned = false; + m_dispatch = false; + setTrace(); + } inline void setMsg(Message* message) - { m_message = message; m_owned = false; m_dispatch = false; } + { m_message = message; m_owned = false; m_dispatch = false; setTrace(); } static void initialize(ScriptContext* context); void runAsync(ObjList& stack, Message* msg, bool owned); protected: @@ -500,6 +529,13 @@ protected: void getRow(ObjList& stack, const ExpOperation* row, GenObject* context); void getResult(ObjList& stack, const ExpOperation& row, const ExpOperation& col, GenObject* context); bool installHook(ObjList& stack, const ExpOperation& oper, GenObject* context); + inline void setTrace() + { + m_traceId = m_message ? m_message->getValue(YSTRING("trace_id")) : ""; + m_traceLvl = m_message ? m_message->getIntValue(YSTRING("trace_lvl"),DebugInfo,DebugGoOn,DebugAll) : DebugInfo; + TelEngine::destruct(m_traceLst); + m_traceLst = m_message ? (m_message->getBoolValue(YSTRING("trace_to_msg"),false) ? new ObjList() : 0) : 0; + } ObjList m_handlers; ObjList m_hooks; String m_trackName; @@ -507,6 +543,9 @@ protected: bool m_dispatch; bool m_owned; bool m_trackPrio; + String m_traceId; + int m_traceLvl; + ObjList* m_traceLst; }; class JsHandler : public MessageHandler @@ -1287,6 +1326,32 @@ bool JsEngine::runNative(ObjList& stack, const ExpOperation& oper, GenObject* co Alarm(this,info,level,"%s",str.c_str()); } } + else if (oper.name() == YSTRING("lineNo")) { + if (oper.number()) + return false; + ScriptRun* runner = YOBJECT(ScriptRun,context); + if (!runner) + return false; + ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)runner->currentLineNo())); + } + else if (oper.name() == YSTRING("fileName") || oper.name() == YSTRING("fileNo")) { + if (oper.number() > 1) + return false; + bool wholePath = false; + if (oper.number() == 1) { + ExpOperation* op = popValue(stack,context); + if (!op) + return false; + wholePath = op->valBoolean(); + } + ScriptRun* runner = YOBJECT(ScriptRun,context); + if (!runner) + return false; + String fileName = runner->currentFileName(wholePath); + if (oper.name() == YSTRING("fileNo")) + fileName << ":" << runner->currentLineNo(); + ExpEvaluator::pushOne(stack,new ExpOperation(fileName)); + } else if (oper.name() == YSTRING("sleep")) { if (oper.number() != 1) return false; @@ -2280,7 +2345,7 @@ bool JsMessage::runNative(ObjList& stack, const ExpOperation& oper, GenObject* c switch (extractArgs(stack,oper,context,args)) { case 3: skip = static_cast(args[2])->valBoolean(skip); - // intentional + // intentional case 2: prefix = static_cast(args[1]); // intentional @@ -2365,6 +2430,73 @@ bool JsMessage::runNative(ObjList& stack, const ExpOperation& oper, GenObject* c return false; } } + else if (oper.name() == YSTRING("trace")) { + if (!m_message) + return true; + ObjList args; + unsigned int c = extractArgs(stack,oper,context,args); + if (c < 2) + return false; + ExpOperation* ret = static_cast(args[0]); + ExpOperation* op = static_cast(args[1]); + + int level = -1; + int limit = s_allowAbort ? DebugFail : DebugTest; + if (op->number() > 1 && op->isInteger()) { + level = (int)op->number(); + if (level > DebugAll) + level = DebugAll; + else if (level < limit) + level = limit; + } + + String str; + ScriptRun* runner = YOBJECT(ScriptRun,context); + if (m_traceId) { + if (!runner) + return false; + str = runner->currentFileName(); + str << ":" << runner->currentLineNo(); + if (ret->isBoolean()) + str << " - return:" << ret->valBoolean(); + } + + for (unsigned int i = 2; i < c; i++) { + ExpOperation* op = static_cast(args[i]); + if (!op) + continue; + else if (*op) { + if (str) + str << " "; + str << *op; + } + } + + DebugEnabler* dbg = &__plugin; + if (runner && runner->context()) { + JsEngine* engine = YOBJECT(JsEngine,runner->context()->params().getParam(YSTRING("Engine"))); + if (engine) + dbg = engine; + } + + if (m_traceId) { + if (level > m_traceLvl || level == -1) + level = m_traceLvl; + if (level < limit) + level = limit; + Debug(dbg,level,"Trace:%s %s",m_traceId.c_str(),str.c_str()); + if (m_traceLst) + m_traceLst->append(new String(str)); + } + else if (level > -1 && str) + Debug(dbg,level,"%s",str.c_str()); + + if (!JsParser::isUndefined(*ret)) + ExpEvaluator::pushOne(stack,JsParser::isNull(*ret) ? + JsParser::nullClone() : new ExpOperation(*ret)); + else + ExpEvaluator::pushOne(stack,new ExpWrapper(0,0)); + } else return JsObject::runNative(stack,oper,context); return true;