diff --git a/examples/test262/index.js b/examples/test262/index.js index ec6df26..80b5960 100755 --- a/examples/test262/index.js +++ b/examples/test262/index.js @@ -58,6 +58,7 @@ let state = { memoryOffset: 0, memoryStatus: '', memoryStatusAt: 0, + fullClearNextRender: false, searchMode: false, searchQuery: '' }; @@ -123,6 +124,8 @@ const byteStatSuffixes = new Set([ 'bytes', 'cstack', 'rss', + 'residentSize', + 'physFootprint', 'virtualSize' ]); @@ -210,6 +213,40 @@ function ensureMemoryCache() { return state.memoryCache; } +function hasGcMarkProfileControls() { + return Ant.raw + && typeof Ant.raw.gcMarkProfile === 'function' + && typeof Ant.raw.gcMarkProfileEnable === 'function' + && typeof Ant.raw.gcMarkProfileReset === 'function'; +} + +function toggleGcMarkProfile() { + if (!hasGcMarkProfileControls()) { + setMemoryStatus(`${c.red}GC mark profiler unavailable${c.reset}`); + return false; + } + + const current = Ant.raw.gcMarkProfile(); + const enabled = Ant.raw.gcMarkProfileEnable(!current.enabled); + refreshMemoryCache(); + setMemoryStatus(enabled + ? `${c.green}GC mark profiler enabled${c.reset}` + : `${c.yellow}GC mark profiler disabled${c.reset}`); + return true; +} + +function resetGcMarkProfile() { + if (!hasGcMarkProfileControls()) { + setMemoryStatus(`${c.red}GC mark profiler unavailable${c.reset}`); + return false; + } + + Ant.raw.gcMarkProfileReset(); + refreshMemoryCache(); + setMemoryStatus(`${c.green}GC mark profiler reset${c.reset}`); + return true; +} + function memoryStatusText() { if (!state.memoryStatus) return ''; if (Date.now() - state.memoryStatusAt > 4000) { @@ -361,7 +398,7 @@ function buildScreen() { lines.push(' '.repeat(cols)); const status = memoryStatusText(); lines.push(pad( - `${c.dim}↑↓ scroll · PgUp/PgDn · g/G top/bottom · r refresh · c copy · m browse · q quit${c.reset} ${c.dim}[${state.memoryOffset + 1}-${end}/${memoryLines.length}]${c.reset} ${status}${status ? ' ' : ''}${c.cyan}${fps.current} fps${c.reset}`, + `${c.dim}↑↓ scroll · PgUp/PgDn · g/G top/bottom · r refresh · e profiler · R reset profiler · c copy · m browse · q quit${c.reset} ${c.dim}[${state.memoryOffset + 1}-${end}/${memoryLines.length}]${c.reset} ${status}${status ? ' ' : ''}${c.cyan}${fps.current} fps${c.reset}`, cols )); linesArePadded = true; @@ -374,7 +411,9 @@ function render() { if (pendingRender) return; fps.update(); const screen = buildScreen(); - const ok = process.stdout.write(`${term.syncStart}${term.hideCursor}${term.home}${screen}${term.syncEnd}`); + const clear = state.fullClearNextRender ? term.clearScreen : ''; + state.fullClearNextRender = false; + const ok = process.stdout.write(`${term.syncStart}${term.hideCursor}${term.home}${clear}${screen}${term.syncEnd}`); if (!ok) pendingRender = true; } @@ -503,6 +542,7 @@ function handleKey(key) { state.mode = 'browse'; } else { state.mode = 'memory'; + state.fullClearNextRender = true; state.memoryOffset = 0; refreshMemoryCache(); setMemoryStatus(`${c.green}Refreshed memory stats${c.reset}`); @@ -516,6 +556,16 @@ function handleKey(key) { needsRender = true; } break; + case 'e': + if (state.mode === 'memory') { + needsRender = toggleGcMarkProfile() || needsRender; + } + break; + case 'R': + if (state.mode === 'memory') { + needsRender = resetGcMarkProfile() || needsRender; + } + break; case 'c': if (state.mode === 'memory') { copyMemoryStats(); diff --git a/examples/tui/index.js b/examples/tui/index.js index cb7f509..42b8556 100644 --- a/examples/tui/index.js +++ b/examples/tui/index.js @@ -1,20 +1,4 @@ -import { - Screen, - List, - ProgressBar, - Input, - Table, - colors, - box, - keys, - codes, - modal, - confirm, - pad, - padCenter, - truncate, - visibleLength -} from './tuey.js'; +import { Screen, List, ProgressBar, Input, colors, box, keys, codes, modal, confirm, pad, padCenter, truncate, visibleLength } from './tuey.js'; const screen = new Screen({ fullscreen: true, hideCursor: true }); @@ -386,11 +370,12 @@ function showMemoryModal() { if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB'; return (bytes / 1024 / 1024).toFixed(2) + ' MB'; }; + const fmtMaybe = value => (typeof value === 'number' ? fmt(value) : 'n/a'); modal(screen, { id: 'memory', width: 40, - height: 10, + height: 11, title: 'Memory Usage', titleStyle: colors.bold + colors.yellow, borderStyle: box.rounded, @@ -408,7 +393,9 @@ function showMemoryModal() { buf.writeStyled(ox, oy + y++, ` Pools: ${colors.bold}${fmt(mem.pools.totalUsed)} / ${fmt(mem.pools.totalCapacity)}${codes.reset}`); buf.writeStyled(ox, oy + y++, ` Alloc: ${colors.bold}${fmt(mem.alloc.total)}${codes.reset} (${mem.alloc.objectCount} objects)`); buf.writeStyled(ox, oy + y++, ` External: ${colors.bold}${fmt(mem.external.total)}${codes.reset}`); - buf.writeStyled(ox, oy + y++, ` RSS: ${colors.bold}${fmt(mem.rss)}${codes.reset}`); + buf.writeStyled(ox, oy + y++, ` RSS: ${colors.bold}${fmtMaybe(mem.rss)}${codes.reset}`); + buf.writeStyled(ox, oy + y++, ` Resident: ${colors.bold}${fmtMaybe(mem.residentSize)}${codes.reset}`); + buf.writeStyled(ox, oy + y++, ` Footprint:${colors.bold}${fmtMaybe(mem.physFootprint)}${codes.reset}`); y++; buf.writeStyled(ox, oy + y, colors.dim + ' m/Esc: Close' + codes.reset); @@ -613,7 +600,7 @@ function handleKey(key) { } return false; }, - render: (buf, w, h, ox, oy) => { + render: (buf, _w, _h, ox, oy) => { buf.writeStyled(ox, oy + 1, ' Name: ' + nameInput.render()); buf.writeStyled(ox, oy + 3, colors.dim + ' Enter: Save Esc: Cancel' + codes.reset); } diff --git a/src/modules/builtin.c b/src/modules/builtin.c index 7209e0a..28a6215 100644 --- a/src/modules/builtin.c +++ b/src/modules/builtin.c @@ -319,9 +319,19 @@ static ant_value_t js_stats_fn(ant_t *js, ant_value_t *args, int nargs) { struct mach_task_basic_info info; mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT; if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &count) == KERN_SUCCESS) { - js_set(js, result, "rss", js_mknum((double)info.resident_size)); + js_set(js, result, "residentSize", js_mknum((double)info.resident_size)); js_set(js, result, "virtualSize", js_mknum((double)info.virtual_size)); } + + task_vm_info_data_t vm_info; + mach_msg_type_number_t vm_count = TASK_VM_INFO_COUNT; + if (task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)&vm_info, &vm_count) == KERN_SUCCESS) { + js_set(js, result, "rss", js_mknum((double)vm_info.phys_footprint)); + js_set(js, result, "physFootprint", js_mknum((double)vm_info.phys_footprint)); + } else if (vtype(js_get(js, result, "rss")) == T_UNDEF) { + ant_value_t resident = js_get(js, result, "residentSize"); + if (vtype(resident) != T_UNDEF) js_set(js, result, "rss", resident); + } #elif defined(__linux__) struct rusage usage; if (getrusage(RUSAGE_SELF, &usage) == 0) {