/******************************************************************************* * Copyright (c) 2009 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 * Alena Laskavaia - Bug 482947 - Valgrind Message API's: get rid of launch dependency *******************************************************************************/ package org.eclipse.linuxtools.internal.valgrind.core; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Stack; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.model.IPersistableSourceLocator; import org.eclipse.debug.core.model.ISourceLocator; import org.eclipse.debug.core.sourcelookup.IPersistableSourceLocator2; import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector; import org.eclipse.linuxtools.valgrind.core.CommandLineConstants; import org.eclipse.linuxtools.valgrind.core.IValgrindMessage; import org.eclipse.linuxtools.valgrind.core.ValgrindParserUtils; /** * Parser for paring valgrind generic output into array of hierarchical messages */ public class ValgrindCoreParser { private static final String AT = "at"; //$NON-NLS-1$ private static final String BY = "by"; //$NON-NLS-1$ private List<IValgrindMessage> messages; private int pid; private ILaunch launch; private ISourceLocator locator; /** * When using this method make sure locator passed to this method can * outlive disposal of launch object if it was derived from it, use * {@link #copyLaunchSourceLocator(ILaunch)} if needed * * @param inputFile * - file to parse * @param launch * - launch object, can be null * @param locator * - source locator * @throws IOException if file is not found or error reading it */ public ValgrindCoreParser(File inputFile, ILaunch launch, ISourceLocator locator) throws IOException { this.launch = launch; this.locator = locator; // keep track of nested messages and their corresponding indents Stack<IValgrindMessage> messageStack = new Stack<>(); Stack<Integer> indentStack = new Stack<>(); messages = new ArrayList<>(); try (BufferedReader br = new BufferedReader(new FileReader(inputFile))) { pid = ValgrindParserUtils.parsePID(inputFile.getName(), CommandLineConstants.LOG_PREFIX); String line; while ((line = br.readLine()) != null) { // remove PID string // might encounter warnings also #325130 // fixed #423371 - handle timestamp (e.g. ==00:00:00:01.175 52756728==) line = line.replaceFirst("==([\\d:\\.]+\\s)?\\d+==|\\*\\*\\d+\\*\\*", ""); //$NON-NLS-1$ //$NON-NLS-2$ int indent; for (indent = 0; indent < line.length() && line.charAt(indent) == ' '; indent++){} line = line.trim(); if (!line.isEmpty()) { /* * indent == 1 -> top level message * indent > 1 -> child message * indent == 0 -> should not occur */ if (indent == 1) { // top-level message, clear stacks IValgrindMessage message = getMessage(null, line); messages.add(message); messageStack.clear(); messageStack.push(message); indentStack.clear(); indentStack.push(indent); } else if (indent > 1) { /** * We assume that an indented child message has a * parent, but this may not be the case. * See BZ #360225 */ if (indentStack.isEmpty()) { // pretend this is a top level message IValgrindMessage message = getMessage(null, line); messages.add(message); messageStack.clear(); messageStack.push(message); indentStack.clear(); indentStack.push(1); } else { // find this message's parent while (indent <= indentStack.peek()) { messageStack.pop(); indentStack.pop(); } messageStack.push(getMessage(messageStack.peek(), line)); indentStack.push(indent); } } } } } } private IValgrindMessage getMessage(IValgrindMessage message, String line) { if (line.startsWith(AT) || line.startsWith(BY)) { Object[] parsed = ValgrindParserUtils.parseFilename(line); String filename = (String) parsed[0]; int lineNo = (Integer) parsed[1]; return new ValgrindStackFrame(message, line, launch, locator, filename, lineNo); } return new ValgrindError(message, line, launch, pid); } /** * Return messages from paring * @return all parsed messages * */ public IValgrindMessage[] getMessages() { return messages.toArray(new IValgrindMessage[messages.size()]); } /** * Constructor * @param inputFile - file to parse * @param launch - launch object can be null * @throws IOException if cannot open file */ public ValgrindCoreParser(File inputFile, ILaunch launch) throws IOException { this(inputFile, launch, copyLaunchSourceLocator(launch)); } /** * Return a safe source locator from launch object which won't be disposed if launch object is disposed * @param launch - launch object * @return source locator * */ public static ISourceLocator copyLaunchSourceLocator(ILaunch launch) { if (launch == null) return null; ISourceLocator sourceLocator = launch.getSourceLocator(); ILaunchConfiguration launchConfiguration = launch.getLaunchConfiguration(); // sourceLocator from launch object will be disposed when launch is // gone, // since we want to use it later we need to create a copy of it if (sourceLocator instanceof ISourceLookupDirector) { try { ISourceLookupDirector director = (ISourceLookupDirector) sourceLocator; String id = director.getId(); String memento = director.getMemento(); IPersistableSourceLocator sourceLocatorCopy = DebugPlugin.getDefault().getLaunchManager() .newSourceLocator(id); if (sourceLocatorCopy instanceof IPersistableSourceLocator2) { ((IPersistableSourceLocator2) sourceLocatorCopy).initializeFromMemento(memento, launchConfiguration); } else sourceLocatorCopy.initializeFromMemento(memento); return sourceLocatorCopy; } catch (CoreException e) { // ignore } } return sourceLocator; } }