/*******************************************************************************
* Copyright (c) 2009, 2016 STMicroelectronics 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:
* Xavier Raynaud <xavier.raynaud@st.com> - initial API and implementation
*******************************************************************************/
package org.eclipse.linuxtools.internal.gcov.parser;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.util.ArrayList;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.linuxtools.internal.gcov.Activator;
import org.eclipse.linuxtools.internal.gcov.utils.BEDataInputStream;
import org.eclipse.linuxtools.internal.gcov.utils.LEDataInputStream;
import org.eclipse.linuxtools.internal.gcov.utils.MasksGenerator;
import org.eclipse.osgi.util.NLS;
public class GcdaRecordsParser {
private static final int GCOV_DATA_MAGIC = 0x67636461; // en ASCII: 67=g 63=c 64=d 61=a
private static final int GCOV_TAG_FUNCTION = 0x01000000;
private static final int GCOV_COUNTER_ARCS = 0x01a10000;
private static final int GCOV_TAG_OBJECT_SYMMARY = 0xa1000000;
private static final int GCOV_TAG_PROGRAM_SUMMARY = 0xa3000000;
private final ArrayList<GcnoFunction> fnctns;
private long objSmryNbrPgmRuns = 0;
private long pgmSmryChksm = 0;
private long pgmSmryNbrPgmRuns = 0;
private long objSmryChksm = 0;
private long objSmryArcCnts = 0;
private long objSmrytotalCnts = 0;
private long objSmryRunMax = 0;
private long objSmrySumMax = 0;
public GcdaRecordsParser(ArrayList<GcnoFunction> fnctns) {
this.fnctns = fnctns;
}
public void parseGcdaRecord(DataInput stream) throws IOException, CoreException {
// header data
int magic = 0;
// data & flags to process tests
GcnoFunction currentFnctn = null;
// read magic
magic = stream.readInt();
if (magic == GCOV_DATA_MAGIC) {
stream = new BEDataInputStream((DataInputStream) stream);
} else {
magic = (magic >> 16) | (magic << 16);
magic = ((magic & 0xff00ff) << 8) | ((magic >> 8) & 0xff00ff);
if (magic == GCOV_DATA_MAGIC) {
stream = new LEDataInputStream((DataInputStream) stream);
} else {
String message = NLS.bind(Messages.GcdaRecordsParser_magic_num_error, magic);
Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, message);
throw new CoreException(status);
}
}
// read version
int version = stream.readInt();
// read stamp
// stamp = stream.readInt();
stream.readInt();
while (true) {
try {
// parse header
int tag = stream.readInt();
/*
* Move on to the next tag if an unused level (tag == O) is encountered, these do no have corresponding
* data lengths.
*/
if (tag == 0) {
continue;
}
long length = (stream.readInt() & MasksGenerator.UNSIGNED_INT_MASK);
// parse gcda data
switch (tag) {
case GCOV_TAG_FUNCTION: {
long fnctnId = stream.readInt() & MasksGenerator.UNSIGNED_INT_MASK;
if (!fnctns.isEmpty()) {
boolean fnctnFound = false;
for (GcnoFunction f : fnctns) {
if (f.getIdent() == fnctnId) {
fnctnFound = true;
currentFnctn = f;
long fnctnChksm = stream.readInt() & MasksGenerator.UNSIGNED_INT_MASK;
if (f.getCheksum() != fnctnChksm) {
String message = NLS.bind(Messages.GcdaRecordsParser_checksum_error, new Object[] {
currentFnctn.getName(), fnctnId });
Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, message);
throw new CoreException(status);
}
/*
* danielhb, 2012-08-06: Gcov versions 4.7.0 or later (long value = 875575082) has
* different format for the data file: prior format: announce_function: header
* int32:ident int32:checksum new format: announce_function: header int32:ident
* int32:lineno_checksum int32:cfg_checksum TL;DR Need to consume the extra long value.
*/
if (version >= 875575082) {
// long cfgChksm = (stream.readInt()&MasksGenerator.UNSIGNED_INT_MASK);
stream.readInt();
}
break;
}
}
if (!fnctnFound) {
currentFnctn = null;
String message = NLS.bind(Messages.GcdaRecordsParser_func_not_found, fnctnId);
Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, message);
throw new CoreException(status);
}
}
break;
}
case GCOV_COUNTER_ARCS: {
if (currentFnctn == null) {
String message = Messages.GcdaRecordsParser_func_counter_error;
Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, message);
throw new CoreException(status);
}
if (length != 2 * (currentFnctn.getNumCounts())) {
String message = Messages.GcdaRecordsParser_content_inconsistent;
Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, message);
throw new CoreException(status);
}
ArrayList<Block> fnctnBlcks = currentFnctn.getFunctionBlocks();
if (fnctnBlcks.isEmpty()) {
String message = Messages.GcdaRecordsParser_func_block_empty;
Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, message);
throw new CoreException(status);
}
for (int i = 0; i < fnctnBlcks.size(); i++) {
Block b = fnctnBlcks.get(i);
int nonFakeExit = 0;
ArrayList<Arc> arcsExit = b.getExitArcs();
for (Arc extArc : arcsExit) {
if (!extArc.isFake()) {
nonFakeExit++;
}
if (!extArc.isOnTree()) {
long arcsCnts = stream.readLong();
extArc.setCount(arcsCnts);
extArc.setCountValid(true);
b.decNumSuccs();
extArc.getDstnatnBlock().decNumPreds();
}
}
// If there is only one non-fake exit, it is an
// unconditional branch.
if (nonFakeExit == 1) {
for (Arc extArc : arcsExit) {
if (!extArc.isFake()) {
extArc.setUnconditionnal(true);
// If this block is instrumenting a call, it might be
// an artificial block. It is not artificial if it has
// a non-fallthrough exit, or the destination of this
// arc has more than one entry. Mark the destination
// block as a return site, if none of those conditions hold.
if (b.isCallSite() && extArc.isFallthrough()
&& extArc.getDstnatnBlock().getEntryArcs().get(0).equals(extArc)
&& extArc.getDstnatnBlock().getEntryArcs().size() == 1) {
extArc.getDstnatnBlock().setCallReturn(true);
}
}
}
}
}
// counters arcs process data reset
currentFnctn = null;
break;
}
case GCOV_TAG_OBJECT_SYMMARY: {
objSmryChksm = (stream.readInt() & MasksGenerator.UNSIGNED_INT_MASK);
objSmryArcCnts = (stream.readInt() & MasksGenerator.UNSIGNED_INT_MASK);
objSmryNbrPgmRuns = (stream.readInt() & MasksGenerator.UNSIGNED_INT_MASK);
objSmrytotalCnts = stream.readLong();
objSmryRunMax = stream.readLong();
objSmrySumMax = stream.readLong();
break;
}
// program summary tag
case GCOV_TAG_PROGRAM_SUMMARY: {
// long[] pgmSmryskips = new long[(int) length];
pgmSmryChksm = (stream.readInt() & MasksGenerator.UNSIGNED_INT_MASK);
stream.readInt();
pgmSmryNbrPgmRuns = (stream.readInt() & MasksGenerator.UNSIGNED_INT_MASK);
for (int i = 0; i < length - 3; i++) {
// pgmSmryskips[i] = (stream.readInt() & MasksGenerator.UNSIGNED_INT_MASK);
stream.readInt();
}
break;
}
default: {
// System.out.println("wrong gcda tag");
break;
}
}
} catch (EOFException e) {
break;
}
}
}
/**
* @return the objSmryNbrPgmRuns
*/
public long getObjSmryNbrPgmRuns() {
return objSmryNbrPgmRuns;
}
/**
* @return the objSmryChksm
*/
public long getObjSmryChksm() {
return objSmryChksm;
}
/**
* @return the objSmryArcCnts
*/
public long getObjSmryArcCnts() {
return objSmryArcCnts;
}
/**
* @return the objSmrytotalCnts
*/
public long getObjSmrytotalCnts() {
return objSmrytotalCnts;
}
/**
* @return the objSmryRunMax
*/
public long getObjSmryRunMax() {
return objSmryRunMax;
}
/**
* @return the objSmrySumMax
*/
public long getObjSmrySumMax() {
return objSmrySumMax;
}
/**
* @return the pgmSmryChksm
*/
public long getPgmSmryChksm() {
return pgmSmryChksm;
}
/**
* @return the prgSmryNbrPgmRuns
*/
public long getPgmSmryNbrPgmRuns() {
return pgmSmryNbrPgmRuns;
}
}