| /******************************************************************************* |
| * 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; |
| } |
| } |