Implement jerry_get_backtrace_from API function (#4454)

JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
Zoltan Herczeg
2021-01-12 19:52:05 +01:00
committed by GitHub
parent c46f4c3e40
commit 3193e6d0dc
7 changed files with 313 additions and 6 deletions
+138
View File
@@ -8876,6 +8876,7 @@ backtrace_handler (const jerry_value_t function_obj,
if (!jerry_is_feature_enabled (JERRY_FEATURE_LINE_INFO))
{
printf ("Line info disabled, no backtrace will be printed\n");
return jerry_create_undefined ();
}
/* If the line info feature is disabled an empty array will be returned. */
@@ -8948,9 +8949,146 @@ main (void)
**See also**
- [jerry_get_backtrace_from](#jerry_get_backtrace_from)
- [jerry_create_external_function](#jerry_create_external_function)
## jerry_get_backtrace_from
**Summary**
Get backtrace. The backtrace is an array of strings where
each string contains the position of the corresponding frame.
The array length is zero if the backtrace is not available.
Collecting the trace starts after the function specified in
the `ignored_function` parameter. This parameter can be used to
skip the helper function(s) which collects the backtrace from
the backtrace data.
*Notes*:
- Returned value must be freed with [jerry_release_value](#jerry_release_value) when it
is no longer needed.
- This feature depends on build option (`JERRY_LINE_INFO`) and can be checked
in runtime with the `JERRY_FEATURE_LINE_INFO` feature enum value,
see: [jerry_is_feature_enabled](#jerry_is_feature_enabled).
**Prototype**
```c
jerry_value_t
jerry_get_backtrace_from (uint32_t max_depth, jerry_value_t ignored_function);
```
- `max_depth` - backtrace collection stops after reaching this value, 0 = unlimited
- `ignored_function` - if this function is present in the backtrace, the backtrace
only contains the stack frames after the topmost instance of
this function. Otherwise this parameter is ignored
- return value
- a newly constructed JS array
*New in version [[NEXT_RELEASE]]*.
**Example**
[doctest]: # (name="02.API-REFERENCE-jsbacktracefrom.c")
```c
#include <stdio.h>
#include <string.h>
#include "jerryscript.h"
static jerry_value_t
backtrace_handler (const jerry_value_t function_obj,
const jerry_value_t this_val,
const jerry_value_t args_p[],
const jerry_length_t args_count)
{
if (!jerry_is_feature_enabled (JERRY_FEATURE_LINE_INFO))
{
printf ("Line info disabled, no backtrace will be printed\n");
return jerry_create_undefined ();
}
if (args_count < 1)
{
printf ("Ignored function is not specified\n");
return jerry_create_undefined ();
}
/* If the line info feature is disabled an empty array will be returned. */
jerry_value_t backtrace_array = jerry_get_backtrace_from (0, args_p[0]);
uint32_t array_length = jerry_get_array_length (backtrace_array);
for (uint32_t idx = 0; idx < array_length; idx++)
{
jerry_value_t property = jerry_get_property_by_index (backtrace_array, idx);
jerry_char_t string_buffer[64];
jerry_size_t copied_bytes = jerry_substring_to_char_buffer (property,
0,
63,
string_buffer,
63);
string_buffer[copied_bytes] = '\0';
printf(" %d: %s\n", idx, string_buffer);
jerry_release_value (property);
}
jerry_release_value (backtrace_array);
return jerry_create_undefined ();
} /* backtrace_handler */
int
main (void)
{
jerry_init (JERRY_INIT_EMPTY);
jerry_value_t global = jerry_get_global_object ();
/* Register the "dump_backtrace" method. */
{
jerry_value_t func = jerry_create_external_function (backtrace_handler);
jerry_value_t name = jerry_create_string ((const jerry_char_t *) "backtrace");
jerry_value_t result = jerry_set_property (global, name, func);
jerry_release_value (result);
jerry_release_value (name);
jerry_release_value (func);
}
jerry_release_value (global);
const char *source = ("function f() { g (); }\n"
"function g() { h (); }\n"
"function h() { backtrace (g); }\n"
"f ();\n");
const char *resource = "demo_memoryjs";
jerry_value_t program = jerry_parse ((const jerry_char_t *) resource,
strlen (resource),
(const jerry_char_t *) source,
strlen (source),
JERRY_PARSE_NO_OPTS);
if (!jerry_value_is_error (program))
{
jerry_value_t run_result = jerry_run (program);
jerry_release_value (run_result);
}
jerry_release_value (program);
jerry_cleanup ();
return 0;
}
```
**See also**
- [jerry_get_backtrace](#jerry_get_backtrace)
## jerry_get_resource_name
**Summary**
+44 -1
View File
@@ -4523,9 +4523,52 @@ jerry_set_vm_exec_stop_callback (jerry_vm_exec_stop_callback_t stop_cb, /**< per
jerry_value_t
jerry_get_backtrace (uint32_t max_depth) /**< depth limit of the backtrace */
{
return vm_get_backtrace (max_depth);
return vm_get_backtrace (max_depth, NULL);
} /* jerry_get_backtrace */
/**
* Get backtrace. The backtrace is an array of strings where
* each string contains the position of the corresponding frame.
* The array length is zero if the backtrace is not available.
*
* @return array value
*/
jerry_value_t
jerry_get_backtrace_from (uint32_t max_depth, /**< depth limit of the backtrace */
jerry_value_t ignored_function) /**< collect backtrace after this function */
{
ecma_object_t *ignored_function_p = NULL;
if (ecma_is_value_object (ignored_function))
{
ignored_function_p = ecma_get_object_from_value (ignored_function);
while (true)
{
ecma_object_type_t type = ecma_get_object_type (ignored_function_p);
if (type == ECMA_OBJECT_TYPE_FUNCTION || type == ECMA_OBJECT_TYPE_NATIVE_FUNCTION)
{
break;
}
if (type == ECMA_OBJECT_TYPE_BOUND_FUNCTION)
{
ecma_bound_function_t *bound_func_p = (ecma_bound_function_t *) ignored_function_p;
jmem_cpointer_tag_t target_function = bound_func_p->header.u.bound_function.target_function;
ignored_function_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, target_function);
continue;
}
ignored_function_p = NULL;
break;
}
}
return vm_get_backtrace (max_depth, ignored_function_p);
} /* jerry_get_backtrace_from */
/**
* Get the resource name (usually a file name) of the currently executed script or the given function object
*
+1 -1
View File
@@ -152,7 +152,7 @@ ecma_new_standard_error (ecma_standard_error_t error_type) /**< native error typ
NULL);
ecma_deref_ecma_string (stack_str_p);
ecma_value_t backtrace_value = vm_get_backtrace (0);
ecma_value_t backtrace_value = vm_get_backtrace (0, NULL);
prop_value_p->value = backtrace_value;
ecma_deref_object (ecma_get_object_from_value (backtrace_value));
+1
View File
@@ -750,6 +750,7 @@ jerry_context_t *jerry_create_context (uint32_t heap_size, jerry_context_alloc_t
*/
void jerry_set_vm_exec_stop_callback (jerry_vm_exec_stop_callback_t stop_cb, void *user_p, uint32_t frequency);
jerry_value_t jerry_get_backtrace (uint32_t max_depth);
jerry_value_t jerry_get_backtrace_from (uint32_t max_depth, jerry_value_t ignored_function);
jerry_value_t jerry_get_resource_name (const jerry_value_t value);
jerry_value_t jerry_get_new_target (void);
+33 -2
View File
@@ -59,15 +59,45 @@ vm_is_direct_eval_form_call (void)
* @return array ecma value
*/
ecma_value_t
vm_get_backtrace (uint32_t max_depth) /**< maximum backtrace depth, 0 = unlimited */
vm_get_backtrace (uint32_t max_depth, /**< maximum backtrace depth, 0 = unlimited */
ecma_object_t *ignored_function_p) /**< ignore functions up to this function */
{
#if ENABLED (JERRY_LINE_INFO)
vm_frame_ctx_t *context_p = JERRY_CONTEXT (vm_top_context_p);
if (ignored_function_p != NULL)
{
JERRY_ASSERT (ecma_get_object_type (ignored_function_p) == ECMA_OBJECT_TYPE_FUNCTION
|| ecma_get_object_type (ignored_function_p) == ECMA_OBJECT_TYPE_NATIVE_FUNCTION);
while (true)
{
if (context_p == NULL)
{
context_p = JERRY_CONTEXT (vm_top_context_p);
break;
}
if (context_p->shared_p->status_flags & VM_FRAME_CTX_SHARED_HAS_ARG_LIST)
{
vm_frame_ctx_shared_args_t *shared_args_p = (vm_frame_ctx_shared_args_t *) context_p->shared_p;
if (shared_args_p->function_object_p == ignored_function_p)
{
context_p = context_p->prev_context_p;
break;
}
}
context_p = context_p->prev_context_p;
}
}
if (max_depth == 0)
{
max_depth = UINT32_MAX;
}
vm_frame_ctx_t *context_p = JERRY_CONTEXT (vm_top_context_p);
ecma_object_t *array_p = ecma_op_new_array_object (0);
JERRY_ASSERT (ecma_op_object_is_fast_array (array_p));
uint32_t index = 0;
@@ -108,6 +138,7 @@ vm_get_backtrace (uint32_t max_depth) /**< maximum backtrace depth, 0 = unlimite
return ecma_make_object_value (array_p);
#else /* !ENABLED (JERRY_LINE_INFO) */
JERRY_UNUSED (max_depth);
JERRY_UNUSED (ignored_function_p);
return ecma_make_object_value (ecma_op_new_array_object (0));
#endif /* ENABLED (JERRY_LINE_INFO) */
+1 -1
View File
@@ -490,7 +490,7 @@ ecma_value_t vm_execute (vm_frame_ctx_t *frame_ctx_p);
bool vm_is_strict_mode (void);
bool vm_is_direct_eval_form_call (void);
ecma_value_t vm_get_backtrace (uint32_t max_depth);
ecma_value_t vm_get_backtrace (uint32_t max_depth, ecma_object_t *ignored_function_p);
/**
* @}
+95 -1
View File
@@ -28,11 +28,16 @@ backtrace_handler (const jerry_value_t function_obj, /**< function object */
uint32_t max_depth = 0;
if (args_count > 0 && jerry_value_is_number (args_p[0]))
if (args_count >= 1 && jerry_value_is_number (args_p[0]))
{
max_depth = (uint32_t) jerry_get_number_value (args_p[0]);
}
if (args_count >= 2)
{
return jerry_get_backtrace_from (max_depth, args_p[1]);
}
return jerry_get_backtrace (max_depth);
} /* backtrace_handler */
@@ -153,6 +158,95 @@ test_get_backtrace_api_call (void)
jerry_release_value (backtrace);
/* Ignore f and g this time. */
source = ("function f() {\n"
" return backtrace(0, g);\n"
"}\n"
"\n"
"function g() {\n"
" return f();\n"
"}\n"
"\n"
"function h() {\n"
" return g();\n"
"}\n"
"\n"
"h();\n");
backtrace = run ("something_ignore.js", source);
TEST_ASSERT (!jerry_value_is_error (backtrace)
&& jerry_value_is_array (backtrace));
TEST_ASSERT (jerry_get_array_length (backtrace) == 2);
compare (backtrace, 0, "something_ignore.js:10");
compare (backtrace, 1, "something_ignore.js:13");
jerry_release_value (backtrace);
/* Use bound function this time. */
source = ("function f() {\n"
" return backtrace(0, i);\n"
"}\n"
"\n"
"function g(u, v) {\n"
" return v();\n"
"}\n"
"\n"
"var h = g.bind(null, 0)\n"
"var i = h.bind(null, f)\n"
"\n"
"function j() {\n"
" return i();\n"
"}\n"
"\n"
"j();\n");
backtrace = run ("something_bound.js", source);
TEST_ASSERT (!jerry_value_is_error (backtrace)
&& jerry_value_is_array (backtrace));
TEST_ASSERT (jerry_get_array_length (backtrace) == 2);
compare (backtrace, 0, "something_bound.js:13");
compare (backtrace, 1, "something_bound.js:16");
jerry_release_value (backtrace);
/* Use invalid function this time. */
source = ("function f() {\n"
" return backtrace(0, ':)');\n"
"}\n"
"\n"
"function g() {\n"
" return f();\n"
"}\n"
"\n"
"function h() {\n"
" return g();\n"
"}\n"
"\n"
"h();\n");
backtrace = run ("nothing_ignore.js", source);
TEST_ASSERT (!jerry_value_is_error (backtrace)
&& jerry_value_is_array (backtrace));
TEST_ASSERT (jerry_get_array_length (backtrace) == 4);
compare (backtrace, 0, "nothing_ignore.js:2");
compare (backtrace, 1, "nothing_ignore.js:6");
compare (backtrace, 2, "nothing_ignore.js:10");
compare (backtrace, 3, "nothing_ignore.js:13");
jerry_release_value (backtrace);
jerry_cleanup ();
} /* test_get_backtrace_api_call */