blob: 098fd977b8b00912c0be36df59f76c97b8d2c088 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2011 Nokia and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Nokia - Initial API and implementation
* Broadcom - loadSectionIntoHeap() buffer creation optimization
*******************************************************************************/
package org.eclipse.cdt.debug.edc.internal.symbols.files;
import java.io.IOException;
import java.nio.ByteOrder;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.cdt.debug.edc.IStreamBuffer;
import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
import org.eclipse.cdt.debug.edc.internal.FileStreamBuffer;
import org.eclipse.cdt.debug.edc.internal.MemoryStreamBuffer;
import org.eclipse.cdt.utils.ERandomAccessFile;
import org.eclipse.core.runtime.IPath;
/**
* Handle mapping sections into memory and caching this content.
*/
public class SectionMapper implements ISectionMapper {
private final IPath hostFile;
private final boolean isLE;
private ERandomAccessFile efile;
/** all sections loaded for any reason */
private Map<SectionInfo, IStreamBuffer> loadedSections;
/** subset of sections loaded into memory maps */
private Map<SectionInfo, IStreamBuffer> mappedBuffers;
public SectionMapper(IPath hostFile, boolean isLE) {
this.hostFile = hostFile;
this.isLE = isLE;
this.efile = null;
this.loadedSections = new HashMap<SectionInfo, IStreamBuffer>();
this.mappedBuffers = new HashMap<SectionInfo, IStreamBuffer>();
}
public void dispose() {
for (Map.Entry<SectionInfo, IStreamBuffer> entry: loadedSections.entrySet()) {
IStreamBuffer buffer = entry.getValue();
if (buffer == null)
continue;
if (mappedBuffers.containsKey(entry.getKey()))
FileStatistics.currentMemoryMappedBuffers -= buffer.capacity();
else
FileStatistics.currentHeapAllocatedBuffers -= buffer.capacity();
}
loadedSections.clear();
mappedBuffers.clear();
close();
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.symbols.exe.ISectionMapper#getMappedFile()
*/
public IPath getMappedFile() {
return hostFile;
}
/**
*
*/
private void ensureOpen() throws IOException {
if (efile == null) {
FileStatistics.log("Opening " + hostFile.toFile());
FileStatistics.executablesOpened++;
FileStatistics.executablesOpen++;
efile = new ERandomAccessFile(hostFile.toFile(), "r");
}
}
private void close() {
if (!mappedBuffers.isEmpty())
throw new IllegalStateException("cannot close file; mapped buffers open");
if (efile != null) {
try {
FileStatistics.log("Closing " + hostFile.toFile());
FileStatistics.executablesOpen--;
efile.close();
} catch (IOException e) {
// ignore
}
}
efile = null;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.symbols.exe.IExecutableSectionMapper#getSectionBuffer(org.eclipse.cdt.debug.edc.internal.symbols.exe.SectionInfo)
*/
public IStreamBuffer getSectionBuffer(SectionInfo section) throws IOException {
ensureOpen();
IStreamBuffer buffer = loadedSections.get(section);
if (buffer == null) {
buffer = loadSection(section);
loadedSections.put(section, buffer);
// TODO: flush data occasionally, before #dispose()
}
return buffer;
}
private IStreamBuffer loadSection(SectionInfo section)
throws IOException {
FileStatistics.log("Loading " + section + " from " + hostFile.toFile());
IStreamBuffer buffer = null;
// If the sym file is too large, it's useless reading it
// into the heap and choking the memory.
// Just read it on-demand from disk.
try {
if (section.sectionSize > 4 * 1024 * 1024) {
buffer = loadSectionIntoFileStreamBuffer(section);
} else {
buffer = loadSectionIntoHeap(section);
}
} catch (IOException e) {
buffer = loadSectionIntoHeap(section);
}
return buffer;
}
/**
* Load section contents into a streaming buffer. This is a little slower but
* does not allocate any more than a page of memory at a time.
*
* @param section
* @param buffer
* @return new {@link IStreamBuffer}
*/
private IStreamBuffer loadSectionIntoFileStreamBuffer(SectionInfo section) throws IOException {
IStreamBuffer buffer = null;
try {
buffer = new FileStreamBuffer(efile, isLE ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN,
section.fileOffset, section.sectionSize);
mappedBuffers.put(section, buffer);
FileStatistics.currentMemoryMappedBuffers += buffer.capacity();
FileStatistics.totalMemoryMappedBuffers += buffer.capacity();
} catch (Throwable e2) {
EDCDebugger.getMessageLogger().logError("Failed to make buffer for section " + section, e2);
}
return buffer;
}
private IStreamBuffer loadSectionIntoHeap(SectionInfo section)
throws IOException {
IStreamBuffer buffer;
// try to load the section into memory because it will
// be faster
byte[] data = new byte[(int)section.sectionSize];
efile.seek(section.fileOffset);
int bytesRead = efile.read(data);
buffer = new MemoryStreamBuffer(data, isLE ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN,
0 , (bytesRead != -1) ? bytesRead : 0);
FileStatistics.currentHeapAllocatedBuffers += data.length;
FileStatistics.totalHeapAllocatedBuffers += data.length;
return buffer;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.symbols.exe.ISectionMapper#releaseBuffer(java.nio.ByteBuffer)
*/
public void releaseSectionBuffer(SectionInfo section) {
IStreamBuffer buffer = loadedSections.remove(section);
if (buffer != null) {
if (mappedBuffers.remove(section) != null) {
FileStatistics.currentMemoryMappedBuffers -= buffer.capacity();
if (mappedBuffers.isEmpty()) {
close();
}
} else {
FileStatistics.currentHeapAllocatedBuffers -= buffer.capacity();
}
}
}
}