]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
FS-11194: [mod_v8] Implement JavaScript Process Status with Heap statistics.
authorAndrey Volk <andywolk@gmail.com>
Tue, 24 Jul 2018 11:01:55 +0000 (14:01 +0300)
committerAndrey Volk <andywolk@gmail.com>
Thu, 25 Jul 2019 20:01:22 +0000 (00:01 +0400)
src/mod/languages/mod_v8/include/fsglobal.hpp
src/mod/languages/mod_v8/include/javascript.hpp
src/mod/languages/mod_v8/mod_v8.cpp
src/mod/languages/mod_v8/mod_v8.h
src/mod/languages/mod_v8/src/fsglobal.cpp
src/mod/languages/mod_v8/src/jsmain.cpp

index 7095a12dd24070d05fb5ddef78307d00ff312c67..cf143ce5d728cf9101e386a37b93a7155bf162d4 100644 (file)
@@ -63,6 +63,8 @@ public:
        JS_FUNCTION_DEF_STATIC(FetchURL);
        JS_FUNCTION_DEF_STATIC(FetchURLHash);
        JS_FUNCTION_DEF_STATIC(FetchURLFile);
+       JS_FUNCTION_DEF_STATIC(Version);
+       JS_FUNCTION_DEF_STATIC(Id);
 };
 
 #endif /* FS_GLOBAL_H */
index a080a508deeeb6ef42d47079ae1898582dec8906..800e5894aff7cd0d593446d514c00c6511a7aa89 100644 (file)
@@ -332,7 +332,6 @@ public:
        static void Dispose();                                                                                          /* Deinitialize the V8 engine */
 
        static void Include(const v8::FunctionCallbackInfo<v8::Value>& args);           /* Adds functionality to include another JavaScript from the running script */
-       static void Version(const v8::FunctionCallbackInfo<v8::Value>& args);           /* Internal Version function accessable from JS - used to get the current V( version */
        static const std::string GetExceptionInfo(v8::Isolate* isolate, v8::TryCatch* try_catch);       /* Get the exception information from a V8 TryCatch instance */
 
        const std::vector<const js_class_definition_t *>& GetExtenderClasses() const;/* Returns the list of class definitions */
@@ -355,7 +354,7 @@ public:
        int GetForcedTerminationLineNumber(void);
 
        /* Method to force termination of a script */
-       static void ExitScript(v8::Isolate *isolate, const char *msg);
+       static void ExitScript(v8::Isolate *isolate, const char *msg, bool jskill = false);
 
        /* Get the filename and line number of the current JS stack */
        static char *GetStackInfo(v8::Isolate *isolate, int *lineNumber);
