///|
fn frame_json(f : AllocationFrame) -> Json {
{
"function_id": @json.to_json(f.function_id),
"name": @json.to_json(f.name),
"script_name": @json.to_json(f.script_name),
"script_id": @json.to_json(f.script_id),
"line": @json.to_json(f.line),
"column": @json.to_json(f.column),
}
}
///|
fn Inspector::source_query(
self : Inspector,
handle : Int32,
req : Json,
) -> Json raise HeapError {
let current = self.get(handle)
let node = current.graph.snapshot.node(
selected_node(current.graph.snapshot, req),
)
let mut source_handle = handle
let mut result = advanced_query(current, req, "source", 0, 100)
let frames = array(field(result, "frames"), "frames")
match optional(req, "fallback_handle") {
Some(value) => {
guard bool_option(req, "same_isolate", false) else {
raise Identity(
"allocation-source fallback requires same-isolate confirmation",
)
}
let fallback = integer(value, "fallback_handle")
if frames.is_empty() {
let old = self.get(fallback)
match old.graph.snapshot.find_id(node.id) {
Some(index) => {
let previous = old.graph.snapshot.nodes[index]
guard previous.kind == node.kind && previous.name == node.name else {
raise Identity("source fallback identity conflict")
}
result = advanced_query(
old,
{ "id": @json.to_json(node.id) },
"source",
0,
100,
)
source_handle = fallback
}
None => ()
}
}
}
None => ()
}
match result {
Object(m) => {
let fields = m.copy()
fields["node"] = node_json(current, node)
fields["source_snapshot_handle"] = @json.to_json(source_handle)
fields["used_fallback"] = @json.to_json(source_handle != handle)
@json.to_json(fields)
}
_ => result
}
}
///|
fn advanced_query(
a : Analysis,
req : Json,
op : String,
offset : Int32,
limit : Int32,
) -> Json raise HeapError {
let s = a.graph.snapshot
match op {
"source" => {
let index = selected_node(s, req)
let frames = s.allocation_stack(index)
let location = match s.source_location(index) {
None => Json::null()
Some(l) =>
{
"script_id": @json.to_json(l.script_id),
"line": @json.to_json(l.line),
"column": @json.to_json(l.column),
"coordinate_base": 0,
}
}
let totals = match s.allocation_totals(index) {
None => Json::null()
Some((count, size)) =>
{ "count": bytes_json(count), "bytes": bytes_json(size) }
}
{
"node": node_json(a, s.nodes[index]),
"location": location,
"frames": @json.to_json(frames.map(frame_json)),
"allocation_totals": totals,
"available": @json.to_json(!frames.is_empty()),
"interpretation": "Allocation frames require tracking during capture. Counts and bytes describe the allocation site, not this object's current size.",
}
}
"paths" => {
let search = a.graph.root_paths(
selected_node(s, req),
max_paths=int_option(req, "max_paths", 5),
max_depth=int_option(req, "max_depth", 64),
max_work=int_option(req, "max_work", 50000),
)
{
"paths": @json.to_json(
search.paths.map(p => p.map(e => edge_json(a, e))),
),
"complete": @json.to_json(search.complete),
"examined": @json.to_json(search.examined),
"stop_reason": @json.to_json(search.stop_reason),
}
}
"tree" => {
let node = match optional(req, "id") {
Some(_) => selected_node(s, req)
None => int_option(req, "index", s.root)
}
{
"node": node_json(a, s.node(node)),
"children": node_page_json(a, a.dominated(node, offset~, limit~)),
"ancestors": @json.to_json(
a.dominator_chain(node).map(n => node_json(a, n)),
),
}
}
"cycles" | "cycle" => {
let q = string_option(req, "query", "")
let all = a.graph.cycles()
if op == "cycle" {
let id = int_option(req, "cycle_id", -1)
let mut group : CycleGroup? = None
for c in all {
if c.id == id {
group = Some(c)
break
}
}
match group {
None => raise NotFound("cycle \{id}")
Some(c) => {
let items : Array[Json] = []
for i in 0..= offset && items.length() < limit {
items.push(node_json(a, s.nodes[c.members[i]]))
}
}
{
"items": @json.to_json(items),
"total": @json.to_json(c.members.length()),
"offset": @json.to_json(offset),
"cycle_id": @json.to_json(id),
}
}
}
} else {
let groups = all.filter(c => {
q == "" || c.members.any(i => s.nodes[i].name.contains(q))
})
let items : Array[Json] = []
for i in 0..= offset && items.length() < limit {
let c = groups[i]
let sample : Array[Json] = []
for j in 0.. raise Invalid("unknown operation: \{op}")
}
}