Memory.cpp revision 851e30ec6a1b1d2c154bb7d69ed0d05b5fd14705
1//===-- Memory.cpp ----------------------------------------------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "lldb/Target/Memory.h"
11// C Includes
12// C++ Includes
13// Other libraries and framework includes
14// Project includes
15#include "lldb/Core/DataBufferHeap.h"
16#include "lldb/Core/State.h"
17#include "lldb/Core/Log.h"
18#include "lldb/Target/Process.h"
19
20using namespace lldb;
21using namespace lldb_private;
22
23//----------------------------------------------------------------------
24// MemoryCache constructor
25//----------------------------------------------------------------------
26MemoryCache::MemoryCache(Process &process) :
27    m_process (process),
28    m_cache_line_byte_size (512),
29    m_mutex (Mutex::eMutexTypeRecursive),
30    m_cache (),
31    m_invalid_ranges ()
32{
33}
34
35//----------------------------------------------------------------------
36// Destructor
37//----------------------------------------------------------------------
38MemoryCache::~MemoryCache()
39{
40}
41
42void
43MemoryCache::Clear()
44{
45    Mutex::Locker locker (m_mutex);
46    m_cache.clear();
47}
48
49void
50MemoryCache::Flush (addr_t addr, size_t size)
51{
52    if (size == 0)
53        return;
54
55    Mutex::Locker locker (m_mutex);
56    if (m_cache.empty())
57        return;
58
59    const uint32_t cache_line_byte_size = m_cache_line_byte_size;
60    const addr_t end_addr = (addr + size - 1);
61    const addr_t first_cache_line_addr = addr - (addr % cache_line_byte_size);
62    const addr_t last_cache_line_addr = end_addr - (end_addr % cache_line_byte_size);
63    // Watch for overflow where size will cause us to go off the end of the
64    // 64 bit address space
65    uint32_t num_cache_lines;
66    if (last_cache_line_addr >= first_cache_line_addr)
67        num_cache_lines = ((last_cache_line_addr - first_cache_line_addr)/cache_line_byte_size) + 1;
68    else
69        num_cache_lines = (UINT64_MAX - first_cache_line_addr + 1)/cache_line_byte_size;
70
71    uint32_t cache_idx = 0;
72    for (addr_t curr_addr = first_cache_line_addr;
73         cache_idx < num_cache_lines;
74         curr_addr += cache_line_byte_size, ++cache_idx)
75    {
76        BlockMap::iterator pos = m_cache.find (curr_addr);
77        if (pos != m_cache.end())
78            m_cache.erase(pos);
79    }
80}
81
82void
83MemoryCache::AddInvalidRange (lldb::addr_t base_addr, lldb::addr_t byte_size)
84{
85    if (byte_size > 0)
86    {
87        Mutex::Locker locker (m_mutex);
88        InvalidRanges::Entry range (base_addr, byte_size);
89        m_invalid_ranges.Append(range);
90        m_invalid_ranges.Sort();
91    }
92}
93
94bool
95MemoryCache::RemoveInvalidRange (lldb::addr_t base_addr, lldb::addr_t byte_size)
96{
97    if (byte_size > 0)
98    {
99        Mutex::Locker locker (m_mutex);
100        const uint32_t idx = m_invalid_ranges.FindEntryIndexThatContains(base_addr);
101        if (idx != UINT32_MAX)
102        {
103            const InvalidRanges::Entry *entry = m_invalid_ranges.GetEntryAtIndex (idx);
104            if (entry->GetRangeBase() == base_addr && entry->GetByteSize() == byte_size)
105                return m_invalid_ranges.RemoveEntrtAtIndex (idx);
106        }
107    }
108    return false;
109}
110
111
112
113size_t
114MemoryCache::Read (addr_t addr,
115                   void *dst,
116                   size_t dst_len,
117                   Error &error)
118{
119    size_t bytes_left = dst_len;
120    if (dst && bytes_left > 0)
121    {
122        const uint32_t cache_line_byte_size = m_cache_line_byte_size;
123        uint8_t *dst_buf = (uint8_t *)dst;
124        addr_t curr_addr = addr - (addr % cache_line_byte_size);
125        addr_t cache_offset = addr - curr_addr;
126        Mutex::Locker locker (m_mutex);
127
128        while (bytes_left > 0)
129        {
130            if (m_invalid_ranges.FindEntryThatContains(curr_addr))
131                return dst_len - bytes_left;
132
133            BlockMap::const_iterator pos = m_cache.find (curr_addr);
134            BlockMap::const_iterator end = m_cache.end ();
135
136            if (pos != end)
137            {
138                size_t curr_read_size = cache_line_byte_size - cache_offset;
139                if (curr_read_size > bytes_left)
140                    curr_read_size = bytes_left;
141
142                memcpy (dst_buf + dst_len - bytes_left, pos->second->GetBytes() + cache_offset, curr_read_size);
143
144                bytes_left -= curr_read_size;
145                curr_addr += curr_read_size + cache_offset;
146                cache_offset = 0;
147
148                if (bytes_left > 0)
149                {
150                    // Get sequential cache page hits
151                    for (++pos; (pos != end) && (bytes_left > 0); ++pos)
152                    {
153                        assert ((curr_addr % cache_line_byte_size) == 0);
154
155                        if (pos->first != curr_addr)
156                            break;
157
158                        curr_read_size = pos->second->GetByteSize();
159                        if (curr_read_size > bytes_left)
160                            curr_read_size = bytes_left;
161
162                        memcpy (dst_buf + dst_len - bytes_left, pos->second->GetBytes(), curr_read_size);
163
164                        bytes_left -= curr_read_size;
165                        curr_addr += curr_read_size;
166
167                        // We have a cache page that succeeded to read some bytes
168                        // but not an entire page. If this happens, we must cap
169                        // off how much data we are able to read...
170                        if (pos->second->GetByteSize() != cache_line_byte_size)
171                            return dst_len - bytes_left;
172                    }
173                }
174            }
175
176            // We need to read from the process
177
178            if (bytes_left > 0)
179            {
180                assert ((curr_addr % cache_line_byte_size) == 0);
181                std::auto_ptr<DataBufferHeap> data_buffer_heap_ap(new DataBufferHeap (cache_line_byte_size, 0));
182                size_t process_bytes_read = m_process.ReadMemoryFromInferior (curr_addr,
183                                                                              data_buffer_heap_ap->GetBytes(),
184                                                                              data_buffer_heap_ap->GetByteSize(),
185                                                                              error);
186                if (process_bytes_read == 0)
187                    return dst_len - bytes_left;
188
189                if (process_bytes_read != cache_line_byte_size)
190                    data_buffer_heap_ap->SetByteSize (process_bytes_read);
191                m_cache[curr_addr] = DataBufferSP (data_buffer_heap_ap.release());
192                // We have read data and put it into the cache, continue through the
193                // loop again to get the data out of the cache...
194            }
195        }
196    }
197
198    return dst_len - bytes_left;
199}
200
201
202
203AllocatedBlock::AllocatedBlock (lldb::addr_t addr,
204                                uint32_t byte_size,
205                                uint32_t permissions,
206                                uint32_t chunk_size) :
207    m_addr (addr),
208    m_byte_size (byte_size),
209    m_permissions (permissions),
210    m_chunk_size (chunk_size),
211    m_offset_to_chunk_size ()
212//    m_allocated (byte_size / chunk_size)
213{
214    assert (byte_size > chunk_size);
215}
216
217AllocatedBlock::~AllocatedBlock ()
218{
219}
220
221lldb::addr_t
222AllocatedBlock::ReserveBlock (uint32_t size)
223{
224    addr_t addr = LLDB_INVALID_ADDRESS;
225    if (size <= m_byte_size)
226    {
227        const uint32_t needed_chunks = CalculateChunksNeededForSize (size);
228        LogSP log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
229
230        if (m_offset_to_chunk_size.empty())
231        {
232            m_offset_to_chunk_size[0] = needed_chunks;
233            if (log)
234                log->Printf ("[1] AllocatedBlock::ReserveBlock (size = %u (0x%x)) => offset = 0x%x, %u %u bit chunks", size, size, 0, needed_chunks, m_chunk_size);
235            addr = m_addr;
236        }
237        else
238        {
239            uint32_t last_offset = 0;
240            OffsetToChunkSize::const_iterator pos = m_offset_to_chunk_size.begin();
241            OffsetToChunkSize::const_iterator end = m_offset_to_chunk_size.end();
242            while (pos != end)
243            {
244                if (pos->first > last_offset)
245                {
246                    const uint32_t bytes_available = pos->first - last_offset;
247                    const uint32_t num_chunks = CalculateChunksNeededForSize (bytes_available);
248                    if (num_chunks >= needed_chunks)
249                    {
250                        m_offset_to_chunk_size[last_offset] = needed_chunks;
251                        if (log)
252                            log->Printf ("[2] AllocatedBlock::ReserveBlock (size = %u (0x%x)) => offset = 0x%x, %u %u bit chunks", size, size, last_offset, needed_chunks, m_chunk_size);
253                        addr = m_addr + last_offset;
254                        break;
255                    }
256                }
257
258                last_offset = pos->first + pos->second * m_chunk_size;
259
260                if (++pos == end)
261                {
262                    // Last entry...
263                    const uint32_t chunks_left = CalculateChunksNeededForSize (m_byte_size - last_offset);
264                    if (chunks_left >= needed_chunks)
265                    {
266                        m_offset_to_chunk_size[last_offset] = needed_chunks;
267                        if (log)
268                            log->Printf ("[3] AllocatedBlock::ReserveBlock (size = %u (0x%x)) => offset = 0x%x, %u %u bit chunks", size, size, last_offset, needed_chunks, m_chunk_size);
269                        addr = m_addr + last_offset;
270                        break;
271                    }
272                }
273            }
274        }
275//        const uint32_t total_chunks = m_allocated.size ();
276//        uint32_t unallocated_idx = 0;
277//        uint32_t allocated_idx = m_allocated.find_first();
278//        uint32_t first_chunk_idx = UINT32_MAX;
279//        uint32_t num_chunks;
280//        while (1)
281//        {
282//            if (allocated_idx == UINT32_MAX)
283//            {
284//                // No more bits are set starting from unallocated_idx, so we
285//                // either have enough chunks for the request, or we don't.
286//                // Eiter way we break out of the while loop...
287//                num_chunks = total_chunks - unallocated_idx;
288//                if (needed_chunks <= num_chunks)
289//                    first_chunk_idx = unallocated_idx;
290//                break;
291//            }
292//            else if (allocated_idx > unallocated_idx)
293//            {
294//                // We have some allocated chunks, check if there are enough
295//                // free chunks to satisfy the request?
296//                num_chunks = allocated_idx - unallocated_idx;
297//                if (needed_chunks <= num_chunks)
298//                {
299//                    // Yep, we have enough!
300//                    first_chunk_idx = unallocated_idx;
301//                    break;
302//                }
303//            }
304//
305//            while (unallocated_idx < total_chunks)
306//            {
307//                if (m_allocated[unallocated_idx])
308//                    ++unallocated_idx;
309//                else
310//                    break;
311//            }
312//
313//            if (unallocated_idx >= total_chunks)
314//                break;
315//
316//            allocated_idx = m_allocated.find_next(unallocated_idx);
317//        }
318//
319//        if (first_chunk_idx != UINT32_MAX)
320//        {
321//            const uint32_t end_bit_idx = unallocated_idx + needed_chunks;
322//            for (uint32_t idx = first_chunk_idx; idx < end_bit_idx; ++idx)
323//                m_allocated.set(idx);
324//            return m_addr + m_chunk_size * first_chunk_idx;
325//        }
326    }
327    LogSP log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
328    if (log)
329        log->Printf ("AllocatedBlock::ReserveBlock (size = %u (0x%x)) => 0x%16.16llx", size, size, (uint64_t)addr);
330    return addr;
331}
332
333bool
334AllocatedBlock::FreeBlock (addr_t addr)
335{
336    uint32_t offset = addr - m_addr;
337    OffsetToChunkSize::iterator pos = m_offset_to_chunk_size.find (offset);
338    bool success = false;
339    if (pos != m_offset_to_chunk_size.end())
340    {
341        m_offset_to_chunk_size.erase (pos);
342        success = true;
343    }
344    LogSP log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
345    if (log)
346        log->Printf ("AllocatedBlock::FreeBlock (addr = 0x%16.16llx) => %i", (uint64_t)addr, success);
347    return success;
348}
349
350
351AllocatedMemoryCache::AllocatedMemoryCache (Process &process) :
352    m_process (process),
353    m_mutex (Mutex::eMutexTypeRecursive),
354    m_memory_map()
355{
356}
357
358AllocatedMemoryCache::~AllocatedMemoryCache ()
359{
360}
361
362
363void
364AllocatedMemoryCache::Clear()
365{
366    Mutex::Locker locker (m_mutex);
367    if (m_process.IsAlive())
368    {
369        PermissionsToBlockMap::iterator pos, end = m_memory_map.end();
370        for (pos = m_memory_map.begin(); pos != end; ++pos)
371            m_process.DoDeallocateMemory(pos->second->GetBaseAddress());
372    }
373    m_memory_map.clear();
374}
375
376
377AllocatedMemoryCache::AllocatedBlockSP
378AllocatedMemoryCache::AllocatePage (uint32_t byte_size,
379                                    uint32_t permissions,
380                                    uint32_t chunk_size,
381                                    Error &error)
382{
383    AllocatedBlockSP block_sp;
384    const size_t page_size = 4096;
385    const size_t num_pages = (byte_size + page_size - 1) / page_size;
386    const size_t page_byte_size = num_pages * page_size;
387
388    addr_t addr = m_process.DoAllocateMemory(page_byte_size, permissions, error);
389
390    LogSP log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
391    if (log)
392    {
393        log->Printf ("Process::DoAllocateMemory (byte_size = 0x%8.8zx, permissions = %s) => 0x%16.16llx",
394                     page_byte_size,
395                     GetPermissionsAsCString(permissions),
396                     (uint64_t)addr);
397    }
398
399    if (addr != LLDB_INVALID_ADDRESS)
400    {
401        block_sp.reset (new AllocatedBlock (addr, page_byte_size, permissions, chunk_size));
402        m_memory_map.insert (std::make_pair (permissions, block_sp));
403    }
404    return block_sp;
405}
406
407lldb::addr_t
408AllocatedMemoryCache::AllocateMemory (size_t byte_size,
409                                      uint32_t permissions,
410                                      Error &error)
411{
412    Mutex::Locker locker (m_mutex);
413
414    addr_t addr = LLDB_INVALID_ADDRESS;
415    std::pair<PermissionsToBlockMap::iterator, PermissionsToBlockMap::iterator> range = m_memory_map.equal_range (permissions);
416
417    for (PermissionsToBlockMap::iterator pos = range.first; pos != range.second; ++pos)
418    {
419        addr = (*pos).second->ReserveBlock (byte_size);
420    }
421
422    if (addr == LLDB_INVALID_ADDRESS)
423    {
424        AllocatedBlockSP block_sp (AllocatePage (byte_size, permissions, 16, error));
425
426        if (block_sp)
427            addr = block_sp->ReserveBlock (byte_size);
428    }
429    LogSP log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
430    if (log)
431        log->Printf ("AllocatedMemoryCache::AllocateMemory (byte_size = 0x%8.8zx, permissions = %s) => 0x%16.16llx", byte_size, GetPermissionsAsCString(permissions), (uint64_t)addr);
432    return addr;
433}
434
435bool
436AllocatedMemoryCache::DeallocateMemory (lldb::addr_t addr)
437{
438    Mutex::Locker locker (m_mutex);
439
440    PermissionsToBlockMap::iterator pos, end = m_memory_map.end();
441    bool success = false;
442    for (pos = m_memory_map.begin(); pos != end; ++pos)
443    {
444        if (pos->second->Contains (addr))
445        {
446            success = pos->second->FreeBlock (addr);
447            break;
448        }
449    }
450    LogSP log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
451    if (log)
452        log->Printf("AllocatedMemoryCache::DeallocateMemory (addr = 0x%16.16llx) => %i", (uint64_t)addr, success);
453    return success;
454}
455
456
457