Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion crates/perry-runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,15 @@ crate-type = ["rlib", "staticlib"]
# actually needs (see optimized_libs.rs), so the heavy subsystems below
# (regex engine, Temporal, URL/IDNA, normalize, segmenter) are *opt-in per
# app* and absent from binaries that never use them.
default = ["full", "regex-engine", "temporal", "url-engine", "string-normalize", "intl-segmenter"]
default = ["full", "regex-engine", "temporal", "url-engine", "string-normalize", "intl-segmenter", "diagnostics"]
# Cold-path diagnostic JSON serializers (~67 KB of code + the `serde_json`
# pulled only by them, which dead-strips when unreferenced): GC cycle telemetry
# (`PERRY_GC_DIAG`), typed-feedback trace dump (`PERRY_TYPED_FEEDBACK`), the v8
# heap-snapshot builder (`v8.getHeapSnapshot`/`writeHeapSnapshot`), and
# `process.report`. None are on a hot path. The env-driven dev diagnostics
# degrade gracefully when off (auto-optimize leaves this off unless the program
# uses a heap-snapshot / `process.report` API, which the compiler detects).
diagnostics = []
# The user's regular-expression engine (`regex` + `fancy-regex`, ~1.2 MB of
# DFA/NFA machinery). A program that never evaluates a regex literal, `RegExp`,
# a regex-coercing string method, or a glob API can't produce a RegExp at
Expand Down
6 changes: 4 additions & 2 deletions crates/perry-runtime/src/arena/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ pub use allocators::{
pub(crate) use allocators::{arena_alloc_gc_old_excluding_pages, arena_alloc_gc_survivor};

// walk.rs
#[cfg(feature = "diagnostics")]
pub(crate) use walk::ArenaRegionTelemetry;
pub use walk::{
arena_block_count, arena_in_use_bytes, arena_total_bytes, arena_walk_objects,
arena_walk_objects_addr_sorted, arena_walk_objects_filtered,
Expand All @@ -61,8 +63,8 @@ pub use walk::{
};
pub(crate) use walk::{
arena_block_snapshots, arena_telemetry_snapshot, general_block_in_recent_window,
ArenaBlockSnapshot, ArenaObjectCursor, ArenaObjectCursorBuilder, ArenaRegionTelemetry,
ArenaTelemetrySnapshot, ArenaWalkOrder,
ArenaBlockSnapshot, ArenaObjectCursor, ArenaObjectCursorBuilder, ArenaTelemetrySnapshot,
ArenaWalkOrder,
};

// reset.rs
Expand Down
1 change: 1 addition & 0 deletions crates/perry-runtime/src/arena/walk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ pub(crate) struct ArenaRegionTelemetry {
}

#[derive(Clone, Copy, Default)]
#[cfg_attr(not(feature = "diagnostics"), allow(dead_code))]
pub(crate) struct ArenaTelemetrySnapshot {
pub(crate) arena: ArenaRegionTelemetry,
pub(crate) survivor0: ArenaRegionTelemetry,
Expand Down
1 change: 1 addition & 0 deletions crates/perry-runtime/src/gc/cycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub(super) enum GcCyclePhase {
}

impl GcCyclePhase {
#[cfg(feature = "diagnostics")]
#[inline]
pub(super) const fn as_str(self) -> &'static str {
match self {
Expand Down
2 changes: 2 additions & 0 deletions crates/perry-runtime/src/gc/malloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ impl MallocKindTelemetry {
}
}

#[cfg(feature = "diagnostics")]
pub(super) fn reset_cycle_deltas(&mut self) {
self.allocated_count = 0;
self.allocated_bytes = 0;
Expand Down Expand Up @@ -273,6 +274,7 @@ impl MallocState {
counters.copied_minor_validation_lookups.saturating_add(1);
}

#[cfg(feature = "diagnostics")]
pub(super) fn take_kind_telemetry(
&mut self,
) -> [MallocKindTelemetry; MALLOC_KIND_BUCKET_COUNT] {
Expand Down
2 changes: 2 additions & 0 deletions crates/perry-runtime/src/gc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ mod cycle;
use cycle::*;
mod verify;
pub use verify::*;
#[cfg(feature = "diagnostics")]
mod heap_snapshot;
#[cfg(feature = "diagnostics")]
pub use heap_snapshot::gc_build_v8_heap_snapshot_json;

pub fn gc_collect_minor() -> u64 {
Expand Down
1 change: 1 addition & 0 deletions crates/perry-runtime/src/gc/oldgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ impl Default for EvacuationPolicyDecision {
}

#[derive(Clone, Copy, Default)]
#[cfg_attr(not(feature = "diagnostics"), allow(dead_code))]
pub(super) struct SweepTraceStats {
pub(super) dead_bytes: u64,
// Compatibility alias for dead_bytes.
Expand Down
3 changes: 3 additions & 0 deletions crates/perry-runtime/src/gc/policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ pub(super) enum GcCollectionKind {
}

impl GcCollectionKind {
#[cfg(feature = "diagnostics")]
#[inline]
pub(super) fn as_str(self) -> &'static str {
match self {
Expand Down Expand Up @@ -394,6 +395,7 @@ pub(super) enum GcTriggerKind {
}

impl GcTriggerKind {
#[cfg(feature = "diagnostics")]
#[inline]
pub(super) fn as_str(self) -> &'static str {
match self {
Expand Down Expand Up @@ -466,6 +468,7 @@ impl DeferredGcRequest {
}

#[derive(Clone, Copy)]
#[cfg_attr(not(feature = "diagnostics"), allow(dead_code))]
pub(super) struct GcStepSnapshot {
pub(super) arena_step_bytes: usize,
pub(super) next_arena_trigger_bytes: usize,
Expand Down
1 change: 1 addition & 0 deletions crates/perry-runtime/src/gc/roots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ pub(super) enum ConservativeStackScanDecision {
}

impl ConservativeStackScanDecision {
#[cfg(feature = "diagnostics")]
#[inline]
pub(super) const fn as_str(self) -> &'static str {
match self {
Expand Down
30 changes: 30 additions & 0 deletions crates/perry-runtime/src/gc/telemetry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ thread_local! {
}

#[derive(Clone, Copy, Default)]
#[cfg_attr(not(feature = "diagnostics"), allow(dead_code))]
pub(super) struct RememberedSetTraceStats {
pub(super) entries_scanned: usize,
pub(super) valid_roots: usize,
Expand Down Expand Up @@ -98,6 +99,7 @@ pub(super) enum CopiedMinorFallbackReason {
}

impl CopiedMinorFallbackReason {
#[cfg(feature = "diagnostics")]
#[inline]
pub(super) const fn as_str(self) -> &'static str {
match self {
Expand Down Expand Up @@ -218,6 +220,7 @@ impl RootSourceSlotTraceStats {
}

#[derive(Clone, Copy, Default)]
#[cfg_attr(not(feature = "diagnostics"), allow(dead_code))]
pub(super) struct NativeStackFallbackTraceStats {
pub(super) decision: ConservativeStackScanDecision,
pub(super) scanned: bool,
Expand Down Expand Up @@ -524,6 +527,7 @@ pub(super) enum AllocatorMaintenanceStatus {
}

impl AllocatorMaintenanceStatus {
#[cfg(feature = "diagnostics")]
#[inline]
pub(super) const fn as_str(self) -> &'static str {
match self {
Expand All @@ -543,6 +547,7 @@ pub(super) enum AllocatorMaintenanceReason {
}

impl AllocatorMaintenanceReason {
#[cfg(feature = "diagnostics")]
#[inline]
pub(super) const fn as_str(self) -> &'static str {
match self {
Expand All @@ -554,17 +559,20 @@ impl AllocatorMaintenanceReason {
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(not(feature = "diagnostics"), allow(dead_code))]
pub(super) struct AllocatorMaintenanceEvent {
pub(super) status: AllocatorMaintenanceStatus,
pub(super) reason: AllocatorMaintenanceReason,
pub(super) elapsed_us: u64,
}

#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[cfg_attr(not(feature = "diagnostics"), allow(dead_code))]
pub(super) struct AllocatorMaintenanceTrace {
pub(super) malloc_trim: Option<AllocatorMaintenanceEvent>,
}

#[cfg_attr(not(feature = "diagnostics"), allow(dead_code))]
pub(super) struct GcCycleTrace {
pub(super) collection_kind: GcCollectionKind,
pub(super) trigger_kind: GcTriggerKind,
Expand Down Expand Up @@ -719,6 +727,7 @@ impl GcCycleTrace {
}
}

#[cfg(feature = "diagnostics")]
pub(super) fn into_json(mut self, steps_after: GcStepSnapshot) -> serde_json::Value {
self.capture_layout_scans();
self.debt.record(GcDebtSnapshot::current());
Expand Down Expand Up @@ -964,6 +973,7 @@ impl GcCycleTrace {
})
}

#[cfg(feature = "diagnostics")]
pub(super) fn emit(self, steps_after: GcStepSnapshot) {
let event = self.into_json(steps_after);
#[cfg(test)]
Expand All @@ -972,8 +982,16 @@ impl GcCycleTrace {
eprintln!("{line}");
}
}

#[cfg(not(feature = "diagnostics"))]
pub(super) fn emit(self, _steps_after: GcStepSnapshot) {
eprintln!(
"[gc] cycle (diagnostics feature disabled — rebuild without --no-default-features for JSON trace)"
);
}
}

#[cfg(feature = "diagnostics")]
pub(super) fn debt_snapshot_json(snapshot: GcDebtSnapshot) -> serde_json::Value {
serde_json::json!({
"arena_debt_bytes": snapshot.arena_debt_bytes,
Expand All @@ -982,6 +1000,7 @@ pub(super) fn debt_snapshot_json(snapshot: GcDebtSnapshot) -> serde_json::Value
})
}

#[cfg(feature = "diagnostics")]
pub(super) fn pause_budget_json(
progress_kind: GcProgressKind,
progress_budget: GcPauseBudget,
Expand All @@ -999,6 +1018,7 @@ pub(super) fn pause_budget_json(
})
}

#[cfg(feature = "diagnostics")]
pub(super) fn pause_step_json(step: GcPauseStepTrace) -> serde_json::Value {
let progress_budget = gc_progress_contract().budget_for(step.progress_kind);
let within_soft_pause_target = progress_budget
Expand Down Expand Up @@ -1026,6 +1046,7 @@ pub(super) fn pause_step_json(step: GcPauseStepTrace) -> serde_json::Value {
})
}

#[cfg(feature = "diagnostics")]
pub(super) fn allocator_maintenance_json(
trace: AllocatorMaintenanceTrace,
progress_kind: GcProgressKind,
Expand All @@ -1045,6 +1066,7 @@ pub(super) fn allocator_maintenance_json(
})
}

#[cfg(feature = "diagnostics")]
fn default_malloc_trim_maintenance(progress_kind: GcProgressKind) -> AllocatorMaintenanceEvent {
if progress_kind.is_budgeted() {
return AllocatorMaintenanceEvent {
Expand Down Expand Up @@ -1134,6 +1156,7 @@ pub(super) fn malloc_object_count() -> usize {
MALLOC_STATE.with(|s| s.borrow().objects.len())
}

#[cfg(feature = "diagnostics")]
pub(super) fn malloc_kind_telemetry_row(
obj_type: u8,
counters: MallocKindTelemetry,
Expand All @@ -1154,6 +1177,7 @@ pub(super) fn malloc_kind_telemetry_row(
})
}

#[cfg(feature = "diagnostics")]
pub(super) fn root_source_slot_json(stats: RootSourceSlotTraceStats) -> serde_json::Value {
serde_json::json!({
"registered_scanners": stats.registered_scanners,
Expand All @@ -1164,6 +1188,7 @@ pub(super) fn root_source_slot_json(stats: RootSourceSlotTraceStats) -> serde_js
})
}

#[cfg(feature = "diagnostics")]
pub(super) fn root_sources_json(stats: RootSourcesTraceStats) -> serde_json::Value {
serde_json::json!({
"compiled_shadow": root_source_slot_json(stats.compiled_shadow),
Expand All @@ -1183,6 +1208,7 @@ pub(super) fn root_sources_json(stats: RootSourcesTraceStats) -> serde_json::Val
})
}

#[cfg(feature = "diagnostics")]
pub(super) fn malloc_kind_telemetry_json_from_snapshot(
snapshot: [MallocKindTelemetry; MALLOC_KIND_BUCKET_COUNT],
) -> serde_json::Value {
Expand All @@ -1201,11 +1227,13 @@ pub(super) fn malloc_kind_telemetry_json_from_snapshot(
serde_json::Value::Array(rows)
}

#[cfg(feature = "diagnostics")]
pub(super) fn take_malloc_kind_telemetry_json() -> serde_json::Value {
let snapshot = MALLOC_STATE.with(|s| s.borrow_mut().take_kind_telemetry());
malloc_kind_telemetry_json_from_snapshot(snapshot)
}

#[cfg(feature = "diagnostics")]
pub(super) fn arena_region_json(region: crate::arena::ArenaRegionTelemetry) -> serde_json::Value {
serde_json::json!({
"in_use_bytes": region.in_use_bytes,
Expand All @@ -1214,6 +1242,7 @@ pub(super) fn arena_region_json(region: crate::arena::ArenaRegionTelemetry) -> s
})
}

#[cfg(feature = "diagnostics")]
pub(super) fn arena_snapshot_json(
snapshot: crate::arena::ArenaTelemetrySnapshot,
) -> serde_json::Value {
Expand All @@ -1229,6 +1258,7 @@ pub(super) fn arena_snapshot_json(
})
}

#[cfg(feature = "diagnostics")]
pub(super) fn steps_json(before: GcStepSnapshot, after: GcStepSnapshot) -> serde_json::Value {
serde_json::json!({
"arena_step_bytes": {
Expand Down
1 change: 1 addition & 0 deletions crates/perry-runtime/src/gc/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,7 @@ pub(crate) unsafe fn gc_type_finalize_unmarked_payload(obj_type: u8, user_ptr: *
}
}

#[cfg(feature = "diagnostics")]
#[inline]
pub(super) fn gc_type_name(obj_type: u8) -> &'static str {
gc_type_info(obj_type).map_or("unknown", |info| info.name)
Expand Down
10 changes: 10 additions & 0 deletions crates/perry-runtime/src/node_v8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,12 @@ pub extern "C" fn js_v8_cached_data_version_tag() -> f64 {
#[no_mangle]
pub extern "C" fn js_v8_get_heap_snapshot(options: f64) -> f64 {
validate_heap_snapshot_options(options);
#[cfg(feature = "diagnostics")]
let json = crate::gc::gc_build_v8_heap_snapshot_json();
// OFF stub: the compiler enables `diagnostics` whenever a program uses the
// v8 heap-snapshot APIs, so this branch is unreachable in practice.
#[cfg(not(feature = "diagnostics"))]
let json = String::from("{}");
snapshot_readable_stream(&json)
}

Expand All @@ -352,7 +357,12 @@ pub extern "C" fn js_v8_write_heap_snapshot(filename: f64, options: f64) -> f64
}
};
validate_heap_snapshot_options(options);
#[cfg(feature = "diagnostics")]
let json = crate::gc::gc_build_v8_heap_snapshot_json();
// OFF stub: unreachable in practice (compiler enables `diagnostics` when a
// program uses the v8 heap-snapshot APIs).
#[cfg(not(feature = "diagnostics"))]
let json = String::from("{}");
match std::fs::write(&path, json.as_bytes()) {
Ok(()) => string_value(&path),
Err(err) => unsafe {
Expand Down
6 changes: 6 additions & 0 deletions crates/perry-runtime/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -801,7 +801,12 @@ extern "C" fn process_report_function_write_report(
let filename = module_value_to_string(file_arg)
.filter(|s| !s.is_empty())
.unwrap_or_else(process_report_default_filename);
// OFF stub: unreachable in practice (the compiler enables `diagnostics`
// whenever a program references `process.report`).
#[cfg(feature = "diagnostics")]
let report_json = process_report_json_string("API", Some(&filename));
#[cfg(not(feature = "diagnostics"))]
let report_json = String::from("{}");
if let Err(err) = std::fs::write(&filename, report_json) {
crate::fs::validate::throw_type_error_with_code(
&format!("Failed to write diagnostic report to {filename}: {err}"),
Expand Down Expand Up @@ -1282,6 +1287,7 @@ fn node_platform_name() -> &'static str {
}
}

#[cfg(feature = "diagnostics")]
fn process_report_json_string(trigger: &str, filename: Option<&str>) -> String {
let args: Vec<String> = std::env::args().collect();
let command_line = if args.is_empty() {
Expand Down
16 changes: 9 additions & 7 deletions crates/perry-runtime/src/typed_feedback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
//! has actually seen at runtime.

use std::collections::{BTreeMap, HashMap};
use std::sync::{
atomic::{AtomicBool, Ordering},
LazyLock, Mutex,
};
#[cfg(any(feature = "diagnostics", test))]
use std::sync::atomic::AtomicBool;
#[cfg(any(feature = "diagnostics", test))]
use std::sync::atomic::Ordering;
use std::sync::{LazyLock, Mutex};

use crate::array::ArrayHeader;
use crate::object::ObjectHeader;
Expand All @@ -21,6 +22,7 @@ const POLYMORPHIC_CAP: usize = 4;

static REGISTRY: LazyLock<Mutex<TypedFeedbackRegistry>> =
LazyLock::new(|| Mutex::new(TypedFeedbackRegistry::default()));
#[cfg(any(feature = "diagnostics", test))]
static TRACE_DUMPED: AtomicBool = AtomicBool::new(false);

#[cfg(not(test))]
Expand Down Expand Up @@ -961,9 +963,9 @@ pub use guards::{

#[path = "typed_feedback/trace.rs"]
mod trace;
pub use trace::{
js_typed_feedback_maybe_dump_trace, typed_feedback_snapshot, typed_feedback_trace_json,
};
pub use trace::typed_feedback_snapshot;
#[cfg(feature = "diagnostics")]
pub use trace::{js_typed_feedback_maybe_dump_trace, typed_feedback_trace_json};

fn hash_bytes(bytes: &[u8]) -> u64 {
let mut h = 0xcbf2_9ce4_8422_2325u64;
Expand Down
Loading