index 8c0d6c0bef64f8dc10e194cd09af0cab185e1fe8..49763e05c03828c961dde60fa4a9fba29ad9abd3 100644 (file)
@@ -125,6 +125,8 @@ typedef struct {
        v8::Platform *v8platform;
        switch_hash_t *compiled_script_hash;
        switch_mutex_t *compiled_script_hash_mutex;
+       map<string, Isolate *> *task_manager;
+       switch_mutex_t *task_manager_mutex;
        char *script_caching;
        switch_time_t cache_expires_seconds;
        bool performance_monitor;
@@ -607,7 +609,7 @@ static int v8_parse_and_execute(switch_core_session_t *session, const char *inpu
        JSMain *js;
        Isolate *isolate;
        char *arg, *argv[512];
-       int argc = 0, x = 0, y = 0;
+       int argc = 0;
        unsigned int flags = 0;
        char *path = NULL;
        string result_string;
@@ -642,7 +644,26 @@ static int v8_parse_and_execute(switch_core_session_t *session, const char *inpu
                        HandleScope scope(isolate);
 
                        // Store our object internally
-                       isolate->SetData(0, js);
+                       isolate->SetData(ISOLATE_DATA_OBJECT, js);
+
+                       // Set isolate related data.
+                       switch_uuid_t task_id;
+                       switch_uuid_get(&task_id);
+                       char str_task_id[SWITCH_UUID_FORMATTED_LENGTH + 1];
+                       switch_uuid_format(str_task_id, &task_id);
+
+                       js_isolate_private_data_t *private_data = new js_isolate_private_data_t();
+                       private_data->str_task_id = str_task_id;
+                       private_data->input_code = input_code;
+                       private_data->start_time = switch_time_now();
+
+                       // Store private data internally
+                       isolate->SetData(ISOLATE_DATA_PRIVATE, private_data);
+
+                       // Add isolate to the task manager 
+                       switch_mutex_lock(globals.task_manager_mutex);
+                       (*globals.task_manager)[str_task_id] = isolate;
+                       switch_mutex_unlock(globals.task_manager_mutex);
 
                        // New global template
                        Handle<ObjectTemplate> global = ObjectTemplate::New(isolate);
@@ -650,9 +671,6 @@ static int v8_parse_and_execute(switch_core_session_t *session, const char *inpu
                        if (global.IsEmpty()) {
                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create JS global object template\n");
                        } else {
-                               /* Function to print current V8 version */
-                               global->Set(String::NewFromUtf8(isolate, "version"), FunctionTemplate::New(isolate, JSMain::Version));
-
                                /* Add all global functions */
                                for (size_t i = 0; i < js->GetExtenderFunctions().size(); i++) {
                                        js_function_t *proc = js->GetExtenderFunctions()[i];
@@ -670,7 +688,7 @@ static int v8_parse_and_execute(switch_core_session_t *session, const char *inpu
 
 #ifdef V8_ENABLE_DEBUGGING
                                        Persistent<Context> *debug_context = new Persistent<Context>();
-                                       isolate->SetData(1, debug_context);
+                                       isolate->SetData(ISOLATE_DATA_DEBUG, debug_context);
                                        debug_context->Reset(isolate, context);
 
                                        //v8::Locker lck(isolate);
@@ -749,7 +767,7 @@ static int v8_parse_and_execute(switch_core_session_t *session, const char *inpu
 
                                                // Add arguments before running script.
                                                Local<Array> arguments = Array::New(isolate, argc);
-                                               for (y = 0; y < argc; y++) {
+                                               for (int y = 0; y < argc; y++) {
                                                        arguments->Set(Integer::New(isolate, y), String::NewFromUtf8(isolate, argv[y]));
                                                }
                                                context->Global()->Set(String::NewFromUtf8(isolate, "argv"), arguments);
@@ -786,11 +804,11 @@ static int v8_parse_and_execute(switch_core_session_t *session, const char *inpu
                                        if (!script_data) {
                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No script to execute!\n");
                                        } else {
-                                               /* Store our base directoy in variable 'scriptPath' */
-                                               char *path = v8_get_script_path(script_file);
-                                               if (path) {
-                                                       context->Global()->Set(String::NewFromUtf8(isolate, "scriptPath"), String::NewFromUtf8(isolate, path));
-                                                       free(path);
+                                               /* Store our base directory in variable 'scriptPath' */
+                                               char *scriptPath = v8_get_script_path(script_file);
+                                               if (scriptPath) {
+                                                       context->Global()->Set(String::NewFromUtf8(isolate, "scriptPath"), String::NewFromUtf8(isolate, scriptPath));
+                                                       switch_safe_free(scriptPath);
                                                }
 
                                                TryCatch try_catch(isolate);
@@ -818,10 +836,10 @@ static int v8_parse_and_execute(switch_core_session_t *session, const char *inpu
 #endif
 
 #if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5
-                                                       Handle<Value> result;
+                                                       Handle<Value> script_result;
 
                                                        if (!v8_script.IsEmpty()) {
-                                                               result = v8_script.ToLocalChecked()->Run();
+                                                               script_result = v8_script.ToLocalChecked()->Run();
                                                        }
 
                                                        switch_mutex_lock(globals.mutex);
@@ -842,9 +860,9 @@ static int v8_parse_and_execute(switch_core_session_t *session, const char *inpu
                                                                        switch_log_printf(SWITCH_CHANNEL_ID_LOG, js->GetForcedTerminationScriptFile(), modname, js->GetForcedTerminationLineNumber(), NULL, SWITCH_LOG_NOTICE, "Script exited with info [%s]\n", js->GetForcedTerminationMessage());
                                                                }
 
-                                                               if (!result.IsEmpty()) {
+                                                               if (!script_result.IsEmpty()) {
                                                                        // Return result as string
-                                                                       String::Utf8Value ascii(result);
+                                                                       String::Utf8Value ascii(script_result);
                                                                        if (*ascii) {
                                                                                res = *ascii;
                                                                        }
@@ -868,7 +886,7 @@ static int v8_parse_and_execute(switch_core_session_t *session, const char *inpu
 #endif
                                        }
 #ifdef V8_ENABLE_DEBUGGING
-                                       isolate->SetData(1, NULL);
+                                       isolate->SetData(ISOLATE_DATA_DEBUG, NULL);
                                        if (debug_listen_port > 0) {
                                                Debug::DisableAgent();
                                        }
@@ -877,7 +895,16 @@ static int v8_parse_and_execute(switch_core_session_t *session, const char *inpu
 #endif
                                }
                        }
-                       isolate->SetData(0, NULL);
+
+                       // Remove isolate from the task manager
+                       switch_mutex_lock(globals.task_manager_mutex);
+                       globals.task_manager->erase(str_task_id);
+                       switch_mutex_unlock(globals.task_manager_mutex);
+                       
+                       isolate->SetData(ISOLATE_DATA_PRIVATE, NULL);
+                       isolate->SetData(ISOLATE_DATA_OBJECT, NULL);
+
+                       delete private_data;
                }
 
 #ifdef V8_FORCE_GC_AFTER_EXECUTION
@@ -1178,6 +1205,279 @@ SWITCH_STANDARD_API(jsmon_function)
        return SWITCH_STATUS_SUCCESS;
 }
 
+SWITCH_STANDARD_API(kill_function)
+{
+       if (!zstr(cmd)) {
+               switch_mutex_lock(globals.task_manager_mutex);
+
+               auto isolate_it = globals.task_manager->find(cmd);
+               if (isolate_it != globals.task_manager->end()) {
+                       Isolate * isolate = isolate_it->second;
+                       JSMain *js = JSMain::GetScriptInstanceFromIsolate(isolate);
+                       if (js)
+                               js->ExitScript(isolate, "Script termination requested by jskill API.", true);
+               }
+
+               switch_mutex_unlock(globals.task_manager_mutex);
+
+               stream->write_function(stream, "+OK\n");
+       }
+       else {
+               stream->write_function(stream, "false");
+       }
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+inline static void stream_write_safe_d(switch_stream_handle_t *stream, const char *str) {
+       if (!str) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
+               stream->write_function(stream, "-ERR Memory Error!\n");
+       }
+       else {
+               stream->write_function(stream, "%s", str);
+       }
+}
+#define stream_write_safe(output_text) stream_write_safe_d(stream, output_text)
+
+SWITCH_STANDARD_API(process_status_function)
+{
+       char *mydata = NULL, *argv[3] = { 0 };
+       char *as = NULL, *output_text = NULL, *delim = ",";     
+       cJSON *json = NULL, *row;
+       switch_xml_t xml = NULL, xml_row, xml_field;
+       int rows = 0, f_off = 0, count = 0;
+       char tmp_str[50];
+       std::vector<js_isolate_private_data_t> tasks;
+
+       if (cmd && *cmd && (mydata = strdup(cmd))) {
+               switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
+               if (argv[1] && !strcasecmp(argv[0], "as")) {
+                       as = argv[1];
+                       if (!strcasecmp(as, "csv")) {
+                               if (argv[2]) delim = argv[2];
+                       }
+               }
+       }
+
+       if (!as) {
+               as = "plain";
+       }
+
+       if (!strcasecmp(as, "json")) {
+               if (!(json = cJSON_CreateArray())) {
+                       goto end;
+               }
+       } else if (!strcasecmp(as, "xml")) {
+               if (!(xml = switch_xml_new("result"))) {
+                       goto end;
+               }
+       } else if (!strcasecmp(as, "delim") || !strcasecmp(as, "csv")) {
+               stream->write_function(stream, "%s%s", "task_id", delim);
+               stream->write_function(stream, "%s%s", "input_code", delim);
+               stream->write_function(stream, "%s%s", "execution_time", delim);
+
+               stream->write_function(stream, "%s%s", "total_physical_size", delim);
+               stream->write_function(stream, "%s%s", "total_heap_size_executable", delim);
+               stream->write_function(stream, "%s%s", "total_heap_size", delim);
+               stream->write_function(stream, "%s%s", "used_heap_size", delim);
+               stream->write_function(stream, "%s%s", "heap_size_limit", delim);
+               stream->write_function(stream, "%s%s", "malloced_memory", delim);
+               stream->write_function(stream, "%s%s", "peak_malloced_memory", "\n");           
+       } else {
+               stream->write_function(stream, "JavaScript process status.\n");
+       }
+       
+       switch_mutex_lock(globals.task_manager_mutex);
+
+       for (auto isolate_pair : *globals.task_manager) {
+               Isolate *isolate = isolate_pair.second;
+
+               js_isolate_private_data_t *isolate_private_data = (js_isolate_private_data_t*)isolate->GetData(ISOLATE_DATA_PRIVATE);
+               js_isolate_private_data_t private_data = *isolate_private_data;
+
+               isolate->GetHeapStatistics(&private_data.stats);
+
+               tasks.push_back(private_data);
+       }
+
+       switch_mutex_unlock(globals.task_manager_mutex);
+
+       for (auto isolate_private_data : tasks) {
+               count++;
+
+               js_isolate_private_data_t *private_data = (js_isolate_private_data_t *)&(isolate_private_data);
+
+               switch_time_t end = switch_time_now();
+               unsigned int delay = (end - private_data->start_time) / 1000;
+
+               if (!strcasecmp(as, "plain")) {
+
+                       stream->write_function(stream, "\nTask id: %s\n", private_data->str_task_id.c_str());
+                       stream->write_function(stream, "input_code: %s\n", (private_data->input_code[0] == '~' ? "inline" : private_data->input_code.c_str()));
+                       stream->write_function(stream, "execution_time: %u ms\n", delay);
+
+                       stream->write_function(stream, "total_physical_size: %u\n", private_data->stats.total_physical_size());
+                       stream->write_function(stream, "total_heap_size_executable: %u\n", private_data->stats.total_heap_size_executable());
+                       stream->write_function(stream, "total_heap_size: %u\n", private_data->stats.total_heap_size());
+                       stream->write_function(stream, "used_heap_size: %u\n", private_data->stats.used_heap_size());
+                       stream->write_function(stream, "heap_size_limit: %u\n", private_data->stats.heap_size_limit());
+                       stream->write_function(stream, "malloced_memory: %u\n", private_data->stats.malloced_memory());
+                       stream->write_function(stream, "peak_malloced_memory: %u\n", private_data->stats.peak_malloced_memory());
+               } else if (!strcasecmp(as, "json")) {
+                       if (!(row = cJSON_CreateObject())) {                            
+                               goto end;
+                       }
+
+                       cJSON_AddItemToArray(json, row);
+
+                       cJSON_AddItemToObject(row, "task_id", cJSON_CreateString(private_data->str_task_id.c_str()));
+                       cJSON_AddItemToObject(row, "input_code", cJSON_CreateString((private_data->input_code[0] == '~' ? "inline" : private_data->input_code.c_str())));
+                       cJSON_AddItemToObject(row, "execution_time", cJSON_CreateNumber(delay));
+
+                       cJSON_AddItemToObject(row, "total_physical_size", cJSON_CreateNumber(private_data->stats.total_physical_size()));
+                       cJSON_AddItemToObject(row, "total_heap_size_executable", cJSON_CreateNumber(private_data->stats.total_heap_size_executable()));
+                       cJSON_AddItemToObject(row, "total_heap_size", cJSON_CreateNumber(private_data->stats.total_heap_size()));
+                       cJSON_AddItemToObject(row, "used_heap_size", cJSON_CreateNumber(private_data->stats.used_heap_size()));
+                       cJSON_AddItemToObject(row, "heap_size_limit", cJSON_CreateNumber(private_data->stats.heap_size_limit()));
+                       cJSON_AddItemToObject(row, "malloced_memory", cJSON_CreateNumber(private_data->stats.malloced_memory()));
+                       cJSON_AddItemToObject(row, "peak_malloced_memory", cJSON_CreateNumber(private_data->stats.peak_malloced_memory()));
+               } else if (!strcasecmp(as, "delim") || !strcasecmp(as, "csv")) {
+                       stream->write_function(stream, "%s%s", private_data->str_task_id.c_str(), delim);
+                       stream->write_function(stream, "%s%s", (private_data->input_code[0] == '~' ? "inline" : private_data->input_code.c_str()), delim);
+
+                       switch_snprintf(tmp_str, sizeof(tmp_str), "%u", delay);
+                       stream->write_function(stream, "%s%s", tmp_str, delim);
+
+                       switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.total_physical_size());
+                       stream->write_function(stream, "%s%s", tmp_str, delim);
+
+                       switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.total_heap_size_executable());
+                       stream->write_function(stream, "%s%s", tmp_str, delim);
+
+                       switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.total_heap_size());
+                       stream->write_function(stream, "%s%s", tmp_str, delim);
+
+                       switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.used_heap_size());
+                       stream->write_function(stream, "%s%s", tmp_str, delim);
+
+                       switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.heap_size_limit());
+                       stream->write_function(stream, "%s%s", tmp_str, delim);
+
+                       switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.malloced_memory());
+                       stream->write_function(stream, "%s%s", tmp_str, delim);
+
+                       switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.peak_malloced_memory());
+                       stream->write_function(stream, "%s%s", tmp_str, "\n");
+
+               } else if (!strcasecmp(as, "xml")) {
+                       if (!(xml_row = switch_xml_add_child_d(xml, "row", rows++))) {
+                               goto end;
+                       }
+
+                       switch_snprintf(tmp_str, sizeof(tmp_str), "%d", rows);
+                       switch_xml_set_attr(switch_xml_set_flag(xml_row, SWITCH_XML_DUP), strdup("row_id"), strdup(tmp_str));
+
+                       if (!(xml_field = switch_xml_add_child_d(xml_row, "task_id", f_off++))) {                               
+                               goto end;
+                       } 
+                       switch_xml_set_txt_d(xml_field, private_data->str_task_id.c_str());
+
+                       if (!(xml_field = switch_xml_add_child_d(xml_row, "input_code", f_off++))) {
+                               goto end;
+                       }
+                       switch_xml_set_txt_d(xml_field, (private_data->input_code[0] == '~' ? "inline" : private_data->input_code.c_str()));
+
+                       if (!(xml_field = switch_xml_add_child_d(xml_row, "execution_time", f_off++))) {
+                               goto end;
+                       }
+                       switch_snprintf(tmp_str, sizeof(tmp_str), "%u", delay);
+                       switch_xml_set_txt_d(xml_field, tmp_str);
+
+                       if (!(xml_field = switch_xml_add_child_d(xml_row, "total_physical_size", f_off++))) {
+                               goto end;
+                       }
+                       switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.total_physical_size());
+                       switch_xml_set_txt_d(xml_field, tmp_str);
+
+                       if (!(xml_field = switch_xml_add_child_d(xml_row, "total_heap_size_executable", f_off++))) {
+                               goto end;
+                       }
+                       switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.total_heap_size_executable());
+                       switch_xml_set_txt_d(xml_field, tmp_str);
+
+                       if (!(xml_field = switch_xml_add_child_d(xml_row, "total_heap_size", f_off++))) {
+                               goto end;
+                       }
+                       switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.total_heap_size());
+                       switch_xml_set_txt_d(xml_field, tmp_str);
+
+                       if (!(xml_field = switch_xml_add_child_d(xml_row, "used_heap_size", f_off++))) {
+                               goto end;
+                       }
+                       switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.used_heap_size());
+                       switch_xml_set_txt_d(xml_field, tmp_str);
+
+                       if (!(xml_field = switch_xml_add_child_d(xml_row, "heap_size_limit", f_off++))) {
+                               goto end;
+                       }
+                       switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.heap_size_limit());
+                       switch_xml_set_txt_d(xml_field, tmp_str);
+
+                       if (!(xml_field = switch_xml_add_child_d(xml_row, "malloced_memory", f_off++))) {
+                               goto end;
+                       }
+                       switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.malloced_memory());
+                       switch_xml_set_txt_d(xml_field, tmp_str);
+
+                       if (!(xml_field = switch_xml_add_child_d(xml_row, "peak_malloced_memory", f_off++))) {
+                               goto end;
+                       }
+                       switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.peak_malloced_memory());
+                       switch_xml_set_txt_d(xml_field, tmp_str);
+
+               }
+       }
+
+       if (!strcasecmp(as, "json")) {
+               cJSON *result;
+
+               if (!(result = cJSON_CreateObject())) {
+                       stream->write_function(stream, "-ERR Error creating json object!\n");
+                       goto end;
+               }
+               else {
+                       cJSON_AddItemToObject(result, "row_count", cJSON_CreateNumber(count));
+                       cJSON_AddItemToObject(result, "rows", json);
+
+                       output_text = cJSON_PrintUnformatted(result);
+                       json = result;
+               }
+
+               stream_write_safe(output_text);
+
+       } else if (!strcasecmp(as, "xml")) {
+               switch_snprintf(tmp_str, sizeof(tmp_str), "%u", count);
+               switch_xml_set_attr(switch_xml_set_flag(xml, SWITCH_XML_DUP), strdup("row_count"), strdup(tmp_str));
+
+               output_text = switch_xml_toxml(xml, SWITCH_FALSE);
+
+               stream_write_safe(output_text);
+
+       } else if (!strcasecmp(as, "delim") || !strcasecmp(as, "csv")) {
+               stream->write_function(stream, "%s%u total.%s", "\n", count, "\n");
+       }
+
+end:
+       
+       switch_xml_free(xml);
+       cJSON_Delete(json);
+       switch_safe_free(output_text);
+       switch_safe_free(mydata);
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
 SWITCH_MODULE_LOAD_FUNCTION(mod_v8_load)
 {
        switch_application_interface_t *app_interface;
@@ -1193,12 +1493,15 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_v8_load)
 
 #if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5
        switch_mutex_init(&globals.compiled_script_hash_mutex, SWITCH_MUTEX_NESTED, globals.pool);
