/***************************************************************************** * Copyright (c) 2009 g-Eclipse Consortium * 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 * * Initial development of the original code was made for the * g-Eclipse project founded by European Union * project number: FP6-IST-034327 http://www.geclipse.eu/ * * Contributors: * Thomas Koeckerbauer MNM-Team, LMU Munich - initial API and implementation *****************************************************************************/ package eu.geclipse.traceview.utils; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; import java.util.Vector; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.preference.IPreferenceStore; import eu.geclipse.traceview.internal.Activator; import eu.geclipse.traceview.preferences.PreferenceConstants; final class OffsetEntry { final int id; final int[] buffer; final int elementCount; final int intOffset; final int mask; final int shift; final int fillPosShift; final int mask2; final int bits; final boolean signed; final boolean needsRead; final static int[] signedTable = new int[32]; static { for (int i = 0; i < 32; i++) { signedTable[i] = makeMask( i + 1 , 31 - i ); } } public OffsetEntry( final int id, final int bits, final boolean signed, final int[] intOffset, final int[] shift ) { this( id, 1, bits, signed, intOffset, shift); } public OffsetEntry( final int id, final int elementCount, final int bits, final boolean signed, final int[] intOffset, final int[] shift ) { if (shift[0] == 32) { shift[0] = 0; intOffset[0]++; } this.id = id; this.bits = bits; this.signed = signed; this.intOffset = intOffset[0]; this.fillPosShift = shift[0]; this.elementCount = elementCount; this.buffer = new int[((shift[0] + this.bits*elementCount) / 32)+1]; if ((shift[0] + this.bits)>=32) { int bits1 = 32 - shift[0]; int bits2 = bits - bits1; this.mask = makeMask( shift[0], bits1 ); this.needsRead = true; this.shift = shift[0] - bits2; this.mask2 = makeMask( 0, bits2 ); intOffset[0] += this.buffer.length; } else { this.shift = shift[0]; this.mask = makeMask( this.shift, bits ); this.needsRead = this.mask != 0xffffffff; this.mask2 = 0; // not used intOffset[0] += this.buffer.length - 1; } shift[0] = (shift[0] + this.bits*elementCount) % 32; } static int makeMask( final int shift, final int bits ) { int mask = 0; for (int i = 0; i < bits; i++) mask |= 1 << (shift+i); return mask; } void checkRange( final int value ) { if (signed) { int max = makeMask( 0, bits-1 ); int min = 0-max-1; if (value > max || value < min) System.out.println( "Value out of range (id: " + id + ", value: " + value + ", min: " + min + ", max: " + max + ")" ); } else { long max = makeMask( 0, bits ); max &= 0x00000000ffffffffl; if (value > max) System.out.println( "Value out of range (id: " + id + ", value: " + value + ", max: " + max + ")" ); } } void write( final int value ) { // checkRange( value ); // for debugging only buffer[0] &= ~mask; buffer[0] |= (value << shift) & mask; if (mask2 != 0) { buffer[1] &= ~mask2; buffer[1] |= value & mask2; } } void writeArray( final int[] value ) { int j=0; int m1 = mask; int m2 = mask2; int sh = shift; int fillPosSh = fillPosShift; for (int i = 0; i < elementCount; i++) { buffer[j] &= ~m1; buffer[j] |= (value[i] << sh) & m1; if (m2 != 0) { j++; buffer[j] &= ~m2; buffer[j] |= value[i] & m2; } long lm1 = m1 & 0x00000000ffffffffl; lm1 <<= bits; long lm2 = m2 & 0x00000000ffffffffl; lm2 <<= bits; if ((0x00000000ffffffffl & lm1) != 0l) { m1 = (int)(0x00000000ffffffffl & lm1); m2 = (int)(0x00000000ffffffffl & (lm1>>>32 | lm2)); } else { m1 = (int)(0x00000000ffffffffl & (lm1>>>32 | lm2)); m2 = (int)(0x00000000ffffffffl & (lm2>>>32)); } fillPosSh += bits; if (fillPosSh == 32) j++; if (fillPosSh >= 32) fillPosSh -= 32; if (m2 != 0) { sh = 32 - bits; } else { sh = fillPosSh; } } } int read() { int data = (buffer[0] & mask) >>> shift; if (mask2 != 0) { data |= buffer[1] & mask2; } if (signed && 0 != ( data & (1<<(bits-1)))) { data |= OffsetEntry.signedTable[bits-1]; } return data; } void readArray( final int[] data ) { int j = 0; int m1 = mask; int m2 = mask2; int sh = shift; int fillPosSh = fillPosShift; for (int i = 0; i < elementCount; i++) { data[i] = (buffer[j] & m1) >>> sh; if (m2 != 0) { j++; data[i] |= buffer[j] & m2; } long lm1 = m1 & 0x00000000ffffffffl; lm1 <<= bits; long lm2 = m2 & 0x00000000ffffffffl; lm2 <<= bits; if ((0x00000000ffffffffl & lm1) != 0l) { m1 = (int)(0x00000000ffffffffl & lm1); m2 = (int)(0x00000000ffffffffl & (lm1>>>32 | lm2)); } else { m1 = (int)(0x00000000ffffffffl & (lm1>>>32 | lm2)); m2 = (int)(0x00000000ffffffffl & (lm2>>>32)); } if (signed && 0 != ( data[i] & (1<<(bits-1)))) { data[i] |= OffsetEntry.signedTable[bits-1]; } fillPosSh += bits; if (fillPosSh == 32) j++; if (fillPosSh >= 32) fillPosSh -= 32; if (m2 != 0) { sh = 32 - bits; } else { sh = fillPosSh; } } } } public abstract class AbstractTraceFileCache extends AbstractTrace { private static String metaDataFilename = "cache.data"; //$NON-NLS-1$ protected TraceCacheFile[] cacheFiles; protected int[] procsInFile; protected File cacheDir; protected File dataFile; protected Properties metaData; protected int[] cacheFileNr; protected int[] cacheIndex; protected int[] maxLogClk; protected final Map<String, Integer> sourceFilenames = new HashMap<String, Integer>(); protected IPath tracePath; private int cacheFileCount; private final Vector<OffsetEntry> entries = new Vector<OffsetEntry>(); private final int[] intOffset = new int[1]; private final int[] shift = new int[1]; private String tracefileName; abstract public int estimateMaxLogicalClock(); final public int getBitsForMaxValue(int value) { int bits = 32; while( ( (1<<(bits-1)) & value ) == 0 || bits == 1 ) { bits--; } return bits; } final public int getEventSize() { return (shift[0] == 0) ? intOffset[0] : intOffset[0] + 1; } final public void addEntry( int id, int bits, boolean signed ) { if (bits > 32) bits = 32; if (id >= entries.size()) entries.setSize( id+1 ); OffsetEntry entry = new OffsetEntry(id, bits, signed, intOffset, shift); entries.set( id, entry ); } final public void addEntry( int id, int elementCount, int bits, boolean signed ) { if (bits > 32) bits = 32; if (id >= entries.size()) entries.setSize( id+1 ); OffsetEntry entry = new OffsetEntry(id, elementCount, bits, signed, intOffset, shift); entries.set( id, entry ); } final public boolean openCacheDir( final String tracefileName, final String traceOptions, final long lastModified ) throws IOException { boolean cacheLoaded = false; this.tracefileName = tracefileName; IPreferenceStore store = Activator.getDefault().getPreferenceStore(); File tmpDir = new File( store.getString( PreferenceConstants.P_CACHE_DIR ) ); this.cacheDir = new File(tmpDir, "tracecache_" + Integer.toHexString( traceOptions.hashCode() + tracefileName.hashCode() ) ); this.dataFile = new File(cacheDir, metaDataFilename); this.cacheFileNr = new int[getNumberOfProcesses()]; this.cacheIndex = new int[getNumberOfProcesses()]; this.maxLogClk = new int[getNumberOfProcesses()]; this.metaData = new Properties(); if ( cacheDir.exists() && dataFile.exists() ) { long modTime = dataFile.lastModified(); loadCacheMetadata(); cacheFileCount = Integer.parseInt( ( String )metaData.get( "cachefilecount" ) ); this.procsInFile = new int[cacheFileCount]; for(int fileNr = 0; fileNr < cacheFileCount; fileNr++) { long cacheModTime = new File(cacheDir, "cachefile_" + fileNr).lastModified(); if ( cacheModTime < modTime ) modTime = cacheModTime; } if ( modTime > lastModified ) { for(int fileNr = 0; fileNr < cacheFileCount; fileNr++) { String mapping = ( String )metaData.get( "file_"+fileNr ); StringTokenizer tok = new StringTokenizer(mapping, ","); int idx = 0; while (tok.hasMoreTokens()) { String token = tok.nextToken(); int procNr = Integer.parseInt( token ); cacheFileNr[procNr] = fileNr; cacheIndex[procNr] = idx; idx++; procsInFile[fileNr] = idx; } } String filename = ""; for( int i = 0; filename != null; i++ ) { filename = ( String )metaData.get( "sourcefile_"+i ); if ( filename != null ) sourceFilenames.put( filename, i ); } cacheLoaded = true; } } else { cacheDir.mkdir(); } if ( !cacheLoaded ) { createCacheFileMapping(); } openCacheFiles(); return cacheLoaded; } final protected void createCacheFileMapping() { Vector<Integer> factors = factorize( getNumberOfProcesses() ); this.cacheFileCount = 1; for (int i = factors.size()-1; i>=0; i--) { int factor = factors.get( i ).intValue(); if (this.cacheFileCount * factor <= 32) this.cacheFileCount *= factor; } int procsPerFile = getNumberOfProcesses() / this.cacheFileCount; this.procsInFile = new int[this.cacheFileCount]; for (int fileNr = 0; fileNr < this.cacheFileCount; fileNr++ ) { this.procsInFile[fileNr] = procsPerFile; } this.cacheFileNr = new int[getNumberOfProcesses()]; this.cacheIndex = new int[getNumberOfProcesses()]; for (int i = 0; i < getNumberOfProcesses(); i++) { this.cacheFileNr[i] = i / procsPerFile; this.cacheIndex[i] = i % procsPerFile; } } final private Vector<Integer> factorize(int x) { Vector<Integer> resultVector = new Vector<Integer>(); int d = 2; do { if (x % d != 0) d++; else { resultVector.add( Integer.valueOf(d) ); x /= d; d = 2; } } while (x != 1); return resultVector; } final private void openCacheFiles() throws FileNotFoundException { cacheFiles = new TraceCacheFile[cacheFileCount]; for (int fileNr = 0; fileNr < cacheFileCount; fileNr++ ) { cacheFiles[fileNr] = new TraceCacheFile(cacheDir, fileNr); } } final private void loadCacheMetadata() throws IOException { metaData = new Properties(); FileInputStream in = new FileInputStream(dataFile); metaData.load( in ); in.close(); for (int i = 0; i < getNumberOfProcesses(); i++) { String count = ( String )metaData.get( "eventcount_" + i ); if (count != null) { maxLogClk[i] = Integer.parseInt( count ); } } } final protected void saveCacheMetadata() throws IOException { try { for (int i = 0; i < getNumberOfProcesses(); i++) { metaData.put( "eventcount_" + i, Integer.toString( maxLogClk[i] ) ); } metaData.put( "cachefilecount", Integer.toString( cacheFileCount ) ); for(int fileNr = 0; fileNr < cacheFileCount; fileNr++) { StringBuilder mapping = new StringBuilder(); for( int procNr = 0; procNr < getNumberOfProcesses(); procNr++ ) { if (cacheFileNr[procNr] == fileNr) { if (mapping.length() != 0) mapping.append( ',' ); mapping.append( procNr ); } } metaData.put( "file_" + fileNr, mapping.toString() ); } FileOutputStream out = new FileOutputStream(dataFile); metaData.store( out, "Trace file cache meta data (" + tracefileName + ')' ); out.close(); }catch(Exception e) { e.printStackTrace(); } } final void write( final int processId, final int logicalClock, final int id, final int value ) { try { TraceCacheFile file = cacheFiles[cacheFileNr[processId]]; OffsetEntry off = entries.get( id ); int offset = calcEventOffset( processId, logicalClock, off.intOffset ); synchronized( off ) { if (off.needsRead) { file.read( offset, off.buffer ); } off.write( value ); file.write( offset, off.buffer ); } } catch( IOException e ) { // TODO Auto-generated catch block e.printStackTrace(); } } final void writeArray( final int processId, final int logicalClock, final int id, final int[] value ) { try { TraceCacheFile file = cacheFiles[cacheFileNr[processId]]; OffsetEntry off = entries.get( id ); int offset = calcEventOffset( processId, logicalClock, off.intOffset ); synchronized( off ) { if (off.needsRead) { file.read( offset, off.buffer ); } off.writeArray( value ); file.write( offset, off.buffer ); } } catch( IOException e ) { // TODO Auto-generated catch block e.printStackTrace(); } } final int read( final int processId, final int logicalClock, final int id ) { try { TraceCacheFile file = cacheFiles[cacheFileNr[processId]]; OffsetEntry off = entries.get( id ); int offset = calcEventOffset( processId, logicalClock, off.intOffset ); synchronized( off ) { file.read( offset, off.buffer ); return off.read(); } } catch( IOException e ) { // TODO Auto-generated catch block e.printStackTrace(); } return 0; } final void readArray( final int processId, final int logicalClock, final int id, final int[] data ) { try { TraceCacheFile file = cacheFiles[cacheFileNr[processId]]; OffsetEntry off = entries.get( id ); int offset = calcEventOffset( processId, logicalClock, off.intOffset ); synchronized( off ) { file.read( offset, off.buffer ); off.readArray( data ); } } catch( IOException e ) { // TODO Auto-generated catch block e.printStackTrace(); } } private final int calcEventOffset( final int processId, final int logicalClock, final int offset ) { return getEventSize() * (this.cacheIndex[processId] + procsInFile[cacheFileNr[processId]] * logicalClock ) + offset; } final int getMaximumLogicalClock( final int processNr ) { return this.maxLogClk[processNr]; } final void setMaximumLogicalClock( final int processNr, final int value) { this.maxLogClk[processNr] = value; } final String getSourceFilenameForIndex( final int index ) { return (String)metaData.get( "sourcefile_"+index ); } final int addSourceFilename( final String filename ) { int index; if ( sourceFilenames.containsKey( filename ) ) { index = sourceFilenames.get( filename ); } else { index = sourceFilenames.size(); metaData.put( "sourcefile_"+index, filename ); sourceFilenames.put( filename, index ); } return index; } protected final void enableMemoryMap() { try { for (int fileNr = 0; fileNr < this.cacheFileCount; fileNr++ ) { cacheFiles[fileNr].enableMemoryMap(); } } catch( IOException e ) { Activator.logStatus( new Status( IStatus.WARNING, Activator.PLUGIN_ID, "Could not create memory map for all trace cache files, continuing without" ) ); } } public IPath getPath() { return this.tracePath; } }