From 27669eda0464b34545aa13aa154cfbd9f0a5888f Mon Sep 17 00:00:00 2001
From: Andrey Volk <andywolk@gmail.com>
Date: Tue, 24 Jul 2018 14:01:55 +0300
Subject: [PATCH] FS-11194: [mod_v8] Implement JavaScript Process Status with
 Heap statistics.

---
 src/mod/languages/mod_v8/include/fsglobal.hpp |   2 +
 .../languages/mod_v8/include/javascript.hpp   |   3 +-
 src/mod/languages/mod_v8/mod_v8.cpp           | 344 +++++++++++++++++-
 src/mod/languages/mod_v8/mod_v8.h             |  13 +
 src/mod/languages/mod_v8/src/fsglobal.cpp     |  15 +
 src/mod/languages/mod_v8/src/jsmain.cpp       |  12 +-
 6 files changed, 362 insertions(+), 27 deletions(-)

diff --git a/src/mod/languages/mod_v8/include/fsglobal.hpp b/src/mod/languages/mod_v8/include/fsglobal.hpp
index 7095a12dd2..cf143ce5d7 100644
--- a/src/mod/languages/mod_v8/include/fsglobal.hpp
+++ b/src/mod/languages/mod_v8/include/fsglobal.hpp
@@ -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 */
diff --git a/src/mod/languages/mod_v8/include/javascript.hpp b/src/mod/languages/mod_v8/include/javascript.hpp
index a080a508de..800e5894af 100644
--- a/src/mod/languages/mod_v8/include/javascript.hpp
+++ b/src/mod/languages/mod_v8/include/javascript.hpp
@@ -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);
diff --git a/src/mod/languages/mod_v8/mod_v8.cpp b/src/mod/languages/mod_v8/mod_v8.cpp
index 8c0d6c0bef..49763e05c0 100644
--- a/src/mod/languages/mod_v8/mod_v8.cpp
+++ b/src/mod/languages/mod_v8/mod_v8.cpp
@@ -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
 
diff --git a/src/mod/languages/mod_v8/mod_v8.h b/src/mod/languages/mod_v8/mod_v8.h
index a333d6490d..820057f78f 100644
--- a/src/mod/languages/mod_v8/mod_v8.h
+++ b/src/mod/languages/mod_v8/mod_v8.h
@@ -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);
diff --git a/src/mod/languages/mod_v8/src/fsglobal.cpp b/src/mod/languages/mod_v8/src/fsglobal.cpp
index 89f94b40cb..99a0626f8f 100644
--- a/src/mod/languages/mod_v8/src/fsglobal.cpp
+++ b/src/mod/languages/mod_v8/src/fsglobal.cpp
@@ -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}
 };
 
diff --git a/src/mod/languages/mod_v8/src/jsmain.cpp b/src/mod/languages/mod_v8/src/jsmain.cpp
index 86fed7b27f..d473d9f085 100644
--- a/src/mod/languages/mod_v8/src/jsmain.cpp
+++ b/src/mod/languages/mod_v8/src/jsmain.cpp
@@ -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();