+       switch_mutex_init(&globals.task_manager_mutex, SWITCH_MUTEX_NESTED, globals.pool);
        switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, globals.pool);
 #endif
        switch_mutex_init(&globals.event_mutex, SWITCH_MUTEX_NESTED, globals.pool);
        globals.event_handlers = new set<FSEventHandler *>();
 
        if (load_modules() != SWITCH_STATUS_SUCCESS) {
+               delete globals.event_handlers;
+               switch_event_unbind(&globals.event_node);
                return SWITCH_STATUS_FALSE;
        }
 
@@ -1211,6 +1514,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_v8_load)
        JSMain::Initialize(&globals.v8platform);
 
        switch_core_hash_init(&globals.compiled_script_hash);
+       globals.task_manager = new map<string, Isolate *>();
 #else
        JSMain::Initialize();
 #endif
@@ -1234,6 +1538,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_v8_load)
        SWITCH_ADD_API(jsrun_interface, "jsrun", "run a script", launch_async, "jsrun <script> [additional_vars [...]]");
        SWITCH_ADD_API(jsapi_interface, "jsapi", "execute an api call", jsapi_function, "jsapi <script> [additional_vars [...]]");
        SWITCH_ADD_API(jsmon_interface, "jsmon", "toggle performance monitor", jsmon_function, "jsmon on|off");
