DataBufferMemoryMap.cpp revision 263363
131567Ssef//===-- DataBufferMemoryMap.cpp ---------------------------------*- C++ -*-===//
2204977Simp//
331899Ssef//                     The LLVM Compiler Infrastructure
431899Ssef//
531899Ssef// This file is distributed under the University of Illinois Open Source
631899Ssef// License. See LICENSE.TXT for details.
731899Ssef//
831899Ssef//===----------------------------------------------------------------------===//
931899Ssef
1031899Ssef
1131899Ssef#include <errno.h>
1231899Ssef#include <fcntl.h>
1331899Ssef#include <limits.h>
1431899Ssef#include <sys/stat.h>
1531899Ssef#ifdef _WIN32
1631899Ssef#include "lldb/Host/windows/windows.h"
1731899Ssef#else
1831899Ssef#include <sys/mman.h>
1931899Ssef#endif
2031899Ssef
2131899Ssef#include "lldb/Core/DataBufferMemoryMap.h"
2231899Ssef#include "lldb/Core/Error.h"
2331899Ssef#include "lldb/Host/File.h"
2431899Ssef#include "lldb/Host/FileSpec.h"
2531899Ssef#include "lldb/Host/Host.h"
2631899Ssef#include "lldb/Core/Log.h"
2731899Ssef#include "lldb/lldb-private-log.h"
2831899Ssef
2931899Ssefusing namespace lldb;
3031899Ssefusing namespace lldb_private;
3131899Ssef
32290052Sjhb//----------------------------------------------------------------------
33290052Sjhb// Default Constructor
3432275Scharnier//----------------------------------------------------------------------
35290052SjhbDataBufferMemoryMap::DataBufferMemoryMap() :
3632275Scharnier    m_mmap_addr(NULL),
37168569Sdelphij    m_mmap_size(0),
3885301Sdes    m_data(NULL),
3985301Sdes    m_size(0)
4085301Sdes{
4185301Sdes}
4231567Ssef
4331567Ssef//----------------------------------------------------------------------
44101282Smdodd// Virtual destructor since this class inherits from a pure virtual
4531567Ssef// base class.
46179051Sjhb//----------------------------------------------------------------------
4731567SsefDataBufferMemoryMap::~DataBufferMemoryMap()
48290052Sjhb{
49290052Sjhb    Clear();
50240562Szont}
51240005Szont
52290052Sjhb//----------------------------------------------------------------------
53240562Szont// Return a pointer to the bytes owned by this object, or NULL if
5431567Ssef// the object contains no bytes.
55240562Szont//----------------------------------------------------------------------
56290052Sjhbuint8_t *
57240562SzontDataBufferMemoryMap::GetBytes()
58240005Szont{
59290052Sjhb    return m_data;
60240005Szont}
6131567Ssef
62240005Szont//----------------------------------------------------------------------
63240005Szont// Return a const pointer to the bytes owned by this object, or NULL
64240005Szont// if the object contains no bytes.
65240005Szont//----------------------------------------------------------------------
66240005Szontconst uint8_t *
67240005SzontDataBufferMemoryMap::GetBytes() const
68240005Szont{
69290052Sjhb    return m_data;
70290052Sjhb}
71290052Sjhb
72290052Sjhb//----------------------------------------------------------------------
73290052Sjhb// Return the number of bytes this object currently contains.
74290052Sjhb//----------------------------------------------------------------------
75290052Sjhbuint64_t
76290052SjhbDataBufferMemoryMap::GetByteSize() const
77290052Sjhb{
78290052Sjhb    return m_size;
79290052Sjhb}
80290052Sjhb
81290052Sjhb//----------------------------------------------------------------------
82240005Szont// Reverts this object to an empty state by unmapping any memory
8331567Ssef// that is currently owned.
84290052Sjhb//----------------------------------------------------------------------
8531567Ssefvoid
8631567SsefDataBufferMemoryMap::Clear()
8731567Ssef{
8831567Ssef    if (m_mmap_addr != NULL)
8931567Ssef    {
90228396Sed        Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP));
91240005Szont        if (log)
92240005Szont            log->Printf("DataBufferMemoryMap::Clear() m_mmap_addr = %p, m_mmap_size = %zu", m_mmap_addr, m_mmap_size);
93240005Szont#ifdef _WIN32
94240005Szont        UnmapViewOfFile(m_mmap_addr);
95240005Szont#else
9631567Ssef        ::munmap((void *)m_mmap_addr, m_mmap_size);
9731567Ssef#endif
9831567Ssef        m_mmap_addr = NULL;
99240005Szont        m_mmap_size = 0;
10031567Ssef        m_data = NULL;
10131567Ssef        m_size = 0;
102290052Sjhb    }
103290052Sjhb}
104290052Sjhb
105122348Smarcel//----------------------------------------------------------------------
106240005Szont// Memory map "length" bytes from "file" starting "offset"
107240562Szont// bytes into the file. If "length" is set to SIZE_MAX, then
108290052Sjhb// map as many bytes as possible.
10931567Ssef//
110240562Szont// Returns the number of bytes mapped starting from the requested
111240562Szont// offset.
112240005Szont//----------------------------------------------------------------------
113240005Szontsize_t
114240005SzontDataBufferMemoryMap::MemoryMapFromFileSpec (const FileSpec* filespec,
11531567Ssef                                            lldb::offset_t offset,
116290052Sjhb                                            lldb::offset_t length,
117290052Sjhb                                            bool writeable)
118290052Sjhb{
119296010Sjhb    if (filespec != NULL)
120296010Sjhb    {
121101374Smdodd        Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP));
122290052Sjhb        if (log)
123290052Sjhb        {
124290052Sjhb            log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec(file=\"%s\", offset=0x%" PRIx64 ", length=0x%" PRIx64 ", writeable=%i",
125290052Sjhb                        filespec->GetPath().c_str(),
126290052Sjhb                        offset,
127240005Szont                        length,
128240005Szont                        writeable);
129171055Sdelphij        }
130290052Sjhb        char path[PATH_MAX];
131240005Szont        if (filespec->GetPath(path, sizeof(path)))
132290052Sjhb        {
133290052Sjhb            uint32_t options = File::eOpenOptionRead;
134171055Sdelphij            if (writeable)
135290052Sjhb                options |= File::eOpenOptionWrite;
136290052Sjhb
137290052Sjhb            File file;
138290052Sjhb            Error error (file.Open(path, options));
139290052Sjhb            if (error.Success())
140290052Sjhb            {
141290052Sjhb                const bool fd_is_file = true;
142101282Smdodd                return MemoryMapFromFileDescriptor (file.GetDescriptor(), offset, length, writeable, fd_is_file);
143290052Sjhb            }
144        }
145    }
146    // We should only get here if there was an error
147    Clear();
148    return 0;
149}
150
151
152#ifdef _WIN32
153static size_t win32memmapalignment = 0;
154void LoadWin32MemMapAlignment ()
155{
156  SYSTEM_INFO data;
157  GetSystemInfo(&data);
158  win32memmapalignment = data.dwAllocationGranularity;
159}
160#endif
161
162//----------------------------------------------------------------------
163// The file descriptor FD is assumed to already be opened as read only
164// and the STAT structure is assumed to a valid pointer and already
165// containing valid data from a call to stat().
166//
167// Memory map FILE_LENGTH bytes in FILE starting FILE_OFFSET bytes into
168// the file. If FILE_LENGTH is set to SIZE_MAX, then map as many bytes
169// as possible.
170//
171// RETURNS
172//  Number of bytes mapped starting from the requested offset.
173//----------------------------------------------------------------------
174size_t
175DataBufferMemoryMap::MemoryMapFromFileDescriptor (int fd,
176                                                  lldb::offset_t offset,
177                                                  lldb::offset_t length,
178                                                  bool writeable,
179                                                  bool fd_is_file)
180{
181    Clear();
182    if (fd >= 0)
183    {
184        Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP|LIBLLDB_LOG_VERBOSE));
185        if (log)
186        {
187#ifdef _WIN32
188            log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec(fd=%p, offset=0x%" PRIx64 ", length=0x%" PRIx64 ", writeable=%i, fd_is_file=%i)",
189#else
190            log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec(fd=%i, offset=0x%" PRIx64 ", length=0x%" PRIx64 ", writeable=%i, fd_is_file=%i)",
191#endif
192                        fd,
193                        offset,
194                        length,
195                        writeable,
196                        fd_is_file);
197        }
198#ifdef _WIN32
199        HANDLE handle = (HANDLE)_get_osfhandle(fd);
200        DWORD file_size_low, file_size_high;
201        file_size_low = GetFileSize(handle, &file_size_high);
202        const size_t file_size = (file_size_high << 32) | file_size_low;
203        const size_t max_bytes_available = file_size - offset;
204        if (length == SIZE_MAX)
205        {
206            length = max_bytes_available;
207        }
208        else if (length > max_bytes_available)
209        {
210            // Cap the length if too much data was requested
211            length = max_bytes_available;
212        }
213
214        if (length > 0)
215        {
216            HANDLE fileMapping = CreateFileMapping(handle, NULL, writeable ? PAGE_READWRITE : PAGE_READONLY, file_size_high, file_size_low, NULL);
217            if (fileMapping != NULL)
218            {
219                if (win32memmapalignment == 0) LoadWin32MemMapAlignment();
220                lldb::offset_t realoffset = offset;
221                lldb::offset_t delta = 0;
222                if (realoffset % win32memmapalignment != 0) {
223                  realoffset = realoffset / win32memmapalignment * win32memmapalignment;
224                  delta = offset - realoffset;
225	              }
226
227                LPVOID data = MapViewOfFile(fileMapping, writeable ? FILE_MAP_WRITE : FILE_MAP_READ, 0, realoffset, length + delta);
228                m_mmap_addr = (uint8_t *)data;
229                if (!data) {
230                  Error error;
231                  error.SetErrorToErrno ();
232                } else {
233                  m_data = m_mmap_addr + delta;
234                  m_size = length;
235                }
236                CloseHandle(fileMapping);
237            }
238        }
239#else
240        struct stat stat;
241        if (::fstat(fd, &stat) == 0)
242        {
243            if (S_ISREG(stat.st_mode) && (stat.st_size > offset))
244            {
245                const size_t max_bytes_available = stat.st_size - offset;
246                if (length == SIZE_MAX)
247                {
248                    length = max_bytes_available;
249                }
250                else if (length > max_bytes_available)
251                {
252                    // Cap the length if too much data was requested
253                    length = max_bytes_available;
254                }
255
256                if (length > 0)
257                {
258                    int prot = PROT_READ;
259                    if (writeable)
260                        prot |= PROT_WRITE;
261
262                    int flags = MAP_PRIVATE;
263                    if (fd_is_file)
264                        flags |= MAP_FILE;
265
266                    m_mmap_addr = (uint8_t *)::mmap(NULL, length, prot, flags, fd, offset);
267                    Error error;
268
269                    if (m_mmap_addr == (void*)-1)
270                    {
271                        error.SetErrorToErrno ();
272                        if (error.GetError() == EINVAL)
273                        {
274                            // We may still have a shot at memory mapping if we align things correctly
275                            size_t page_offset = offset % Host::GetPageSize();
276                            if (page_offset != 0)
277                            {
278                                m_mmap_addr = (uint8_t *)::mmap(NULL, length + page_offset, prot, flags, fd, offset - page_offset);
279                                if (m_mmap_addr == (void*)-1)
280                                {
281                                    // Failed to map file
282                                    m_mmap_addr = NULL;
283                                }
284                                else if (m_mmap_addr != NULL)
285                                {
286                                    // We recovered and were able to memory map
287                                    // after we aligned things to page boundaries
288
289                                    // Save the actual mmap'ed size
290                                    m_mmap_size = length + page_offset;
291                                    // Our data is at an offset into the the mapped data
292                                    m_data = m_mmap_addr + page_offset;
293                                    // Our pretend size is the size that was requestd
294                                    m_size = length;
295                                }
296                            }
297                        }
298                        if (error.GetError() == ENOMEM)
299                        {
300                           error.SetErrorStringWithFormat("could not allocate %" PRId64 " bytes of memory to mmap in file", (uint64_t) length);
301                        }
302                    }
303                    else
304                    {
305                        // We were able to map the requested data in one chunk
306                        // where our mmap and actual data are the same.
307                        m_mmap_size = length;
308                        m_data = m_mmap_addr;
309                        m_size = length;
310                    }
311
312                    if (log)
313                    {
314                        log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec() m_mmap_addr = %p, m_mmap_size = %zu, error = %s",
315                                    m_mmap_addr, m_mmap_size, error.AsCString());
316                    }
317                }
318            }
319        }
320#endif
321    }
322    return GetByteSize ();
323}
324