1use getset::{CopyGetters, Getters};
10
11use crate::*;
12
13mod primitive_array;
14
15pub use primitive_array::PrimitiveArray;
16pub use primitive_array::PrimitiveArrayType;
17
18pub enum SubRecord<'a> {
19 GcRootUnknown(GcRootUnknown),
21 GcRootThreadObj(GcRootThreadObj),
22 GcRootJniGlobal(GcRootJniGlobal),
23 GcRootJniLocalRef(GcRootJniLocalRef),
24 GcRootJavaStackFrame(GcRootJavaStackFrame),
25 GcRootNativeStack(GcRootNativeStack),
27 GcRootSystemClass(GcRootSystemClass),
28 GcRootThreadBlock(GcRootThreadBlock),
30 GcRootBusyMonitor(GcRootBusyMonitor),
31 Class(Class<'a>),
32 Instance(Instance<'a>),
33 ObjectArray(ObjectArray<'a>),
34 PrimitiveArray(PrimitiveArray<'a>),
35}
36
37impl<'a> fmt::Debug for SubRecord<'a> {
38 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
39 write!(
40 f,
41 "{}",
42 match self {
43 SubRecord::GcRootUnknown(_) => "GcRootUnknown",
44 SubRecord::GcRootThreadObj(_) => "GcRootThreadObj",
45 SubRecord::GcRootJniGlobal(_) => "GcRootJniGlobal",
46 SubRecord::GcRootJniLocalRef(_) => "GcRootJniLocalRef",
47 SubRecord::GcRootJavaStackFrame(_) => "GcRootJavaStackFrame",
48 SubRecord::GcRootNativeStack(_) => "GcRootNativeStack",
49 SubRecord::GcRootSystemClass(_) => "GcRootSystemClass",
50 SubRecord::GcRootThreadBlock(_) => "GcRootThreadBlock",
51 SubRecord::GcRootBusyMonitor(_) => "GcRootBusyMonitor",
52 SubRecord::Class(_) => "Class",
53 SubRecord::Instance(_) => "Instance",
54 SubRecord::ObjectArray(_) => "ObjectArray",
55 SubRecord::PrimitiveArray(_) => "PrimitiveArray",
56 }
57 )
58 }
59}
60
61impl<'a> SubRecord<'a> {
62 pub(crate) fn parse(input: &[u8], id_size: IdSize) -> nom::IResult<&[u8], SubRecord> {
63 let (input, tag_byte) = number::be_u8(input)?;
65
66 let (input, variant) = match tag_byte {
69 0xFF => GcRootUnknown::parse(input, id_size)
70 .map(|(input, r)| (input, SubRecord::GcRootUnknown(r))),
71 0x08 => GcRootThreadObj::parse(input, id_size)
72 .map(|(input, r)| (input, SubRecord::GcRootThreadObj(r))),
73 0x01 => GcRootJniGlobal::parse(input, id_size)
74 .map(|(input, r)| (input, SubRecord::GcRootJniGlobal(r))),
75 0x02 => GcRootJniLocalRef::parse(input, id_size)
76 .map(|(input, r)| (input, SubRecord::GcRootJniLocalRef(r))),
77 0x03 => GcRootJavaStackFrame::parse(input, id_size)
78 .map(|(input, r)| (input, SubRecord::GcRootJavaStackFrame(r))),
79 0x04 => GcRootNativeStack::parse(input, id_size)
80 .map(|(input, r)| (input, SubRecord::GcRootNativeStack(r))),
81 0x05 => GcRootSystemClass::parse(input, id_size)
82 .map(|(input, r)| (input, SubRecord::GcRootSystemClass(r))),
83 0x06 => GcRootThreadBlock::parse(input, id_size)
84 .map(|(input, r)| (input, SubRecord::GcRootThreadBlock(r))),
85 0x07 => GcRootBusyMonitor::parse(input, id_size)
86 .map(|(input, r)| (input, SubRecord::GcRootBusyMonitor(r))),
87 0x20 => Class::parse(input, id_size).map(|(input, r)| (input, SubRecord::Class(r))),
88 0x21 => {
89 Instance::parse(input, id_size).map(|(input, r)| (input, SubRecord::Instance(r)))
90 }
91 0x22 => ObjectArray::parse(input, id_size)
92 .map(|(input, r)| (input, SubRecord::ObjectArray(r))),
93 0x23 => PrimitiveArray::parse(input, id_size)
94 .map(|(input, r)| (input, SubRecord::PrimitiveArray(r))),
95 _ => panic!("Unexpected sub-record type {:#X}", tag_byte),
96 }?;
97
98 Ok((input, variant))
99 }
100}
101
102#[derive(CopyGetters, Copy, Clone, Debug)]
103pub struct GcRootUnknown {
104 #[get_copy = "pub"]
105 obj_id: Id,
106}
107
108impl GcRootUnknown {
109 fn parse(input: &[u8], id_size: IdSize) -> nom::IResult<&[u8], Self> {
110 let (input, id) = Id::parse(input, id_size)?;
112
113 Ok((input, GcRootUnknown { obj_id: id }))
114 }
115}
116
117#[derive(CopyGetters, Copy, Clone, Debug)]
118pub struct GcRootThreadObj {
119 #[get_copy = "pub"]
121 thread_obj_id: Option<Id>,
122 #[get_copy = "pub"]
123 thread_serial: Serial,
124 #[get_copy = "pub"]
125 stack_trace_serial: Serial,
126}
127
128impl GcRootThreadObj {
129 fn parse(input: &[u8], id_size: IdSize) -> nom::IResult<&[u8], Self> {
130 let (input, thread_obj_id) = parse_optional_id(input, id_size)?;
132 let (input, thread_serial) = number::be_u32(input)?;
133 let (input, stack_trace_serial) = number::be_u32(input)?;
134
135 Ok((
136 input,
137 GcRootThreadObj {
138 thread_obj_id,
139 thread_serial: thread_serial.into(),
140 stack_trace_serial: stack_trace_serial.into(),
141 },
142 ))
143 }
144}
145
146#[derive(CopyGetters, Copy, Clone, Debug)]
147pub struct GcRootJniGlobal {
148 #[get_copy = "pub"]
149 obj_id: Id,
150 #[get_copy = "pub"]
151 jni_global_ref_id: Id,
152}
153
154impl GcRootJniGlobal {
155 fn parse(input: &[u8], id_size: IdSize) -> nom::IResult<&[u8], Self> {
156 let (input, obj_id) = Id::parse(input, id_size)?;
158 let (input, jni_global_ref_id) = Id::parse(input, id_size)?;
159
160 Ok((
161 input,
162 GcRootJniGlobal {
163 obj_id,
164 jni_global_ref_id,
165 },
166 ))
167 }
168}
169
170#[derive(CopyGetters, Copy, Clone, Debug)]
171pub struct GcRootJniLocalRef {
172 #[get_copy = "pub"]
173 obj_id: Id,
174 #[get_copy = "pub"]
175 thread_serial: Serial,
176 #[get_copy = "pub"]
177 frame_index: Option<u32>,
178}
179
180impl GcRootJniLocalRef {
181 fn parse(input: &[u8], id_size: IdSize) -> nom::IResult<&[u8], Self> {
182 let (input, obj_id) = Id::parse(input, id_size)?;
184 let (input, thread_serial) = number::be_u32(input)?;
185 let (input, frame_index) = parse_optional_u32(input)?;
186
187 Ok((
188 input,
189 GcRootJniLocalRef {
190 obj_id,
191 thread_serial: thread_serial.into(),
192 frame_index,
193 },
194 ))
195 }
196}
197
198#[derive(CopyGetters, Copy, Clone, Debug)]
199pub struct GcRootJavaStackFrame {
200 #[get_copy = "pub"]
201 obj_id: Id,
202 #[get_copy = "pub"]
203 thread_serial: Serial,
204 #[get_copy = "pub"]
205 frame_index: Option<u32>,
206}
207
208impl GcRootJavaStackFrame {
209 fn parse(input: &[u8], id_size: IdSize) -> nom::IResult<&[u8], Self> {
210 let (input, obj_id) = Id::parse(input, id_size)?;
212 let (input, thread_serial) = number::be_u32(input)?;
213 let (input, frame_index) = parse_optional_u32(input)?;
214
215 Ok((
216 input,
217 GcRootJavaStackFrame {
218 obj_id,
219 thread_serial: thread_serial.into(),
220 frame_index,
221 },
222 ))
223 }
224}
225
226#[derive(CopyGetters, Copy, Clone, Debug)]
227pub struct GcRootNativeStack {
228 #[get_copy = "pub"]
229 obj_id: Id,
230 #[get_copy = "pub"]
231 thread_serial: Serial,
232}
233
234impl GcRootNativeStack {
235 fn parse(input: &[u8], id_size: IdSize) -> nom::IResult<&[u8], Self> {
236 let (input, obj_id) = Id::parse(input, id_size)?;
238 let (input, thread_serial) = number::be_u32(input)?;
239
240 Ok((
241 input,
242 GcRootNativeStack {
243 obj_id,
244 thread_serial: thread_serial.into(),
245 },
246 ))
247 }
248}
249
250#[derive(CopyGetters, Copy, Clone, Debug)]
251pub struct GcRootSystemClass {
252 #[get_copy = "pub"]
253 obj_id: Id,
254}
255
256impl GcRootSystemClass {
257 fn parse(input: &[u8], id_size: IdSize) -> nom::IResult<&[u8], Self> {
258 let (input, obj_id) = Id::parse(input, id_size)?;
260
261 Ok((input, GcRootSystemClass { obj_id }))
262 }
263}
264
265#[derive(CopyGetters, Copy, Clone, Debug)]
266pub struct GcRootThreadBlock {
267 #[get_copy = "pub"]
268 obj_id: Id,
269 #[get_copy = "pub"]
270 thread_serial: Serial,
271}
272
273impl GcRootThreadBlock {
274 fn parse(input: &[u8], id_size: IdSize) -> nom::IResult<&[u8], Self> {
275 let (input, obj_id) = Id::parse(input, id_size)?;
277 let (input, thread_serial) = number::be_u32(input)?;
278
279 Ok((
280 input,
281 GcRootThreadBlock {
282 obj_id,
283 thread_serial: thread_serial.into(),
284 },
285 ))
286 }
287}
288
289#[derive(CopyGetters, Copy, Clone, Debug)]
290pub struct GcRootBusyMonitor {
291 #[get_copy = "pub"]
292 obj_id: Id,
293}
294
295impl GcRootBusyMonitor {
296 fn parse(input: &[u8], id_size: IdSize) -> nom::IResult<&[u8], Self> {
297 let (input, obj_id) = Id::parse(input, id_size)?;
299
300 Ok((input, GcRootBusyMonitor { obj_id }))
301 }
302}
303
304#[derive(CopyGetters)]
309pub struct Class<'a> {
310 id_size: IdSize,
311 #[get_copy = "pub"]
312 obj_id: Id,
313 #[get_copy = "pub"]
314 stack_trace_serial: Serial,
315 #[get_copy = "pub"]
317 super_class_obj_id: Option<Id>,
318 #[get_copy = "pub"]
319 class_loader_obj_id: Option<Id>,
320 #[get_copy = "pub"]
321 signers_obj_id: Option<Id>,
322 #[get_copy = "pub"]
324 protection_domain_obj_id: Option<Id>,
325 #[get_copy = "pub"]
326 instance_size_bytes: u32,
327 num_static_fields: u16,
328 static_fields: &'a [u8],
329 num_instance_fields: u16,
330 instance_fields: &'a [u8],
331}
332
333impl<'a> Class<'a> {
334 pub fn static_fields(&self) -> StaticFieldEntries {
336 StaticFieldEntries {
337 iter: ParsingIterator::new_stateless_id_size(
338 self.id_size,
339 self.static_fields,
340 self.num_static_fields as u32,
341 ),
342 }
343 }
344
345 pub fn instance_field_descriptors(&self) -> FieldDescriptors {
352 FieldDescriptors {
353 iter: ParsingIterator::new_stateless_id_size(
354 self.id_size,
355 self.instance_fields,
356 self.num_instance_fields as u32,
357 ),
358 }
359 }
360
361 fn parse(input: &[u8], id_size: IdSize) -> nom::IResult<&[u8], Class> {
362 let (input, obj_id) = Id::parse(input, id_size)?;
365 let (input, stack_trace_serial) = number::be_u32(input)?;
366 let (input, super_class_obj_id) = parse_optional_id(input, id_size)?;
367 let (input, class_loader_obj_id) = parse_optional_id(input, id_size)?;
368 let (input, signers_obj_id) = parse_optional_id(input, id_size)?;
369 let (input, protection_domain_obj_id) = parse_optional_id(input, id_size)?;
370 let (input, _) = Id::parse(input, id_size)?;
372 let (input, _) = Id::parse(input, id_size)?;
373 let (input, instance_size_bytes) = number::be_u32(input)?;
374 let (input, constant_pool_len) = number::be_u16(input)?;
375 assert_eq!(0, constant_pool_len);
379
380 let (input, num_static_fields) = number::be_u16(input)?;
381
382 let input_before_static_fields = input;
389 let mut input_after_static_fields = input;
391 for _ in 0..num_static_fields {
392 let (input, _) = StaticFieldEntry::parse(input_after_static_fields, id_size)?;
393 input_after_static_fields = input;
394 }
395
396 let static_fields_byte_len =
397 input_before_static_fields.len() - input_after_static_fields.len();
398 let (_input, static_fields) = bytes::take(static_fields_byte_len)(input)?;
399
400 let (input, num_instance_fields) = number::be_u16(input_after_static_fields)?;
402
403 let instance_fields_byte_len = num_instance_fields as usize * (id_size.size_in_bytes() + 1);
405
406 let (input, instance_fields) = bytes::take(instance_fields_byte_len)(input)?;
407
408 Ok((
409 input,
410 Class {
411 id_size,
412 obj_id,
413 stack_trace_serial: stack_trace_serial.into(),
414 super_class_obj_id,
415 class_loader_obj_id,
416 signers_obj_id,
417 protection_domain_obj_id,
418 instance_size_bytes,
419 num_static_fields,
420 static_fields,
421 num_instance_fields,
422 instance_fields,
423 },
424 ))
425 }
426}
427
428pub struct StaticFieldEntries<'a> {
430 iter: ParsingIterator<'a, StaticFieldEntry, IdSizeParserWrapper<StaticFieldEntry>>,
431}
432
433impl<'a> Iterator for StaticFieldEntries<'a> {
434 type Item = ParseResult<'a, StaticFieldEntry>;
435
436 fn next(&mut self) -> Option<Self::Item> {
437 self.iter.next()
438 }
439}
440
441pub struct FieldDescriptors<'a> {
443 iter: ParsingIterator<'a, FieldDescriptor, IdSizeParserWrapper<FieldDescriptor>>,
444}
445
446impl<'a> Iterator for FieldDescriptors<'a> {
447 type Item = ParseResult<'a, FieldDescriptor>;
448
449 fn next(&mut self) -> Option<Self::Item> {
450 self.iter.next()
451 }
452}
453
454#[derive(CopyGetters, Getters)]
456pub struct Instance<'a> {
457 #[get_copy = "pub"]
458 obj_id: Id,
459 #[get_copy = "pub"]
460 stack_trace_serial: Serial,
461 #[get_copy = "pub"]
462 class_obj_id: Id,
463 #[get = "pub"]
501 fields: &'a [u8],
502}
503
504impl<'a> Instance<'a> {
505 fn parse(input: &[u8], id_size: IdSize) -> nom::IResult<&[u8], Instance> {
506 let (input, obj_id) = Id::parse(input, id_size)?;
508 let (input, stack_trace_serial) = number::be_u32(input)?;
509 let (input, class_obj_id) = Id::parse(input, id_size)?;
510 let (input, fields_byte_len) = number::be_u32(input)?;
511 let (input, fields) = bytes::take(fields_byte_len)(input)?;
512
513 Ok((
514 input,
515 Instance {
516 obj_id,
517 stack_trace_serial: stack_trace_serial.into(),
518 class_obj_id,
519 fields,
520 },
521 ))
522 }
523}
524
525#[derive(CopyGetters)]
527pub struct ObjectArray<'a> {
528 #[get_copy = "pub"]
529 obj_id: Id,
530 #[get_copy = "pub"]
531 stack_trace_serial: Serial,
532 #[get_copy = "pub"]
534 array_class_obj_id: Id,
535 num_elements: u32,
536 contents: &'a [u8],
537}
538
539impl<'a> ObjectArray<'a> {
540 fn parse(input: &[u8], id_size: IdSize) -> nom::IResult<&[u8], ObjectArray> {
541 let (input, obj_id) = Id::parse(input, id_size)?;
543 let (input, stack_trace_serial) = number::be_u32(input)?;
544 let (input, num_elements) = number::be_u32(input)?;
545 let (input, array_class_id) = Id::parse(input, id_size)?;
546
547 let id_bytes_len = num_elements as usize * id_size.size_in_bytes();
548
549 let (input, contents) = bytes::take(id_bytes_len)(input)?;
550
551 Ok((
552 input,
553 ObjectArray {
554 obj_id,
555 stack_trace_serial: stack_trace_serial.into(),
556 array_class_obj_id: array_class_id,
557 num_elements,
558 contents,
559 },
560 ))
561 }
562
563 pub fn elements(&self, id_size: IdSize) -> NullableIds {
565 NullableIds {
566 iter: ParsingIterator::new_stateless_id_size(id_size, self.contents, self.num_elements),
567 }
568 }
569}
570
571#[allow(unused)]
573enum ConstantPoolEntry {}
574
575#[derive(CopyGetters, Clone, Copy, Debug)]
577pub struct StaticFieldEntry {
578 #[get_copy = "pub"]
579 name_id: Id,
580 #[get_copy = "pub"]
581 field_type: FieldType,
582 #[get_copy = "pub"]
583 value: FieldValue,
584}
585
586impl StatelessParserWithId for StaticFieldEntry {
587 fn parse(input: &[u8], id_size: IdSize) -> nom::IResult<&[u8], Self> {
588 let (input, name_id) = Id::parse(input, id_size)?;
589
590 let (input, field_type) = FieldType::parse(input)?;
591 let (input, value) = field_type.parse_value(input, id_size)?;
592
593 Ok((
594 input,
595 StaticFieldEntry {
596 name_id,
597 field_type,
598 value,
599 },
600 ))
601 }
602}
603
604#[derive(Clone, Copy, Debug)]
605pub enum FieldValue {
606 ObjectId(Option<Id>),
607 Boolean(bool),
608 Char(u16),
609 Float(f32),
610 Double(f64),
611 Byte(i8),
612 Short(i16),
613 Int(i32),
614 Long(i64),
615}
616
617#[derive(CopyGetters, Clone, Copy, Debug)]
619pub struct FieldDescriptor {
620 #[get_copy = "pub"]
621 name_id: Id,
622 #[get_copy = "pub"]
623 field_type: FieldType,
624}
625
626impl StatelessParserWithId for FieldDescriptor {
627 fn parse(input: &[u8], id_size: IdSize) -> nom::IResult<&[u8], Self> {
628 let (input, name_id) = Id::parse(input, id_size)?;
629
630 let (input, field_type) = FieldType::parse(input)?;
631
632 Ok((
633 input,
634 FieldDescriptor {
635 name_id,
636 field_type,
637 },
638 ))
639 }
640}
641
642#[derive(Clone, Copy, Debug)]
643pub enum FieldType {
644 ObjectId,
645 Boolean,
646 Char,
647 Float,
648 Double,
649 Byte,
650 Short,
651 Int,
652 Long,
653}
654
655impl FieldType {
656 fn parse(input: &[u8]) -> nom::IResult<&[u8], Self> {
657 let (input, type_byte) = number::be_u8(input)?;
658
659 let field_type = match type_byte {
661 0x02 => FieldType::ObjectId,
663 0x04 => FieldType::Boolean,
664 0x05 => FieldType::Char,
665 0x06 => FieldType::Float,
666 0x07 => FieldType::Double,
667 0x08 => FieldType::Byte,
668 0x09 => FieldType::Short,
669 0x0A => FieldType::Int,
670 0x0B => FieldType::Long,
671 _ => panic!("Unexpected field type {:#X}", type_byte),
672 };
673
674 Ok((input, field_type))
675 }
676
677 pub fn parse_value<'a>(
679 &self,
680 input: &'a [u8],
681 id_size: IdSize,
682 ) -> nom::IResult<&'a [u8], FieldValue> {
683 match self {
685 FieldType::ObjectId => parse_optional_id(input, id_size)
686 .map(|(input, id)| (input, FieldValue::ObjectId(id))),
687 FieldType::Boolean => {
688 bool::parse(input).map(|(input, b)| (input, FieldValue::Boolean(b)))
689 }
690 FieldType::Char => u16::parse(input).map(|(input, c)| (input, FieldValue::Char(c))),
691 FieldType::Float => f32::parse(input).map(|(input, f)| (input, FieldValue::Float(f))),
692 FieldType::Double => f64::parse(input).map(|(input, f)| (input, FieldValue::Double(f))),
693 FieldType::Byte => i8::parse(input).map(|(input, b)| (input, FieldValue::Byte(b))),
694 FieldType::Short => i16::parse(input).map(|(input, s)| (input, FieldValue::Short(s))),
695 FieldType::Int => i32::parse(input).map(|(input, i)| (input, FieldValue::Int(i))),
696 FieldType::Long => i64::parse(input).map(|(input, l)| (input, FieldValue::Long(l))),
697 }
698 }
699
700 pub fn java_type_name(&self) -> &'static str {
701 match self {
702 FieldType::ObjectId => "Object",
703 FieldType::Boolean => "boolean",
704 FieldType::Char => "char",
705 FieldType::Float => "float",
706 FieldType::Double => "double",
707 FieldType::Byte => "byte",
708 FieldType::Short => "short",
709 FieldType::Int => "int",
710 FieldType::Long => "long",
711 }
712 }
713}
714
715fn parse_optional_id(input: &[u8], id_size: IdSize) -> nom::IResult<&[u8], Option<Id>> {
716 Id::parse(input, id_size).map(|(input, id)| {
717 if id.id == 0 {
718 (input, None)
719 } else {
720 (input, Some(id))
721 }
722 })
723}
724
725fn parse_optional_u32(input: &[u8]) -> nom::IResult<&[u8], Option<u32>> {
726 number::be_u32(input).map(|(input, index)| {
727 if index == u32::max_value() {
728 (input, None)
729 } else {
730 (input, Some(index))
731 }
732 })
733}
734
735pub struct NullableIds<'a> {
737 iter: ParsingIterator<'a, Option<Id>, IdSizeParserWrapper<Option<Id>>>,
738}
739
740impl<'a> Iterator for NullableIds<'a> {
741 type Item = ParseResult<'a, Option<Id>>;
742
743 fn next(&mut self) -> Option<Self::Item> {
744 self.iter.next()
745 }
746}
747
748impl StatelessParserWithId for Option<Id> {
749 fn parse(input: &[u8], id_size: IdSize) -> nom::IResult<&[u8], Self> {
750 parse_optional_id(input, id_size)
751 }
752}