rm dump profile

This commit is contained in:
2026-01-31 12:01:33 -06:00
parent 67e82fd12c
commit 3a0ea31896

View File

@@ -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);