Introduce generic backtrace capturing (#4555)

Remove jerry_get_backtrace_from function

JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
Zoltan Herczeg
2021-02-04 16:33:59 +01:00
committed by GitHub
parent feb2855f0c
commit 3623ac789d
8 changed files with 673 additions and 297 deletions
+422 -144
View File
@@ -245,6 +245,15 @@ memory blocks but the performance may drop after the garbage collection.
*New in version 2.0*.
## jerry_backtrace_frame_types_t
List of backtrace frame types returned by
[jerry_backtrace_get_frame_type](#jerry_backtrace_get_frame_type).
- JERRY_BACKTRACE_FRAME_JS - indicates that the frame is created for a JavaScript function/method
*New in version [[NEXT_RELEASE]]*.
## jerry_generate_snapshot_opts_t
Flags for [jerry_generate_snapshot](#jerry_generate_snapshot) and
@@ -524,6 +533,50 @@ typedef struct
- [jerry_define_own_property](#jerry_define_own_property)
## jerry_backtrace_location_t
**Summary**
Source code location data retreived by
[jerry_backtrace_get_location](#jerry_backtrace_get_location).
**Prototype**
```c
typedef struct
{
jerry_value_t resource_name; /**< resource name */
jerry_size_t line; /**< line index */
jerry_size_t column; /**< column index */
} jerry_backtrace_location_t;
```
*New in version [[NEXT_RELEASE]]*.
## jerry_backtrace_frame_t
**Summary**
Backtrace frame data passed to the [jerry_backtrace_callback_t](#jerry_backtrace_callback_t)
handler. This is an internal data structure which fields can be accessed by helper functions
such as [jerry_backtrace_get_location](#jerry_backtrace_get_location).
**Prototype**
```c
/**
* Internal data structure for jerry_backtrace_frame_t definition.
*/
struct jerry_backtrace_frame_internal_t;
/**
* Backtrace frame data passed to the jerry_backtrace_callback_t handler.
*/
typedef struct jerry_backtrace_frame_internal_t jerry_backtrace_frame_t;
```
*New in version [[NEXT_RELEASE]]*.
## jerry_heap_stats_t
**Summary**
@@ -627,6 +680,32 @@ typedef void (*jerry_error_object_created_callback_t) (const jerry_value_t error
- [jerry_set_error_object_created_callback](#jerry_set_error_object_created_callback)
## jerry_backtrace_callback_t
**Summary**
Callback function which is called by [jerry_backtrace_capture](#jerry_backtrace_capture)
for each stack frame.
**Prototype**
```c
typedef bool (*jerry_backtrace_callback_t) (jerry_backtrace_frame_t *frame_p, void *user_p);
```
- `frame_p` - pointer to [jerry_backtrace_frame_t](#jerry_backtrace_frame_t) data.
- `user_p` - pointer passed to [jerry_backtrace_capture](#jerry_backtrace_capture).
- return value
- true, to continue capturing more frames
- false, to end the stack capturing
*New in version [[NEXT_RELEASE]]*.
**See also**
- [jerry_backtrace_capture](#jerry_backtrace_capture)
- [jerry_backtrace_frame_t](#jerry_backtrace_frame_t)
## jerry_object_native_info_t
**Summary**
@@ -8931,90 +9010,7 @@ main (void)
- [jerry_register_magic_strings](#jerry_register_magic_strings)
# Miscellaneous functions
## jerry_set_vm_exec_stop_callback
**Summary**
When JERRY_FEATURE_VM_EXEC_STOP is enabled a callback function can be
specified by this function. This callback is periodically called when
JerryScript executes an ECMAScript program.
If the callback returns with undefined value the ECMAScript execution
continues. Otherwise the result is thrown by the engine (if the error
flag is not set for the returned value the engine automatically sets
it). The callback function might be called again even if it threw
an error. In this case the function must throw the same error again.
To reduce the CPU overhead of constantly checking the termination
condition the callback is called when a backward jump is executed
or an exception is caught. Setting the `frequency` to a greater
than `1` value reduces this overhead further. If its value is N
only every Nth event (backward jump, etc.) trigger the next check.
**Prototype**
```c
void
jerry_set_vm_exec_stop_callback (jerry_vm_exec_stop_callback_t stop_cb,
void *user_p,
uint32_t frequency);
```
- `stop_cb` - periodically called callback (passing NULL disables this feature)
- `user_p` - user pointer passed to the `stop_cb` function
- `frequency` - frequency of calling the `stop_cb` function
*New in version 2.0*.
**Example**
[doctest]: # (test="link")
```c
#include "jerryscript.h"
static int countdown = 10;
static jerry_value_t
vm_exec_stop_callback (void *user_p)
{
while (countdown > 0)
{
countdown--;
return jerry_create_undefined ();
}
// The error flag is added automatically.
return jerry_create_string ((const jerry_char_t *) "Abort script");
}
int
main (void)
{
jerry_init (JERRY_INIT_EMPTY);
jerry_set_vm_exec_stop_callback (vm_exec_stop_callback, &countdown, 16);
// Inifinte loop.
const jerry_char_t script[] = "while(true) {}";
jerry_value_t parsed_code = jerry_parse (NULL, 0, script, sizeof (script) - 1, JERRY_PARSE_NO_OPTS);
jerry_release_value (jerry_run (parsed_code));
jerry_release_value (parsed_code);
jerry_cleanup ();
}
```
**See also**
- [jerry_init](#jerry_init)
- [jerry_cleanup](#jerry_cleanup)
- [jerry_parse](#jerry_parse)
- [jerry_run](#jerry_run)
- [jerry_vm_exec_stop_callback_t](#jerry_vm_exec_stop_callback_t)
# Backtrace functions
## jerry_get_backtrace
@@ -9099,7 +9095,7 @@ main (void)
jerry_value_t global = jerry_get_global_object ();
/* Register the "dump_backtrace" method. */
/* Register the "capture_backtrace" method. */
{
jerry_value_t func = jerry_create_external_function (backtrace_handler);
jerry_value_t name = jerry_create_string ((const jerry_char_t *) "backtrace");
@@ -9141,90 +9137,58 @@ main (void)
- [jerry_create_external_function](#jerry_create_external_function)
## jerry_get_backtrace_from
## jerry_backtrace_capture
**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).
Low-level function to capture each backtrace frame. The captured frame data
is passed to a callback function. To improve performance, the majority of
the frame data is not initialized when the callback function is called. The
initialization of these fields can be done later by helper functions such
as [jerry_backtrace_get_location](#jerry_backtrace_get_location).
**Prototype**
```c
jerry_value_t
jerry_get_backtrace_from (uint32_t max_depth, jerry_value_t ignored_function);
void
jerry_backtrace_capture (jerry_backtrace_callback_t callback, void *user_p);
```
- `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
- `callback` - a [jerry_backtrace_callback_t](#jerry_backtrace_callback_t) callback
which is called for each captured frame
- `user_p` - pointer passed to the `callback` function, can be NULL
*New in version 2.4*.
*New in version [[NEXT_RELEASE]]*.
**Example**
[doctest]: # (name="02.API-REFERENCE-jsbacktracefrom.c")
[doctest]: # (name="02.API-REFERENCE-jscapturebacktrace.c")
```c
#include <stdio.h>
#include <string.h>
#include "jerryscript.h"
static bool
backtrace_callback (jerry_backtrace_frame_t *frame_p,
void *user_p)
{
printf (" A stack frame is captured\n");
return true;
}
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 ();
}
(void) function_obj;
(void) this_val;
(void) args_p;
(void) args_count;
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);
jerry_backtrace_capture (&backtrace_callback, NULL);
return jerry_create_undefined ();
} /* backtrace_handler */
@@ -9252,7 +9216,7 @@ main (void)
"function g() { h (); }\n"
"function h() { backtrace (g); }\n"
"f ();\n");
const char *resource = "demo_memoryjs";
const char *resource = "demo_backtrace.js";
jerry_value_t program = jerry_parse ((const jerry_char_t *) resource,
strlen (resource),
@@ -9275,8 +9239,322 @@ main (void)
**See also**
- [jerry_get_backtrace](#jerry_get_backtrace)
- [jerry_backtrace_get_frame_type](#jerry_backtrace_get_frame_type)
- [jerry_backtrace_get_location](#jerry_backtrace_get_location)
- [jerry_backtrace_get_function](#jerry_backtrace_get_function)
- [jerry_backtrace_is_strict](#jerry_backtrace_is_strict)
## jerry_backtrace_get_frame_type
**Summary**
Returns with the type of the backtrace frame. This function can only be called
from the callback function of [jerry_backtrace_capture](#jerry_backtrace_capture),
and the value becomes invalid after the callback returns.
**Prototype**
```c
jerry_backtrace_frame_types_t
jerry_backtrace_get_frame_type (jerry_backtrace_frame_t *frame_p);
```
- `frame_p` - a frame passed to the [jerry_backtrace_callback_t](#jerry_backtrace_callback_t) callback
- return value
- frame type listed in [jerry_backtrace_frame_types_t](#jerry_backtrace_frame_types_t)
*New in version [[NEXT_RELEASE]]*.
**Example**
See the example of [jerry_backtrace_capture](#jerry_backtrace_capture)
with the following callback function:
```c
static bool
backtrace_callback (jerry_backtrace_frame_t *frame_p,
void *user_p)
{
switch (jerry_backtrace_get_frame_type (frame_p))
{
case JERRY_BACKTRACE_FRAME_JS:
{
printf (" ECMAScript frame\n");
break;
}
default:
{
printf (" Other frame\n");
break;
}
}
return true;
}
```
**See also**
- [jerry_backtrace_capture](#jerry_backtrace_capture)
## jerry_backtrace_get_location
**Summary**
Initialize and return with the location private field of a backtrace
frame. If the location is not available, the returned value is NULL.
This function can only be called from the callback function of
[jerry_backtrace_capture](#jerry_backtrace_capture), and the value
becomes invalid after the callback returns.
*Notes*:
- Location information can only be retrieved if JERRY_FEATURE_LINE_INFO feature is
enabled. Otherwise the function always returns with NULL.
- The returned data must not be modified, and does not need to be freed.
Any cleanup is done automatically after the callback is returned.
**Prototype**
```c
const jerry_backtrace_location_t *
jerry_backtrace_get_location (jerry_backtrace_frame_t *frame_p);
```
- `frame_p` - a frame passed to the [jerry_backtrace_callback_t](#jerry_backtrace_callback_t) callback
- return value
- pointer to the location private field if the location is available,
- NULL otherwise
*New in version [[NEXT_RELEASE]]*.
**Example**
See the example of [jerry_backtrace_capture](#jerry_backtrace_capture)
with the following callback function:
```c
static bool
backtrace_callback (jerry_backtrace_frame_t *frame_p,
void *user_p)
{
const jerry_backtrace_location_t *location_p;
location_p = jerry_backtrace_get_location (frame_p);
if (location_p == NULL)
{
printf ("No location info is available\n");
return true;
}
jerry_char_t string_buffer[64];
jerry_size_t copied_bytes = jerry_substring_to_char_buffer (location_p->resource_name,
0,
63,
string_buffer,
63);
string_buffer[copied_bytes] = '\0';
printf(" %s:%d:%d\n", string_buffer, (int) location_p->line, (int) location_p->column);
return true;
}
```
**See also**
- [jerry_backtrace_capture](#jerry_backtrace_capture)
## jerry_backtrace_get_function
**Summary**
Initialize and return with the called function private field of a backtrace frame.
The backtrace frame is created for running the code bound to this function. This
function can only be called from the callback function of
[jerry_backtrace_capture](#jerry_backtrace_capture), and the value becomes invalid
after the callback returns.
*Notes*:
- The returned data must not be modified, and does not need to be freed.
Any cleanup is done automatically after the callback is returned.
**Prototype**
```c
const jerry_value_t *
jerry_backtrace_get_function (jerry_backtrace_frame_t *frame_p);
```
- `frame_p` - a frame passed to the [jerry_backtrace_callback_t](#jerry_backtrace_callback_t) callback
- return value
- pointer to the called function if the function is available,
- NULL otherwise
*New in version [[NEXT_RELEASE]]*.
**Example**
See the example of [jerry_backtrace_capture](#jerry_backtrace_capture)
with the following callback function:
```c
static bool
backtrace_callback (jerry_backtrace_frame_t *frame_p,
void *user_p)
{
jerry_value_t *function_p = jerry_backtrace_get_function (frame_p);
if (function_p != NULL)
{
printf ("Called function is available");
return true;
}
printf ("Called function is NOT available");
return true;
}
```
**See also**
- [jerry_backtrace_capture](#jerry_backtrace_capture)
## jerry_backtrace_is_strict
**Summary**
Returns true, if the code bound to the backtrace frame is strict mode
code. This function can only be called from the callback function of
[jerry_backtrace_capture](#jerry_backtrace_capture), and the value
becomes invalid after the callback returns.
**Prototype**
```c
bool
jerry_backtrace_is_strict (jerry_backtrace_frame_t *frame_p);
```
- `frame_p` - a frame passed to the [jerry_backtrace_callback_t](#jerry_backtrace_callback_t) callback
- return value
- true, if strict mode code is bound to the frame
- false, otherwise
*New in version [[NEXT_RELEASE]]*.
**Example**
See the example of [jerry_backtrace_capture](#jerry_backtrace_capture)
with the following callback function:
```c
static bool
backtrace_callback (jerry_backtrace_frame_t *frame_p,
void *user_p)
{
if (jerry_backtrace_is_strict (frame_p))
{
printf ("Strict mode code is running");
return truel
}
printf ("Non-strict mode code is running");
return true;
}
```
**See also**
- [jerry_backtrace_capture](#jerry_backtrace_capture)
# Miscellaneous functions
## jerry_set_vm_exec_stop_callback
**Summary**
When JERRY_FEATURE_VM_EXEC_STOP is enabled a callback function can be
specified by this function. This callback is periodically called when
JerryScript executes an ECMAScript program.
If the callback returns with undefined value the ECMAScript execution
continues. Otherwise the result is thrown by the engine (if the error
flag is not set for the returned value the engine automatically sets
it). The callback function might be called again even if it threw
an error. In this case the function must throw the same error again.
To reduce the CPU overhead of constantly checking the termination
condition the callback is called when a backward jump is executed
or an exception is caught. Setting the `frequency` to a greater
than `1` value reduces this overhead further. If its value is N
only every Nth event (backward jump, etc.) trigger the next check.
**Prototype**
```c
void
jerry_set_vm_exec_stop_callback (jerry_vm_exec_stop_callback_t stop_cb,
void *user_p,
uint32_t frequency);
```
- `stop_cb` - periodically called callback (passing NULL disables this feature)
- `user_p` - user pointer passed to the `stop_cb` function
- `frequency` - frequency of calling the `stop_cb` function
*New in version 2.0*.
**Example**
[doctest]: # (test="link")
```c
#include "jerryscript.h"
static int countdown = 10;
static jerry_value_t
vm_exec_stop_callback (void *user_p)
{
while (countdown > 0)
{
countdown--;
return jerry_create_undefined ();
}
// The error flag is added automatically.
return jerry_create_string ((const jerry_char_t *) "Abort script");
}
int
main (void)
{
jerry_init (JERRY_INIT_EMPTY);
jerry_set_vm_exec_stop_callback (vm_exec_stop_callback, &countdown, 16);
// Infinite loop.
const jerry_char_t script[] = "while(true) {}";
jerry_value_t parsed_code = jerry_parse (NULL, 0, script, sizeof (script) - 1, JERRY_PARSE_NO_OPTS);
jerry_release_value (jerry_run (parsed_code));
jerry_release_value (parsed_code);
jerry_cleanup ();
}
```
**See also**
- [jerry_init](#jerry_init)
- [jerry_cleanup](#jerry_cleanup)
- [jerry_parse](#jerry_parse)
- [jerry_run](#jerry_run)
- [jerry_vm_exec_stop_callback_t](#jerry_vm_exec_stop_callback_t)
## jerry_get_resource_name
**Summary**
+87 -29
View File
@@ -4681,51 +4681,109 @@ 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, NULL);
return vm_get_backtrace (max_depth);
} /* 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
* Low-level function to capture each backtrace frame.
* The captured frame data is passed to a callback function.
*/
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 */
void
jerry_backtrace_capture (jerry_backtrace_callback_t callback, /**< callback function */
void *user_p) /**< user pointer passed to the callback function */
{
ecma_object_t *ignored_function_p = NULL;
jerry_backtrace_frame_t frame;
vm_frame_ctx_t *context_p = JERRY_CONTEXT (vm_top_context_p);
if (ecma_is_value_object (ignored_function))
while (context_p != NULL)
{
ignored_function_p = ecma_get_object_from_value (ignored_function);
frame.context_p = context_p;
frame.frame_type = JERRY_BACKTRACE_FRAME_JS;
while (true)
if (!callback (&frame, user_p))
{
ecma_object_type_t type = ecma_get_object_type (ignored_function_p);
return;
}
if (type == ECMA_OBJECT_TYPE_FUNCTION || type == ECMA_OBJECT_TYPE_NATIVE_FUNCTION)
{
break;
}
context_p = context_p->prev_context_p;
}
} /* jerry_backtrace_capture */
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;
/**
* Returns with the type of the backtrace frame.
*
* @return frame type listed in jerry_backtrace_frame_types_t
*/
jerry_backtrace_frame_types_t
jerry_backtrace_get_frame_type (jerry_backtrace_frame_t *frame_p) /**< frame pointer */
{
return (jerry_backtrace_frame_types_t) frame_p->frame_type;
} /* jerry_backtrace_get_frame_type */
ignored_function_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, target_function);
continue;
}
/**
* Initialize and return with the location private field of a backtrace frame.
*
* @return pointer to the location private field - if the location is available,
* NULL - otherwise
*/
const jerry_backtrace_location_t *
jerry_backtrace_get_location (jerry_backtrace_frame_t *frame_p) /**< frame pointer */
{
JERRY_UNUSED (frame_p);
ignored_function_p = NULL;
break;
#if ENABLED (JERRY_LINE_INFO)
if (frame_p->frame_type == JERRY_BACKTRACE_FRAME_JS)
{
vm_frame_ctx_t *context_p = frame_p->context_p;
frame_p->location.resource_name = ecma_get_resource_name (context_p->shared_p->bytecode_header_p);
frame_p->location.line = context_p->current_line;
frame_p->location.column = 1;
return &frame_p->location;
}
#endif /* ENABLED (JERRY_LINE_INFO) */
return NULL;
} /* jerry_backtrace_get_location */
/**
* Initialize and return with the called function private field of a backtrace frame.
* The backtrace frame is created for running the code bound to this function.
*
* @return pointer to the called function - if the function is available,
* NULL - otherwise
*/
const jerry_value_t *
jerry_backtrace_get_function (jerry_backtrace_frame_t *frame_p) /**< frame pointer */
{
if (frame_p->frame_type == JERRY_BACKTRACE_FRAME_JS)
{
vm_frame_ctx_t *context_p = frame_p->context_p;
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;
frame_p->function = ecma_make_object_value (shared_args_p->function_object_p);
return &frame_p->function;
}
}
return vm_get_backtrace (max_depth, ignored_function_p);
} /* jerry_get_backtrace_from */
return NULL;
} /* jerry_backtrace_get_function */
/**
* Returns true, if the code bound to the backtrace frame is strict mode code.
*
* @return true - if strict mode code is bound to the frame,
* false - otherwise
*/
bool
jerry_backtrace_is_strict (jerry_backtrace_frame_t *frame_p) /**< frame pointer */
{
return (frame_p->frame_type == JERRY_BACKTRACE_FRAME_JS
&& (frame_p->context_p->status_flags & VM_FRAME_CTX_IS_STRICT) != 0);
} /* jerry_backtrace_is_strict */
/**
* Get the resource name (usually a file name) of the currently executed script or the given function object
+1 -1
View File
@@ -177,7 +177,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, NULL);
ecma_value_t backtrace_value = vm_get_backtrace (0);
prop_value_p->value = backtrace_value;
ecma_deref_object (ecma_get_object_from_value (backtrace_value));
+43 -2
View File
@@ -205,6 +205,34 @@ typedef struct
jerry_value_t setter;
} jerry_property_descriptor_t;
/**
* List of backtrace frame types returned by jerry_backtrace_get_frame_type.
*/
typedef enum
{
JERRY_BACKTRACE_FRAME_JS, /**< indicates that the frame is created for a JavaScript function/method */
} jerry_backtrace_frame_types_t;
/**
* Location info retreived by jerry_backtrace_get_location.
*/
typedef struct
{
jerry_value_t resource_name; /**< resource name */
jerry_size_t line; /**< line index */
jerry_size_t column; /**< column index */
} jerry_backtrace_location_t;
/**
* Internal data structure for jerry_backtrace_frame_t definition.
*/
struct jerry_backtrace_frame_internal_t;
/**
* Backtrace frame data passed to the jerry_backtrace_callback_t handler.
*/
typedef struct jerry_backtrace_frame_internal_t jerry_backtrace_frame_t;
/**
* Description of JerryScript heap memory stats.
* It is for memory profiling.
@@ -237,6 +265,11 @@ typedef void (*jerry_object_native_free_callback_t) (void *native_p);
*/
typedef void (*jerry_error_object_created_callback_t) (const jerry_value_t error_object, void *user_p);
/**
* Callback function which is called by jerry_backtrace_capture for each stack frame.
*/
typedef bool (*jerry_backtrace_callback_t) (jerry_backtrace_frame_t *frame_p, void *user_p);
/**
* Callback which tells whether the ECMAScript execution should be stopped.
*
@@ -765,12 +798,20 @@ void jerry_heap_free (void *mem_p, size_t size);
*/
jerry_context_t *jerry_create_context (uint32_t heap_size, jerry_context_alloc_t alloc, void *cb_data_p);
/**
* Backtrace functions.
*/
jerry_value_t jerry_get_backtrace (uint32_t max_depth);
void jerry_backtrace_capture (jerry_backtrace_callback_t callback, void *user_p);
jerry_backtrace_frame_types_t jerry_backtrace_get_frame_type (jerry_backtrace_frame_t *frame_p);
const jerry_backtrace_location_t *jerry_backtrace_get_location (jerry_backtrace_frame_t *frame_p);
const jerry_value_t *jerry_backtrace_get_function (jerry_backtrace_frame_t *frame_p);
bool jerry_backtrace_is_strict (jerry_backtrace_frame_t *frame_p);
/**
* Miscellaneous functions.
*/
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);
+11
View File
@@ -151,6 +151,17 @@ typedef struct
vm_frame_ctx_t frame_ctx; /**< frame context part */
} vm_executable_object_t;
/**
* Real backtrace frame data passed to the jerry_backtrace_callback_t handler.
*/
struct jerry_backtrace_frame_internal_t
{
vm_frame_ctx_t *context_p; /**< context pointer */
uint8_t frame_type; /**< frame type */
jerry_backtrace_location_t location; /**< location information */
ecma_value_t function; /**< function reference */
};
/**
* @}
* @}
+1 -31
View File
@@ -59,40 +59,11 @@ 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 */
ecma_object_t *ignored_function_p) /**< ignore functions up to this function */
vm_get_backtrace (uint32_t max_depth) /**< maximum backtrace depth, 0 = unlimited */
{
#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;
@@ -138,7 +109,6 @@ 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
@@ -489,7 +489,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_object_t *ignored_function_p);
ecma_value_t vm_get_backtrace (uint32_t max_depth);
/**
* @}
+107 -89
View File
@@ -33,14 +33,109 @@ backtrace_handler (const jerry_value_t function_obj, /**< function object */
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 */
static void
compare_string (jerry_value_t left_value, /* string value */
const char *right_p) /* string to compare */
{
jerry_char_t buffer[64];
size_t length = strlen (right_p);
TEST_ASSERT (length <= sizeof (buffer));
TEST_ASSERT (jerry_value_is_string (left_value));
TEST_ASSERT (jerry_get_string_size (left_value) == length);
TEST_ASSERT (jerry_string_to_char_buffer (left_value, buffer, sizeof (buffer)) == length);
TEST_ASSERT (memcmp (buffer, right_p, length) == 0);
} /* compare_string */
static const jerry_value_t *handler_args_p;
static int frame_index;
static bool
backtrace_callback (jerry_backtrace_frame_t *frame_p, /* frame information */
void *user_p) /* user data */
{
TEST_ASSERT ((void *) handler_args_p == user_p);
TEST_ASSERT (jerry_backtrace_get_frame_type (frame_p) == JERRY_BACKTRACE_FRAME_JS);
const jerry_backtrace_location_t *location_p = jerry_backtrace_get_location (frame_p);
const jerry_value_t *function_p = jerry_backtrace_get_function (frame_p);
TEST_ASSERT (location_p != NULL);
TEST_ASSERT (function_p != NULL);
compare_string (location_p->resource_name, "capture_test.js");
++frame_index;
if (frame_index == 1)
{
TEST_ASSERT (!jerry_backtrace_is_strict (frame_p));
TEST_ASSERT (location_p->line == 2);
TEST_ASSERT (location_p->column == 1);
TEST_ASSERT (handler_args_p[0] == *function_p);
return true;
}
if (frame_index == 2)
{
TEST_ASSERT (jerry_backtrace_is_strict (frame_p));
TEST_ASSERT (location_p->line == 7);
TEST_ASSERT (location_p->column == 1);
TEST_ASSERT (handler_args_p[1] == *function_p);
return true;
}
TEST_ASSERT (frame_index == 3);
TEST_ASSERT (!jerry_backtrace_is_strict (frame_p));
TEST_ASSERT (location_p->line == 11);
TEST_ASSERT (location_p->column == 1);
TEST_ASSERT (handler_args_p[2] == *function_p);
return false;
} /* backtrace_callback */
static jerry_value_t
capture_handler (const jerry_value_t function_obj, /**< function object */
const jerry_value_t this_val, /**< this value */
const jerry_value_t args_p[], /**< argument list */
const jerry_length_t args_count) /**< argument count */
{
JERRY_UNUSED (function_obj);
JERRY_UNUSED (this_val);
JERRY_UNUSED (args_p);
JERRY_UNUSED (args_count);
TEST_ASSERT (args_count == 3);
frame_index = 0;
handler_args_p = args_p;
jerry_backtrace_capture (backtrace_callback, (void *) args_p);
TEST_ASSERT (frame_index == 3);
return jerry_create_undefined ();
} /* capture_handler */
static void
register_callback (jerry_external_handler_t handler_p, /**< callback function */
char *name_p) /**< name of the function */
{
jerry_value_t global = jerry_get_global_object ();
jerry_value_t func = jerry_create_external_function (handler_p);
jerry_value_t name = jerry_create_string ((const jerry_char_t *) name_p);
jerry_value_t result = jerry_set_property (global, name, func);
TEST_ASSERT (!jerry_value_is_error (result));
jerry_release_value (result);
jerry_release_value (name);
jerry_release_value (func);
jerry_release_value (global);
} /* register_callback */
static jerry_value_t
run (const char *resource_name_p, /**< resource name */
const char *source_p) /**< source code */
@@ -89,18 +184,8 @@ test_get_backtrace_api_call (void)
{
jerry_init (JERRY_INIT_EMPTY);
jerry_value_t global = jerry_get_global_object ();
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);
TEST_ASSERT (!jerry_value_is_error (result));
jerry_release_value (result);
jerry_release_value (name);
jerry_release_value (func);
jerry_release_value (global);
register_callback (backtrace_handler, "backtrace");
register_callback (capture_handler, "capture");
const char *source = ("function f() {\n"
" return backtrace(0);\n"
@@ -158,13 +243,14 @@ test_get_backtrace_api_call (void)
jerry_release_value (backtrace);
/* Ignore f and g this time. */
/* Test frame capturing. */
source = ("function f() {\n"
" return backtrace(0, g);\n"
" return capture(f, g, h);\n"
"}\n"
"\n"
"function g() {\n"
" 'use strict';\n"
" return f();\n"
"}\n"
"\n"
@@ -174,77 +260,9 @@ test_get_backtrace_api_call (void)
"\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");
backtrace = run ("capture_test.js", source);
TEST_ASSERT (jerry_value_is_undefined (backtrace));
jerry_release_value (backtrace);
jerry_cleanup ();