objectdump.rs 6 KB
Newer Older
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
1
// Copyright 2017 The Australian National University
qinsoon's avatar
qinsoon committed
2
//
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
3 4 5
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
qinsoon's avatar
qinsoon committed
6
//
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
7
//     http://www.apache.org/licenses/LICENSE-2.0
qinsoon's avatar
qinsoon committed
8
//
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
9 10 11 12 13 14
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

qinsoon's avatar
qinsoon committed
15 16
use utils::Address;
use utils::ByteSize;
17 18 19 20 21 22
use utils::POINTER_SIZE;
use common::gctype::*;

use MY_GC;
use objectmodel;

qinsoon's avatar
qinsoon committed
23
use std::collections::HashMap;
24
use std::sync::Arc;
qinsoon's avatar
qinsoon committed
25 26 27

pub struct HeapDump {
    pub objects: HashMap<Address, ObjectDump>,
28
    pub relocatable_refs: HashMap<Address, String>
qinsoon's avatar
qinsoon committed
29 30 31 32
}

pub struct ObjectDump {
    pub mem_start: Address,
qinsoon's avatar
qinsoon committed
33
    pub mem_size: ByteSize,
34 35

    pub reference_addr: Address,
36
    pub reference_offsets: Vec<ByteSize> // based on reference_addr
37 38 39 40
}

impl HeapDump {
    pub fn from_roots(roots: Vec<Address>) -> HeapDump {
41
        trace!("dump heap from {:?}", roots);
qinsoon's avatar
qinsoon committed
42 43
        let mut work_queue: Vec<Address> = roots;
        let mut heap: HeapDump = HeapDump {
44
            objects: HashMap::new(),
45
            relocatable_refs: HashMap::new()
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
        };

        while !work_queue.is_empty() {
            let obj = work_queue.pop().unwrap();

            if !heap.objects.contains_key(&obj) {
                // add this object to heap dump
                let obj_dump = heap.persist_object(obj);
                heap.objects.insert(obj, obj_dump);

                heap.keep_tracing(heap.objects.get(&obj).unwrap(), &mut work_queue);
            }
        }

        heap.label_relocatable_refs();

        heap
    }

    fn persist_object(&self, obj: Address) -> ObjectDump {
66
        trace!("dump object: {}", obj);
67
        let hdr_addr = obj + objectmodel::OBJECT_HEADER_OFFSET;
qinsoon's avatar
qinsoon committed
68
        let hdr = unsafe { hdr_addr.load::<u64>() };
69 70 71 72 73 74 75

        if objectmodel::header_is_fix_size(hdr) {
            // fix sized type
            if objectmodel::header_has_ref_map(hdr) {
                // has ref map
                let ref_map = objectmodel::header_get_ref_map(hdr);

76 77
                trace!("fix sized, ref map as {:b}", ref_map);

78 79 80
                let mut offsets = vec![];
                let mut i = 0;
                while i < objectmodel::REF_MAP_LENGTH {
qinsoon's avatar
qinsoon committed
81
                    let has_ref: bool = ((ref_map >> i) & 1) == 1;
82 83 84 85 86 87 88 89 90

                    if has_ref {
                        offsets.push(i * POINTER_SIZE);
                    }

                    i += 1;
                }

                ObjectDump {
qinsoon's avatar
qinsoon committed
91 92 93 94
                    reference_addr: obj,
                    mem_start: hdr_addr,
                    mem_size: objectmodel::header_get_object_size(hdr) as usize +
                        objectmodel::OBJECT_HEADER_SIZE,
95
                    reference_offsets: offsets
96 97 98 99 100
                }
            } else {
                // by type ID
                let gctype_id = objectmodel::header_get_gctype_id(hdr);

101 102
                trace!("fix size, type id as {}", gctype_id);

103
                let gc_lock = MY_GC.read().unwrap();
qinsoon's avatar
qinsoon committed
104 105
                let gctype: Arc<GCType> =
                    gc_lock.as_ref().unwrap().gc_types[gctype_id as usize].clone();
106 107 108

                ObjectDump {
                    reference_addr: obj,
qinsoon's avatar
qinsoon committed
109 110
                    mem_start: hdr_addr,
                    mem_size: gctype.size() + objectmodel::OBJECT_HEADER_SIZE,
111
                    reference_offsets: gctype.gen_ref_offsets()
112 113 114 115
                }
            }
        } else {
            // hybrids - same as above
qinsoon's avatar
qinsoon committed
116
            let gctype_id = objectmodel::header_get_gctype_id(hdr);
qinsoon's avatar
qinsoon committed
117
            let var_length = objectmodel::header_get_hybrid_length(hdr);
118

119 120
            trace!("var sized, type id as {}", gctype_id);

121
            let gc_lock = MY_GC.read().unwrap();
qinsoon's avatar
qinsoon committed
122 123
            let gctype: Arc<GCType> =
                gc_lock.as_ref().unwrap().gc_types[gctype_id as usize].clone();
124 125 126

            ObjectDump {
                reference_addr: obj,
qinsoon's avatar
qinsoon committed
127 128
                mem_start: hdr_addr,
                mem_size: gctype.size_hybrid(var_length) + objectmodel::OBJECT_HEADER_SIZE,
129
                reference_offsets: gctype.gen_hybrid_ref_offsets(var_length)
130 131 132 133 134 135 136 137
            }
        }
    }

    fn keep_tracing(&self, obj_dump: &ObjectDump, work_queue: &mut Vec<Address>) {
        let base = obj_dump.reference_addr;

        for offset in obj_dump.reference_offsets.iter() {
138
            let field_addr = base + *offset;
qinsoon's avatar
qinsoon committed
139
            let edge = unsafe { field_addr.load::<Address>() };
140

141 142 143
            trace!("object reference from {} -> {} at +[{}]", base, 
                edge, offset);
                
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
            if !edge.is_zero() && !self.objects.contains_key(&edge) {
                work_queue.push(edge);
            }
        }
    }

    fn label_relocatable_refs(&mut self) {
        let mut count = 0;

        for addr in self.objects.keys() {
            let label = format!("GCDUMP_{}_{}", count, addr);
            self.relocatable_refs.insert(*addr, label);

            count += 1;
        }
    }
}

use std::fmt;

impl fmt::Debug for ObjectDump {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
qinsoon's avatar
qinsoon committed
166 167 168 169 170 171 172
        write!(
            f,
            "PersistedObject({}, {} bytes from {}, offsets at {:?})",
            self.reference_addr,
            self.mem_size,
            self.mem_start,
            self.reference_offsets
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
        )
    }
}

impl fmt::Debug for HeapDump {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Heap Dump\n").unwrap();

        write!(f, "---{} objects---\n", self.objects.len()).unwrap();
        for obj in self.objects.iter() {
            write!(f, "{:?}\n", obj).unwrap();
        }

        write!(f, "---{} ref labels---\n", self.relocatable_refs.len()).unwrap();
        for (addr, label) in self.relocatable_refs.iter() {
            write!(f, "{} = {}\n", addr, label).unwrap()
        }

        Ok(())
    }
qinsoon's avatar
qinsoon committed
193
}