1
/*******************************************************************************
2
* Copyright (c) 2008, 2011 Red Hat, Inc. and others.
3
* All rights reserved. This program and the accompanying materials
4
* are made available under the terms of the Eclipse Public License v1.0
5
* which accompanies this distribution, and is available at
6
* http://www.eclipse.org/legal/epl-v10.html
9
* Elliott Baron <ebaron@redhat.com> - initial API and implementation
10
* Martin Oberhuber (Wind River) - [360085] Fix valgrind problem marker lifecycle
11
*******************************************************************************/
12
package org.eclipse.linuxtools.internal.valgrind.launch;
15
import java.io.FileFilter;
16
import java.io.IOException;
18
import java.util.ArrayList;
19
import java.util.Arrays;
20
import java.util.List;
21
import java.util.Stack;
23
import org.eclipse.cdt.debug.core.CDebugUtils;
24
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
25
import org.eclipse.cdt.launch.AbstractCLaunchDelegate;
26
import org.eclipse.core.filesystem.URIUtil;
27
import org.eclipse.core.resources.IFile;
28
import org.eclipse.core.resources.IMarker;
29
import org.eclipse.core.resources.IProject;
30
import org.eclipse.core.resources.IResource;
31
import org.eclipse.core.resources.IWorkspaceRoot;
32
import org.eclipse.core.resources.ResourcesPlugin;
33
import org.eclipse.core.runtime.CoreException;
34
import org.eclipse.core.runtime.IPath;
35
import org.eclipse.core.runtime.IProgressMonitor;
36
import org.eclipse.core.runtime.NullProgressMonitor;
37
import org.eclipse.core.runtime.Path;
38
import org.eclipse.core.runtime.SubMonitor;
39
import org.eclipse.debug.core.DebugPlugin;
40
import org.eclipse.debug.core.ILaunch;
41
import org.eclipse.debug.core.ILaunchConfiguration;
42
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
43
import org.eclipse.debug.core.model.IProcess;
44
import org.eclipse.debug.core.model.ISourceLocator;
45
import org.eclipse.debug.core.sourcelookup.containers.LocalFileStorage;
46
import org.eclipse.debug.ui.DebugUITools;
47
import org.eclipse.debug.ui.sourcelookup.ISourceLookupResult;
48
import org.eclipse.linuxtools.internal.valgrind.core.CommandLineConstants;
49
import org.eclipse.linuxtools.internal.valgrind.core.LaunchConfigurationConstants;
50
import org.eclipse.linuxtools.internal.valgrind.core.ValgrindCommand;
51
import org.eclipse.linuxtools.internal.valgrind.core.ValgrindCoreParser;
52
import org.eclipse.linuxtools.internal.valgrind.core.ValgrindError;
53
import org.eclipse.linuxtools.internal.valgrind.core.ValgrindInfo;
54
import org.eclipse.linuxtools.internal.valgrind.core.ValgrindStackFrame;
55
import org.eclipse.linuxtools.internal.valgrind.ui.ValgrindUIPlugin;
56
import org.eclipse.linuxtools.internal.valgrind.ui.ValgrindViewPart;
57
import org.eclipse.linuxtools.valgrind.core.IValgrindMessage;
58
import org.eclipse.linuxtools.valgrind.launch.IValgrindLaunchDelegate;
59
import org.eclipse.linuxtools.valgrind.launch.IValgrindOutputDirectoryProvider;
60
import org.eclipse.osgi.util.NLS;
61
import org.osgi.framework.Version;
63
public class ValgrindLaunchConfigurationDelegate extends AbstractCLaunchDelegate {
65
protected static final String EMPTY_STRING = ""; //$NON-NLS-1$
66
protected static final String NO = "no"; //$NON-NLS-1$
67
protected static final String YES = "yes"; //$NON-NLS-1$
68
protected static final String EQUALS = "="; //$NON-NLS-1$
70
protected static final String LOG_FILE = CommandLineConstants.LOG_PREFIX + "%p.txt"; //$NON-NLS-1$
71
protected static final FileFilter LOG_FILTER = new FileFilter() {
72
public boolean accept(File pathname) {
73
return pathname.getName().startsWith(CommandLineConstants.LOG_PREFIX);
77
protected String toolID;
78
protected ValgrindCommand command;
79
protected IPath outputPath;
80
protected IValgrindLaunchDelegate dynamicDelegate;
81
protected ILaunchConfiguration config;
82
protected ILaunch launch;
83
protected IProcess process;
84
protected String launchStr;
85
protected Version valgrindVersion; // null if not used
88
public void launch(ILaunchConfiguration config, String mode,
89
ILaunch launch, IProgressMonitor m) throws CoreException {
91
m = new NullProgressMonitor();
94
SubMonitor monitor = SubMonitor.convert(m, Messages.getString("ValgrindLaunchConfigurationDelegate.Profiling_Local_CCPP_Application"), 10); //$NON-NLS-1$
95
// check for cancellation
96
if (monitor.isCanceled()) {
100
this.config = config;
101
this.launch = launch;
103
IProject project = CDebugUtils.verifyCProject(config).getProject();
104
command = getValgrindCommand();
106
// remove any output from previous run
107
ValgrindUIPlugin.getDefault().resetView();
108
// reset stored launch data
109
getPlugin().setCurrentLaunchConfiguration(null);
110
getPlugin().setCurrentLaunch(null);
112
String valgrindCommand= getValgrindCommand().getValgrindCommand();
113
// also ensure Valgrind version is usable
114
valgrindVersion = getPlugin().getValgrindVersion(project);
117
IPath exePath = CDebugUtils.verifyProgramPath(config);
118
String[] arguments = getProgramArgumentsArray(config);
119
File workDir = getWorkingDirectory(config);
120
if (workDir == null) {
121
workDir = new File(System.getProperty("user.home", ".")); //$NON-NLS-1$ //$NON-NLS-2$
124
// set output directory in config
125
IValgrindOutputDirectoryProvider provider = getPlugin().getOutputDirectoryProvider();
126
setOutputPath(config, provider.getOutputPath());
127
outputPath = verifyOutputPath(config);
128
// create/empty output directory
129
createDirectory(outputPath);
131
// tool that was launched
132
toolID = getTool(config);
133
// ask tool extension for arguments
134
dynamicDelegate = getDynamicDelegate(toolID);
135
String[] opts = getValgrindArgumentsArray(config);
137
// set the default source locator if required
138
setDefaultSourceLocator(launch, config);
140
ArrayList<String> cmdLine = new ArrayList<String>(1 + arguments.length);
141
cmdLine.add(valgrindCommand);
142
cmdLine.addAll(Arrays.asList(opts));
143
cmdLine.add(exePath.toOSString());
144
cmdLine.addAll(Arrays.asList(arguments));
145
String[] commandArray = cmdLine.toArray(new String[cmdLine.size()]);
146
boolean usePty = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_USE_TERMINAL, ICDTLaunchConfigurationConstants.USE_TERMINAL_DEFAULT);
149
// check for cancellation
150
if (monitor.isCanceled()) {
155
command.execute(commandArray, getEnvironment(config), workDir, valgrindCommand, usePty, project);
157
process = createNewProcess(launch, command.getProcess(), commandArray[0]);
158
// set the command line used
159
process.setAttribute(IProcess.ATTR_CMDLINE, command.getCommandLine());
160
while (!process.isTerminated()) {
164
// store these for use by other classes
165
getPlugin().setCurrentLaunchConfiguration(config);
166
getPlugin().setCurrentLaunch(launch);
168
// parse Valgrind logs
169
IValgrindMessage[] messages = parseLogs(outputPath);
171
// create launch summary string to distinguish this launch
172
launchStr = createLaunchStr();
175
ValgrindUIPlugin.getDefault().createView(launchStr, toolID);
177
ValgrindViewPart view = ValgrindUIPlugin.getDefault().getView();
178
view.setMessages(messages);
181
// pass off control to extender
182
dynamicDelegate.handleLaunch(config, launch, outputPath, monitor.newChild(2));
184
// initialize tool-specific part of view
185
dynamicDelegate.initializeView(view.getDynamicView(), launchStr, monitor.newChild(1));
188
ValgrindUIPlugin.getDefault().refreshView();
191
ValgrindUIPlugin.getDefault().showView();
193
} catch (IOException e) {
194
abort(Messages.getString("ValgrindLaunchConfigurationDelegate.Error_starting_process"), e, ICDTLaunchConfigurationConstants.ERR_INTERNAL_ERROR); //$NON-NLS-1$
196
} catch (InterruptedException e) {
203
protected IValgrindMessage[] parseLogs(IPath outputPath) throws IOException, CoreException {
204
List<IValgrindMessage> messages = new ArrayList<IValgrindMessage>();
206
for (File log : outputPath.toFile().listFiles(LOG_FILTER)) {
207
ValgrindCoreParser parser = new ValgrindCoreParser(log, launch);
208
IValgrindMessage[] results = parser.getMessages();
210
if (results.length == 0){
211
results = new IValgrindMessage[1];
212
results[0] = new ValgrindInfo(null, Messages.getString("ValgrindOutputView.No_output"), launch);
214
messages.addAll(Arrays.asList(results));
215
createMarkers(results);
218
return messages.toArray(new IValgrindMessage[messages.size()]);
221
protected void createMarkers(IValgrindMessage[] messages) throws CoreException {
222
// find the topmost stack frame within the workspace to annotate with marker
223
// traverse nested errors as well
224
Stack<IValgrindMessage> messageStack = new Stack<IValgrindMessage>();
225
messageStack.addAll(Arrays.asList(messages));
226
while (!messageStack.isEmpty()) {
227
IValgrindMessage message = messageStack.pop();
228
IMarker marker = null;
229
IValgrindMessage[] children = message.getChildren();
230
for (int i = 0; i < children.length; i++) {
231
// if we've found our marker we don't care about any further frames in this stack
232
if (children[i] instanceof ValgrindStackFrame && marker == null) {
233
ValgrindStackFrame frame = (ValgrindStackFrame) children[i];
234
if (frame.getLine() > 0) {
235
ISourceLocator locator = frame.getLaunch().getSourceLocator();
236
ISourceLookupResult result = DebugUITools.lookupSource(frame.getFile(), locator);
237
Object sourceElement = result.getSourceElement();
239
if (sourceElement != null) {
240
// Resolve IResource in case we get a LocalFileStorage object
241
if (sourceElement instanceof LocalFileStorage) {
242
IPath filePath = ((LocalFileStorage) sourceElement).getFullPath();
243
URI fileURI = URIUtil.toURI(filePath);
244
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
245
IFile[] files = root.findFilesForLocationURI(fileURI);
246
if (files.length > 0) {
247
// Take the first match
248
sourceElement = files[0];
252
if (sourceElement instanceof IResource) {
253
IResource resource = (IResource) sourceElement;
254
marker = resource.createMarker(ValgrindLaunchPlugin.MARKER_TYPE);
255
marker.setAttribute(IMarker.MESSAGE, message.getText());
256
marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
257
marker.setAttribute(IMarker.LINE_NUMBER, frame.getLine());
262
else if (children[i] instanceof ValgrindError) {
264
messageStack.push(children[i]);
270
protected IProcess createNewProcess(ILaunch launch, Process systemProcess, String programName) {
271
return DebugPlugin.newProcess(launch, systemProcess, renderProcessLabel(programName));
274
protected ValgrindCommand getValgrindCommand() {
275
return getPlugin().getValgrindCommand();
278
protected ValgrindLaunchPlugin getPlugin() {
279
return ValgrindLaunchPlugin.getDefault();
282
protected IValgrindLaunchDelegate getDynamicDelegate(String toolID) throws CoreException {
283
return getPlugin().getToolDelegate(toolID);
286
protected IPath verifyOutputPath(ILaunchConfiguration config) throws CoreException {
288
String strPath = config.getAttribute(LaunchConfigurationConstants.ATTR_INTERNAL_OUTPUT_DIR, (String) null);
289
if (strPath != null) {
290
result = Path.fromPortableString(strPath);
292
if (result == null) {
293
abort(Messages.getString("ValgrindLaunchConfigurationDelegate.Retrieving_location_failed"), null, ICDTLaunchConfigurationConstants.ERR_INTERNAL_ERROR); //$NON-NLS-1$
298
protected void setOutputPath(ILaunchConfiguration config, IPath outputPath) throws CoreException {
299
ILaunchConfigurationWorkingCopy wc = config.getWorkingCopy();
300
wc.setAttribute(LaunchConfigurationConstants.ATTR_INTERNAL_OUTPUT_DIR, outputPath.toPortableString());
304
protected void createDirectory(IPath path) throws IOException {
305
File outputDir = path.toFile();
307
if (outputDir.exists()) {
308
// delete any preexisting files
309
for (File outputFile : outputDir.listFiles()) {
310
if (outputFile.isFile() && !outputFile.delete()) {
311
throw new IOException(NLS.bind(Messages.getString("ValgrindOutputDirectory.Couldnt_delete"), outputFile.getAbsolutePath())); //$NON-NLS-1$
315
else if (!outputDir.mkdir()) {
316
throw new IOException(NLS.bind(Messages.getString("ValgrindOutputDirectory.Couldnt_create"), outputDir.getAbsolutePath())); //$NON-NLS-1$
320
protected String createLaunchStr() {
321
return config.getName() + " [" + getPlugin().getToolName(toolID) + "] " + process.getLabel(); //$NON-NLS-1$ //$NON-NLS-2$
324
protected String[] getValgrindArgumentsArray(ILaunchConfiguration config) throws CoreException {
325
ArrayList<String> opts = new ArrayList<String>();
326
opts.add(CommandLineConstants.OPT_TOOL + EQUALS + getPlugin().getToolName(toolID));
327
opts.add(CommandLineConstants.OPT_QUIET); // suppress uninteresting output
328
opts.add(CommandLineConstants.OPT_LOGFILE + EQUALS + outputPath.append(LOG_FILE).toOSString());
330
opts.add(CommandLineConstants.OPT_TRACECHILD + EQUALS + (config.getAttribute(LaunchConfigurationConstants.ATTR_GENERAL_TRACECHILD, LaunchConfigurationConstants.DEFAULT_GENERAL_TRACECHILD) ? YES : NO));
331
opts.add(CommandLineConstants.OPT_CHILDSILENT + EQUALS + YES); // necessary for parsing
332
opts.add(CommandLineConstants.OPT_FREERES + EQUALS + (config.getAttribute(LaunchConfigurationConstants.ATTR_GENERAL_FREERES, LaunchConfigurationConstants.DEFAULT_GENERAL_FREERES) ? YES : NO));
334
opts.add(CommandLineConstants.OPT_DEMANGLE + EQUALS + (config.getAttribute(LaunchConfigurationConstants.ATTR_GENERAL_DEMANGLE, LaunchConfigurationConstants.DEFAULT_GENERAL_DEMANGLE) ? YES : NO));
335
opts.add(CommandLineConstants.OPT_NUMCALLERS + EQUALS + config.getAttribute(LaunchConfigurationConstants.ATTR_GENERAL_NUMCALLERS, LaunchConfigurationConstants.DEFAULT_GENERAL_NUMCALLERS));
336
opts.add(CommandLineConstants.OPT_ERRLIMIT + EQUALS + (config.getAttribute(LaunchConfigurationConstants.ATTR_GENERAL_ERRLIMIT, LaunchConfigurationConstants.DEFAULT_GENERAL_ERRLIMIT) ? YES : NO));
337
opts.add(CommandLineConstants.OPT_BELOWMAIN + EQUALS + (config.getAttribute(LaunchConfigurationConstants.ATTR_GENERAL_BELOWMAIN, LaunchConfigurationConstants.DEFAULT_GENERAL_BELOWMAIN) ? YES : NO));
338
opts.add(CommandLineConstants.OPT_MAXFRAME + EQUALS + config.getAttribute(LaunchConfigurationConstants.ATTR_GENERAL_MAXFRAME, LaunchConfigurationConstants.DEFAULT_GENERAL_MAXFRAME));
341
if (valgrindVersion == null || valgrindVersion.compareTo(ValgrindLaunchPlugin.VER_3_4_0) >= 0) {
342
boolean useMainStack = config.getAttribute(LaunchConfigurationConstants.ATTR_GENERAL_MAINSTACK_BOOL, LaunchConfigurationConstants.DEFAULT_GENERAL_MAINSTACK_BOOL);
344
opts.add(CommandLineConstants.OPT_MAINSTACK + EQUALS + config.getAttribute(LaunchConfigurationConstants.ATTR_GENERAL_MAINSTACK, LaunchConfigurationConstants.DEFAULT_GENERAL_MAINSTACK));
349
if (valgrindVersion == null || valgrindVersion.compareTo(ValgrindLaunchPlugin.VER_3_6_0) >= 0) {
350
if (config.getAttribute(LaunchConfigurationConstants.ATTR_GENERAL_DSYMUTIL, LaunchConfigurationConstants.DEFAULT_GENERAL_DSYMUTIL) != LaunchConfigurationConstants.DEFAULT_GENERAL_DSYMUTIL)
351
opts.add(CommandLineConstants.OPT_DSYMUTIL + EQUALS + (config.getAttribute(LaunchConfigurationConstants.ATTR_GENERAL_DSYMUTIL, LaunchConfigurationConstants.DEFAULT_GENERAL_DSYMUTIL) ? YES : NO));
354
List<?> suppFiles = config.getAttribute(LaunchConfigurationConstants.ATTR_GENERAL_SUPPFILES, LaunchConfigurationConstants.DEFAULT_GENERAL_SUPPFILES);
355
for (Object strpath : suppFiles) {
356
IPath suppfile = getPlugin().parseWSPath((String) strpath);
357
if (suppfile != null) {
358
opts.add(CommandLineConstants.OPT_SUPPFILE + EQUALS + suppfile.toOSString());
361
opts.addAll(Arrays.asList(dynamicDelegate.getCommandArray(config, valgrindVersion, outputPath)));
363
String[] ret = new String[opts.size()];
364
return opts.toArray(ret);
367
protected String getTool(ILaunchConfiguration config) throws CoreException {
368
return config.getAttribute(LaunchConfigurationConstants.ATTR_TOOL, LaunchConfigurationConstants.DEFAULT_TOOL);
372
protected String getPluginID() {
373
return ValgrindLaunchPlugin.PLUGIN_ID;
377
public boolean finalLaunchCheck(ILaunchConfiguration configuration,
378
String mode, IProgressMonitor monitor) throws CoreException {
379
//Delete our own problem markers
380
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
381
root.deleteMarkers(ValgrindLaunchPlugin.MARKER_TYPE, true, IResource.DEPTH_INFINITE); //$NON-NLS-1$
382
return super.finalLaunchCheck(configuration, mode, monitor);