/*******************************************************************************
* Copyright (c) 2009 STMicroelectronics.
* 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:
* Xavier Raynaud <xavier.raynaud@st.com> - initial API and implementation
*******************************************************************************/
package org.eclipse.linuxtools.internal.gprof.parser;
import java.io.DataInput;
import java.io.IOException;
import java.io.PrintStream;
import org.eclipse.cdt.core.IBinaryParser.ISymbol;
import org.eclipse.linuxtools.internal.gprof.Messages;
import org.eclipse.linuxtools.internal.gprof.symbolManager.Bucket;
import org.eclipse.linuxtools.internal.gprof.view.histogram.HistRoot;
/**
* Reads the histogram in gmon files
* @author Xavier Raynaud <xavier.raynaud@st.com>
*/
public class HistogramDecoder {
private static final int GMON_HDRSIZE_BSD44 = (3 * 4);
private static final int GMON_HDRSIZE_BSD44_32 = (4 + 4 + 4 + 4 + 4 + GMON_HDRSIZE_BSD44);
private static final int GMON_HDRSIZE_BSD44_64 = (8 + 8 + 4 + 4 + 4 + GMON_HDRSIZE_BSD44);
private static final int GMON_HDRSIZE_OLDBSD_32 = (4 + 4 + 4) ;
private static final int GMON_HDRSIZE_OLDBSD_64 = (8 + 8 + 4);
/** the decoder */
protected final GmonDecoder decoder;
// histogram header
/** Base pc address of sampled buffer */
protected long lowpc;
/** Max pc address of sampled buffer */
protected long highpc;
/** Profiling clock rate */
protected int profRate;
/** usually 's' for seconds, 'm' for milliseconds... */
protected char dimenAbbrev;
/** used when aggregate several gmon files */
private boolean initialized = false;
/** Histogram samples (shorts in the file!). */
protected int[] hist_sample;
/** Total time for all routines. */
protected double total_time;
protected long bucketSize;
/**
* Constructor
* @param decoder the Gmon decoder
*/
public HistogramDecoder(GmonDecoder decoder) {
this.decoder = decoder;
}
protected long readAddress(DataInput stream) throws IOException {
return stream.readInt() & 0xFFFFFFFFL;
}
public boolean hasValues() {
return (this.hist_sample != null && this.hist_sample.length > 0);
}
/**
* Decode the given stream
* @param stream a DataInputStream, pointing on a histogram header in a gmon file.
* @throws IOException if an IO error occurs
*/
public void decodeHeader(DataInput stream) throws IOException {
long lowpc = readAddress(stream);
long highpc = readAddress(stream);
int hist_num_bins = stream.readInt();
int prof_rate = stream.readInt();
byte[] bytes = new byte[15];
stream.readFully(bytes);
byte b = stream.readByte();
if (!isCompatible(lowpc, highpc, prof_rate, hist_num_bins)) {
// TODO exception to normalize
throw new RuntimeException(Messages.HistogramDecoder_INCOMPATIBLE_HIST_HEADER_ERROR_MSG);
}
this.lowpc = lowpc;
this.highpc = highpc;
this.profRate = prof_rate;
hist_sample = new int[hist_num_bins]; // Impl note: JVM sets all integers to 0
dimenAbbrev = (char) b;
long temp = highpc - lowpc;
bucketSize = Math.round(temp/(double)hist_num_bins);
}
/**
* Decode the given stream
* @param stream a DataInputStream, pointing on a histogram header in a gmon file.
* @throws IOException if an IO error occurs
*/
public void decodeOldHeader(DataInput stream) throws IOException {
long lowPC = readAddress(stream);
long highPC = readAddress(stream);
int ncnt = stream.readInt();
int version = stream.readInt();
int headerSize;
int profrate = 0;
if (version == GmonDecoder.GMONVERSION)
{
profrate = stream.readInt();
stream.skipBytes(GMON_HDRSIZE_BSD44);
if (decoder._32_bit_platform) {
headerSize = GMON_HDRSIZE_BSD44_32;
} else {
headerSize = GMON_HDRSIZE_BSD44_64;
}
} else {
/* Old style BSD format. */
if (decoder._32_bit_platform) {
headerSize = GMON_HDRSIZE_OLDBSD_32;
} else {
headerSize = GMON_HDRSIZE_OLDBSD_64;
}
}
int sampBytes = ncnt - headerSize;
int histNumBins = sampBytes / 2;
if (!isCompatible(lowPC, highPC, profrate, histNumBins))
{
// TODO exception to normalize
throw new RuntimeException(Messages.HistogramDecoder_INCOMPATIBLE_HIST_HEADER_ERROR_MSG);
}
this.lowpc = lowPC;
this.highpc = highPC;
this.profRate = profrate;
hist_sample = new int[histNumBins]; // Impl note: JVM sets all integers to 0
dimenAbbrev = 's';
long temp = highpc - lowpc;
bucketSize = Math.round(temp/(double)histNumBins);
}
/**
* Checks whether the gmon file currently parsed is compatible with the previous one (if any).
* @param lowpc
* @param highpc
* @param profrate
* @param sampleCount
* @return whether the gmon file currently parsed is compatible with the previous one (if any).
*/
private boolean isCompatible(long lowpc, long highpc, int profrate, int sampleCount) {
if (!initialized) return true;
return (
(this.lowpc == lowpc) &&
(this.highpc == highpc) &&
(this.profRate == profrate) &&
(this.hist_sample.length == sampleCount)
);
}
/**
* Reads hitogram record
* @param stream a DataInputStream, pointing just after histogram header in a gmon file.
* @throws IOException if an IO error occurs
*/
public void decodeHistRecord(DataInput stream) throws IOException {
for (int i = 0; i<hist_sample.length; i++) {
short rv = stream.readShort();
if (rv != 0) {
int hist_size = (rv & 0xFFFF);
hist_sample[i] += hist_size;
}
}
}
/**
* Print the histogram header, for debug usage.
* @param ps a printstream (typically System.out)
*/
public void printHistHeader(PrintStream ps) {
ps.println(" \nHistogram Header : \n"); //$NON-NLS-1$
ps.print(" Base pc address of sample buffer = 0x"); //$NON-NLS-1$
ps.println(Long.toHexString(lowpc));
ps.print(" Max pc address of sampled buffer = 0x"); //$NON-NLS-1$
ps.println(Long.toHexString(highpc));
ps.print(" Number of histogram samples = "); //$NON-NLS-1$
ps.println(hist_sample.length);
ps.print(" Profiling clock rate = "); //$NON-NLS-1$
ps.println(profRate);
// ps.print(" Physical dimension usually \"seconds\" = ");
// ps.println(dimen);
ps.print(" Physical dimension abreviation : 's' for \"seconds\" 'm' for \"milliseconds\" = "); //$NON-NLS-1$
ps.println(dimenAbbrev);
}
/**
* Print the histogram, for debug usage.
* @param ps a printstream (typically System.out)
*/
public void printHistRecords(PrintStream ps) {
ps.println();
ps.println(" == HISTOGRAM RECORDS == "); //$NON-NLS-1$
ps.println(" ========================= "); //$NON-NLS-1$
printHistHeader(ps);
/*ps.println(" \nHistogram Samples : ");
ISymbol[] symbols = this.decoder.getProgram().getSymbols();
for (ISymbol iSymbol : symbols) {
ps.println(iSymbol.getName() + "\t" + iSymbol.getAddress());
}
for (int i = 0; i<hist_sample.length; i++) {
ps.println("histSample[" + i + "]\t" + hist_sample[i]);
}*/
}
/**
* Assign the hits to the given symbols
* @param symblist
*/
public void assignSamplesSymbol() {
if (hist_sample == null || hist_sample.length == 0) return;
ISymbol[] symblist = this.decoder.getProgram().getSymbols();
/* read samples and assign to namelist symbols */
int j = 1;
for (int i = 0; i < hist_sample.length; i++)
{
int ccnt = hist_sample[i];
if (ccnt != 0)
{
long pcl = lowpc + (bucketSize*i);
long pch = pcl+bucketSize;
total_time += ccnt;
long svalue0;
long svalue1 = symblist[j-1].getAddress().getValue().longValue();
for (j = j-1; j < symblist.length - 1; j++)
{
svalue0 = svalue1;
svalue1 = symblist[j+1].getAddress().getValue().longValue();
/* if high end of tick is below entry address,
* go for next tick. */
if(pch < svalue0) {
break;
}
/* if low end of tick into next routine,
* go for next routine. */
if(pcl < svalue1) {
long start_addr = pcl>svalue0?pcl:svalue0;
long end_addr = pch<svalue1?pch:svalue1;
long overlap = end_addr - start_addr;
if(overlap > 0) {
ISymbol symbol = symblist[j];
int time = (int) ((overlap * ccnt) / bucketSize);
Bucket bck = new Bucket(start_addr, end_addr, time);
addBucket(bck,symbol);
}
}
}
}
}
}
private void addBucket(Bucket b, ISymbol s) {
HistRoot root = this.decoder.getRootNode();
root.addBucket(b, s, decoder.getProgram());
}
/**
* @return the profRate
*/
public int getProfRate() {
return profRate;
}
/**
*
* @return 's' for seconds, 'm' for ms, 'u' for �s....
*/
public char getTimeDimension() {
return dimenAbbrev;
}
/**
* get the bucket size
*/
public long getBucketSize(){
return bucketSize;
}
}