rm dump profile
This commit is contained in:
335
source/quickjs.c
335
source/quickjs.c
@@ -96,8 +96,6 @@
|
||||
/* dump memory usage before running the garbage collector */
|
||||
// #define DUMP_MEM
|
||||
// #define DUMP_OBJECTS /* dump objects in JS_FreeContext */
|
||||
// #define DUMP_ATOMS /* dump atoms in JS_FreeContext */
|
||||
// #define DUMP_SHAPES /* dump shapes in JS_FreeContext */
|
||||
// #define DUMP_READ_OBJECT
|
||||
// #define DUMP_ROPE_REBALANCE
|
||||
|
||||
@@ -106,55 +104,6 @@
|
||||
/* test the GC by forcing it before each object allocation */
|
||||
// #define FORCE_GC_AT_MALLOC
|
||||
|
||||
#ifdef DUMP_PROFILE
|
||||
/* Profiling data structures (defined early since they're embedded in
|
||||
* JSRuntime/JSFunctionBytecode) */
|
||||
|
||||
/* Per-function profiling counters (cheap, always-on under DUMP_PROFILE) */
|
||||
typedef struct JSFunctionProfile {
|
||||
uint64_t entry_count; /* how many times this function was called */
|
||||
uint64_t self_ticks; /* bytecode instructions executed in this function */
|
||||
} JSFunctionProfile;
|
||||
|
||||
/* Forward declarations */
|
||||
typedef struct JSFunctionBytecode JSFunctionBytecode;
|
||||
typedef struct JSVarRef JSVarRef;
|
||||
typedef struct VMFrame VMFrame;
|
||||
|
||||
/* Sampling profiler sample */
|
||||
typedef struct JSProfileSample {
|
||||
JSFunctionBytecode *func; /* function being executed */
|
||||
uint32_t pc_offset; /* bytecode PC offset */
|
||||
uint32_t callsite_id; /* callsite id if available */
|
||||
} JSProfileSample;
|
||||
|
||||
#define MAX_PROFILE_SAMPLES 100000
|
||||
|
||||
/* Call site profiling record */
|
||||
typedef struct JSCallSite {
|
||||
uint32_t pc_offset;
|
||||
uint64_t hit_count;
|
||||
} JSCallSite;
|
||||
|
||||
/* Property access site profiling record */
|
||||
typedef struct JSPropSite {
|
||||
uint32_t pc_offset;
|
||||
JSValue atom; /* property key as JSValue */
|
||||
uint64_t hit_count;
|
||||
} JSPropSite;
|
||||
|
||||
/* Runtime-wide profiling state */
|
||||
typedef struct JSProfileState {
|
||||
/* Sampling profiler */
|
||||
JSProfileSample *samples;
|
||||
uint32_t sample_count;
|
||||
uint32_t sample_capacity;
|
||||
struct timeval last_sample_time;
|
||||
uint32_t sample_interval_us; /* microseconds between samples (1000-5000) */
|
||||
} JSProfileState;
|
||||
|
||||
#endif /* DUMP_PROFILE */
|
||||
|
||||
static inline JS_BOOL JS_VALUE_IS_TEXT (JSValue v) {
|
||||
int tag = JS_VALUE_GET_TAG (v);
|
||||
return tag == JS_TAG_STRING || tag == JS_TAG_STRING_IMM;
|
||||
@@ -165,14 +114,6 @@ static inline JS_BOOL JS_VALUE_IS_NUMBER (JSValue v) {
|
||||
return tag == JS_TAG_INT || tag == JS_TAG_FLOAT64;
|
||||
}
|
||||
|
||||
/* Property descriptor stub (legacy) */
|
||||
typedef struct JSPropertyDescriptor {
|
||||
int flags;
|
||||
JSValue value;
|
||||
JSValue getter;
|
||||
JSValue setter;
|
||||
} JSPropertyDescriptor;
|
||||
|
||||
/* JS_KEY_* macros: JSValue immediate ASCII strings for common property names.
|
||||
These replace JS_ATOM_* for use with the new JSValue-based property API. */
|
||||
#define _JS_KEY1(c1) \
|
||||
@@ -414,10 +355,6 @@ struct JSRuntime {
|
||||
|
||||
/* Record-key IDs (for K_REC keys) */
|
||||
uint32_t rec_key_next;
|
||||
|
||||
#ifdef DUMP_PROFILE
|
||||
JSProfileState profile;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct JSClass {
|
||||
@@ -1835,17 +1772,6 @@ typedef struct JSFunctionBytecode {
|
||||
uint8_t *pc2line_buf;
|
||||
char *source;
|
||||
} debug;
|
||||
|
||||
#ifdef DUMP_PROFILE
|
||||
/* Profiling data */
|
||||
JSFunctionProfile profile;
|
||||
JSCallSite *call_sites;
|
||||
uint32_t call_site_count;
|
||||
uint32_t call_site_capacity;
|
||||
JSPropSite *prop_sites;
|
||||
uint32_t prop_site_count;
|
||||
uint32_t prop_site_capacity;
|
||||
#endif
|
||||
} JSFunctionBytecode;
|
||||
|
||||
typedef struct JSBoundFunction {
|
||||
@@ -1913,7 +1839,6 @@ static JSValue JS_EvalObject (JSContext *ctx, JSValue this_obj, JSValue val,
|
||||
int JS_DeleteProperty (JSContext *ctx, JSValue obj, JSValue prop);
|
||||
JSValue __attribute__ ((format (printf, 2, 3)))
|
||||
JS_ThrowInternalError (JSContext *ctx, const char *fmt, ...);
|
||||
static __maybe_unused void JS_DumpAtoms (JSRuntime *rt);
|
||||
static __maybe_unused void JS_DumpString (JSRuntime *rt, const JSString *p);
|
||||
static __maybe_unused void JS_DumpObjectHeader (JSRuntime *rt);
|
||||
static __maybe_unused void JS_DumpObject (JSRuntime *rt, JSRecord *rec);
|
||||
@@ -1923,7 +1848,6 @@ static __maybe_unused void JS_DumpValueRT (JSRuntime *rt, const char *str,
|
||||
JSValue val);
|
||||
static __maybe_unused void JS_DumpValue (JSContext *ctx, const char *str,
|
||||
JSValue val);
|
||||
static __maybe_unused void JS_DumpShapes (JSRuntime *rt);
|
||||
static void js_dump_value_write (void *opaque, const char *buf, size_t len);
|
||||
static JSValue js_function_apply (JSContext *ctx, JSValue this_val, int argc,
|
||||
JSValue *argv, int magic);
|
||||
@@ -2010,9 +1934,8 @@ static int JS_SetPropertyValue (JSContext *ctx, JSValue this_obj, JSValue prop,
|
||||
JSValue val);
|
||||
static JSValue JS_ToNumberFree (JSContext *ctx, JSValue val);
|
||||
static int JS_GetOwnPropertyInternal (JSContext *ctx,
|
||||
JSPropertyDescriptor *desc, JSRecord *p,
|
||||
JSValue *desc, JSRecord *p,
|
||||
JSValue prop);
|
||||
static void js_free_desc (JSContext *ctx, JSPropertyDescriptor *desc);
|
||||
static void JS_AddIntrinsicBasicObjects (JSContext *ctx);
|
||||
static __exception int js_get_length32 (JSContext *ctx, uint32_t *pres,
|
||||
JSValue obj);
|
||||
@@ -2317,17 +2240,6 @@ JSRuntime *JS_NewRuntime2 (const JSMallocFunctions *mf, void *opaque) {
|
||||
|
||||
rt->current_exception = JS_UNINITIALIZED;
|
||||
|
||||
#ifdef DUMP_PROFILE
|
||||
/* Initialize profiling state */
|
||||
memset (&rt->profile, 0, sizeof (rt->profile));
|
||||
rt->profile.sample_capacity = MAX_PROFILE_SAMPLES;
|
||||
rt->profile.samples = mf->js_malloc (
|
||||
&rt->malloc_state, sizeof (JSProfileSample) * rt->profile.sample_capacity);
|
||||
if (!rt->profile.samples) { rt->profile.sample_capacity = 0; }
|
||||
rt->profile.sample_interval_us = 2000; /* 2ms default */
|
||||
gettimeofday (&rt->profile.last_sample_time, NULL);
|
||||
#endif
|
||||
|
||||
return rt;
|
||||
fail:
|
||||
JS_FreeRuntime (rt);
|
||||
@@ -2594,111 +2506,6 @@ void JS_FreeRuntime (JSRuntime *rt) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DUMP_PROFILE
|
||||
/* Dump profiling statistics */
|
||||
{
|
||||
struct list_head *el;
|
||||
JSGCObjectHeader *p;
|
||||
uint32_t func_count = 0;
|
||||
uint32_t total_funcs = 0;
|
||||
|
||||
printf ("\n=== PROFILING STATISTICS ===\n\n");
|
||||
|
||||
/* Dump sampling profiler results */
|
||||
if (rt->profile.sample_count > 0) {
|
||||
printf ("Sampling Profile (%u samples, interval=%uus):\n",
|
||||
rt->profile.sample_count, rt->profile.sample_interval_us);
|
||||
printf (" %-50s %10s %10s\n", "Function", "Samples", "Percent");
|
||||
printf (" %s\n", "-----------------------------------------------------"
|
||||
"-------------------");
|
||||
|
||||
/* Count samples per function */
|
||||
list_for_each (el, &rt->gc_obj_list) {
|
||||
p = list_entry (el, JSGCObjectHeader, link);
|
||||
if (p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) {
|
||||
JSFunctionBytecode *b = (JSFunctionBytecode *)p;
|
||||
uint32_t sample_hits = 0;
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < rt->profile.sample_count; i++) {
|
||||
if (rt->profile.samples[i].func == b) { sample_hits++; }
|
||||
}
|
||||
|
||||
if (sample_hits > 0) {
|
||||
JSContext *ctx = b->realm ? b->realm : rt->js;
|
||||
const char *fname
|
||||
= ctx ? JS_ToCString (ctx, b->func_name) : NULL;
|
||||
double percent = (100.0 * sample_hits) / rt->profile.sample_count;
|
||||
printf (" %-50s %10u %9.2f%%\n", fname ? fname : "<anonymous>",
|
||||
sample_hits, percent);
|
||||
if (fname && ctx) JS_FreeCString (ctx, fname);
|
||||
}
|
||||
}
|
||||
}
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
/* Dump function counters */
|
||||
printf ("Function Statistics:\n");
|
||||
printf (" %-50s %12s %15s\n", "Function", "Calls", "Instructions");
|
||||
printf (" %s\n", "-------------------------------------------------------"
|
||||
"-----------------");
|
||||
|
||||
list_for_each (el, &rt->gc_obj_list) {
|
||||
p = list_entry (el, JSGCObjectHeader, link);
|
||||
if (p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) {
|
||||
JSFunctionBytecode *b = (JSFunctionBytecode *)p;
|
||||
total_funcs++;
|
||||
if (b->profile.entry_count > 0 || b->profile.self_ticks > 0) {
|
||||
JSContext *ctx = b->realm ? b->realm : rt->js;
|
||||
const char *fname
|
||||
= ctx ? JS_ToCString (ctx, b->func_name) : NULL;
|
||||
printf (" %-50s %12llu %15llu\n", fname ? fname : "<anonymous>",
|
||||
(unsigned long long)b->profile.entry_count,
|
||||
(unsigned long long)b->profile.self_ticks);
|
||||
if (fname && ctx) JS_FreeCString (ctx, fname);
|
||||
func_count++;
|
||||
|
||||
/* Dump hot callsites */
|
||||
if (b->call_site_count > 0) {
|
||||
uint32_t i;
|
||||
printf (" Call sites:\n");
|
||||
for (i = 0; i < b->call_site_count && i < 5; i++) {
|
||||
printf (" PC %6u: %10llu calls\n",
|
||||
b->call_sites[i].pc_offset,
|
||||
(unsigned long long)b->call_sites[i].hit_count);
|
||||
}
|
||||
}
|
||||
|
||||
/* Dump hot property sites */
|
||||
if (b->prop_site_count > 0) {
|
||||
uint32_t i;
|
||||
printf (" Property sites:\n");
|
||||
for (i = 0; i < b->prop_site_count && i < 5; i++) {
|
||||
JSContext *pctx = b->realm ? b->realm : rt->js;
|
||||
const char *pname
|
||||
= pctx ? JS_ToCString (pctx, b->prop_sites[i].atom) : NULL;
|
||||
printf (" PC %6u %-20s: %10llu accesses\n",
|
||||
b->prop_sites[i].pc_offset, pname ? pname : "<unknown>",
|
||||
(unsigned long long)b->prop_sites[i].hit_count);
|
||||
if (pname && pctx) JS_FreeCString (pctx, pname);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (func_count == 0) {
|
||||
printf (" (no functions executed) [%u total functions found]\n",
|
||||
total_funcs);
|
||||
}
|
||||
printf ("\n");
|
||||
|
||||
/* Free profiling data */
|
||||
js_free_rt (rt, rt->profile.samples);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Stone text tables and arena are now per-context, freed in JS_FreeContext
|
||||
*/
|
||||
|
||||
@@ -2849,12 +2656,6 @@ void JS_FreeContext (JSContext *ctx) {
|
||||
if (--ctx->header.ref_count > 0) return;
|
||||
assert (ctx->header.ref_count == 0);
|
||||
|
||||
#ifdef DUMP_ATOMS
|
||||
JS_DumpAtoms (ctx->rt);
|
||||
#endif
|
||||
#ifdef DUMP_SHAPES
|
||||
JS_DumpShapes (ctx->rt);
|
||||
#endif
|
||||
#ifdef DUMP_OBJECTS
|
||||
{
|
||||
struct list_head *el;
|
||||
@@ -5535,25 +5336,20 @@ int JS_GetOwnPropertyNames (JSContext *ctx, JSValue **ptab, uint32_t *plen,
|
||||
returned, the property descriptor 'desc' is filled present.
|
||||
Now uses JSRecord-based lookup. */
|
||||
static int JS_GetOwnPropertyInternal (JSContext *ctx,
|
||||
JSPropertyDescriptor *desc, JSRecord *p,
|
||||
JSValue *desc, JSRecord *p,
|
||||
JSValue prop) {
|
||||
JSRecord *rec = (JSRecord *)p;
|
||||
int slot = rec_find_slot (rec, prop);
|
||||
|
||||
if (slot > 0) {
|
||||
if (desc) {
|
||||
desc->flags = 0; /* All record properties are writable/enumerable */
|
||||
desc->getter = JS_NULL;
|
||||
desc->setter = JS_NULL;
|
||||
desc->value = JS_DupValue (ctx, rec->tab[slot].val);
|
||||
}
|
||||
if (desc)
|
||||
*desc = JS_DupValue(ctx, rec->tab[slot].val);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int JS_GetOwnProperty (JSContext *ctx, JSPropertyDescriptor *desc, JSValue obj,
|
||||
JSValue prop) {
|
||||
int JS_GetOwnProperty (JSContext *ctx, JSValue *desc, JSValue obj, JSValue prop) {
|
||||
if (JS_VALUE_GET_TAG (obj) != JS_TAG_OBJECT) {
|
||||
JS_ThrowTypeErrorNotAnObject (ctx);
|
||||
return -1;
|
||||
@@ -5751,12 +5547,6 @@ static int delete_property (JSContext *ctx, JSRecord *rec, JSValue key) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void js_free_desc (JSContext *ctx, JSPropertyDescriptor *desc) {
|
||||
JS_FreeValue (ctx, desc->getter);
|
||||
JS_FreeValue (ctx, desc->setter);
|
||||
JS_FreeValue (ctx, desc->value);
|
||||
}
|
||||
|
||||
int JS_SetProperty (JSContext *ctx, JSValue this_obj, JSValue prop,
|
||||
JSValue val) {
|
||||
uint32_t tag = JS_VALUE_GET_TAG (this_obj);
|
||||
@@ -8216,83 +8006,6 @@ typedef enum {
|
||||
} OPSpecialObjectEnum;
|
||||
|
||||
/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */
|
||||
#ifdef DUMP_PROFILE
|
||||
/* Sampling profiler: try to take a sample */
|
||||
static void profile_try_sample (JSRuntime *rt, JSFunctionBytecode *b,
|
||||
const uint8_t *pc) {
|
||||
struct timeval now;
|
||||
long elapsed_us;
|
||||
|
||||
gettimeofday (&now, NULL);
|
||||
elapsed_us = (now.tv_sec - rt->profile.last_sample_time.tv_sec) * 1000000L
|
||||
+ (now.tv_usec - rt->profile.last_sample_time.tv_usec);
|
||||
|
||||
/* Only sample if enough time has elapsed */
|
||||
if (elapsed_us >= rt->profile.sample_interval_us) {
|
||||
if (rt->profile.sample_count < rt->profile.sample_capacity) {
|
||||
JSProfileSample *sample
|
||||
= &rt->profile.samples[rt->profile.sample_count++];
|
||||
sample->func = b;
|
||||
sample->pc_offset = (uint32_t)(pc - b->byte_code_buf);
|
||||
sample->callsite_id = 0; /* Could be enhanced to track callsite */
|
||||
}
|
||||
rt->profile.last_sample_time = now;
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function to record a call site */
|
||||
static void profile_record_call_site (JSRuntime *rt, JSFunctionBytecode *b,
|
||||
uint32_t pc_offset) {
|
||||
uint32_t i;
|
||||
/* Check if we already have this callsite */
|
||||
for (i = 0; i < b->call_site_count; i++) {
|
||||
if (b->call_sites[i].pc_offset == pc_offset) {
|
||||
b->call_sites[i].hit_count++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Add new callsite */
|
||||
if (b->call_site_count >= b->call_site_capacity) {
|
||||
uint32_t new_cap = b->call_site_capacity ? b->call_site_capacity * 2 : 16;
|
||||
JSCallSite *new_sites
|
||||
= js_realloc_rt (rt, b->call_sites, new_cap * sizeof (JSCallSite));
|
||||
if (!new_sites) return;
|
||||
b->call_sites = new_sites;
|
||||
b->call_site_capacity = new_cap;
|
||||
}
|
||||
b->call_sites[b->call_site_count].pc_offset = pc_offset;
|
||||
b->call_sites[b->call_site_count].hit_count = 1;
|
||||
b->call_site_count++;
|
||||
}
|
||||
|
||||
/* Helper function to record a property access site */
|
||||
static void profile_record_prop_site (JSRuntime *rt, JSFunctionBytecode *b,
|
||||
uint32_t pc_offset, JSValue key) {
|
||||
uint32_t i;
|
||||
/* Check if we already have this prop site */
|
||||
for (i = 0; i < b->prop_site_count; i++) {
|
||||
if (b->prop_sites[i].pc_offset == pc_offset
|
||||
&& js_key_equal (b->prop_sites[i].atom, key)) {
|
||||
b->prop_sites[i].hit_count++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Add new prop site */
|
||||
if (b->prop_site_count >= b->prop_site_capacity) {
|
||||
uint32_t new_cap = b->prop_site_capacity ? b->prop_site_capacity * 2 : 16;
|
||||
JSPropSite *new_sites
|
||||
= js_realloc_rt (rt, b->prop_sites, new_cap * sizeof (JSPropSite));
|
||||
if (!new_sites) return;
|
||||
b->prop_sites = new_sites;
|
||||
b->prop_site_capacity = new_cap;
|
||||
}
|
||||
b->prop_sites[b->prop_site_count].pc_offset = pc_offset;
|
||||
b->prop_sites[b->prop_site_count].atom = key;
|
||||
b->prop_sites[b->prop_site_count].hit_count = 1;
|
||||
b->prop_site_count++;
|
||||
}
|
||||
#endif
|
||||
|
||||
static JSValue JS_CallInternal (JSContext *caller_ctx, JSValue func_obj,
|
||||
JSValue this_obj, int argc, JSValue *argv,
|
||||
int flags) {
|
||||
@@ -8368,11 +8081,6 @@ static JSValue JS_CallInternal (JSContext *caller_ctx, JSValue func_obj,
|
||||
}
|
||||
b = f->u.func.function_bytecode;
|
||||
|
||||
#ifdef DUMP_PROFILE
|
||||
/* Increment function entry count */
|
||||
b->profile.entry_count++;
|
||||
#endif
|
||||
|
||||
if (unlikely (caller_ctx->trace_hook)
|
||||
&& (caller_ctx->trace_type & JS_HOOK_CALL)) {
|
||||
js_debug dbg;
|
||||
@@ -8428,14 +8136,6 @@ restart:
|
||||
int call_argc;
|
||||
JSValue *call_argv;
|
||||
|
||||
#ifdef DUMP_PROFILE
|
||||
/* Count bytecode instructions executed (self_ticks) */
|
||||
b->profile.self_ticks++;
|
||||
|
||||
/* Try to take a profiling sample */
|
||||
profile_try_sample (rt, b, pc);
|
||||
#endif
|
||||
|
||||
SWITCH (pc) {
|
||||
CASE (OP_push_i32) : *sp++ = JS_NewInt32 (ctx, get_u32 (pc));
|
||||
pc += 4;
|
||||
@@ -8676,10 +8376,6 @@ restart:
|
||||
has_call_argc:
|
||||
call_argv = sp - call_argc;
|
||||
sf->cur_pc = pc;
|
||||
#ifdef DUMP_PROFILE
|
||||
/* Record call site */
|
||||
profile_record_call_site (rt, b, (uint32_t)(pc - b->byte_code_buf));
|
||||
#endif
|
||||
/* TODO: Use trampoline - for now keep recursive */
|
||||
ret_val = JS_CallInternal (ctx, call_argv[-1], JS_NULL, call_argc,
|
||||
call_argv, 0);
|
||||
@@ -8698,10 +8394,6 @@ restart:
|
||||
pc += 2;
|
||||
call_argv = sp - call_argc;
|
||||
sf->cur_pc = pc;
|
||||
#ifdef DUMP_PROFILE
|
||||
/* Record call site */
|
||||
profile_record_call_site (rt, b, (uint32_t)(pc - b->byte_code_buf));
|
||||
#endif
|
||||
/* Proxy method-call: detect [func, "name", ...args]
|
||||
and rewrite as func("name", [args]) */
|
||||
|
||||
@@ -18951,17 +18643,6 @@ static JSValue js_create_function (JSContext *ctx, JSFunctionDef *fd) {
|
||||
b->has_simple_parameter_list = fd->has_simple_parameter_list;
|
||||
b->js_mode = fd->js_mode;
|
||||
|
||||
#ifdef DUMP_PROFILE
|
||||
/* Initialize profiling data */
|
||||
memset (&b->profile, 0, sizeof (b->profile));
|
||||
b->call_sites = NULL;
|
||||
b->call_site_count = 0;
|
||||
b->call_site_capacity = 0;
|
||||
b->prop_sites = NULL;
|
||||
b->prop_site_count = 0;
|
||||
b->prop_site_capacity = 0;
|
||||
#endif
|
||||
|
||||
/* IC removed - shapes no longer used */
|
||||
|
||||
b->is_direct_or_indirect_eval = (fd->eval_type == JS_EVAL_TYPE_DIRECT
|
||||
@@ -19014,12 +18695,6 @@ static void free_function_bytecode (JSRuntime *rt, JSFunctionBytecode *b) {
|
||||
js_free_rt (rt, b->debug.source);
|
||||
}
|
||||
|
||||
#ifdef DUMP_PROFILE
|
||||
/* Free profiling data */
|
||||
js_free_rt (rt, b->call_sites);
|
||||
js_free_rt (rt, b->prop_sites);
|
||||
#endif
|
||||
|
||||
/* IC removed */
|
||||
|
||||
remove_gc_object (&b->header);
|
||||
|
||||
Reference in New Issue
Block a user