/******************************************************************************* * 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.gprof.parser; import java.io.BufferedInputStream; import java.io.DataInput; import java.io.DataInputStream; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.PrintStream; import java.text.DateFormat; import java.util.Date; import java.util.HashMap; import org.eclipse.cdt.core.IBinaryParser.IBinaryObject; import org.eclipse.cdt.core.IBinaryParser.ISymbol; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.Platform; import org.eclipse.linuxtools.binutils.utils.STSymbolManager; import org.eclipse.linuxtools.internal.gprof.Messages; import org.eclipse.linuxtools.internal.gprof.utils.LEDataInputStream; import org.eclipse.linuxtools.internal.gprof.view.histogram.HistRoot; /** * Parser of gmon file */ public class GmonDecoder { /** Histogram record type. */ public static final int VPF_GMON_RECORD_TYPE_HISTOGRAM = 0; /** Callgraph record type. */ public static final int VPF_GMON_RECORD_TYPE_CALLGRAPH = 1; /** Unkwown record type. */ public static final int VPF_GMON_RECORD_TYPE_UNKNOWN = -1; public static final int GMONVERSION = 0x00051879; // header private String cookie; private int gmonVersion; private final IBinaryObject program; final boolean _32_bit_platform; private HistogramDecoder histo; private CallGraphDecoder callGraph; private final PrintStream ps; private final HistRoot rootNode = new HistRoot(this); private String file; private int tag = -1; private final HashMap<ISymbol, String> filenames = new HashMap<>(); private final IProject project; // for dump private boolean shouldDump = false; /** * Constructor * * @param program * @throws IOException */ public GmonDecoder(IBinaryObject program, IProject project) { this(program, null, project); } /** * Constructor * * @param program * @throws IOException */ public GmonDecoder(IBinaryObject program, PrintStream ps, IProject project) { this.program = program; this.ps = ps; this.project = project; program.getBinaryParser().getFormat(); String cpu = program.getCPU(); if (Platform.ARCH_X86_64.equals(cpu) || "ppc64".equals(cpu)) { //$NON-NLS-1$ histo = new HistogramDecoder64(this); callGraph = new CallGraphDecoder64(this); _32_bit_platform = false; } else { _32_bit_platform = true; histo = new HistogramDecoder(this); callGraph = new CallGraphDecoder(this); } } /** * Reads the given file * * @param file * @throws IOException */ public void read(String file) throws IOException { this.file = file; DataInputStream beStream = new DataInputStream(new BufferedInputStream(new FileInputStream(file))); if (program.isLittleEndian()) { try (LEDataInputStream leStream = new LEDataInputStream(beStream)) { leStream.mark(1000); boolean gmonType = readHeader(leStream); if (gmonType) readGmonContent(leStream); else { leStream.reset(); histo.decodeOldHeader(leStream); histo.decodeHistRecord(leStream); try { do { this.callGraph.decodeCallGraphRecord(leStream, true); } while (true); } catch (EOFException e) { // normal. End of file reached. } this.callGraph.populate(rootNode); this.histo.assignSamplesSymbol(); } } } else { try { beStream.mark(1000); boolean gmonType = readHeader(beStream); if (gmonType) { readGmonContent(beStream); } else { beStream.reset(); histo.decodeOldHeader(beStream); histo.decodeHistRecord(beStream); try { do { this.callGraph.decodeCallGraphRecord(beStream, true); } while (true); } catch (EOFException e) { // normal. End of file reached. } this.callGraph.populate(rootNode); this.histo.assignSamplesSymbol(); } } finally { beStream.close(); } } } /** * Read gmon header * * @param stream * the gmon as a stream * @throws IOException * if an IO error occurs or if the stream is not a gmon file. */ private boolean readHeader(DataInput stream) throws IOException { byte[] _cookie = new byte[4]; stream.readFully(_cookie); cookie = new String(_cookie); gmonVersion = stream.readInt(); byte[] spare = new byte[12]; stream.readFully(spare); return "gmon".equals(cookie); //$NON-NLS-1$ } /** * Read the whole content of the GMON file The header should be read before calling this function. * * @param stream * @throws IOException */ private void readGmonContent(DataInput stream) throws IOException { do { // int tag = -1; tag = -1; try { tag = stream.readByte(); } catch (EOFException e) { break; } switch (tag) { case VPF_GMON_RECORD_TYPE_HISTOGRAM: histo.decodeHeader(stream); histo.decodeHistRecord(stream); break; case VPF_GMON_RECORD_TYPE_CALLGRAPH: callGraph.decodeCallGraphRecord(stream, false); break; default: throw new IOException(Messages.GmonDecoder_BAD_TAG_ERROR); } if (shouldDump) { dumpGmonResult(ps == null ? System.out : ps); } } while (true); this.callGraph.populate(rootNode); this.histo.assignSamplesSymbol(); } public void dumpGmonResult(PrintStream ps) { ps.println("-- gmon Results --"); //$NON-NLS-1$ ps.println("cookie " + cookie); //$NON-NLS-1$ ps.println("gmon_version " + gmonVersion); //$NON-NLS-1$ // ps.println("spare "+new String(spare)); ps.println("tag " + tag); //$NON-NLS-1$ switch (tag) { case VPF_GMON_RECORD_TYPE_HISTOGRAM: histo.printHistHeader(ps); histo.printHistRecords(ps); break; default: break; } } /** * @return the histogram decoder */ public HistogramDecoder getHistogramDecoder() { return histo; } /** * @return the program */ public IBinaryObject getProgram() { return program; } /** * @return the rootNode */ public HistRoot getRootNode() { return rootNode; } /** * @return the (last) parsed gmon file */ public String getGmonFile() { return file; } /** * @return the modification timestamp of (last) parsed gmon file */ public String getGmonFileTimeStamp() { return DateFormat.getInstance().format(new Date(new File(file).lastModified())); } public String getFileName(ISymbol s) { String ret = filenames.get(s); if (ret == null) { ret = STSymbolManager.sharedInstance.getFilename(s, project); if (ret == null) { ret = "??"; //$NON-NLS-1$ } filenames.put(s, ret); } return ret; } public void setShouldDump(boolean shouldDump) { this.shouldDump = shouldDump; } public IProject getProject() { return this.project; } }