MemoryDealer.cpp revision 83c0446f27b9542d6c2e724817b2b2d8d1f55085
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "MemoryDealer"
18
19#include <binder/MemoryDealer.h>
20
21#include <utils/Log.h>
22#include <binder/IPCThreadState.h>
23#include <utils/SortedVector.h>
24#include <utils/String8.h>
25#include <binder/MemoryBase.h>
26
27#include <stdint.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <fcntl.h>
31#include <unistd.h>
32#include <errno.h>
33#include <string.h>
34
35#include <sys/stat.h>
36#include <sys/types.h>
37#include <sys/mman.h>
38#include <sys/file.h>
39
40namespace android {
41// ----------------------------------------------------------------------------
42
43HeapInterface::HeapInterface() { }
44HeapInterface::~HeapInterface() { }
45
46// ----------------------------------------------------------------------------
47
48AllocatorInterface::AllocatorInterface() { }
49AllocatorInterface::~AllocatorInterface() { }
50
51// ----------------------------------------------------------------------------
52
53class SimpleMemory : public MemoryBase {
54public:
55    SimpleMemory(const sp<IMemoryHeap>& heap, ssize_t offset, size_t size);
56    virtual ~SimpleMemory();
57};
58
59
60// ----------------------------------------------------------------------------
61
62MemoryDealer::Allocation::Allocation(
63        const sp<MemoryDealer>& dealer, ssize_t offset, size_t size,
64        const sp<IMemory>& memory)
65    : mDealer(dealer), mOffset(offset), mSize(size), mMemory(memory)
66{
67}
68
69MemoryDealer::Allocation::~Allocation()
70{
71    if (mSize) {
72        /* NOTE: it's VERY important to not free allocations of size 0 because
73         * they're special as they don't have any record in the allocator
74         * and could alias some real allocation (their offset is zero). */
75        mDealer->deallocate(mOffset);
76    }
77}
78
79sp<IMemoryHeap> MemoryDealer::Allocation::getMemory(
80    ssize_t* offset, size_t* size) const
81{
82    return mMemory->getMemory(offset, size);
83}
84
85// ----------------------------------------------------------------------------
86
87MemoryDealer::MemoryDealer(size_t size, uint32_t flags, const char* name)
88    : mHeap(new SharedHeap(size, flags, name)),
89    mAllocator(new SimpleBestFitAllocator(size))
90{
91}
92
93MemoryDealer::MemoryDealer(const sp<HeapInterface>& heap)
94    : mHeap(heap),
95    mAllocator(new SimpleBestFitAllocator(heap->virtualSize()))
96{
97}
98
99MemoryDealer::MemoryDealer( const sp<HeapInterface>& heap,
100        const sp<AllocatorInterface>& allocator)
101    : mHeap(heap), mAllocator(allocator)
102{
103}
104
105MemoryDealer::~MemoryDealer()
106{
107}
108
109sp<IMemory> MemoryDealer::allocate(size_t size, uint32_t flags)
110{
111    sp<IMemory> memory;
112    const ssize_t offset = allocator()->allocate(size, flags);
113    if (offset >= 0) {
114        sp<IMemory> new_memory = heap()->mapMemory(offset, size);
115        if (new_memory != 0) {
116            memory = new Allocation(this, offset, size, new_memory);
117        } else {
118            LOGE("couldn't map [%8lx, %u]", offset, size);
119            if (size) {
120                /* NOTE: it's VERY important to not free allocations of size 0
121                 * because they're special as they don't have any record in the
122                 * allocator and could alias some real allocation
123                 * (their offset is zero). */
124                allocator()->deallocate(offset);
125            }
126        }
127    }
128    return memory;
129}
130
131void MemoryDealer::deallocate(size_t offset)
132{
133    allocator()->deallocate(offset);
134}
135
136void MemoryDealer::dump(const char* what, uint32_t flags) const
137{
138    allocator()->dump(what, flags);
139}
140
141const sp<HeapInterface>& MemoryDealer::heap() const {
142    return mHeap;
143}
144
145const sp<AllocatorInterface>& MemoryDealer::allocator() const {
146    return mAllocator;
147}
148
149// ----------------------------------------------------------------------------
150
151// align all the memory blocks on a cache-line boundary
152const int SimpleBestFitAllocator::kMemoryAlign = 32;
153
154SimpleBestFitAllocator::SimpleBestFitAllocator(size_t size)
155{
156    size_t pagesize = getpagesize();
157    mHeapSize = ((size + pagesize-1) & ~(pagesize-1));
158
159    chunk_t* node = new chunk_t(0, mHeapSize / kMemoryAlign);
160    mList.insertHead(node);
161}
162
163SimpleBestFitAllocator::~SimpleBestFitAllocator()
164{
165    while(!mList.isEmpty()) {
166        delete mList.remove(mList.head());
167    }
168}
169
170size_t SimpleBestFitAllocator::size() const
171{
172    return mHeapSize;
173}
174
175size_t SimpleBestFitAllocator::allocate(size_t size, uint32_t flags)
176{
177    Mutex::Autolock _l(mLock);
178    ssize_t offset = alloc(size, flags);
179    return offset;
180}
181
182status_t SimpleBestFitAllocator::deallocate(size_t offset)
183{
184    Mutex::Autolock _l(mLock);
185    chunk_t const * const freed = dealloc(offset);
186    if (freed) {
187        return NO_ERROR;
188    }
189    return NAME_NOT_FOUND;
190}
191
192ssize_t SimpleBestFitAllocator::alloc(size_t size, uint32_t flags)
193{
194    if (size == 0) {
195        return 0;
196    }
197    size = (size + kMemoryAlign-1) / kMemoryAlign;
198    chunk_t* free_chunk = 0;
199    chunk_t* cur = mList.head();
200
201    size_t pagesize = getpagesize();
202    while (cur) {
203        int extra = 0;
204        if (flags & PAGE_ALIGNED)
205            extra = ( -cur->start & ((pagesize/kMemoryAlign)-1) ) ;
206
207        // best fit
208        if (cur->free && (cur->size >= (size+extra))) {
209            if ((!free_chunk) || (cur->size < free_chunk->size)) {
210                free_chunk = cur;
211            }
212            if (cur->size == size) {
213                break;
214            }
215        }
216        cur = cur->next;
217    }
218
219    if (free_chunk) {
220        const size_t free_size = free_chunk->size;
221        free_chunk->free = 0;
222        free_chunk->size = size;
223        if (free_size > size) {
224            int extra = 0;
225            if (flags & PAGE_ALIGNED)
226                extra = ( -free_chunk->start & ((pagesize/kMemoryAlign)-1) ) ;
227            if (extra) {
228                chunk_t* split = new chunk_t(free_chunk->start, extra);
229                free_chunk->start += extra;
230                mList.insertBefore(free_chunk, split);
231            }
232
233            LOGE_IF((flags&PAGE_ALIGNED) &&
234                    ((free_chunk->start*kMemoryAlign)&(pagesize-1)),
235                    "PAGE_ALIGNED requested, but page is not aligned!!!");
236
237            const ssize_t tail_free = free_size - (size+extra);
238            if (tail_free > 0) {
239                chunk_t* split = new chunk_t(
240                        free_chunk->start + free_chunk->size, tail_free);
241                mList.insertAfter(free_chunk, split);
242            }
243        }
244        return (free_chunk->start)*kMemoryAlign;
245    }
246    return NO_MEMORY;
247}
248
249SimpleBestFitAllocator::chunk_t* SimpleBestFitAllocator::dealloc(size_t start)
250{
251    start = start / kMemoryAlign;
252    chunk_t* cur = mList.head();
253    while (cur) {
254        if (cur->start == start) {
255            LOG_FATAL_IF(cur->free,
256                "block at offset 0x%08lX of size 0x%08lX already freed",
257                cur->start*kMemoryAlign, cur->size*kMemoryAlign);
258
259            // merge freed blocks together
260            chunk_t* freed = cur;
261            cur->free = 1;
262            do {
263                chunk_t* const p = cur->prev;
264                chunk_t* const n = cur->next;
265                if (p && (p->free || !cur->size)) {
266                    freed = p;
267                    p->size += cur->size;
268                    mList.remove(cur);
269                    delete cur;
270                }
271                cur = n;
272            } while (cur && cur->free);
273
274            #ifndef NDEBUG
275                if (!freed->free) {
276                    dump_l("dealloc (!freed->free)");
277                }
278            #endif
279            LOG_FATAL_IF(!freed->free,
280                "freed block at offset 0x%08lX of size 0x%08lX is not free!",
281                freed->start * kMemoryAlign, freed->size * kMemoryAlign);
282
283            return freed;
284        }
285        cur = cur->next;
286    }
287    return 0;
288}
289
290void SimpleBestFitAllocator::dump(const char* what, uint32_t flags) const
291{
292    Mutex::Autolock _l(mLock);
293    dump_l(what, flags);
294}
295
296void SimpleBestFitAllocator::dump_l(const char* what, uint32_t flags) const
297{
298    String8 result;
299    dump_l(result, what, flags);
300    LOGD("%s", result.string());
301}
302
303void SimpleBestFitAllocator::dump(String8& result,
304        const char* what, uint32_t flags) const
305{
306    Mutex::Autolock _l(mLock);
307    dump_l(result, what, flags);
308}
309
310void SimpleBestFitAllocator::dump_l(String8& result,
311        const char* what, uint32_t flags) const
312{
313    size_t size = 0;
314    int32_t i = 0;
315    chunk_t const* cur = mList.head();
316
317    const size_t SIZE = 256;
318    char buffer[SIZE];
319    snprintf(buffer, SIZE, "  %s (%p, size=%u)\n",
320            what, this, (unsigned int)mHeapSize);
321
322    result.append(buffer);
323
324    while (cur) {
325        const char* errs[] = {"", "| link bogus NP",
326                            "| link bogus PN", "| link bogus NP+PN" };
327        int np = ((cur->next) && cur->next->prev != cur) ? 1 : 0;
328        int pn = ((cur->prev) && cur->prev->next != cur) ? 2 : 0;
329
330        snprintf(buffer, SIZE, "  %3u: %08x | 0x%08X | 0x%08X | %s %s\n",
331            i, int(cur), int(cur->start*kMemoryAlign),
332            int(cur->size*kMemoryAlign),
333                    int(cur->free) ? "F" : "A",
334                    errs[np|pn]);
335
336        result.append(buffer);
337
338        if (!cur->free)
339            size += cur->size*kMemoryAlign;
340
341        i++;
342        cur = cur->next;
343    }
344    snprintf(buffer, SIZE, "  size allocated: %u (%u KB)\n", int(size), int(size/1024));
345    result.append(buffer);
346}
347
348// ----------------------------------------------------------------------------
349
350SharedHeap::SharedHeap()
351    : HeapInterface(), MemoryHeapBase()
352{
353}
354
355SharedHeap::SharedHeap(size_t size, uint32_t flags, char const * name)
356    : MemoryHeapBase(size, flags, name)
357{
358}
359
360SharedHeap::~SharedHeap()
361{
362}
363
364sp<IMemory> SharedHeap::mapMemory(size_t offset, size_t size)
365{
366    return new SimpleMemory(this, offset, size);
367}
368
369
370SimpleMemory::SimpleMemory(const sp<IMemoryHeap>& heap,
371        ssize_t offset, size_t size)
372    : MemoryBase(heap, offset, size)
373{
374#ifndef NDEBUG
375    void* const start_ptr = (void*)(intptr_t(heap->base()) + offset);
376    memset(start_ptr, 0xda, size);
377#endif
378}
379
380SimpleMemory::~SimpleMemory()
381{
382    size_t freedOffset = getOffset();
383    size_t freedSize   = getSize();
384
385    // keep the size to unmap in excess
386    size_t pagesize = getpagesize();
387    size_t start = freedOffset;
388    size_t end = start + freedSize;
389    start &= ~(pagesize-1);
390    end = (end + pagesize-1) & ~(pagesize-1);
391
392    // give back to the kernel the pages we don't need
393    size_t free_start = freedOffset;
394    size_t free_end = free_start + freedSize;
395    if (start < free_start)
396        start = free_start;
397    if (end > free_end)
398        end = free_end;
399    start = (start + pagesize-1) & ~(pagesize-1);
400    end &= ~(pagesize-1);
401
402    if (start < end) {
403        void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + start);
404        size_t size = end-start;
405
406#ifndef NDEBUG
407        memset(start_ptr, 0xdf, size);
408#endif
409
410        // MADV_REMOVE is not defined on Dapper based Goobuntu
411#ifdef MADV_REMOVE
412        if (size) {
413            int err = madvise(start_ptr, size, MADV_REMOVE);
414            LOGW_IF(err, "madvise(%p, %u, MADV_REMOVE) returned %s",
415                    start_ptr, size, err<0 ? strerror(errno) : "Ok");
416        }
417#endif
418    }
419}
420
421}; // namespace android
422