1/*
2 * Copyright (c) 2011 Apple Inc. All rights reserved.
3 *
4 * @APPLE_APACHE_LICENSE_HEADER_START@
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * @APPLE_APACHE_LICENSE_HEADER_END@
19 */
20/*
21    InUseEnumerator.cpp
22    Malloc zone enumeration implemlentation.
23    Copyright (c) 2004-2011 Apple Inc. All rights reserved.
24 */
25
26#include "BlockIterator.h"
27#include "Definitions.h"
28#include "InUseEnumerator.h"
29#include "Large.h"
30#include "Zone.h"
31
32
33namespace Auto {
34
35    //----- InUseEnumerator -----//
36
37
38    //
39    // scan
40    //
41    // Scan through a task's auto zone looking for
42    //
43    kern_return_t InUseEnumerator::scan() {
44        // calculate the zone size
45        usword_t zone_size = Zone::bytes_needed();
46        // read in zone information
47        Zone *zone = (Zone *)read((void *)_zone_address, zone_size);
48        // check for successful read
49        if (!zone) return _error;
50
51        // Don't record the zone object, otherwise objects allocated by the collector appear as leaks. <rdar://problem/6747283>
52        // record((void *)_zone_address, zone_size, MALLOC_ADMIN_REGION_RANGE_TYPE);
53
54        // record the garbage list.
55        PointerList& garbage_list = zone->garbage_list();
56        record((void*)garbage_list.buffer(), garbage_list.size(), MALLOC_ADMIN_REGION_RANGE_TYPE);
57
58        // iterate though each of the regions
59        Region *region = zone->region_list();
60        while (region != NULL) {
61            // load the base of the region structure
62        	Region *regionReader = (Region *)read(region, Region::bytes_needed());
63            // check for successful read
64            if (!regionReader) return _error;
65
66            // record the region object
67            record(region, Region::bytes_needed(), MALLOC_ADMIN_REGION_RANGE_TYPE);
68            // record the region range
69            record(regionReader->address(), regionReader->size(), MALLOC_PTR_REGION_RANGE_TYPE);
70
71            if (_type_mask & (MALLOC_ADMIN_REGION_RANGE_TYPE | MALLOC_PTR_IN_USE_RANGE_TYPE | AUTO_RETAINED_BLOCK_TYPE)) {
72                // iterate through the subzones
73                SubzoneRangeIterator iterator(regionReader->subzone_range());
74                while (Subzone *subzone = iterator.next()) {
75                    // map in the subzone header
76                    Subzone *subzoneReader = (Subzone *)read(subzone, sizeof(Subzone));
77                    record(subzone, subzoneReader->base_data_size(), MALLOC_ADMIN_REGION_RANGE_TYPE);
78
79                    if (_type_mask & (MALLOC_PTR_IN_USE_RANGE_TYPE | AUTO_RETAINED_BLOCK_TYPE)) {
80                        // map in the subzone + side data when enumerating blocks. no need to map the blocks themselves.
81                        subzoneReader = (Subzone *)read(subzone, subzoneReader->base_data_size());
82                        usword_t n = subzoneReader->allocation_limit();
83                        MemoryReader reader(_task, _reader);
84                        for (usword_t q = 0; q < n; q = subzoneReader->next_quantum(q, reader)) {
85                            if (!subzoneReader->is_free(q)) {
86                                // two cases:  thread local or global.
87                                if (subzoneReader->is_live_thread_local(q)) {
88                                    record(subzoneReader->quantum_address(q), subzoneReader->size(q), MALLOC_PTR_IN_USE_RANGE_TYPE);
89                                } else if (!subzoneReader->is_thread_local(q)) {
90                                    // only global blocks can have a non-zero retain count.
91                                    record(subzoneReader->quantum_address(q), subzoneReader->size(q), MALLOC_PTR_IN_USE_RANGE_TYPE | (subzoneReader->has_refcount(q) ? AUTO_RETAINED_BLOCK_TYPE : 0));
92                                }
93                            }
94                        }
95                    }
96                }
97            }
98            region = regionReader->next();
99        }
100
101        // iterate through the large
102        for (Large *large = zone->large_list(); large; large = large->next()) {
103            record(large, sizeof(Large), MALLOC_ADMIN_REGION_RANGE_TYPE);
104            Large *largeReader = (Large *)read(large, sizeof(Large));
105            unsigned type = MALLOC_PTR_IN_USE_RANGE_TYPE;
106            if (largeReader->refcount() != 0)
107                type |= AUTO_RETAINED_BLOCK_TYPE;
108            record(large->address(), largeReader->size(), type);
109            record(displace(large, sizeof(Large) + largeReader->size()), largeReader->vm_size() - (sizeof(Large) + largeReader->size()), MALLOC_ADMIN_REGION_RANGE_TYPE);
110            large = largeReader;
111        }
112
113        return KERN_SUCCESS;
114    }
115
116};
117