1/*
2 * Copyright (c) 2009 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#include <stdio.h>
21#include <stdlib.h>
22#include <assert.h>
23#include <malloc/malloc.h>
24#include <auto_zone.h>
25
26struct BlockRecorderContext {
27    size_t blocks_in_use;
28    size_t size_in_use;
29    malloc_zone_t *zone;
30};
31typedef struct BlockRecorderContext BlockRecorderContext;
32
33static void blockRecorder(task_t task, void *context, unsigned type, vm_range_t *ranges, unsigned count) {
34    BlockRecorderContext *blockContext = context;
35    while (count--) {
36        blockContext->blocks_in_use++;
37        // <rdar://problem/4574925> scalable malloc shouldn't count the zone as part of the allocated block space.
38        if (ranges->address != (vm_address_t)blockContext->zone) blockContext->size_in_use += ranges->size;
39        ++ranges;
40    }
41}
42
43void test_introspection(malloc_zone_t *zone, boolean_t exact) {
44    static void* pointers[1000];
45    BlockRecorderContext context = { 0, 0, zone };
46    malloc_statistics_t stats;
47    size_t i;
48
49    // allocate 1000 randomly sized blocks.
50    for (i = 0; i < 1000; ++i) {
51        size_t random_size = (random() & 0x1FFFF);
52        pointers[i] = malloc_zone_malloc(zone, random_size);
53    }
54
55    // deallocate a random number of the blocks.
56    for (i = 0; i < 1000; ++i) {
57        if (random() & 0x1) {
58            malloc_zone_free(zone, pointers[i]);
59            pointers[i] = NULL;
60        }
61    }
62
63    // validate that these agree with the values returned by the enumeration APIs.
64    zone->introspect->enumerator(mach_task_self(), &context, MALLOC_PTR_IN_USE_RANGE_TYPE, (vm_address_t)zone, NULL, blockRecorder);
65    malloc_zone_statistics(zone, &stats);
66    if (exact) {
67        assert(stats.blocks_in_use == context.blocks_in_use);
68        assert(stats.size_in_use == context.size_in_use);
69    } else {
70        // the malloc default zone enumerators don't currently match the statistics.
71        assert(stats.blocks_in_use >= context.blocks_in_use);
72        assert(stats.size_in_use >= context.size_in_use);
73    }
74}
75
76int main() {
77    test_introspection(auto_zone_create("auto zone"), true);
78    test_introspection(malloc_default_zone(), false);
79    test_introspection(malloc_create_zone(8192, 0), false);
80    return 0;
81}
82