From 71206f078c6d7e3b262758986e4b96365bfc0dc8 Mon Sep 17 00:00:00 2001 From: oana Date: Wed, 9 Feb 2022 10:59:28 +0000 Subject: [PATCH] Add support for building and running multiple instances of the same script. git-svn-id: http://yate.null.ro/svn/yate/trunk@6542 acf43c95-373e-0410-b603-e72c3f656dc1 --- conf.d/javascript.conf.sample | 10 + libs/yscript/javascript.cpp | 18 +- libs/yscript/script.cpp | 7 +- libs/yscript/yatescript.h | 38 +++- modules/javascript.cpp | 378 ++++++++++++++++++++++++++++------ 5 files changed, 369 insertions(+), 82 deletions(-) diff --git a/conf.d/javascript.conf.sample b/conf.d/javascript.conf.sample index f2b078f1..ca0acbcf 100644 --- a/conf.d/javascript.conf.sample +++ b/conf.d/javascript.conf.sample @@ -44,6 +44,16 @@ ;keep_old_on_fail=no +[instances] +; Build multiple instances of specified scripts +; Each line has to be of the form +; name=number of instances +; The name must correspond with a [scripts] section entry +; Default value is one instance +; Examples: +; faxes=3 + + [scripts] ; Add one entry in this section for each script that is to be loaded on Yate startup ; Each line has to be on the form: diff --git a/libs/yscript/javascript.cpp b/libs/yscript/javascript.cpp index df850b18..5fc5990a 100644 --- a/libs/yscript/javascript.cpp +++ b/libs/yscript/javascript.cpp @@ -35,7 +35,7 @@ class JsContext : public JsObject, public ScriptMutex { YCLASS(JsContext,JsObject) public: - inline JsContext() + inline JsContext(unsigned int instIdx = 0, unsigned int maxInst = 1) : JsObject("Context",0), ScriptMutex(true,"JsContext"), m_trackObjs(0), m_trackObjsMtx(false,"JsObjTrack") { @@ -44,6 +44,7 @@ public: params().addParam(new ExpFunction("parseInt")); params().addParam(new ExpOperation(ExpOperation::nonInteger(),"NaN")); m_objTrack = !!m_trackObjs; + setInstance(instIdx,maxInst); DDebug(DebugAll,"JsContext::JsContext() [%p]",this); } virtual void destroyed(); @@ -3217,7 +3218,11 @@ void JsRunner::traceCheck(const char* title) traceStart(title); return; } - traceStart(title,ns->c_str()); + + String filename = ns->c_str(); + if (context()->instanceIndex()) + filename << "_" << context()->instanceIndex(); + traceStart(title,filename); if (m_stats) { m_stats->ref(); context()->params().setParam(new ExpWrapper(m_stats,s_tracingObj)); @@ -3844,18 +3849,19 @@ void JsParser::adjustPath(String& script, bool extraInc) const } // Create Javascript context -ScriptContext* JsParser::createContext() const +ScriptContext* JsParser::createContext(unsigned int instIdx, unsigned int maxInst) const { - return new JsContext; + return new JsContext(instIdx,maxInst); } -ScriptRun* JsParser::createRunner(ScriptCode* code, ScriptContext* context, const char* title) const +ScriptRun* JsParser::createRunner(ScriptCode* code, ScriptContext* context, const char* title, + unsigned int instIdx, unsigned int maxInst) const { if (!code) return 0; ScriptContext* ctxt = 0; if (!context) - context = ctxt = createContext(); + context = ctxt = createContext(instIdx,maxInst); ScriptRun* runner = new JsRunner(code,context,title); TelEngine::destruct(ctxt); return runner; diff --git a/libs/yscript/script.cpp b/libs/yscript/script.cpp index 5a064598..c5a5051d 100644 --- a/libs/yscript/script.cpp +++ b/libs/yscript/script.cpp @@ -77,18 +77,19 @@ void ScriptParser::setCode(ScriptCode* code) TelEngine::destruct(tmp); } -ScriptContext* ScriptParser::createContext() const +ScriptContext* ScriptParser::createContext(unsigned int instIdx, unsigned int maxInst) const { return new BasicContext; } -ScriptRun* ScriptParser::createRunner(ScriptCode* code, ScriptContext* context, const char* title) const +ScriptRun* ScriptParser::createRunner(ScriptCode* code, ScriptContext* context, const char* title, + unsigned int instIdx, unsigned int maxInst) const { if (!code) return 0; ScriptContext* ctxt = 0; if (!context) - context = ctxt = createContext(); + context = ctxt = createContext(instIdx,maxInst); ScriptRun* runner = new ScriptRun(code,context); TelEngine::destruct(ctxt); return runner; diff --git a/libs/yscript/yatescript.h b/libs/yscript/yatescript.h index bf84b95d..67843299 100644 --- a/libs/yscript/yatescript.h +++ b/libs/yscript/yatescript.h @@ -1603,7 +1603,7 @@ public: * @param name Name of the context */ inline explicit ScriptContext(const char* name = 0) - : m_params(name) + : m_params(name), m_instIdx(0), m_instCount(1) { } /** @@ -1776,8 +1776,22 @@ public: virtual ObjList* countAllocations() { return 0; } + virtual void setInstance(unsigned int idx, unsigned int count) + { + m_instIdx = idx; + m_instCount = count; + } + + virtual unsigned int instanceIndex() const + { return m_instIdx; } + + virtual unsigned int instanceCount() const + { return m_instCount; } + private: NamedList m_params; + unsigned int m_instIdx; // instance index + unsigned int m_instCount; // total number of instances }; /** @@ -2155,7 +2169,7 @@ public: * Create a context adequate for the parsed code * @return A new script context */ - virtual ScriptContext* createContext() const; + virtual ScriptContext* createContext(unsigned int instIdx = 0, unsigned int maxInst = 1) const; /** * Create a runner adequate for a block of parsed code @@ -2164,7 +2178,8 @@ public: * @param title An optional name for the runner * @return A new script runner, NULL if code is NULL */ - virtual ScriptRun* createRunner(ScriptCode* code, ScriptContext* context = 0, const char* title = 0) const; + virtual ScriptRun* createRunner(ScriptCode* code, ScriptContext* context = 0, const char* title = 0, + unsigned int instIdx = 0, unsigned int maxInst = 1) const; /** * Create a runner adequate for the parsed code @@ -2172,8 +2187,9 @@ public: * @param title An optional name for the runner * @return A new script runner, NULL if code is not yet parsed */ - inline ScriptRun* createRunner(ScriptContext* context = 0, const char* title = 0) const - { return createRunner(code(),context,title); } + inline ScriptRun* createRunner(ScriptContext* context = 0, const char* title = 0, + unsigned int instIdx = 0, unsigned int maxInst = 1) const + { return createRunner(code(),context,title,instIdx,maxInst); } /** * Check if a script has a certain function or method @@ -3148,7 +3164,7 @@ public: * Create a context adequate for Javascript code * @return A new Javascript context */ - virtual ScriptContext* createContext() const; + virtual ScriptContext* createContext(unsigned int instIdx = 0, unsigned int maxInst = 1) const; /** * Create a runner adequate for a block of parsed Javascript code @@ -3157,16 +3173,20 @@ public: * @param title An optional name for the runner * @return A new Javascript runner, NULL if code is NULL */ - virtual ScriptRun* createRunner(ScriptCode* code, ScriptContext* context = 0, const char* title = 0) const; + virtual ScriptRun* createRunner(ScriptCode* code, ScriptContext* context = 0, const char* title = 0, + unsigned int instIdx = 0, unsigned int maxInst = 1) const; /** * Create a runner adequate for the parsed Javascript code * @param context Javascript context, an empty one will be allocated if NULL * @param title An optional name for the runner + * @param instIdx Javascript context instance + * @param maxInst Number of context instances * @return A new Javascript runner, NULL if code is not yet parsed */ - inline ScriptRun* createRunner(ScriptContext* context = 0, const char* title = 0) const - { return createRunner(code(),context,title); } + inline ScriptRun* createRunner(ScriptContext* context = 0, const char* title = 0, + unsigned int instIdx = 0, unsigned int maxInst = 1) const + { return createRunner(code(),context,title,instIdx,maxInst); } /** * Check if a script has a certain function or method diff --git a/modules/javascript.cpp b/modules/javascript.cpp index cdea612d..7284be98 100644 --- a/modules/javascript.cpp +++ b/modules/javascript.cpp @@ -182,26 +182,65 @@ private: RefPointer m_message; }; +class JsGlobal; + +class JsGlobalInstance : public RefObject +{ +public: + JsGlobalInstance(JsGlobal* owner, unsigned int index); + ~JsGlobalInstance(); + unsigned int runMain(); + inline ScriptContext* context() + { return m_context; } + const String& toString() const + { return m_name; } +private: + JsGlobal* m_owner; + RefPointer m_context; + String m_name; + unsigned int m_instance; +}; + class JsGlobal : public NamedString { public: - JsGlobal(const char* scriptName, const char* fileName, bool relPath = true, bool fromCfg = true); + JsGlobal(const char* scriptName, const char* fileName, bool relPath = true, + bool fromCfg = true, unsigned int instances = 1); virtual ~JsGlobal(); bool load(); bool fileChanged(const char* fileName) const; + bool updateInstances(unsigned int instances); inline JsParser& parser() { return m_jsCode; } - inline ScriptContext* context() - { return m_context; } inline const String& fileName() { return m_file; } + inline unsigned int instances() const + { return m_instanceCount; } + inline JsGlobalInstance* getInstance(unsigned int idx) const + { + String str = name(); + if (idx) + str << "/" << idx; + JsGlobalInstance* inst = static_cast(m_instances[str]); + if (inst && inst->ref()) + return inst; + return 0; + } + inline JsGlobalInstance* getInstance(const String& name) const + { + JsGlobalInstance* inst = static_cast(m_instances[name]); + if (inst && inst->ref()) + return inst; + return 0; + } bool runMain(); static void markUnused(); static void freeUnused(); static void reloadDynamic(); - static bool initScript(const String& scriptName, const String& fileName, bool relPath = true, bool fromCfg = true); + static bool initScript(const String& scriptName, const String& fileName, + bool relPath = true, bool fromCfg = true, unsigned int instances = 0); static bool reloadScript(const String& scriptName); - static void loadScripts(const NamedList* sect); + static void loadScripts(const NamedList* sect, const NamedList* instSect); inline static ObjList& globals() { return s_globals; } inline static void unloadAll() @@ -212,13 +251,14 @@ public: private: static bool buildNewScript(Lock& lck, ObjList* old, const String& scriptName, - const String& fileName, bool relPath, bool fromCfg, bool fromInit = false); + const String& fileName, bool relPath, bool fromCfg, bool fromInit = false, unsigned int instances = 0); JsParser m_jsCode; - RefPointer m_context; bool m_inUse; bool m_confLoaded; String m_file; + unsigned int m_instanceCount; + ObjList m_instances; static ObjList s_globals; }; @@ -529,6 +569,8 @@ public: params().addParam(new ExpFunction("htoa")); params().addParam(new ExpFunction("btoh")); params().addParam(new ExpFunction("htob")); + params().addParam(new ExpFunction("instanceIndex")); + params().addParam(new ExpFunction("instanceCount")); addConstructor(params(),"Semaphore",new JsSemaphore(mtx)); addConstructor(params(),"HashList",new JsHashList(mtx)); addConstructor(params(),"URI",new JsURI(mtx)); @@ -1249,6 +1291,24 @@ static int counterSort(GenObject* obj1, GenObject* obj2, void* context) return c1 < c2 ? 1 : (c1 > c2 ? -1 : 0); } +// Sort and dump a list of object counters +static void dumpAllocations(String& out, ObjList* counters, unsigned int count, ScriptCode* code) +{ + if (!(counters && code)) + return; + counters->sort(counterSort); + + unsigned int i = 0; + for (ObjList* o = counters->skipNull(); o && i < count; o = o->skipNext(), i++) { + NamedCounter* c = static_cast(o->get()); + uint64_t line = c->toString().toUInt64(); + String fn; + unsigned int fl = 0; + code->getFileLine(line,fn,fl,false); + out << "\r\n" << fn << ":" << fl << " " << c->count(); + } +} + // Obtain a string with top object allocations from context ctx static bool evalCtxtAllocations(String& retVal, unsigned int count, ScriptContext* ctx, ScriptCode* code, const String& scrName) @@ -1262,17 +1322,8 @@ static bool evalCtxtAllocations(String& retVal, unsigned int count, ScriptContex retVal << "Script '" << scrName << "' has no active object tracking\r\n"; return true; } - objCounters->sort(counterSort); String tmp; - unsigned int i = 0; - for (ObjList* o = objCounters->skipNull(); o && i < count; o = o->skipNext(), i++) { - NamedCounter* c = static_cast(o->get()); - uint64_t line = c->toString().toUInt64(); - String fn; - unsigned int fl = 0; - code->getFileLine(line,fn,fl,false); - tmp << "\r\n" << fn << ":" << fl << " " << c->count(); - } + dumpAllocations(tmp,objCounters,count,code); if (!tmp) retVal << "Script '" << scrName << "' has no active object tracking counters\r\n"; else @@ -1281,6 +1332,39 @@ static bool evalCtxtAllocations(String& retVal, unsigned int count, ScriptContex return true; } +// Obtain a string with top object allocations from all script instances contexts +static bool evalInstanceAllocations(String& retVal, unsigned int count, ObjList& list, + ScriptCode* code, const String& scrName) +{ + if (!code) { + retVal << "Script '" << scrName << "' has no associated code\r\n"; + return true; + } + ObjList objCounters; + + for (ObjList* o = list.skipNull(); o; o = o->skipNext()) { + ObjList* l = static_cast(o->get()); + for (ObjList* j = l->skipNull(); j; j = j->skipNext()) { + NamedCounter* nInt = static_cast(j->get()); + NamedCounter* total = static_cast(objCounters[nInt->toString()]); + if (total) + total->add(nInt->count()); + else { + j->set(0,false); + objCounters.insert(nInt); + } + } + o->set(0); + } + String tmp; + dumpAllocations(tmp,&objCounters,count,code); + if (!tmp) + retVal << "Script '" << scrName << "' has no active object tracking counters\r\n"; + else + retVal << "Top " << count << " object allocations for '" << scrName <<"':" << tmp << "\r\n"; + return true; +} + // Utility: return a list of parameters to be used for replaceParams static inline const NamedList* getReplaceParams(GenObject* gen) { @@ -2350,6 +2434,18 @@ bool JsEngine::runNative(ObjList& stack, const ExpOperation& oper, GenObject* co else ExpEvaluator::pushOne(stack,new ExpOperation(false)); } + else if (oper.name() == YSTRING("instanceIndex")) { + ScriptRun* runner = YOBJECT(ScriptRun,context); + if (!(runner && runner->context())) + return false; + ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)runner->context()->instanceIndex())); + } + else if (oper.name() == YSTRING("instanceCount")) { + ScriptRun* runner = YOBJECT(ScriptRun,context); + if (!(runner && runner->context())) + return false; + ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)runner->context()->instanceCount())); + } else return JsObject::runNative(stack,oper,context); return true; @@ -6121,14 +6217,56 @@ void JsAssist::msgPostExecute(const Message& msg, bool handled) runFunction("onPostExecute",const_cast(msg),&handled); } +JsGlobalInstance::JsGlobalInstance(JsGlobal* owner, unsigned int index) + : m_owner(owner), m_instance(index) +{ + m_name << owner->toString(); + if (index) + m_name << "/" << index; + Debug(&__plugin,DebugInfo,"JsGlobalInstance::JsGlobalInstance(%p,%u) Created script instance '%s' [%p]", + m_owner,index,m_name.c_str(),this); +} + +JsGlobalInstance::~JsGlobalInstance() +{ + Debug(&__plugin,DebugInfo,"JsGlobalInstance::~JsGlobalInstance() '%s' destroyed [%p]",m_name.c_str(),this); + if (m_owner->parser().callable("onUnload")) { + ScriptRun* runner = m_owner->parser().createRunner(m_context,NATIVE_TITLE); + if (runner) { + ObjList args; + runner->call("onUnload",args); + TelEngine::destruct(runner); + } + } + if (m_context) { + Lock mylock(m_context->mutex()); + m_context->params().clearParams(); + } +} + +unsigned int JsGlobalInstance::runMain() +{ + DDebug(&__plugin,DebugInfo,"JsGlobalInstance::runMain() Start instance %s",toString().c_str()); + ScriptRun* runner = m_owner->parser().createRunner(m_context,0,m_instance,m_owner->instances()); + if (!runner) + return ScriptRun::Failed; + if (!m_context) + m_context = runner->context(); + m_context->trackObjs(s_trackCreation); + contextInit(runner,toString()); + ScriptRun::Status st = runner->run(); + TelEngine::destruct(runner); + return st; +} ObjList JsGlobal::s_globals; Mutex JsGlobal::s_mutex(false,"JsGlobal"); bool JsGlobal::s_keepOldOnFail = false; -JsGlobal::JsGlobal(const char* scriptName, const char* fileName, bool relPath, bool fromCfg) +JsGlobal::JsGlobal(const char* scriptName, const char* fileName, bool relPath, + bool fromCfg, unsigned int instances) : NamedString(scriptName,fileName), - m_inUse(true), m_confLoaded(fromCfg), m_file(fileName) + m_inUse(true), m_confLoaded(fromCfg), m_file(fileName), m_instanceCount(instances) { m_jsCode.basePath(s_basePath,s_libsPath); if (relPath) @@ -6141,18 +6279,7 @@ JsGlobal::JsGlobal(const char* scriptName, const char* fileName, bool relPath, b JsGlobal::~JsGlobal() { DDebug(&__plugin,DebugAll,"Unloading global Javascript '%s'",name().c_str()); - if (m_jsCode.callable("onUnload")) { - ScriptRun* runner = m_jsCode.createRunner(m_context,NATIVE_TITLE); - if (runner) { - ObjList args; - runner->call("onUnload",args); - TelEngine::destruct(runner); - } - } - if (m_context) { - Lock mylock(m_context->mutex()); - m_context->params().clearParams(); - } + m_instances.clear(); } bool JsGlobal::load() @@ -6172,6 +6299,21 @@ bool JsGlobal::fileChanged(const char* fileName) const return m_jsCode.scriptChanged(fileName,s_basePath,s_libsPath); } +bool JsGlobal::updateInstances(unsigned int instances) +{ + // 0 means that it was called from some place where config was not read, so no change + // or no change in number of instances + if (!instances || instances == m_instanceCount) + return true; + // we already now that current instance is different from what is requested + // so if instances is 1 => m_instance count > 1, m_instanceCount == 1 => instance = 1 + // so we need to completely reload the script + if (instances == 1 || m_instanceCount == 1) + return false; + m_instanceCount = instances; + return runMain(); +} + void JsGlobal::markUnused() { for (ObjList* o = s_globals.skipNull(); o; o = o->skipNext()) { @@ -6207,12 +6349,13 @@ void JsGlobal::reloadDynamic() } } -bool JsGlobal::initScript(const String& scriptName, const String& fileName, bool relPath, bool fromCfg) +bool JsGlobal::initScript(const String& scriptName, const String& fileName, bool relPath, + bool fromCfg, unsigned int instances) { if (fileName.null()) return false; - DDebug(&__plugin,DebugInfo,"Initialize %s script '%s' from %s file '%s'",(fromCfg ? "configured" : "dynamically loaded"), - scriptName.c_str(),(relPath ? "relative" : "absolute"),fileName.c_str()); + DDebug(&__plugin,DebugInfo,"Initialize %s script '%s' from %s file '%s' instances=%u",(fromCfg ? "configured" : "dynamically loaded"), + scriptName.c_str(),(relPath ? "relative" : "absolute"),fileName.c_str(),instances); Lock mylock(JsGlobal::s_mutex); ObjList* o = s_globals.find(scriptName); if (o) { @@ -6224,12 +6367,16 @@ bool JsGlobal::initScript(const String& scriptName, const String& fileName, bool return false; } if (!script->fileChanged(fileName)) { + unsigned int ret = script->updateInstances(instances); script->m_inUse = true; script->m_confLoaded = fromCfg; - return true; + // if positive, we can return. otherwise, it's a transition + // from multiple instances to one or viceversa and we reload the whole script + if (ret) + return true; } } - return buildNewScript(mylock,o,scriptName,fileName,relPath,fromCfg,true); + return buildNewScript(mylock,o,scriptName,fileName,relPath,fromCfg,true,instances); } bool JsGlobal::reloadScript(const String& scriptName) @@ -6245,7 +6392,7 @@ bool JsGlobal::reloadScript(const String& scriptName) return fileName && buildNewScript(mylock,o,scriptName,fileName,false,script->m_confLoaded); } -void JsGlobal::loadScripts(const NamedList* sect) +void JsGlobal::loadScripts(const NamedList* sect, const NamedList* instSect) { if (!sect) return; @@ -6256,33 +6403,60 @@ void JsGlobal::loadScripts(const NamedList* sect) continue; String tmp = *n; Engine::runParams().replaceParams(tmp); - JsGlobal::initScript(n->name(),tmp); + JsGlobal::initScript(n->name(),tmp,true,true,instSect ? instSect->getIntValue(n->name(),0,0) : 0); } } bool JsGlobal::runMain() { - ScriptRun* runner = m_jsCode.createRunner(m_context); - if (!runner) - return false; - if (!m_context) - m_context = runner->context(); - m_context->trackObjs(s_trackCreation); - contextInit(runner,name()); - ScriptRun::Status st = runner->run(); - TelEngine::destruct(runner); - return (ScriptRun::Succeeded == st); + DDebug(&__plugin,DebugInfo,"JsGlobal::runMain() Load and run %u instances, current number of instances:%u", + m_instanceCount,m_instances.count()); + if (m_instanceCount <= 1) { + JsGlobalInstance* inst = new JsGlobalInstance(this,0); + if(ScriptRun::Succeeded != inst->runMain()) + return false; + m_instances.append(inst); + } + else { + unsigned int lCount = m_instances.count(); + // add instances if m_instancesCount is increased + for (unsigned int i = 0; i < m_instanceCount; i++) { + JsGlobalInstance* inst = getInstance(i + 1); + if (inst) { + TelEngine::destruct(inst); + continue; + } + inst = new JsGlobalInstance(this,i + 1); + if(ScriptRun::Succeeded != inst->runMain()) + return false; + m_instances.append(inst); + + } + // remove instances if m_instances was decreased + for (unsigned int i = m_instanceCount; i < lCount; i++) { + JsGlobalInstance* inst = getInstance(i + 1); + if (!inst) + continue; + m_instances.remove(inst); + TelEngine::destruct(inst); + } + + } + return true; } bool JsGlobal::buildNewScript(Lock& lck, ObjList* old, const String& scriptName, - const String& fileName, bool relPath, bool fromCfg, bool fromInit) + const String& fileName, bool relPath, bool fromCfg, bool fromInit, unsigned int instances) { bool objCount = s_trackObj && getObjCounting(); NamedCounter* saved = 0; if (objCount) saved = Thread::setCurrentObjCounter(getObjCounter("js:" + scriptName,true)); JsGlobal* oldScript = old ? static_cast(old->get()) : 0; - JsGlobal* script = new JsGlobal(scriptName,fileName,relPath,fromCfg); + if (0 == instances) // keep number of instances if none is given + instances = oldScript ? oldScript->instances() : 1; + + JsGlobal* script = new JsGlobal(scriptName,fileName,relPath,fromCfg,instances); bool ok = false; if (script->load() || !s_keepOldOnFail || !old) { if (old) @@ -6374,7 +6548,10 @@ bool JsModule::commandExecute(String& retVal, const String& line) Lock lck(JsGlobal::s_mutex); for (ObjList* o = JsGlobal::globals().skipNull(); o ; o = o->skipNext()) { JsGlobal* script = static_cast(o->get()); - retVal << script->name() << " = " << *script << "\r\n"; + retVal << script->name() << " = " << *script; + if (script->instances() > 1) + retVal << ":" << script->instances(); + retVal << "\r\n"; } lck.acquire(this); for (unsigned int i = 0; i < calls().length(); i++) { @@ -6395,12 +6572,18 @@ bool JsModule::commandExecute(String& retVal, const String& line) cmd.extractTo(" ",scr).trimSpaces(); if (scr.null() || cmd.null()) return false; + int pos = scr.find("/"); + String baseScr = scr.substr(0,pos); Lock mylock(JsGlobal::s_mutex);; - JsGlobal* script = static_cast(JsGlobal::globals()[scr]); + JsGlobal* script = static_cast(JsGlobal::globals()[baseScr]); if (script) { - RefPointer ctxt = script->context(); - mylock.drop(); - return evalContext(retVal,cmd,ctxt); + JsGlobalInstance* inst = script->getInstance(scr); + if (inst) { + RefPointer ctxt = inst->context(); + mylock.drop(); + TelEngine::destruct(inst); + return evalContext(retVal,cmd,ctxt); + } } mylock.acquire(this); JsAssist* assist = static_cast(calls()[scr]); @@ -6416,7 +6599,34 @@ bool JsModule::commandExecute(String& retVal, const String& line) if (cmd.startSkip("eval") && cmd.trimSpaces()) return evalContext(retVal,cmd); - if (cmd.startSkip("allocations") && cmd.trimSpaces()) { + if (cmd.startSkip("allocations instance") && cmd.trimSpaces()) { + String scr; + cmd.extractTo(" ",scr).trimSpaces(); + String baseScr = scr.substr(0,scr.find("/")); + unsigned int top = cmd.toInteger(25,0,1,100); + if (scr.null()) + return false; + Lock mylock(JsGlobal::s_mutex);; + JsGlobal* script = static_cast(JsGlobal::globals()[baseScr]); + if (script) { + JsGlobalInstance* inst = script->getInstance(scr); + if (inst) { + bool ret = evalCtxtAllocations(retVal,top,inst->context(),script->parser().code(),scr); + TelEngine::destruct(inst); + return ret; + } + } + mylock.acquire(this); + RefPointer assist = static_cast(calls()[scr]); + if (assist) { + mylock.drop(); + return assist->evalAllocations(retVal,top); + } + retVal << "Cannot find script context: " << scr << "\n\r"; + return true; + } + + if (cmd.startSkip("allocations total") && cmd.trimSpaces()) { String scr; cmd.extractTo(" ",scr).trimSpaces(); unsigned int top = cmd.toInteger(25,0,1,100); @@ -6424,8 +6634,20 @@ bool JsModule::commandExecute(String& retVal, const String& line) return false; Lock mylock(JsGlobal::s_mutex);; JsGlobal* script = static_cast(JsGlobal::globals()[scr]); - if (script) - return evalCtxtAllocations(retVal,top,script->context(),script->parser().code(),scr); + if (script) { + ObjList list; + for (unsigned int i = 0; i <= script->instances(); i++) { + JsGlobalInstance* inst = script->getInstance(i); + if (inst) { + ObjList* c = inst->context() ? inst->context()->countAllocations() : 0; + if (c) + list.insert(c); + TelEngine::destruct(inst); + } + } + mylock.drop(); + return evalInstanceAllocations(retVal,top,list,script->parser().code(),scr); + } mylock.acquire(this); RefPointer assist = static_cast(calls()[scr]); if (assist) { @@ -6519,8 +6741,15 @@ bool JsModule::commandComplete(Message& msg, const String& partLine, const Strin Lock lck(JsGlobal::s_mutex); for (ObjList* o = JsGlobal::globals().skipNull(); o ; o = o->skipNext()) { JsGlobal* script = static_cast(o->get()); - if (script->name()) - itemComplete(msg.retValue(),s_eval + script->name(),partWord); + if (script->name()) { + for (unsigned int i = 0; i <= script->instances(); i++) { + JsGlobalInstance* inst = script->getInstance(i); + if (inst) { + itemComplete(msg.retValue(),s_eval + inst->toString(),partWord); + TelEngine::destruct(inst); + } + } + } } lck.acquire(this); for (unsigned int i = 0; i < calls().length(); i++) { @@ -6536,7 +6765,7 @@ bool JsModule::commandComplete(Message& msg, const String& partLine, const Strin itemComplete(msg.retValue(),*list,partWord); return true; } - else if (partLine == YSTRING("javascript reload") || partLine == YSTRING("javascript allocations")) { + else if (partLine == YSTRING("javascript reload") || partLine == YSTRING("javascript allocations total")) { Lock lck(JsGlobal::s_mutex); for (ObjList* o = JsGlobal::globals().skipNull(); o ; o = o->skipNext()) { JsGlobal* script = static_cast(o->get()); @@ -6545,6 +6774,27 @@ bool JsModule::commandComplete(Message& msg, const String& partLine, const Strin } return true; } + else if (partLine == YSTRING("javascript allocations instance")) { + Lock lck(JsGlobal::s_mutex); + for (ObjList* o = JsGlobal::globals().skipNull(); o ; o = o->skipNext()) { + JsGlobal* script = static_cast(o->get()); + if (script && script->instances() > 1) { + for (unsigned int i = 0; i <= script->instances(); i++) { + JsGlobalInstance* inst = script->getInstance(i); + if (inst) { + itemComplete(msg.retValue(),inst->toString(),partWord); + TelEngine::destruct(inst); + } + } + } + } + return true; + } + else if (partLine == YSTRING("javascript allocations")) { + itemComplete(msg.retValue(),"total",partWord); + itemComplete(msg.retValue(),"instance",partWord); + return 0; + } return Module::commandComplete(msg,partLine,partWord); } @@ -6629,7 +6879,7 @@ bool JsModule::received(Message& msg, int id) if (!m_started) { m_started = true; Configuration cfg(Engine::configFile("javascript")); - JsGlobal::loadScripts(cfg.getSection("late_scripts")); + JsGlobal::loadScripts(cfg.getSection("late_scripts"),cfg.getSection("instances")); } return false; } // switch (id) @@ -6718,9 +6968,9 @@ void JsModule::initialize() } JsGlobal::markUnused(); lck.drop(); - JsGlobal::loadScripts(cfg.getSection("scripts")); + JsGlobal::loadScripts(cfg.getSection("scripts"),cfg.getSection("instances")); if (m_started) - JsGlobal::loadScripts(cfg.getSection("late_scripts")); + JsGlobal::loadScripts(cfg.getSection("late_scripts"),cfg.getSection("instances")); JsGlobal::reloadDynamic(); JsGlobal::freeUnused(); }