///|
/// Checks class layouts and metadata references. Does not infer object liveness.
pub fn Summary::validate(self : Summary) -> Unit raise ParseError {
for id, cls in self.definitions {
if self.class_name(id) is None {
raise Invalid(0, "missing class name: \{id}")
}
ignore(self.hierarchy(id))
for field in cls.fields {
if self.string(field.name_id) is None {
raise Invalid(0, "unresolved instance field name")
}
}
for field in cls.statics {
if self.string(field.name_id) is None {
raise Invalid(0, "unresolved static field name")
}
}
}
for id, object in self.objects {
match object.kind {
Instance => ignore(self.instance_fields(id))
ObjectArray =>
if !self.definitions.contains(object.class_id) {
raise Invalid(object.payload_offset, "missing object-array class")
}
PrimitiveArray =>
// Only boolean payloads have bit patterns that are not valid values.
if object.primitive_kind == 4 {
let start = object.payload_offset
for i in start..<(start + object.payload_bytes) {
if self.data[i].to_int() > 1 {
raise Invalid(i, "invalid boolean array element")
}
}
}
}
}
for _, trace in self.traces {
for id in trace.frame_ids {
if !self.frames.contains(id) {
raise Invalid(0, "unresolved stack frame")
}
}
}
}
///|
pub fn Summary::instance_payload_size(
self : Summary,
class_id : UInt64,
) -> Int raise ParseError {
let mut result = 0
for cls in self.hierarchy(class_id) {
for field in cls.fields {
let width = type_width(field.kind, self.id_size)
if result > 2_147_483_647 - width {
raise Invalid(0, "class layout too large")
}
result += width
}
}
result
}
///|
/// Copies only the requested payload window; offsets are relative to payload.
pub fn Summary::payload(
self : Summary,
id : UInt64,
offset? : Int = 0,
limit? : Int = 100,
) -> Bytes raise ParseError {
let object = self.require_object(id)
let end = page_end(offset, limit, object.payload_bytes)
let start = object.payload_offset
self.data[start + offset:start + end].to_owned()
}
///|
pub fn Summary::root_kinds(self : Summary) -> Map[String, Int] {
let counts : Map[String, Int] = Map([])
for root in self.roots_data {
let label = root.label()
counts[label] = counts.get(label).unwrap_or(0) + 1
}
counts
}
///|
/// Returns all matching GC roots. Multiple roots may refer to one object.
pub fn Summary::roots_for(self : Summary, id : UInt64) -> Array[RootInfo] {
self.roots_data.filter(root => root.object_id == id)
}
///|
pub fn Summary::thread_serials(self : Summary) -> Array[UInt64] {
let serials = self.threads.keys().to_array()
serials.sort()
serials
}
///|
pub fn Summary::trace_serials(self : Summary) -> Array[UInt64] {
let serials = self.traces.keys().to_array()
serials.sort()
serials
}
///|
pub fn Summary::field(
self : Summary,
id : UInt64,
name : String,
declaring_class? : UInt64 = 0UL,
) -> Value? raise ParseError {
let fields = if self.definitions.contains(id) {
self.static_fields(id)
} else {
self.instance_fields(id)
}
let matches = fields.filter(field => {
field.name == name &&
(declaring_class == 0UL || field.declaring_class_id == declaring_class)
})
if matches.length() > 1 {
raise Invalid(0, "ambiguous field name; specify declaring class")
}
matches.get(0).map(field => field.value)
}