+       SWITCH_ADD_API(jsrun_interface, "jsps", "process status", process_status_function, "jsps [as plain|json|xml|delim|csv [<delimeter>]]");
+       SWITCH_ADD_API(jsrun_interface, "jskill", "kill a task", kill_function, "jskill <task_id>");
        SWITCH_ADD_APP(app_interface, "javascript", "Launch JS ivr", "Run a javascript ivr on a channel", v8_dp_function, "<script> [additional_vars [...]]", SAF_SUPPORT_NOMEDIA);
        SWITCH_ADD_CHAT_APP(chat_app_interface, "javascript", "execute a js script", "execute a js script", v8_chat_function, "<script>", SCAF_NONE);
 
@@ -1257,6 +1563,8 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_v8_shutdown)
 
        switch_core_hash_destroy(&globals.compiled_script_hash);
        switch_mutex_destroy(globals.compiled_script_hash_mutex);
+       switch_mutex_destroy(globals.task_manager_mutex);
+       delete globals.task_manager;
        switch_mutex_destroy(globals.mutex);
 #endif
 
index a333d6490dbda74c9d86eca0adf6901ea62beedb..820057f78f04770daef884e8a2a8787e8fec7660 100644 (file)
@@ -83,6 +83,19 @@ typedef struct {
 }
 v8_event_t;
 
