blob: 8a09115ab9aa837aa0a1cbe14695cab07d1bb18b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2018 STMicroelectronics and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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 java.util.HashMap;
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.GcovStringReader;
import org.eclipse.linuxtools.internal.gcov.utils.LEDataInputStream;
import org.eclipse.linuxtools.internal.gcov.utils.MasksGenerator;
import org.eclipse.osgi.util.NLS;
public class GcnoRecordsParser {
private static final int GCOV_NOTE_MAGIC = 0x67636e6f; // en ASCII: 67=g 63=c 6e=n 6f=o
private static final int GCOV_TAG_FUNCTION = 0x01000000;
private static final int GCOV_TAG_BLOCKS = 0x01410000;
private static final int GCOV_TAG_ARCS = 0x01430000;
private static final int GCOV_TAG_LINES = 0x01450000;
private static final int GCC_VER_810 = 1094201642; // GCC 8.1.0 ('A81*')
private static final int GCC_VER_407 = 875575082; // GCC 4.0.7
private GcnoFunction fnctn = null;
private final ArrayList<GcnoFunction> fnctns = new ArrayList<>();
private final ArrayList<SourceFile> currentAllSrcs;
private final HashMap<String, SourceFile> sourceMap;
public GcnoRecordsParser(HashMap<String, SourceFile> sourceMap, ArrayList<SourceFile> allSrcs) {
this.sourceMap = sourceMap;
this.currentAllSrcs = allSrcs;
}
private SourceFile findOrAdd(String fileName) {
SourceFile newsrc = sourceMap.get(fileName);
if (newsrc == null) {
newsrc = new SourceFile(fileName, currentAllSrcs.size() + 1);
currentAllSrcs.add(newsrc);
sourceMap.put(fileName, newsrc);
}
return newsrc; // return the new added element
}
public void parseData(DataInput stream) throws IOException, CoreException {
// header data
int magic = 0;
// blocks data
ArrayList<Block> blocks = null;
// source file data
SourceFile source = null;
// flag
boolean parseFirstFnctn = false;
magic = stream.readInt();
if (magic == GCOV_NOTE_MAGIC) {
stream = new BEDataInputStream((DataInputStream) stream);
} else {
magic = (magic >> 16) | (magic << 16);
magic = ((magic & 0xff00ff) << 8) | ((magic >> 8) & 0xff00ff);
if (magic == GCOV_NOTE_MAGIC) {
stream = new LEDataInputStream((DataInputStream) stream);
} else {
String message = NLS.bind(Messages.GcnoRecordsParser_magic_num_error, magic);
Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, message);
throw new CoreException(status);
}
}
int version = stream.readInt();
// stamp = stream.readInt();
stream.readInt();
/*------------------------------------------------------------------------------
System.out.println("Gcno LE, Magic "+magic+" version "+version+" stamp "+stamp);
*/
while (true) {
try {
int tag;
// parse header
while (true) {
tag = stream.readInt();
if (tag == GCOV_TAG_FUNCTION || tag == GCOV_TAG_BLOCKS || tag == GCOV_TAG_ARCS
|| tag == GCOV_TAG_LINES)
break;
}
int length = stream.readInt();
// parse gcno data
if (tag == GCOV_TAG_FUNCTION) {
// before parse new function, add current function to functions list
if (parseFirstFnctn) {
fnctns.add(fnctn);
}
long fnctnIdent = (stream.readInt() & MasksGenerator.UNSIGNED_INT_MASK);
long fnctnChksm = (stream.readInt() & MasksGenerator.UNSIGNED_INT_MASK);
/*
* 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 >= GCC_VER_407) {
// long cfgChksm = (stream.readInt()&MasksGenerator.UNSIGNED_INT_MASK);
stream.readInt();
}
String fnctnName = GcovStringReader.readString(stream);
if (version >= GCC_VER_810) {
// long artificial = (stream.readInt() & MasksGenerator.UNSIGNED_INT_MASK);
stream.readInt();
}
String fnctnSrcFle = GcovStringReader.readString(stream);
long fnctnFrstLnNmbr = (stream.readInt() & MasksGenerator.UNSIGNED_INT_MASK);
if (version >= GCC_VER_810) {
// long fnctnFrstColumnLnNmbr = (stream.readInt() & MasksGenerator.UNSIGNED_INT_MASK);
stream.readInt();
// long fnctnLastLnNmbr = (stream.readInt() & MasksGenerator.UNSIGNED_INT_MASK);
stream.readInt();
}
fnctn = new GcnoFunction(fnctnIdent, fnctnChksm, fnctnName, fnctnSrcFle, fnctnFrstLnNmbr);
SourceFile srcFle2 = findOrAdd(fnctn.getSrcFile());
if (fnctn.getFirstLineNmbr() >= srcFle2.getNumLines()) {
srcFle2.setNumLines((int) fnctn.getFirstLineNmbr() + 1);
}
srcFle2.addFnctn(fnctn);
parseFirstFnctn = true;
continue;
}
else if (tag == GCOV_TAG_BLOCKS) {
if (version >= GCC_VER_810) {
length = stream.readInt();
}
blocks = new ArrayList<>();
Block blck;
for (int i = 0; i < length; i++) {
if (version >= GCC_VER_810) {
blck = new Block(0); // value not used anywhere
} else {
long BlckFlag = stream.readInt() & MasksGenerator.UNSIGNED_INT_MASK;
blck = new Block(BlckFlag);
}
blocks.add(blck);
}
fnctn.setNumBlocks(length);
continue;
} else if (tag == GCOV_TAG_ARCS) {
int srcBlockIndice = stream.readInt();
int nmbrArcs = (length - 1) / 2;
ArrayList<Arc> arcs = new ArrayList<>(nmbrArcs);
for (int i = 0; i < nmbrArcs; i++) {
int dstnatnBlockIndice = stream.readInt();
long flag = (stream.readInt() & MasksGenerator.UNSIGNED_INT_MASK);
Arc arc = new Arc(srcBlockIndice, dstnatnBlockIndice, flag, blocks);
arcs.add(arc);
}
// each arc, register it as exit of the src block
Block srcBlk = blocks.get(srcBlockIndice);
for (Arc a : arcs) {
srcBlk.addExitArcs(a);
srcBlk.incNumSuccs();
}
// each arc, register it as entry of its dstntn block
for (Arc a : arcs) {
Block dstntnBlk = a.getDstnatnBlock();
dstntnBlk.addEntryArcs(a);
dstntnBlk.incNumPreds();
}
for (Arc a : arcs) {
if (a.isFake()) {
if (a.getSrcBlock() != null) {
// Exceptional exit from this function, the
// source block must be a call.
srcBlk = blocks.get(srcBlockIndice);
srcBlk.setCallSite(true);
a.setCallNonReturn(true);
} else {
a.setNonLoclaReturn(true);
Block dstntnBlk = a.getDstnatnBlock();
dstntnBlk.setNonLocalReturn(true);
}
}
if (!a.isOnTree()) {
fnctn.incNumCounts();
}
// nbrCounts++;
}
fnctn.setFunctionBlocks(blocks);
continue;
}
else if (tag == GCOV_TAG_LINES) {
int numBlock = stream.readInt();
long[] lineNos = new long[length - 1];
int ix = 0;
do {
long lineNumber = stream.readInt() & MasksGenerator.UNSIGNED_INT_MASK;
if (lineNumber != 0) {
if (ix == 0) {
lineNos[ix++] = 0;
lineNos[ix++] = source.getIndex();
}
lineNos[ix++] = lineNumber;
if (lineNumber >= source.getNumLines()) {
source.setNumLines((int) lineNumber + 1);
}
} else {
String fileName = GcovStringReader.readString(stream);
if (fileName.equals(Messages.GcnoRecordsParser_null_string)){
break;
}
source = findOrAdd(fileName);
lineNos[ix++] = 0;
lineNos[ix++] = source.getIndex();
}
} while (true);
fnctn.getFunctionBlocks().get((numBlock)).setEncoding(lineNos);
fnctn.getFunctionBlocks().get((numBlock)).setNumLine(ix);
continue;
}
} catch (EOFException e) {
fnctn.setFunctionBlocks(blocks);
fnctns.add(fnctn);
break;
}
}// while
}
/* Getters */
public ArrayList<GcnoFunction> getFnctns() {
return fnctns;
}
}