1
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#include "SkTypes.h"
11#include <stdio.h>
12#include <stdlib.h>
13
14#ifdef SK_DEBUG
15    #define SK_TAG_BLOCKS
16    // #define SK_TRACK_ALLOC  // enable to see a printf for every alloc/free
17    // #define SK_CHECK_TAGS   // enable to double-check debugging link list
18#endif
19
20#ifdef SK_TAG_BLOCKS
21
22#include "SkThread.h"
23
24// size this (as a multiple of 4) so that the total offset to the internal data
25// is at least a multiple of 8 (since some clients of our malloc may require
26// that.
27static const char kBlockHeaderTag[] = { 's', 'k', 'i', 'a', '1', '2', '3', '4' };
28static const char kBlockTrailerTag[] = { 'a', 'i', 'k', 's' };
29#define kByteFill 0xCD
30#define kDeleteFill 0xEF
31
32static SkBaseMutex& get_block_mutex() {
33    static SkMutex* gBlockMutex;
34    if (NULL == gBlockMutex) {
35        gBlockMutex = new SkMutex;
36    }
37    return *gBlockMutex;
38}
39
40static struct SkBlockHeader* gHeader;
41
42struct SkBlockHeader {
43    SkBlockHeader* fNext;
44#ifdef SK_CHECK_TAGS
45    SkBlockHeader** fTop; // set to verify in debugger that block was alloc'd / freed with same gHeader
46    SkBlockHeader* fPrevious; // set to see in debugger previous block when corruption happens
47#endif
48    size_t fSize;
49    char fHeader[sizeof(kBlockHeaderTag)];
50    // data goes here. The offset to this point must be a multiple of 8
51    char fTrailer[sizeof(kBlockTrailerTag)];
52
53    void* add(size_t realSize)
54    {
55        SkAutoMutexAcquire  ac(get_block_mutex());
56        InMutexValidate();
57        fNext = gHeader;
58#ifdef SK_CHECK_TAGS
59        fTop = &gHeader;
60        fPrevious = NULL;
61        if (fNext != NULL)
62            fNext->fPrevious = this;
63#endif
64        gHeader = this;
65        fSize = realSize;
66        memcpy(fHeader, kBlockHeaderTag, sizeof(kBlockHeaderTag));
67        void* result = fTrailer;
68        void* trailer = (char*)result + realSize;
69        memcpy(trailer, kBlockTrailerTag, sizeof(kBlockTrailerTag));
70        return result;
71    }
72
73    static void Dump()
74    {
75        SkAutoMutexAcquire  ac(get_block_mutex());
76        InMutexValidate();
77        SkBlockHeader* header = gHeader;
78        int count = 0;
79        size_t size = 0;
80        while (header != NULL) {
81            char scratch[256];
82            char* pos = scratch;
83            size_t size = header->fSize;
84            int* data = (int*)(void*)header->fTrailer;
85            pos += sprintf(pos, "%p 0x%08zx (%7zd)  ",
86                data, size, size);
87            size >>= 2;
88            size_t ints = size > 4 ? 4 : size;
89            size_t index;
90            for (index = 0; index < ints; index++)
91                pos += sprintf(pos, "0x%08x ", data[index]);
92            pos += sprintf(pos, " (");
93            for (index = 0; index < ints; index++)
94                pos += sprintf(pos, "%g ", data[index] / 65536.0f);
95            if (ints > 0)
96                --pos;
97            pos += sprintf(pos, ") \"");
98            size_t chars = size > 16 ? 16 : size;
99            char* chPtr = (char*) data;
100            for (index = 0; index < chars; index++) {
101                char ch = chPtr[index];
102                pos += sprintf(pos, "%c", ch >= ' ' && ch < 0x7f ? ch : '?');
103            }
104            pos += sprintf(pos, "\"");
105            SkDebugf("%s\n", scratch);
106            count++;
107            size += header->fSize;
108            header = header->fNext;
109        }
110        SkDebugf("--- count %d  size 0x%08x (%zd) ---\n", count, size, size);
111    }
112
113    void remove() const
114    {
115        SkAutoMutexAcquire  ac(get_block_mutex());
116        SkBlockHeader** findPtr = &gHeader;
117        do {
118            SkBlockHeader* find = *findPtr;
119            SkASSERT(find != NULL);
120            if (find == this) {
121                *findPtr = fNext;
122                break;
123            }
124            findPtr = &find->fNext;
125        } while (true);
126        InMutexValidate();
127        SkASSERT(memcmp(fHeader, kBlockHeaderTag, sizeof(kBlockHeaderTag)) == 0);
128        const char* trailer = fTrailer + fSize;
129        SkASSERT(memcmp(trailer, kBlockTrailerTag, sizeof(kBlockTrailerTag)) == 0);
130    }
131
132    static void Validate()
133    {
134        SkAutoMutexAcquire  ac(get_block_mutex());
135        InMutexValidate();
136    }
137
138private:
139    static void InMutexValidate()
140    {
141        SkBlockHeader* header = gHeader;
142        while (header != NULL) {
143            SkASSERT(memcmp(header->fHeader, kBlockHeaderTag, sizeof(kBlockHeaderTag)) == 0);
144            char* trailer = header->fTrailer + header->fSize;
145            SkASSERT(memcmp(trailer, kBlockTrailerTag, sizeof(kBlockTrailerTag)) == 0);
146            header = header->fNext;
147        }
148    }
149};
150
151void ValidateHeap();
152void ValidateHeap()
153{
154    SkBlockHeader::Validate();
155}
156#else
157void ValidateHeap() {}
158#endif
159
160void sk_throw()
161{
162    SkDEBUGFAIL("sk_throw");
163    abort();
164}
165
166void sk_out_of_memory(void)
167{
168    SkDEBUGFAIL("sk_out_of_memory");
169    abort();
170}
171
172void* sk_malloc_throw(size_t size)
173{
174    return sk_malloc_flags(size, SK_MALLOC_THROW);
175}
176
177void* sk_realloc_throw(void* addr, size_t size)
178{
179#ifdef SK_TAG_BLOCKS
180    ValidateHeap();
181    if (addr != NULL) {
182        SkBlockHeader* header = (SkBlockHeader*)
183            ((char*)addr - SK_OFFSETOF(SkBlockHeader, fTrailer));
184        header->remove();
185#ifdef SK_TRACK_ALLOC
186        printf("sk_realloc_throw %p oldSize=%zd\n", addr, header->fSize);
187#endif
188        addr = header;
189    }
190    size_t realSize = size;
191    if (size)
192        size += sizeof(SkBlockHeader);
193#endif
194
195    void* p = realloc(addr, size);
196    if (size == 0)
197    {
198        ValidateHeap();
199        return p;
200    }
201
202    if (p == NULL)
203        sk_throw();
204#ifdef SK_TAG_BLOCKS
205    else
206    {
207        SkBlockHeader* header = (SkBlockHeader*) p;
208        p = header->add(realSize);
209#ifdef SK_TRACK_ALLOC
210        printf("sk_realloc_throw %p size=%zd\n", p, realSize);
211#endif
212    }
213#endif
214    ValidateHeap();
215    return p;
216}
217
218void sk_free(void* p)
219{
220    if (p)
221    {
222        ValidateHeap();
223#ifdef SK_TAG_BLOCKS
224        SkBlockHeader* header = (SkBlockHeader*)
225            ((char*)p - SK_OFFSETOF(SkBlockHeader, fTrailer));
226        header->remove();
227#ifdef SK_TRACK_ALLOC
228        printf("sk_free %p size=%zd\n", p, header->fSize);
229#endif
230        size_t size = header->fSize + sizeof(SkBlockHeader);
231        memset(header, kDeleteFill, size);
232        p = header;
233#endif
234        ValidateHeap();
235        free(p);
236        ValidateHeap();
237    }
238}
239
240void* sk_malloc_flags(size_t size, unsigned flags)
241{
242    ValidateHeap();
243#ifdef SK_TAG_BLOCKS
244    size_t realSize = size;
245    size += sizeof(SkBlockHeader);
246#endif
247
248    void* p = malloc(size);
249    if (p == NULL)
250    {
251        if (flags & SK_MALLOC_THROW)
252            sk_throw();
253    }
254#ifdef SK_TAG_BLOCKS
255    else
256    {
257        SkBlockHeader* header = (SkBlockHeader*) p;
258        p = header->add(realSize);
259        memset(p, kByteFill, realSize);
260#ifdef SK_TRACK_ALLOC
261        printf("sk_malloc_flags %p size=%zd\n", p, realSize);
262#endif
263    }
264#endif
265    ValidateHeap();
266    return p;
267}
268
269