+#define ISOLATE_DATA_OBJECT            0
+#define ISOLATE_DATA_DEBUG             1
+#define ISOLATE_DATA_PRIVATE   2
+
+/* Isolate Private Data */
+typedef struct {
+       std::string str_task_id;        /* JavaScript task id */
+       std::string input_code;         /* Path to JavaScript source */
+       switch_time_t start_time;       /* Script start_time */
+       
+       v8::HeapStatistics stats;
+} js_isolate_private_data_t;
+
 SWITCH_END_EXTERN_C
 
 void v8_add_event_handler(void *event_handler);
index 89f94b40cb6f0b26deb0aad974baaea16afdc0b9..99a0626f8f0214df90ac49c1b6113c12e88bd9bb 100644 (file)
@@ -748,6 +748,19 @@ JS_GLOBAL_FUNCTION_IMPL_STATIC(FileDelete)
        info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Invalid arguments"));
 }
 
+/* Internal Version function accessable from JS - used to get the current V8 version */
+JS_GLOBAL_FUNCTION_IMPL_STATIC(Version)
+{
+       info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), V8::GetVersion()));
+}
+
+/* TaskId assigned to the script - used to manage the task */
+JS_GLOBAL_FUNCTION_IMPL_STATIC(Id)
+{
+       js_isolate_private_data_t *private_data = (js_isolate_private_data_t*)info.GetIsolate()->GetData(ISOLATE_DATA_PRIVATE);
+       info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), private_data->str_task_id.c_str()));
+}
+
 static const js_function_t fs_proc[] = {
        {"console_log", FSGlobal::Log},                         // Deprecated
        {"consoleLog", FSGlobal::Log},
@@ -770,6 +783,8 @@ static const js_function_t fs_proc[] = {
        {"fetchUrl", FSGlobal::FetchURL},
        {"fetchUrlHash", FSGlobal::FetchURLHash},
        {"fetchUrlFile", FSGlobal::FetchURLFile},
+       {"id", FSGlobal::Id },
+       {"version", FSGlobal::Version},
        {0}
 };
 
index 86fed7b27f3cce377cf14ca4104714bc494a9a75..d473d9f085071963adf2e3edbb99e80a58f18cd6 100644 (file)
@@ -264,11 +264,6 @@ void JSMain::Log(const v8::FunctionCallbackInfo<Value>& args)
        args.GetReturnValue().Set(Undefined(args.GetIsolate()));
 }
 
