/******************************************************************************* * Copyright (c) 2008 Red Hat, Inc. * 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: * Elliott Baron <ebaron@redhat.com> - initial API and implementation *******************************************************************************/ package org.eclipse.linuxtools.internal.valgrind.massif; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import org.eclipse.linuxtools.internal.valgrind.massif.MassifSnapshot.SnapshotType; import org.eclipse.linuxtools.internal.valgrind.massif.MassifSnapshot.TimeUnit; import org.eclipse.linuxtools.valgrind.core.ValgrindParserUtils; import org.eclipse.osgi.util.NLS; public class MassifParser { private static final String COLON = ":"; //$NON-NLS-1$ private static final String SPACE = " "; //$NON-NLS-1$ private static final String EQUALS = "="; //$NON-NLS-1$ private static final String CMD = "cmd"; //$NON-NLS-1$ private static final String TIME_UNIT = "time_unit"; //$NON-NLS-1$ private static final String SNAPSHOT = "snapshot"; //$NON-NLS-1$ private static final String TIME = "time"; //$NON-NLS-1$ private static final String MEM_HEAP_B = "mem_heap_B"; //$NON-NLS-1$ private static final String MEM_HEAP_EXTRA_B = "mem_heap_extra_B"; //$NON-NLS-1$ private static final String MEM_STACKS_B = "mem_stacks_B"; //$NON-NLS-1$ private static final String HEAP_TREE = "heap_tree"; //$NON-NLS-1$ private static final String INSTRUCTIONS = "i"; //$NON-NLS-1$ private static final String MILLISECONDS = "ms"; //$NON-NLS-1$ private static final String BYTES = "B"; //$NON-NLS-1$ private static final String PEAK = "peak"; //$NON-NLS-1$ private static final String DETAILED = "detailed"; //$NON-NLS-1$ private static final String EMPTY = "empty"; //$NON-NLS-1$ private Integer pid; private MassifSnapshot[] snapshots; public MassifParser(File inputFile) throws IOException { ArrayList<MassifSnapshot> list = new ArrayList<>(); try (BufferedReader br = new BufferedReader(new FileReader(inputFile))){ String line; MassifSnapshot snapshot = null; String cmd = null; TimeUnit unit = null; int n = 0; // retrive PID from filename String filename = inputFile.getName(); pid = ValgrindParserUtils.parsePID(filename, MassifLaunchDelegate.OUT_PREFIX); // parse contents of file while ((line = br.readLine()) != null) { if (line.startsWith(CMD + COLON)){ cmd = ValgrindParserUtils.parseStrValue(line, COLON + SPACE); } else if (line.startsWith(TIME_UNIT + COLON)) { unit = parseTimeUnit(line); } else if (line.startsWith(SNAPSHOT)) { if (snapshot != null) { // this snapshot finished parsing list.add(snapshot); n++; } snapshot = new MassifSnapshot(n); snapshot.setCmd(cmd); snapshot.setUnit(unit); } else if (line.startsWith(TIME + EQUALS)) { snapshot.setTime(ValgrindParserUtils.parseLongValue(line, EQUALS)); } else if (line.startsWith(MEM_HEAP_B + EQUALS)) { snapshot.setHeapBytes(ValgrindParserUtils.parseLongValue(line, EQUALS)); } else if (line.startsWith(MEM_HEAP_EXTRA_B + EQUALS)) { snapshot.setHeapExtra(ValgrindParserUtils.parseLongValue(line, EQUALS)); } else if (line.startsWith(MEM_STACKS_B + EQUALS)) { snapshot.setStacks(ValgrindParserUtils.parseLongValue(line, EQUALS)); } else if (line.startsWith(HEAP_TREE + EQUALS)) { SnapshotType type = parseSnapshotType(line); snapshot.setType(type); switch (type) { case DETAILED: case PEAK: MassifHeapTreeNode node = parseTree(snapshot, null, br); node.setText(NLS.bind(Messages.getString("MassifParser.Snapshot_n"), n, node.getText())); // prepend snapshot number //$NON-NLS-1$ snapshot.setRoot(node); } } } if (snapshot != null) { // last snapshot that finished parsing list.add(snapshot); } snapshots = list.toArray(new MassifSnapshot[list.size()]); } } private MassifHeapTreeNode parseTree(MassifSnapshot snapshot, MassifHeapTreeNode parent, BufferedReader br) throws IOException { String line = br.readLine(); if (line == null) { throw new IOException(Messages.getString("MassifParser.Unexpected_EOF")); //$NON-NLS-1$ } line = line.trim(); // remove leading whitespace String[] parts = line.split(" "); //$NON-NLS-1$ // bounds checking so we can fail with a more informative error if (parts.length < 2) { ValgrindParserUtils.fail(line); } Integer numChildren = parseNumChildren(parts[0]); if (numChildren == null) { ValgrindParserUtils.fail(line); } Long numBytes = parseNumBytes(parts[1]); if (numBytes == null) { ValgrindParserUtils.fail(line); } double percentage; if (numBytes.intValue() == 0) { percentage = 0; } else { percentage = numBytes.doubleValue() / snapshot.getTotal() * 100; } MassifHeapTreeNode node; String address = null; String function = null; String filename = null; int lineNo = 0; if (parts[2].startsWith("0x")) { //$NON-NLS-1$ // we extend the above bounds checking if (parts.length < 3) { ValgrindParserUtils.fail(line); } // remove colon from address address = parts[2].substring(0, parts[2].length() - 1); function = parseFunction(parts[3], line); // Parse source file if specified Object[] subparts = ValgrindParserUtils.parseFilename(line); filename = (String) subparts[0]; lineNo = (Integer) subparts[1]; node = new MassifHeapTreeNode(parent, percentage, numBytes, address, function, filename, lineNo); } else { // concatenate the rest StringBuffer text = new StringBuffer(); for (int i = 2; i < parts.length; i++) { text.append(parts[i]); text.append(" "); //$NON-NLS-1$ } node = new MassifHeapTreeNode(parent, percentage, numBytes, text.toString().trim()); } for (int i = 0; i < numChildren.intValue(); i++) { node.addChild(parseTree(snapshot, node, br)); } return node; } private String parseFunction(String start, String line) throws IOException { String function = null; int ix = line.lastIndexOf('('); if (ix >= 0) { function = line.substring(line.indexOf(start), ix); } else { function = line.substring(line.indexOf(start)); } if (function != null) { function = function.trim(); } else { ValgrindParserUtils.fail(line); } return function; } private Long parseNumBytes(String string) { Long result = null; if (ValgrindParserUtils.isNumber(string)) { result = Long.parseLong(string); } return result; } /* * format is "n[0-9]+:" */ private Integer parseNumChildren(String string) { Integer result = null; if (string.length() >= 3) { String number = string.substring(1, string.length() - 1); if (ValgrindParserUtils.isNumber(number)) { result = Integer.parseInt(number); } } return result; } public Integer getPid() { return pid; } public MassifSnapshot[] getSnapshots() { return snapshots; } private SnapshotType parseSnapshotType(String line) throws IOException { SnapshotType result = null; String[] parts = line.split(EQUALS); if (parts.length > 1) { String type = parts[1]; if (type.equals(EMPTY)) { result = SnapshotType.EMPTY; } else if (type.equals(DETAILED)) { result = SnapshotType.DETAILED; } else if (type.equals(PEAK)) { result = SnapshotType.PEAK; } } if (result == null) { ValgrindParserUtils.fail(line); } return result; } private TimeUnit parseTimeUnit(String line) throws IOException { TimeUnit result = null; String[] parts = line.split(COLON + SPACE); if (parts.length > 1) { String type = parts[1]; if (type.equals(INSTRUCTIONS)) { result = TimeUnit.INSTRUCTIONS; } else if (type.equals(MILLISECONDS)) { result = TimeUnit.MILLISECONDS; } else if (type.equals(BYTES)) { result = TimeUnit.BYTES; } } if (result == null) { ValgrindParserUtils.fail(line); } return result; } }