-void JSMain::Version(const v8::FunctionCallbackInfo<Value>& args)
-{
-       args.GetReturnValue().Set(String::NewFromUtf8(args.GetIsolate(), V8::GetVersion()));
-}
-
 const string JSMain::ExecuteScript(const string& filename, bool *resultIsError)
 {
        // Get the file and load into a string.
@@ -553,7 +548,7 @@ int JSMain::GetForcedTerminationLineNumber(void)
        return forcedTerminationLineNumber;
 }
 
-void JSMain::ExitScript(Isolate *isolate, const char *msg)
+void JSMain::ExitScript(Isolate *isolate, const char *msg, bool jskill)
 {
        if (!isolate) {
                return;
@@ -580,7 +575,10 @@ void JSMain::ExitScript(Isolate *isolate, const char *msg)
                        js_strdup(js->forcedTerminationMessage, msg);
                }
 
-               js->forcedTerminationScriptFile = GetStackInfo(isolate, &js->forcedTerminationLineNumber);
+               /* When forcefully killed, don't call GetStackInfo() because isolate is locked by another thread */
+               if (!jskill) {
+                       js->forcedTerminationScriptFile = GetStackInfo(isolate, &js->forcedTerminationLineNumber);
+               }
        }
 
        isolate->TerminateExecution();